Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
12fc3e0
Add comprehensive Ollama integration for AgentScope
devnomad-byte Dec 29, 2025
0f4448e
Merge branch 'agentscope-ai:main' into main
devnomad-byte Dec 29, 2025
9d2e4c0
fix: format issues for Spotless check
devnomad-byte Dec 29, 2025
e3b05f7
Merge branch 'main' of https://github.com/devnomad-byte/agentscope-java
devnomad-byte Dec 29, 2025
0712df0
fix: replace wildcard imports with specific imports
devnomad-byte Dec 29, 2025
334db73
feat: add OllamaEmbeddingRequest DTO for embedding API requests
devnomad-byte Dec 29, 2025
f097fe5
fix(test): initialize mocks and fix Mockito matchers in OllamaHttpCli…
devnomad-byte Dec 29, 2025
b823939
fix(test): initialize mocks and fix Mockito matchers in OllamaHttpCli…
devnomad-byte Dec 29, 2025
788bbcd
style: apply spotless to OllamaHttpClientTest
devnomad-byte Dec 29, 2025
a4efdef
fix: wrap runtime exceptions during JSON serialization in OllamaHttpC…
devnomad-byte Dec 29, 2025
77cd1ee
OllamaHttpException
devnomad-byte Dec 29, 2025
7339f21
push OllamaHttpClient
devnomad-byte Dec 30, 2025
e01be4e
delete description Email
devnomad-byte Dec 30, 2025
4b68484
delete email
devnomad-byte Dec 30, 2025
3e9c1ec
fix(ci): 添加audio.mp3占位文件以修复CI构建错误
devnomad-byte Dec 30, 2025
2031981
Update .licenserc.yaml
devnomad-byte Dec 30, 2025
5ef69f6
Update ThinkOption.java
devnomad-byte Dec 30, 2025
6fef4e5
Merge branch 'main' into main
devnomad-byte Dec 30, 2025
2ef0324
Remove non-standard Javadoc annotations
devnomad-byte Dec 31, 2025
93f1f6f
delete -Djdk.net.URLClassPath.disableClassPathURLCheck=true
devnomad-byte Dec 31, 2025
4fc4ffb
Merge branch 'main' into main
devnomad-byte Dec 31, 2025
ac6e042
delete audio.mp3 image.png
devnomad-byte Dec 31, 2025
067f6f9
Merge branch 'main' of https://github.com/devnomad-byte/agentscope-java
devnomad-byte Dec 31, 2025
ab8e0a2
Add OllamaChatModel behavior consistency tests
devnomad-byte Jan 5, 2026
302f758
Merge branch 'agentscope-ai:main' into main
devnomad-byte Jan 5, 2026
6e6c572
Add OllamaChatModel behavior consistency tests
devnomad-byte Jan 5, 2026
dc1628b
update copyright year from 2024-2025 to 2024-2026
devnomad-byte Jan 5, 2026
f4ad21f
Update OllamaOptions.java
devnomad-byte Jan 5, 2026
62d3111
Update MediaUtilsTest.java
devnomad-byte Jan 5, 2026
8f8f658
Update MediaUtilsTest.java
devnomad-byte Jan 5, 2026
f40a861
Merge branch 'main' into main
devnomad-byte Jan 5, 2026
57abb76
Delete OllamaChatModelBehaviorTest.java
devnomad-byte Jan 5, 2026
3f75466
Merge branch 'main' of https://github.com/devnomad-byte/agentscope-java
devnomad-byte Jan 5, 2026
1a31f0c
Merge branch 'main' into main
devnomad-byte Jan 5, 2026
43840bf
replace fully qualified names with imports
devnomad-byte Jan 6, 2026
1b8bf74
remove fully qualified class references in agentScope tests
devnomad-byte Jan 6, 2026
c11ff4d
Merge branch 'main' into main
devnomad-byte Jan 6, 2026
8f6409c
Fix Ollama formatter alignment and add NDJSON support for JdkHttpTran…
devnomad-byte Jan 6, 2026
5934419
Add NDJSON support to JdkHttpTransport and unify transport constants
devnomad-byte Jan 7, 2026
b7b5d8a
Merge branch 'main' into main
devnomad-byte Jan 7, 2026
e4f715f
Unify JSON processing to JsonUtils utility class
devnomad-byte Jan 7, 2026
aced3d2
Merge branch 'main' into main
devnomad-byte Jan 7, 2026
ef8f274
Unify Logger usage in Ollama classes
devnomad-byte Jan 7, 2026
a7f2a8c
Unify Logger usage in Ollama classes
devnomad-byte Jan 7, 2026
49726f9
Update OllamaTextEmbeddingEmbedTest.java
devnomad-byte Jan 7, 2026
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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright 2024-2026 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.agentscope.core.formatter.ollama;

