From 055e7399e1ae2f6f2c666796b97dc2a9e09c7db1 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Wed, 29 Apr 2026 18:22:39 +0800 Subject: [PATCH] feat(react-agent-dashscope): add DashScope entry and DashScopeSpec DashScope.of(modelName) returns DashScopeSpec with a thinking sub-builder using DashScope's native budget term. Co-Authored-By: Claude Opus 4.7 --- .../liteflow/agent/dashscope/DashScope.java | 8 +++ .../agent/dashscope/DashScopeSpec.java | 67 +++++++++++++++++++ .../agent/dashscope/DashScopeThinking.java | 9 +++ .../agent/dashscope/DashScopeEntryTest.java | 41 ++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 liteflow-react-agent/liteflow-react-agent-dashscope/src/main/java/com/yomahub/liteflow/agent/dashscope/DashScope.java create mode 100644 liteflow-react-agent/liteflow-react-agent-dashscope/src/main/java/com/yomahub/liteflow/agent/dashscope/DashScopeSpec.java create mode 100644 liteflow-react-agent/liteflow-react-agent-dashscope/src/main/java/com/yomahub/liteflow/agent/dashscope/DashScopeThinking.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/agent/dashscope/DashScopeEntryTest.java diff --git a/liteflow-react-agent/liteflow-react-agent-dashscope/src/main/java/com/yomahub/liteflow/agent/dashscope/DashScope.java b/liteflow-react-agent/liteflow-react-agent-dashscope/src/main/java/com/yomahub/liteflow/agent/dashscope/DashScope.java new file mode 100644 index 000000000..70d23e2eb --- /dev/null +++ b/liteflow-react-agent/liteflow-react-agent-dashscope/src/main/java/com/yomahub/liteflow/agent/dashscope/DashScope.java @@ -0,0 +1,8 @@ +package com.yomahub.liteflow.agent.dashscope; + +public final class DashScope { + private DashScope() {} + public static DashScopeSpec of(String modelName) { + return new DashScopeSpec(modelName); + } +} diff --git a/liteflow-react-agent/liteflow-react-agent-dashscope/src/main/java/com/yomahub/liteflow/agent/dashscope/DashScopeSpec.java b/liteflow-react-agent/liteflow-react-agent-dashscope/src/main/java/com/yomahub/liteflow/agent/dashscope/DashScopeSpec.java new file mode 100644 index 000000000..0d79b38b9 --- /dev/null +++ b/liteflow-react-agent/liteflow-react-agent-dashscope/src/main/java/com/yomahub/liteflow/agent/dashscope/DashScopeSpec.java @@ -0,0 +1,67 @@ +package com.yomahub.liteflow.agent.dashscope; + +import com.yomahub.liteflow.agent.model.CredentialResolver; +import com.yomahub.liteflow.agent.model.ModelSpec; +import com.yomahub.liteflow.property.agent.AgentConfig; +import com.yomahub.liteflow.property.agent.PlatformCredential; +import io.agentscope.core.model.DashScopeChatModel; +import io.agentscope.core.model.GenerateOptions; +import io.agentscope.core.model.Model; + +import java.util.function.Consumer; + +public class DashScopeSpec extends ModelSpec { + + private final String modelName; + private Integer thinkingBudget; + + public DashScopeSpec(String modelName) { this.modelName = modelName; } + + public DashScopeSpec thinking(Consumer c) { + DashScopeThinking t = new DashScopeThinking(); + c.accept(t); + this.thinkingBudget = t.getBudget(); + return this; + } + + public String getModelName() { return modelName; } + public Integer getThinkingBudget() { return thinkingBudget; } + + @Override + public Model resolve(AgentConfig cfg) { + PlatformCredential cred = CredentialResolver.requireFirstClass( + cfg.getDashscope(), "liteflow.agent.dashscope"); + + DashScopeChatModel.Builder builder = DashScopeChatModel.builder() + .apiKey(cred.getApiKey()) + .modelName(modelName); + GenerateOptions options = buildGenerateOptions(); + if (options != null) { + builder.defaultOptions(options); + } + if (getStream() != null) { + builder.stream(getStream()); + } + if (thinkingBudget != null) { + builder.enableThinking(true); + } + return builder.build(); + } + + private GenerateOptions buildGenerateOptions() { + if (getTemperature() == null && getTopP() == null && getTopK() == null + && getMaxTokens() == null && getSeed() == null + && getCacheControl() == null && thinkingBudget == null) { + return null; + } + GenerateOptions.Builder b = GenerateOptions.builder(); + if (getTemperature() != null) b.temperature(getTemperature()); + if (getTopP() != null) b.topP(getTopP()); + if (getTopK() != null) b.topK(getTopK()); + if (getMaxTokens() != null) b.maxTokens(getMaxTokens()); + if (getSeed() != null) b.seed(getSeed()); + if (getCacheControl() != null) b.cacheControl(getCacheControl()); + if (thinkingBudget != null) b.thinkingBudget(thinkingBudget); + return b.build(); + } +} diff --git a/liteflow-react-agent/liteflow-react-agent-dashscope/src/main/java/com/yomahub/liteflow/agent/dashscope/DashScopeThinking.java b/liteflow-react-agent/liteflow-react-agent-dashscope/src/main/java/com/yomahub/liteflow/agent/dashscope/DashScopeThinking.java new file mode 100644 index 000000000..253c5ff26 --- /dev/null +++ b/liteflow-react-agent/liteflow-react-agent-dashscope/src/main/java/com/yomahub/liteflow/agent/dashscope/DashScopeThinking.java @@ -0,0 +1,9 @@ +package com.yomahub.liteflow.agent.dashscope; + +/** DashScope(通义千问)thinking 子构建器,沿用 thinking_budget 术语。 */ +public final class DashScopeThinking { + private Integer budget; + + public DashScopeThinking budget(int tokens) { this.budget = tokens; return this; } + public Integer getBudget() { return budget; } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/agent/dashscope/DashScopeEntryTest.java b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/agent/dashscope/DashScopeEntryTest.java new file mode 100644 index 000000000..de7817534 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-react-agent/src/test/java/com/yomahub/liteflow/agent/dashscope/DashScopeEntryTest.java @@ -0,0 +1,41 @@ +package com.yomahub.liteflow.agent.dashscope; + +import com.yomahub.liteflow.agent.exception.AgentConfigException; +import com.yomahub.liteflow.property.agent.AgentConfig; +import io.agentscope.core.model.DashScopeChatModel; +import io.agentscope.core.model.Model; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class DashScopeEntryTest { + + @Test + void buildsDashScopeChatModel() { + AgentConfig cfg = new AgentConfig(); + cfg.getDashscope().setApiKey("ds-key"); + + Model model = DashScope.of("qwen-max") + .temperature(0.4) + .thinking(t -> t.budget(2048)) + .resolve(cfg); + + assertTrue(model instanceof DashScopeChatModel); + assertEquals("qwen-max", ((DashScopeChatModel) model).getModelName()); + } + + @Test + void thinkingBudgetStored() { + DashScopeSpec spec = DashScope.of("qwen-max") + .thinking(t -> t.budget(1024)); + assertEquals(1024, spec.getThinkingBudget()); + } + + @Test + void throwsWhenApiKeyMissing() { + AgentConfig cfg = new AgentConfig(); + AgentConfigException ex = assertThrows(AgentConfigException.class, + () -> DashScope.of("qwen-max").resolve(cfg)); + assertTrue(ex.getMessage().contains("liteflow.agent.dashscope")); + } +}