diff --git a/agentscope-core/src/main/java/io/agentscope/core/ReActAgent.java b/agentscope-core/src/main/java/io/agentscope/core/ReActAgent.java index 2de490fa0..3622ded47 100644 --- a/agentscope-core/src/main/java/io/agentscope/core/ReActAgent.java +++ b/agentscope-core/src/main/java/io/agentscope/core/ReActAgent.java @@ -136,6 +136,7 @@ public class ReActAgent extends StructuredOutputCapableAgent { private final String sysPrompt; private final Model model; private final int maxIters; + private final GenerateOptions generateOptions; private final ExecutionConfig modelExecutionConfig; private final ExecutionConfig toolExecutionConfig; private final PlanNotebook planNotebook; @@ -157,6 +158,7 @@ private ReActAgent(Builder builder, Toolkit agentToolkit) { this.sysPrompt = builder.sysPrompt; this.model = builder.model; this.maxIters = builder.maxIters; + this.generateOptions = builder.generateOptions; this.modelExecutionConfig = builder.modelExecutionConfig; this.toolExecutionConfig = builder.toolExecutionConfig; this.planNotebook = builder.planNotebook; @@ -786,11 +788,14 @@ private List extractPendingToolCalls() { @Override protected GenerateOptions buildGenerateOptions() { - GenerateOptions.Builder builder = GenerateOptions.builder(); - if (modelExecutionConfig != null) { - builder.executionConfig(modelExecutionConfig); - } - return builder.build(); + // Build options with modelExecutionConfig (timeout, retry, etc.) + GenerateOptions executionOptions = + modelExecutionConfig != null + ? GenerateOptions.builder().executionConfig(modelExecutionConfig).build() + : null; + // Merge: user's generateOptions (temperature, topP, maxTokens, etc.) as base, + // executionOptions overrides executionConfig + return GenerateOptions.mergeOptions(executionOptions, generateOptions); } // ==================== Hook Notification Methods ==================== @@ -807,7 +812,8 @@ private Mono notifyHooks(T event) { } private Mono notifyPreReasoningEvent(List msgs) { - return notifyHooks(new PreReasoningEvent(this, model.getModelName(), null, msgs)); + return notifyHooks( + new PreReasoningEvent(this, model.getModelName(), buildGenerateOptions(), msgs)); } private Mono notifyPostReasoning(Msg msg) { @@ -977,6 +983,7 @@ public static class Builder { private Toolkit toolkit = new Toolkit(); private Memory memory = new InMemoryMemory(); private int maxIters = 10; + private GenerateOptions generateOptions; private ExecutionConfig modelExecutionConfig; private ExecutionConfig toolExecutionConfig; private final Set hooks = new LinkedHashSet<>(); @@ -1124,6 +1131,22 @@ public Builder enableMetaTool(boolean enableMetaTool) { return this; } + /** + * Sets the generation options for model API calls. + * + *

This configuration includes generation parameters such as temperature, topP, + * maxTokens, etc. When set, these options will be passed to the model and recorded + * in trace spans (e.g., gen_ai.request.temperature for OpenTelemetry). + * + * @param generateOptions The generation options (temperature, topP, maxTokens, etc.) + * @return This builder instance for method chaining + * @see GenerateOptions + */ + public Builder generateOptions(GenerateOptions generateOptions) { + this.generateOptions = generateOptions; + return this; + } + /** * Sets the execution configuration for model API calls. * diff --git a/agentscope-core/src/test/java/io/agentscope/core/agent/ReActAgentTest.java b/agentscope-core/src/test/java/io/agentscope/core/agent/ReActAgentTest.java index 46536c692..433b4a262 100644 --- a/agentscope-core/src/test/java/io/agentscope/core/agent/ReActAgentTest.java +++ b/agentscope-core/src/test/java/io/agentscope/core/agent/ReActAgentTest.java @@ -36,6 +36,7 @@ import io.agentscope.core.message.ToolUseBlock; import io.agentscope.core.model.ChatResponse; import io.agentscope.core.model.ChatUsage; +import io.agentscope.core.model.GenerateOptions; import io.agentscope.core.tool.Toolkit; import io.agentscope.core.util.JsonUtils; import java.time.Duration; @@ -101,6 +102,34 @@ void testInitialization() { assertTrue(agent.getMemory().getMessages().isEmpty(), "Memory should be empty initially"); } + @Test + @DisplayName("Should pass generateOptions (temperature, topP, maxTokens) to model for tracing") + void testGenerateOptionsPassedToModel() { + GenerateOptions options = + GenerateOptions.builder().temperature(0.7).topP(0.9).maxTokens(1000).build(); + + ReActAgent agentWithOptions = + ReActAgent.builder() + .name(TestConstants.TEST_REACT_AGENT_NAME) + .sysPrompt(TestConstants.DEFAULT_SYS_PROMPT) + .model(mockModel) + .toolkit(mockToolkit) + .memory(memory) + .generateOptions(options) + .build(); + + Msg userMsg = TestUtils.createUserMessage("User", TestConstants.TEST_USER_INPUT); + agentWithOptions + .call(userMsg) + .block(Duration.ofMillis(TestConstants.DEFAULT_TEST_TIMEOUT_MS)); + + GenerateOptions lastOptions = mockModel.getLastOptions(); + assertNotNull(lastOptions, "Model should receive GenerateOptions"); + assertEquals(0.7, lastOptions.getTemperature(), "Temperature should be passed to model"); + assertEquals(0.9, lastOptions.getTopP(), "TopP should be passed to model"); + assertEquals(1000, lastOptions.getMaxTokens(), "MaxTokens should be passed to model"); + } + @Test @DisplayName("Should generate simple text response") void testSimpleReply() {