import io.agentscope.core.formatter.ollama.dto.OllamaMessage;
import io.agentscope.core.message.Base64Source;
import io.agentscope.core.message.ContentBlock;
import io.agentscope.core.message.ImageBlock;
import io.agentscope.core.message.Msg;
import io.agentscope.core.message.TextBlock;
import io.agentscope.core.message.ToolResultBlock;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Merges multi-agent conversation messages for Ollama API.
*
*/
public class OllamaConversationMerger {
private static final Logger log = LoggerFactory.getLogger(OllamaConversationMerger.class);
private static final String HISTORY_START_TAG = "<history>";
private static final String HISTORY_END_TAG = "</history>";

private final String conversationHistoryPrompt;

public OllamaConversationMerger(String conversationHistoryPrompt) {
this.conversationHistoryPrompt = conversationHistoryPrompt;
}

public OllamaMessage mergeToMessage(
List<Msg> msgs,
Function<Msg, String> nameExtractor,
Function<List<ContentBlock>, String> toolResultConverter,
String historyPrompt) {

StringBuilder textAccumulator = new StringBuilder();
if (historyPrompt != null && !historyPrompt.isEmpty()) {
textAccumulator.append(historyPrompt);
}
textAccumulator.append(HISTORY_START_TAG).append("\n");

List<String> images = new ArrayList<>();

for (Msg msg : msgs) {
String name = nameExtractor.apply(msg);

for (ContentBlock block : msg.getContent()) {
if (block instanceof TextBlock) {
textAccumulator
.append(name)
.append(": ")
.append(((TextBlock) block).getText())
.append("\n");
} else if (block instanceof ImageBlock) {
ImageBlock imageBlock = (ImageBlock) block;
if (imageBlock.getSource() instanceof Base64Source) {
Base64Source source = (Base64Source) imageBlock.getSource();
images.add(source.getData());
textAccumulator.append(name).append(": [Image]\n");
} else {
log.warn(
"URL image source not yet supported for Ollama, skipping image"
+ " block in merger");
textAccumulator.append(name).append(": [Image - processing failed]\n");
}
} else if (block instanceof ToolResultBlock) {
// Tool results in history are usually just appended as text
ToolResultBlock toolResult = (ToolResultBlock) block;

// Simplify: Just append tool result string.
Object output = toolResult.getOutput();
String resultText =
output instanceof String ? (String) output : String.valueOf(output);

textAccumulator
.append(name)
.append(" (")
.append(toolResult.getName())
.append("): ")
.append(resultText)
.append("\n");
}
}
}

textAccumulator.append(HISTORY_END_TAG);

OllamaMessage message = new OllamaMessage();
message.setRole("user"); // Merged history is typically treated as user input context
message.setContent(textAccumulator.toString());
if (!images.isEmpty()) {
message.setImages(images);
}

return message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2024-2026 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.agentscope.core.formatter.ollama;

import io.agentscope.core.formatter.MediaUtils;
import io.agentscope.core.message.Base64Source;
import io.agentscope.core.message.ImageBlock;
import io.agentscope.core.message.Source;
import io.agentscope.core.message.URLSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Handles media content conversion for Ollama API.
* Converts ImageBlock to Base64 strings as required by Ollama.
*
*/
public class OllamaMediaConverter {

private static final Logger log = LoggerFactory.getLogger(OllamaMediaConverter.class);

/**
* Convert ImageBlock to Base64 string for Ollama API.
*
* <p>Ollama API expects an array of base64-encoded strings for images.
* This method handles:
* <ul>
* <li>Base64 sources → Returns raw base64 data</li>
* <li>Local file URLs → Reads file and converts to base64</li>
* <li>Remote URLs → Downloads content and converts to base64</li>
* </ul>
*
* @param imageBlock The image block to convert
* @return Base64 encoded string of the image
* @throws Exception If conversion, file reading, or download fails
*/
public String convertImageBlockToBase64(ImageBlock imageBlock) throws Exception {
Source source = imageBlock.getSource();

if (source instanceof Base64Source base64Source) {
// Ollama expects raw base64 string without data URI prefix
return base64Source.getData();
} else if (source instanceof URLSource urlSource) {
String url = urlSource.getUrl();
MediaUtils.validateImageExtension(url);

if (MediaUtils.isLocalFile(url)) {
// Read local file to base64
// Remove file:// prefix if present for local path
String path = url.startsWith("file://") ? url.substring(7) : url;
return MediaUtils.fileToBase64(path);
} else {
// Download remote URL to base64
return MediaUtils.downloadUrlToBase64(url);
}
} else {
throw new IllegalArgumentException("Unsupported source type: " + source.getClass());
}
}
}
Loading
Loading