Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
92fd3fd
feat: Introduce dedicated DTOs for Gemini API request and response st…
Aias00 Dec 16, 2025
b0b02b4
Update agentscope-extensions/agentscope-extensions-mem0/pom.xml
Aias00 Dec 16, 2025
16896c7
Update agentscope-core/src/main/java/io/agentscope/core/formatter/gem…
Aias00 Dec 16, 2025
364901f
Update agentscope-core/src/main/java/io/agentscope/core/model/GeminiC…
Aias00 Dec 16, 2025
d2bacf1
Update agentscope-core/src/main/java/io/agentscope/core/model/GeminiC…
Aias00 Dec 16, 2025
27ebc28
feat: Enhance Gemini integration by converting ThinkingBlock messages…
Aias00 Dec 16, 2025
ab9db03
feat: Add Gemini 3 compatibility adjustments, move API key to header,…
Aias00 Dec 16, 2025
5f0f0ab
Merge branch 'main' into feat/replace_genai_sdk
Aias00 Dec 16, 2025
8444d7b
feat: Introduce accessToken authentication and configurable base URL …
Aias00 Dec 16, 2025
19d0c78
feat: Add GeminiChatExample demonstrating basic Agent setup with Goog…
Aias00 Dec 17, 2025
4ec28cb
Merge branch 'refs/heads/main' into feat/replace_genai_sdk
Aias00 Dec 17, 2025
21dca17
refactor: Remove unnecessary whitespace in GeminiChatExample.java
Aias00 Dec 17, 2025
75375bb
Merge branch 'main' into feat/replace_genai_sdk
Aias00 Dec 17, 2025
dea16ab
feat: Add thoughtSignature support and Gemini 3 provider integration
Aias00 Dec 17, 2025
f323fd6
feat: Enhance Gemini integration with schema cleaning and support for…
Aias00 Dec 18, 2025
764bc52
feat: Improve code readability with formatting adjustments and enhanc…
Aias00 Dec 25, 2025
83094c8
Merge branch 'refs/heads/main' into feat/replace_genai_sdk
Aias00 Dec 25, 2025
3148701
feat: Update conversation history prompt and comment out unused Gemin…
Aias00 Dec 25, 2025
3f89137
feat(gemini): add support for custom system prompts in ReActAgent cre…
Aias00 Dec 25, 2025
c2deeb6
feat(gemini): enhance agent initialization with personalized prompts …
Aias00 Dec 25, 2025
efa68e7
Merge branch 'refs/heads/main' into feat/replace_genai_sdk
Aias00 Dec 25, 2025
da39722
feat(gemini): simplify response formatting by removing unnecessary pr…
Aias00 Dec 25, 2025
bc49486
Merge branch 'main' into feat/replace_genai_sdk
Aias00 Dec 25, 2025
a98f20f
feat(gemini): enhance signature handling and metadata integration in …
Aias00 Dec 25, 2025
da7880e
Merge branch 'main' into feat/replace_genai_sdk
Aias00 Dec 25, 2025
4367218
Merge branch 'main' into feat/replace_genai_sdk
Aias00 Dec 30, 2025
6bd25cc
Merge branch 'refs/heads/main' into feat/replace_genai_sdk
Aias00 Jan 4, 2026
65d8716
feat(gemini): add prompt feedback handling and system instruction sup…
Aias00 Jan 5, 2026
cbea7ab
Merge branch 'refs/heads/main' into feat/replace_genai_sdk
Aias00 Jan 5, 2026
809b2b6
refactor(gemini): remove unused ObjectMapper import and simplify logg…
Aias00 Jan 5, 2026
dd2a9a6
chore(gemini): update copyright year to 2026 and fix license URL
Aias00 Jan 5, 2026
79263fa
Merge branch 'main' into feat/replace_genai_sdk
Aias00 Jan 5, 2026
ebdf77b
chore(gemini): update copyright year to 2026 and fix license URL
Aias00 Jan 5, 2026
4d6a4e3
feat(gemini): preserve tool call roles in message formatting
Aias00 Jan 6, 2026
fe54012
feat(gemini): preserve tool call roles in message formatting
Aias00 Jan 6, 2026
191b3af
Merge branch 'refs/heads/main' into feat/replace_genai_sdk
Aias00 Jan 6, 2026
082b355
feat(gemini): refactor function call creation and update test assertions
Aias00 Jan 6, 2026
d5d47ed
feat(gemini): update Gemini SDK integration and enhance documentation
Aias00 Jan 6, 2026
cebe6e9
feat(gemini): rename GeminiNativeProvider to GeminiProvider and refac…
Aias00 Jan 6, 2026
b71070f
refactor(gemini): clean up whitespace in GeminiProvider.java
Aias00 Jan 6, 2026
fd7866e
Merge branch 'main' into feat/replace_genai_sdk
Aias00 Jan 12, 2026
af7d01a
test(genai): update formatter
Aias00 Jan 12, 2026
3122d67
test(genai): update converter
Aias00 Jan 12, 2026
b9cfd45
test(genai): update converter
Aias00 Jan 12, 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
5 changes: 0 additions & 5 deletions agentscope-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,6 @@
</exclusions>
</dependency>

