Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,37 +1,36 @@
name: SonarCloud
name: SonarQube
on:
push:
branches:
- master
pull_request:
types: [ opened, synchronize, reopened ]
types: [opened, synchronize, reopened]
jobs:
build:
name: Build and analyze
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 17
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'zulu' # Alternative distribution options are available
- name: Cache SonarCloud packages
uses: actions/cache@v3
- name: Cache SonarQube packages
uses: actions/cache@v4
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Gradle packages
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
restore-keys: ${{ runner.os }}-gradle
- name: Build and analyze
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: ./gradlew build sonar --info
run: ./gradlew build sonar --info
60 changes: 30 additions & 30 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,42 +47,42 @@ jobs:
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support

steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Checkout repository
uses: actions/checkout@v4

- uses: actions/setup-java@v1
with:
java-version: 17
- uses: actions/setup-java@v1
with:
java-version: 17

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.

# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
queries: security-extended
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
queries: security-extended


# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3

# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun

# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.

# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
23 changes: 21 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# 1.1.0
# 2.0.0

- **BREAKING**: Major refactor of message handling for chat completions:
- The `nl.dannyj.mistral.models.completion.Message` class has been removed.
- Introduced `nl.dannyj.mistral.models.completion.message.ChatMessage` as an abstract base class for messages.
- New concrete message classes: `SystemMessage`, `UserMessage`, `AssistantMessage`, `ToolMessage`.
- Message content is now represented by `List<nl.dannyj.mistral.models.completion.content.ContentChunk>` (e.g., `TextChunk`, `ImageURLChunk`) to support multi-modal inputs.
- `ChatCompletionRequest` now expects `List<ChatMessage>`.
- Streaming: `DeltaChoice` now contains a `nl.dannyj.mistral.models.completion.message.DeltaMessage` object which holds the actual delta (`role`, `content`, `toolCalls`).
- **BREAKING**: Changes to `nl.dannyj.mistral.models.completion.Choice`:
- The `finishReason` field type changed from `String` to `nl.dannyj.mistral.models.completion.FinishReason` (enum).
- The `logProbs` field has been removed.
- **BREAKING**: `encodingFormat` field removed from `EmbeddingRequest` as it is no longer supported by the API.
- **BREAKING**: The `nl.dannyj.mistral.models.model.ModelPermission` class has been removed.
- **BREAKING**: Package changes for core classes:
- `MessageChunk` moved to `nl.dannyj.mistral.models.completion.message.MessageChunk`.
- `MessageRole` moved to `nl.dannyj.mistral.models.completion.message.MessageRole`.
- Added support for function calling (tools).
- Added `nl.dannyj.mistral.models.model.ModelCapabilities` to provide model capability information (e.g., `supportsVision()`, `supportsFunctionCalling()`).
- Increased default read timeout to 120 seconds.
- Made it easier to configure timeouts ([issue #7](https://github.com/Dannyj1/mistral-java-client/issues/7))
- Update jackson-databind from 2.16.1 to 2.18.3
- Update jakarta.validation-api from 3.1.0-M1 to 3.1.1
- Update okio from 3.8.0 to 3.11.0
- Update okio from 3.8.0 to 3.11.0
2 changes: 1 addition & 1 deletion CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within
Expand Down
225 changes: 225 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
# Migration Guide: Updating Message Handling

This guide outlines the necessary changes to migrate your code from the previous message handling structure (using the
`Message` class) to the new structure introduced to support multi-modal content and
tool calls, based on the abstract `ChatMessage` class.

## Core Changes

1. **Message Class Removed:** The old `Message` class has been removed.
2. **ChatMessage Hierarchy:** A new abstract base class `ChatMessage` has been
introduced. Specific message types now extend this class:
- `SystemMessage`
- `UserMessage`
- `AssistantMessage`
- `ToolMessage`
3. **Multi-Modal Content:** Messages (primarily `UserMessage`, `AssistantMessage`, `ToolMessage`) now use a
`List<ContentChunk>` for their `content` field to support text, images, etc. The
`ContentChunk` interface has implementations like `TextChunk`,
`ImageURLChunk`, etc.
- `getTextContent()` helper methods were introduced to prevent having to loop over `ContentChunk`s to find `TextChunk`s
4. **Tool Calls:** `AssistantMessage` now includes a `List<ToolCall> toolCalls` field, and a `ToolMessage` message type was introduced to support function calling/tools.
5. **Streaming Deltas:** The `DeltaChoice` class (used in streaming responses via `MessageChunk`) now contains a
`DeltaMessage` object (accessible via `getDelta()`). This `DeltaMessage`
object, in turn, holds the partial delta fields: `role`, `content` (as `List<ContentChunk>`), and `toolCalls`.

## Migration Steps

### 1. Update `ChatCompletionRequest` Message List

The `messages` field in `ChatCompletionRequest` now expects `List<ChatMessage>` instead of `List<Message>`.

**Before:**

```java
import nl.dannyj.mistral.models.completion.Message;

// ...
List<Message> messages = ...;
ChatCompletionRequest request = ChatCompletionRequest.builder()
.messages(messages)
// ...
.build();
```

**After:**

```java
import nl.dannyj.mistral.models.completion.ChatMessage;

// ...
List<ChatMessage> messages = ...; // See MessageListBuilder changes below
ChatCompletionRequest request = ChatCompletionRequest.builder()
.messages(messages)
// ...
.build();
```

### 2. Update `MessageListBuilder` Usage

The `MessageListBuilder` now works with `ChatMessage` and its subclasses.

**Before:**

```java
import nl.dannyj.mistral.models.completion.Message;
import nl.dannyj.mistral.builders.MessageListBuilder;

// ...
List<Message> messages = new MessageListBuilder()
.system("System prompt")
.user("User query")
.assistant("Assistant response")
.build();
```

**After:**

```java
import nl.dannyj.mistral.models.completion.ChatMessage;
import nl.dannyj.mistral.builders.MessageListBuilder;

// ...
List<ChatMessage> messages = new MessageListBuilder()
.system("System prompt") // Creates SystemMessage
.user("User query") // Creates UserMessage
.assistant("Assistant response") // Creates AssistantMessage (text only)
// .assistant(listOfToolCalls) // Use this overload for tool calls
// .tool("Tool result", "tool_call_id_123") // Creates ToolMessage
.build();

// Alternatively, add pre-constructed messages:
// messages = new MessageListBuilder()
// .message(new SystemMessage("System prompt"))
// .message(new UserMessage("User query"))
// .message(AssistantMessage.fromText("Assistant response")) // Factory method
// .build();
```

### 3. Update Handling of Non-Streaming Responses (`ChatCompletionResponse`)

The `message` field within the `Choice` object (obtained from `ChatCompletionResponse.getChoices()`) is now specifically
an `AssistantMessage`.

**Before:**

```java
ChatCompletionResponse response = client.createChatCompletion(request);
Message responseMsg = response.getChoices().get(0).getMessage();
String content = responseMsg.getContent();
System.out.println(content);
```

**After:**

```java
import nl.dannyj.mistral.models.completion.message.AssistantMessage;
import nl.dannyj.mistral.models.completion.content.ContentChunk;
import nl.dannyj.mistral.models.completion.content.TextChunk;

// ...
ChatCompletionResponse response = client.createChatCompletion(request);
AssistantMessage responseMsg = response.getChoices().get(0).getMessage();

// Extract text content (handle potential multi-modal content)
String textContent = "";
if (responseMsg.getContent() != null) {
for (ContentChunk chunk : responseMsg.getContent()) {
if (chunk instanceof TextChunk textChunk) {
textContent += textChunk.getText(); // Append text from TextChunks
}
// Handle other chunk types (ImageURLChunk, etc.) if necessary
}
}
System.out.println(textContent);

// Check for tool calls if applicable
if (responseMsg.getToolCalls() != null) {
// Process tool calls
}
```

### 4. Update Handling of Streaming Responses (`ChatCompletionChunkCallback`)

The `onChunkReceived` method receives `MessageChunk`. The structure within `MessageChunk.getChoices().get(0)` (which is
a `DeltaChoice`) has changed. Access `role`, `content`, and `toolCalls` directly from the `DeltaChoice` object.

**Before:**

```java
client.createChatCompletionStream(request, new ChatCompletionChunkCallback() {
@Override
public void onChunkReceived(MessageChunk chunk) {
// Old structure assumed delta was nested in a Message object
String contentDelta = chunk.getChoices().get(0).getMessage().getContent();
if (contentDelta != null) {
System.out.print(contentDelta);
}
}
// ... onComplete, onError
});
```

**After:**

```java
import nl.dannyj.mistral.models.completion.DeltaChoice;
import nl.dannyj.mistral.models.completion.message.DeltaMessage; // <-- Add this import
import nl.dannyj.mistral.models.completion.message.MessageChunk; // <-- Updated import path
import nl.dannyj.mistral.models.completion.content.ContentChunk;
import nl.dannyj.mistral.models.completion.content.TextChunk;
// ...
client.createChatCompletionStream(request, new ChatCompletionChunkCallback() {
@Override
public void onChunkReceived(MessageChunk chunk) {
if (chunk.getChoices() == null || chunk.getChoices().isEmpty()) return;

DeltaChoice deltaChoice = chunk.getChoices().get(0);
DeltaMessage delta = deltaChoice.getDelta(); // <-- Get the DeltaMessage

// Check for text content delta using the convenience method
String textDelta = delta.getTextContent();

if (textDelta != null) {
System.out.print(textDelta);
}

}
// ... onComplete, onError
});
```

### 5. Update Handling of `Choice` Object in `ChatCompletionResponse`

The `Choice` class, which is part of the `ChatCompletionResponse.getChoices()`, has the following breaking changes:

#### `finishReason` Type Change
- The `finishReason` field is now of type `FinishReason` (an enum) instead of a raw `String`.
- **Before:** `String finishReason = choice.getFinishReason();`
- **After:** `FinishReason finishReason = choice.getFinishReason();`
- To get the underlying string value (e.g., "stop", "length"), you can call `finishReason.getReason()`.

#### Example: Handling `finishReason` (within the context of iterating choices from `ChatCompletionResponse`)

**Before(inside loop processing `choice`):**
```java
String reason = choice.getFinishReason();
if ("stop".equals(reason)) {
// ...
}
```

**After(inside loop processing `choice`):**
```java
import nl.dannyj.mistral.models.completion.FinishReason; // Ensure import
// ...
FinishReason reason = choice.getFinishReason();
if (reason == FinishReason.STOP) {
// ...
}
// Or, to compare with the string value:
// if ("stop".equals(reason.getReason())) { ... }
```

#### `logProbs` Field Removal
- The `logProbs` field (previously a `String`) has been removed from the `Choice` class as it is no longer returned by the API.
Loading