<!-- Google Gemini Java SDK -->
<dependency>
<groupId>com.google.genai</groupId>
<artifactId>google-genai</artifactId>
</dependency>

<!-- Anthropic Java SDK -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,26 @@
*/
package io.agentscope.core.formatter.gemini;

import com.google.genai.types.Content;
import com.google.genai.types.GenerateContentConfig;
import com.google.genai.types.GenerateContentResponse;
import com.google.genai.types.ThinkingConfig;
import com.google.genai.types.Tool;
import com.google.genai.types.ToolConfig;
import io.agentscope.core.formatter.AbstractBaseFormatter;
import io.agentscope.core.formatter.gemini.dto.GeminiContent;
import io.agentscope.core.formatter.gemini.dto.GeminiGenerationConfig;
import io.agentscope.core.formatter.gemini.dto.GeminiGenerationConfig.GeminiThinkingConfig;
import io.agentscope.core.formatter.gemini.dto.GeminiRequest;
import io.agentscope.core.formatter.gemini.dto.GeminiResponse;
import io.agentscope.core.formatter.gemini.dto.GeminiTool;
import io.agentscope.core.formatter.gemini.dto.GeminiToolConfig;
import io.agentscope.core.message.Msg;
import io.agentscope.core.message.MsgRole;
import io.agentscope.core.message.ToolUseBlock;
import io.agentscope.core.model.ChatResponse;
import io.agentscope.core.model.GenerateOptions;
import io.agentscope.core.model.ToolChoice;
import io.agentscope.core.model.ToolSchema;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;

/**
* Formatter for Gemini Content Generation API.
Expand All @@ -48,8 +54,7 @@
* </ul>
*/
public class GeminiChatFormatter
extends AbstractBaseFormatter<
Content, GenerateContentResponse, GenerateContentConfig.Builder> {
extends AbstractBaseFormatter<GeminiContent, GeminiResponse, GeminiRequest> {

private final GeminiMessageConverter messageConverter;
private final GeminiResponseParser responseParser;
Expand All @@ -65,142 +70,219 @@ public GeminiChatFormatter() {
}

@Override
protected List<Content> doFormat(List<Msg> msgs) {
return messageConverter.convertMessages(msgs);
protected List<GeminiContent> doFormat(List<Msg> msgs) {
if (msgs == null) {
return new ArrayList<>();
}
int startIndex = computeStartIndex(msgs);

// Gemini API requires contents to start with "user" role
// If first remaining message is ASSISTANT (from another agent), convert it to USER
// Exception: Do not convert if it contains ToolUseBlock, as function calls must be MODEL
// role
if (startIndex < msgs.size()
&& msgs.get(startIndex).getRole() == MsgRole.ASSISTANT
&& msgs.get(startIndex).getContent().stream()
.noneMatch(block -> block instanceof ToolUseBlock)) {
List<GeminiContent> result = new ArrayList<>();

// Convert first ASSISTANT message to USER role for multi-agent compatibility
GeminiContent userContent = new GeminiContent();
userContent.setRole("user");
userContent.setParts(
messageConverter
.convertMessages(List.of(msgs.get(startIndex)))
.get(0)
.getParts());
result.add(userContent);

// Add remaining messages
if (startIndex + 1 < msgs.size()) {
result.addAll(
messageConverter.convertMessages(
msgs.subList(startIndex + 1, msgs.size())));
}

return result;
}

// Return remaining messages (excluding SYSTEM)
if (startIndex > 0 && startIndex < msgs.size()) {
return messageConverter.convertMessages(msgs.subList(startIndex, msgs.size()));
} else if (startIndex == 0) {
return messageConverter.convertMessages(msgs);
}

return new ArrayList<>();
}

/**
* Apply system instruction to the request if present.
*
* @param request The Gemini request to configure
* @param originalMessages The original message list (used to extract system prompt)
*/
public void applySystemInstruction(GeminiRequest request, List<Msg> originalMessages) {
GeminiContent systemInstruction = buildSystemInstruction(originalMessages);
if (systemInstruction != null) {
request.setSystemInstruction(systemInstruction);
} else {
request.setSystemInstruction(null);
}
}

@Override
public ChatResponse parseResponse(GenerateContentResponse response, Instant startTime) {
public ChatResponse parseResponse(GeminiResponse response, Instant startTime) {
return responseParser.parseResponse(response, startTime);
}

@Override
public void applyOptions(
GenerateContentConfig.Builder configBuilder,
GenerateOptions options,
GenerateOptions defaultOptions) {
GeminiRequest request, GenerateOptions options, GenerateOptions defaultOptions) {

// Ensure generation config exists
if (request.getGenerationConfig() == null) {
request.setGenerationConfig(new GeminiGenerationConfig());
}
GeminiGenerationConfig config = request.getGenerationConfig();

// Apply each option with fallback to defaultOptions
applyFloatOption(
GenerateOptions::getTemperature,
options,
defaultOptions,
configBuilder::temperature);
applyDoubleOption(
GenerateOptions::getTemperature, options, defaultOptions, config::setTemperature);

applyFloatOption(GenerateOptions::getTopP, options, defaultOptions, configBuilder::topP);
applyDoubleOption(GenerateOptions::getTopP, options, defaultOptions, config::setTopP);

// Apply topK (Gemini uses Float for topK)
applyIntegerAsFloatOption(
GenerateOptions::getTopK, options, defaultOptions, configBuilder::topK);
// topK: Integer in GenerateOptions -> Double in GeminiGenerationConfig
applyIntegerAsDoubleOption(
GenerateOptions::getTopK, options, defaultOptions, config::setTopK);

// Apply seed
applyLongAsIntOption(
GenerateOptions::getSeed, options, defaultOptions, configBuilder::seed);
// seed: Long in GenerateOptions -> Integer in GeminiGenerationConfig
applyLongAsIntegerOption(
GenerateOptions::getSeed, options, defaultOptions, config::setSeed);

applyIntegerOption(
GenerateOptions::getMaxTokens,
options,
defaultOptions,
configBuilder::maxOutputTokens);
GenerateOptions::getMaxTokens, options, defaultOptions, config::setMaxOutputTokens);

applyFloatOption(
applyDoubleOption(
GenerateOptions::getFrequencyPenalty,
options,
defaultOptions,
configBuilder::frequencyPenalty);
config::setFrequencyPenalty);

applyFloatOption(
applyDoubleOption(
GenerateOptions::getPresencePenalty,
options,
defaultOptions,
configBuilder::presencePenalty);
config::setPresencePenalty);

// Apply ThinkingConfig if either includeThoughts or thinkingBudget is set
Integer thinkingBudget =
getOptionOrDefault(options, defaultOptions, GenerateOptions::getThinkingBudget);

if (thinkingBudget != null) {
ThinkingConfig.Builder thinkingConfigBuilder = ThinkingConfig.builder();
thinkingConfigBuilder.includeThoughts(true);
thinkingConfigBuilder.thinkingBudget(thinkingBudget);
configBuilder.thinkingConfig(thinkingConfigBuilder.build());
GeminiThinkingConfig thinkingConfig = new GeminiThinkingConfig();
thinkingConfig.setIncludeThoughts(true);
thinkingConfig.setThinkingBudget(thinkingBudget);
config.setThinkingConfig(thinkingConfig);
}
}

/**
* Apply Float option with fallback logic.
* Apply Double option with fallback logic.
*/
private void applyFloatOption(
java.util.function.Function<GenerateOptions, Double> accessor,
private void applyDoubleOption(
Function<GenerateOptions, Double> accessor,
GenerateOptions options,
GenerateOptions defaultOptions,
java.util.function.Consumer<Float> setter) {
Consumer<Double> setter) {

Double value = getOptionOrDefault(options, defaultOptions, accessor);
if (value != null) {
setter.accept(value.floatValue());
setter.accept(value);
}
}

/**
* Apply Integer option with fallback logic.
* Apply Integer option as Double with fallback logic.
*/
private void applyIntegerOption(
java.util.function.Function<GenerateOptions, Integer> accessor,
private void applyIntegerAsDoubleOption(
Function<GenerateOptions, Integer> accessor,
GenerateOptions options,
GenerateOptions defaultOptions,
java.util.function.Consumer<Integer> setter) {
Consumer<Double> setter) {

Integer value = getOptionOrDefault(options, defaultOptions, accessor);
if (value != null) {
setter.accept(value);
setter.accept(value.doubleValue());
}
}

/**
* Apply Integer option as Float with fallback logic (for Gemini topK which requires Float).
* Apply Long option as Integer with fallback logic.
*/
private void applyIntegerAsFloatOption(
java.util.function.Function<GenerateOptions, Integer> accessor,
private void applyLongAsIntegerOption(
Function<GenerateOptions, Long> accessor,
GenerateOptions options,
GenerateOptions defaultOptions,
java.util.function.Consumer<Float> setter) {
Consumer<Integer> setter) {

Integer value = getOptionOrDefault(options, defaultOptions, accessor);
Long value = getOptionOrDefault(options, defaultOptions, accessor);
if (value != null) {
setter.accept(value.floatValue());
setter.accept(value.intValue());
}
}

/**
* Apply Long option as Integer with fallback logic (for Gemini seed which requires Integer).
* Apply Integer option with fallback logic.
*/
private void applyLongAsIntOption(
java.util.function.Function<GenerateOptions, Long> accessor,
private void applyIntegerOption(
Function<GenerateOptions, Integer> accessor,
GenerateOptions options,
GenerateOptions defaultOptions,
java.util.function.Consumer<Integer> setter) {
Consumer<Integer> setter) {

Long value = getOptionOrDefault(options, defaultOptions, accessor);
Integer value = getOptionOrDefault(options, defaultOptions, accessor);
if (value != null) {
setter.accept(value.intValue());
setter.accept(value);
}
}

@Override
public void applyTools(GenerateContentConfig.Builder configBuilder, List<ToolSchema> tools) {
Tool tool = toolsHelper.convertToGeminiTool(tools);
public void applyTools(GeminiRequest request, List<ToolSchema> tools) {
GeminiTool tool = toolsHelper.convertToGeminiTool(tools);
if (tool != null) {
configBuilder.tools(List.of(tool));
// Gemini API expects a list of tools, typically one tool object containing
// function declarations
request.setTools(List.of(tool));
}
}

@Override
public void applyToolChoice(
GenerateContentConfig.Builder configBuilder, ToolChoice toolChoice) {
ToolConfig toolConfig = toolsHelper.convertToolChoice(toolChoice);
public void applyToolChoice(GeminiRequest request, ToolChoice toolChoice) {
GeminiToolConfig toolConfig = toolsHelper.convertToolChoice(toolChoice);
if (toolConfig != null) {
configBuilder.toolConfig(toolConfig);
request.setToolConfig(toolConfig);
}
}

private int computeStartIndex(List<Msg> msgs) {
if (msgs == null || msgs.isEmpty()) {
return 0;
}
return msgs.get(0).getRole() == MsgRole.SYSTEM ? 1 : 0;
}

private GeminiContent buildSystemInstruction(List<Msg> msgs) {
if (msgs == null || msgs.isEmpty()) {
return null;
}

Msg first = msgs.get(0);
if (first.getRole() != MsgRole.SYSTEM) {
return null;
}

List<GeminiContent> converted = messageConverter.convertMessages(List.of(first));
return converted.isEmpty() ? null : converted.get(0);
}
}
Loading
Loading