Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/agentscope-ai_agentscope-java.json
OpenCode 68a9071cf1 feat: reconcile local master with origin (exa/hot-platform hunters, leaks ledger, latest results)
Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
2026-08-07 12:53:47 +08:00

1 line
238 KiB
JSON

{"repo": "agentscope-ai/agentscope-java", "files": {"agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-openai/src/main/java/io/agentscope/extensions/model/openai/compat/deepseek/DeepSeekModelProvider.java": {"ef06c7ac978d7c61b173e47a57b8772b1dd5f216": {"t": 1785745655.3540683, "n": 4131, "h": "dd1d60ccab400576", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.compat.deepseek;\n\nimport static io.agentscope.core.model.ModelProviderSupport.firstNonBlank;\nimport static io.agentscope.core.model.ModelProviderSupport.trimToNull;\nimport static io.agentscope.extensions.model.openai.OpenAIModelProviderSupport.applyAdvancedOptions;\n\nimport io.agentscope.core.model.GenerateOptions;\nimport io.agentscope.core.model.Model;\nimport io.agentscope.core.model.ModelContextWindows;\nimport io.agentscope.core.model.ModelCreationContext;\nimport io.agentscope.core.model.spi.ModelProvider;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\n/** DeepSeek provider registered through {@link java.util.ServiceLoader}. */\npublic final class DeepSeekModelProvider implements ModelProvider {\n\n private static final String PREFIX = \"deepseek:\";\n private static final Pattern MODEL_ID = Pattern.compile(\"deepseek:.+\");\n public static final String DEFAULT_BASE_URL = \"https://api.deepseek.com\";\n\n @Override\n public String providerId() {\n return \"deepseek\";\n }\n\n @Override\n public boolean supports(String modelId) {\n return modelId != null && MODEL_ID.matcher(modelId).matches();\n }\n\n @Override\n public Model create(String modelId) {\n return create(modelId, ModelCreationContext.empty());\n }\n\n @Override\n public Model create(String modelId, ModelCreationContext context) {\n if (!supports(modelId)) {\n throw new IllegalArgumentException(\"Unsupported DeepSeek model id: \" + modelId);\n }\n\n String apiKey = firstNonBlank(context.getApiKey(), System.getenv(\"DEEPSEEK_API_KEY\"));\n if (apiKey == null) {\n throw new IllegalStateException(\n \"Environment variable DEEPSEEK_API_KEY is required to auto-create model: \"\n + modelId);\n }\n String modelName = trimToNull(modelId.substring(PREFIX.length()));\n String baseUrl = firstNonBlank(context.getBaseUrl(), DEFAULT_BASE_URL);\n String endpointPath = trimToNull(context.getEndpointPath());\n boolean stream = context.getStream() != null ? context.getStream() : true;\n\n OpenAIChatModel.Builder builder =\n OpenAIChatModel.builder().apiKey(apiKey).modelName(modelName).stream(stream)\n .baseUrl(baseUrl)\n .endpointPath(endpointPath)\n .formatter(new DeepSeekFormatter())\n .nativeStructuredOutput(false)\n .contextWindowSize(\n ModelContextWindows.lookup(\n modelName, ModelContextWindows.DEEPSEEK));\n\n GenerateOptions userOptions = context.component(GenerateOptions.class);\n GenerateOptions deepSeekDefaults = deepSeekDefaultOptions(context);\n applyAdvancedOptions(\n builder, context, GenerateOptions.mergeOptions(userOptions, deepSeekDefaults));\n return builder.build();\n }\n\n private static GenerateOptions deepSeekDefaultOptions(ModelCreationContext context) {\n Boolean thinkingEnabled = context.getEnableThinking();\n if (thinkingEnabled == null) {\n return null;\n }\n\n return GenerateOptions.builder()\n .additionalBodyParam(\n \"thinking\", Map.of(\"type\", thinkingEnabled ? \"enabled\" : \"disabled\"))\n .build();\n }\n}\n"}, "3a4a298bd1f81cb759d985da8c68e616b432df35": {"t": 1785755442.8506665, "n": 4131, "h": "dd1d60ccab400576", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.compat.deepseek;\n\nimport static io.agentscope.core.model.ModelProviderSupport.firstNonBlank;\nimport static io.agentscope.core.model.ModelProviderSupport.trimToNull;\nimport static io.agentscope.extensions.model.openai.OpenAIModelProviderSupport.applyAdvancedOptions;\n\nimport io.agentscope.core.model.GenerateOptions;\nimport io.agentscope.core.model.Model;\nimport io.agentscope.core.model.ModelContextWindows;\nimport io.agentscope.core.model.ModelCreationContext;\nimport io.agentscope.core.model.spi.ModelProvider;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\n/** DeepSeek provider registered through {@link java.util.ServiceLoader}. */\npublic final class DeepSeekModelProvider implements ModelProvider {\n\n private static final String PREFIX = \"deepseek:\";\n private static final Pattern MODEL_ID = Pattern.compile(\"deepseek:.+\");\n public static final String DEFAULT_BASE_URL = \"https://api.deepseek.com\";\n\n @Override\n public String providerId() {\n return \"deepseek\";\n }\n\n @Override\n public boolean supports(String modelId) {\n return modelId != null && MODEL_ID.matcher(modelId).matches();\n }\n\n @Override\n public Model create(String modelId) {\n return create(modelId, ModelCreationContext.empty());\n }\n\n @Override\n public Model create(String modelId, ModelCreationContext context) {\n if (!supports(modelId)) {\n throw new IllegalArgumentException(\"Unsupported DeepSeek model id: \" + modelId);\n }\n\n String apiKey = firstNonBlank(context.getApiKey(), System.getenv(\"DEEPSEEK_API_KEY\"));\n if (apiKey == null) {\n throw new IllegalStateException(\n \"Environment variable DEEPSEEK_API_KEY is required to auto-create model: \"\n + modelId);\n }\n String modelName = trimToNull(modelId.substring(PREFIX.length()));\n String baseUrl = firstNonBlank(context.getBaseUrl(), DEFAULT_BASE_URL);\n String endpointPath = trimToNull(context.getEndpointPath());\n boolean stream = context.getStream() != null ? context.getStream() : true;\n\n OpenAIChatModel.Builder builder =\n OpenAIChatModel.builder().apiKey(apiKey).modelName(modelName).stream(stream)\n .baseUrl(baseUrl)\n .endpointPath(endpointPath)\n .formatter(new DeepSeekFormatter())\n .nativeStructuredOutput(false)\n .contextWindowSize(\n ModelContextWindows.lookup(\n modelName, ModelContextWindows.DEEPSEEK));\n\n GenerateOptions userOptions = context.component(GenerateOptions.class);\n GenerateOptions deepSeekDefaults = deepSeekDefaultOptions(context);\n applyAdvancedOptions(\n builder, context, GenerateOptions.mergeOptions(userOptions, deepSeekDefaults));\n return builder.build();\n }\n\n private static GenerateOptions deepSeekDefaultOptions(ModelCreationContext context) {\n Boolean thinkingEnabled = context.getEnableThinking();\n if (thinkingEnabled == null) {\n return null;\n }\n\n return GenerateOptions.builder()\n .additionalBodyParam(\n \"thinking\", Map.of(\"type\", thinkingEnabled ? \"enabled\" : \"disabled\"))\n .build();\n }\n}\n"}, "48a77f180ead00cf4883608388a3dc10968c1197": {"t": 1785755444.5886405, "n": 4151, "h": "08574b24eafb0747", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.compat.deepseek;\n\nimport static io.agentscope.extensions.model.openai.ModelProviderSupport.applyAdvancedOptions;\nimport static io.agentscope.extensions.model.openai.ModelProviderSupport.firstNonBlank;\nimport static io.agentscope.extensions.model.openai.ModelProviderSupport.trimToNull;\n\nimport io.agentscope.core.model.GenerateOptions;\nimport io.agentscope.core.model.Model;\nimport io.agentscope.core.model.ModelContextWindows;\nimport io.agentscope.core.model.ModelCreationContext;\nimport io.agentscope.core.model.spi.ModelProvider;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\n/** DeepSeek provider registered through {@link java.util.ServiceLoader}. */\npublic final class DeepSeekModelProvider implements ModelProvider {\n\n private static final String PREFIX = \"deepseek:\";\n private static final Pattern MODEL_ID = Pattern.compile(\"deepseek:.+\");\n public static final String DEFAULT_BASE_URL = \"https://api.deepseek.com\";\n\n @Override\n public String providerId() {\n return \"deepseek\";\n }\n\n @Override\n public boolean supports(String modelId) {\n return modelId != null && MODEL_ID.matcher(modelId).matches();\n }\n\n @Override\n public Model create(String modelId) {\n return create(modelId, ModelCreationContext.empty());\n }\n\n @Override\n public Model create(String modelId, ModelCreationContext context) {\n if (!supports(modelId)) {\n throw new IllegalArgumentException(\"Unsupported DeepSeek model id: \" + modelId);\n }\n\n String apiKey = firstNonBlank(context.getApiKey(), System.getenv(\"DEEPSEEK_API_KEY\"));\n if (apiKey == null) {\n throw new IllegalStateException(\n \"Environment variable DEEPSEEK_API_KEY is required to auto-create model: \"\n + modelId);\n }\n String modelName = trimToNull(modelId.substring(PREFIX.length()));\n String baseUrl = firstNonBlank(context.getBaseUrl(), DEFAULT_BASE_URL);\n String endpointPath = trimToNull(context.getEndpointPath());\n boolean stream = context.getStream() != null ? context.getStream() : true;\n\n OpenAIChatModel.Builder builder =\n OpenAIChatModel.builder().apiKey(apiKey).modelName(modelName).stream(stream)\n .baseUrl(baseUrl)\n .endpointPath(endpointPath)\n .formatter(new DeepSeekFormatter())\n .nativeStructuredOutput(false)\n .contextWindowSize(\n ModelContextWindows.lookup(\n modelName, ModelContextWindows.DEEPSEEK));\n\n GenerateOptions userOptions = context.component(GenerateOptions.class);\n GenerateOptions deepSeekDefaults = deepSeekDefaultOptions(context);\n applyAdvancedOptions(\n builder, context, GenerateOptions.mergeOptions(userOptions, deepSeekDefaults));\n return builder.build();\n }\n\n private static GenerateOptions deepSeekDefaultOptions(ModelCreationContext context) {\n Boolean thinkingEnabled = context.getEnableThinking();\n if (thinkingEnabled == null) {\n return null;\n }\n\n return GenerateOptions.builder()\n .additionalBodyParam(\n \"thinking\", Map.of(\"type\", thinkingEnabled ? \"enabled\" : \"disabled\"))\n .build();\n }\n}\n"}, "9fbaaf8d814a17daccbcb54a1c7832a617fa62b1": {"t": 1785755445.9434123, "n": 7885, "h": "734700ef6c39536f", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.compat.deepseek;\n\nimport io.agentscope.core.formatter.Formatter;\nimport io.agentscope.core.model.GenerateOptions;\nimport io.agentscope.core.model.Model;\nimport io.agentscope.core.model.ModelContextWindows;\nimport io.agentscope.core.model.ModelCreationContext;\nimport io.agentscope.core.model.spi.ModelProvider;\nimport io.agentscope.core.model.transport.HttpTransport;\nimport io.agentscope.core.model.transport.ProxyConfig;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport io.agentscope.extensions.model.openai.dto.OpenAIRequest;\nimport io.agentscope.extensions.model.openai.dto.OpenAIResponse;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\n/** DeepSeek provider registered through {@link java.util.ServiceLoader}. */\npublic final class DeepSeekModelProvider implements ModelProvider {\n\n public static final String DEFAULT_BASE_URL = \"https://api.deepseek.com\";\n\n private static final String PREFIX = \"deepseek:\";\n private static final Pattern MODEL_ID = Pattern.compile(\"deepseek:.+\");\n private static final String OPTION_CONTEXT_WINDOW_SIZE = \"contextWindowSize\";\n private static final String OPTION_NATIVE_STRUCTURED_OUTPUT = \"nativeStructuredOutput\";\n private static final String OPTION_NATIVE_STRUCTURED_OUTPUT_WITH_TOOLS =\n \"nativeStructuredOutputWithTools\";\n\n @Override\n public String providerId() {\n return \"deepseek\";\n }\n\n @Override\n public boolean supports(String modelId) {\n return modelId != null && MODEL_ID.matcher(modelId).matches();\n }\n\n @Override\n public Model create(String modelId) {\n return create(modelId, ModelCreationContext.empty());\n }\n\n @Override\n public Model create(String modelId, ModelCreationContext context) {\n if (!supports(modelId)) {\n throw new IllegalArgumentException(\"Unsupported DeepSeek model id: \" + modelId);\n }\n String modelName = trimToNull(modelId.substring(PREFIX.length()));\n String apiKey = firstNonBlank(context.getApiKey(), System.getenv(\"DEEPSEEK_API_KEY\"));\n if (apiKey == null) {\n throw new IllegalStateException(\n \"Environment variable DEEPSEEK_API_KEY is required to auto-create model: \"\n + modelId);\n }\n\n OpenAIChatModel.Builder builder =\n OpenAIChatModel.builder()\n .apiKey(apiKey)\n .modelName(modelName)\n .baseUrl(DEFAULT_BASE_URL)\n .formatter(new DeepSeekFormatter())\n .contextWindowSize(\n ModelContextWindows.lookup(modelName, ModelContextWindows.DEEPSEEK))\n .nativeStructuredOutput(false)\n .nativeStructuredOutputWithTools(false)\n .stream(context.getStream() != null ? context.getStream() : true);\n\n String baseUrl = trimToNull(context.getBaseUrl());\n if (baseUrl != null) {\n builder.baseUrl(baseUrl);\n }\n String endpointPath = trimToNull(context.getEndpointPath());\n if (endpointPath != null) {\n builder.endpointPath(endpointPath);\n }\n applyAdvancedOptions(builder, context);\n return builder.build();\n }\n\n @SuppressWarnings(\"unchecked\")\n private static void applyAdvancedOptions(\n OpenAIChatModel.Builder builder, ModelCreationContext context) {\n GenerateOptions userOptions = context.component(GenerateOptions.class);\n GenerateOptions deepSeekDefaults = deepSeekDefaultOptions(context);\n GenerateOptions mergedOptions = GenerateOptions.mergeOptions(userOptions, deepSeekDefaults);\n if (mergedOptions != null) {\n builder.generateOptions(mergedOptions);\n }\n\n HttpTransport httpTransport = context.component(HttpTransport.class);\n if (httpTransport != null) {\n builder.httpTransport(httpTransport);\n }\n ProxyConfig proxyConfig = context.component(ProxyConfig.class);\n if (proxyConfig != null) {\n builder.proxy(proxyConfig);\n }\n Formatter<OpenAIMessage, OpenAIResponse, OpenAIRequest> formatter =\n (Formatter<OpenAIMessage, OpenAIResponse, OpenAIRequest>)\n findAssignableComponent(context, Formatter.class);\n if (formatter != null) {\n builder.formatter(formatter);\n }\n Integer contextWindowSize = intOption(context, OPTION_CONTEXT_WINDOW_SIZE);\n if (contextWindowSize != null) {\n builder.contextWindowSize(contextWindowSize);\n }\n Boolean nativeStructuredOutput = booleanOption(context, OPTION_NATIVE_STRUCTURED_OUTPUT);\n if (nativeStructuredOutput != null) {\n builder.nativeStructuredOutput(nativeStructuredOutput);\n }\n Boolean nativeStructuredOutputWithTools =\n booleanOption(context, OPTION_NATIVE_STRUCTURED_OUTPUT_WITH_TOOLS);\n if (nativeStructuredOutputWithTools != null) {\n builder.nativeStructuredOutputWithTools(nativeStructuredOutputWithTools);\n }\n }\n\n private static GenerateOptions deepSeekDefaultOptions(ModelCreationContext context) {\n Boolean thinkingEnabled = context.getEnableThinking();\n if (thinkingEnabled == null) {\n return null;\n }\n\n return GenerateOptions.builder()\n .additionalBodyParam(\n \"thinking\", Map.of(\"type\", thinkingEnabled ? \"enabled\" : \"disabled\"))\n .build();\n }\n\n private static String firstNonBlank(String preferred, String fallback) {\n String normalized = trimToNull(preferred);\n return normalized != null ? normalized : trimToNull(fallback);\n }\n\n private static String trimToNull(String value) {\n if (value == null) {\n return null;\n }\n String trimmed = value.trim();\n return trimmed.isEmpty() ? null : trimmed;\n }\n\n private static Object findAssignableComponent(\n ModelCreationContext context, Class<?> componentType) {\n for (Object value : context.getComponents().values()) {\n if (componentType.isInstance(value)) {\n return value;\n }\n }\n return null;\n }\n\n private static Integer intOption(ModelCreationContext context, String key) {\n Object value = context.option(key);\n if (value == null) {\n return null;\n }\n if (value instanceof Number number) {\n return number.intValue();\n }\n throw new IllegalArgumentException(\n \"ModelCreationContext option \" + key + \" must be a number\");\n }\n\n private static Boolean booleanOption(ModelCreationContext context, String key) {\n Object value = context.option(key);\n if (value == null) {\n return null;\n }\n if (value instanceof Boolean bool) {\n return bool;\n }\n throw new IllegalArgumentException(\n \"ModelCreationContext option \" + key + \" must be a boolean\");\n }\n}\n"}, "74db7572f20f07f05439a0e1b0d6992f9f7c6a38": {"t": 1785755447.3548613, "n": 7873, "h": "d2160d8f7c5793b7", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.compat.deepseek;\n\nimport io.agentscope.core.formatter.Formatter;\nimport io.agentscope.core.model.GenerateOptions;\nimport io.agentscope.core.model.Model;\nimport io.agentscope.core.model.ModelContextWindows;\nimport io.agentscope.core.model.ModelCreationContext;\nimport io.agentscope.core.model.spi.ModelProvider;\nimport io.agentscope.core.model.transport.HttpTransport;\nimport io.agentscope.core.model.transport.ProxyConfig;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport io.agentscope.extensions.model.openai.dto.OpenAIRequest;\nimport io.agentscope.extensions.model.openai.dto.OpenAIResponse;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\n/** DeepSeek provider registered through {@link java.util.ServiceLoader}. */\npublic final class DeepSeekModelProvider implements ModelProvider {\n\n public static final String DEFAULT_BASE_URL = \"https://api.deepseek.com\";\n\n private static final String PREFIX = \"deepseek:\";\n private static final Pattern MODEL_ID = Pattern.compile(\"deepseek:.+\");\n private static final String OPTION_CONTEXT_WINDOW_SIZE = \"contextWindowSize\";\n private static final String OPTION_NATIVE_STRUCTURED_OUTPUT = \"nativeStructuredOutput\";\n private static final String OPTION_NATIVE_STRUCTURED_OUTPUT_WITH_TOOLS =\n \"nativeStructuredOutputWithTools\";\n\n @Override\n public String providerId() {\n return \"deepseek\";\n }\n\n @Override\n public boolean supports(String modelId) {\n return modelId != null && MODEL_ID.matcher(modelId).matches();\n }\n\n @Override\n public Model create(String modelId) {\n return create(modelId, ModelCreationContext.empty());\n }\n\n @Override\n public Model create(String modelId, ModelCreationContext context) {\n if (!supports(modelId)) {\n throw new IllegalArgumentException(\"Unsupported DeepSeek model id: \" + modelId);\n }\n String modelName = modelId.substring(PREFIX.length());\n String apiKey = firstNonBlank(context.getApiKey(), System.getenv(\"DEEPSEEK_API_KEY\"));\n if (apiKey == null) {\n throw new IllegalStateException(\n \"Environment variable DEEPSEEK_API_KEY is required to auto-create model: \"\n + modelId);\n }\n\n OpenAIChatModel.Builder builder =\n OpenAIChatModel.builder()\n .apiKey(apiKey)\n .modelName(modelName)\n .baseUrl(DEFAULT_BASE_URL)\n .formatter(new DeepSeekFormatter())\n .contextWindowSize(\n ModelContextWindows.lookup(modelName, ModelContextWindows.DEEPSEEK))\n .nativeStructuredOutput(false)\n .nativeStructuredOutputWithTools(false)\n .stream(context.getStream() != null ? context.getStream() : true);\n\n String baseUrl = trimToNull(context.getBaseUrl());\n if (baseUrl != null) {\n builder.baseUrl(baseUrl);\n }\n String endpointPath = trimToNull(context.getEndpointPath());\n if (endpointPath != null) {\n builder.endpointPath(endpointPath);\n }\n applyAdvancedOptions(builder, context);\n return builder.build();\n }\n\n @SuppressWarnings(\"unchecked\")\n private static void applyAdvancedOptions(\n OpenAIChatModel.Builder builder, ModelCreationContext context) {\n GenerateOptions userOptions = context.component(GenerateOptions.class);\n GenerateOptions deepSeekDefaults = deepSeekDefaultOptions(context);\n GenerateOptions mergedOptions = GenerateOptions.mergeOptions(userOptions, deepSeekDefaults);\n if (mergedOptions != null) {\n builder.generateOptions(mergedOptions);\n }\n\n HttpTransport httpTransport = context.component(HttpTransport.class);\n if (httpTransport != null) {\n builder.httpTransport(httpTransport);\n }\n ProxyConfig proxyConfig = context.component(ProxyConfig.class);\n if (proxyConfig != null) {\n builder.proxy(proxyConfig);\n }\n Formatter<OpenAIMessage, OpenAIResponse, OpenAIRequest> formatter =\n (Formatter<OpenAIMessage, OpenAIResponse, OpenAIRequest>)\n findAssignableComponent(context, Formatter.class);\n if (formatter != null) {\n builder.formatter(formatter);\n }\n Integer contextWindowSize = intOption(context, OPTION_CONTEXT_WINDOW_SIZE);\n if (contextWindowSize != null) {\n builder.contextWindowSize(contextWindowSize);\n }\n Boolean nativeStructuredOutput = booleanOption(context, OPTION_NATIVE_STRUCTURED_OUTPUT);\n if (nativeStructuredOutput != null) {\n builder.nativeStructuredOutput(nativeStructuredOutput);\n }\n Boolean nativeStructuredOutputWithTools =\n booleanOption(context, OPTION_NATIVE_STRUCTURED_OUTPUT_WITH_TOOLS);\n if (nativeStructuredOutputWithTools != null) {\n builder.nativeStructuredOutputWithTools(nativeStructuredOutputWithTools);\n }\n }\n\n private static GenerateOptions deepSeekDefaultOptions(ModelCreationContext context) {\n Boolean thinkingEnabled = context.getEnableThinking();\n if (thinkingEnabled == null) {\n return null;\n }\n\n return GenerateOptions.builder()\n .additionalBodyParam(\n \"thinking\", Map.of(\"type\", thinkingEnabled ? \"enabled\" : \"disabled\"))\n .build();\n }\n\n private static String firstNonBlank(String preferred, String fallback) {\n String normalized = trimToNull(preferred);\n return normalized != null ? normalized : trimToNull(fallback);\n }\n\n private static String trimToNull(String value) {\n if (value == null) {\n return null;\n }\n String trimmed = value.trim();\n return trimmed.isEmpty() ? null : trimmed;\n }\n\n private static Object findAssignableComponent(\n ModelCreationContext context, Class<?> componentType) {\n for (Object value : context.getComponents().values()) {\n if (componentType.isInstance(value)) {\n return value;\n }\n }\n return null;\n }\n\n private static Integer intOption(ModelCreationContext context, String key) {\n Object value = context.option(key);\n if (value == null) {\n return null;\n }\n if (value instanceof Number number) {\n return number.intValue();\n }\n throw new IllegalArgumentException(\n \"ModelCreationContext option \" + key + \" must be a number\");\n }\n\n private static Boolean booleanOption(ModelCreationContext context, String key) {\n Object value = context.option(key);\n if (value == null) {\n return null;\n }\n if (value instanceof Boolean bool) {\n return bool;\n }\n throw new IllegalArgumentException(\n \"ModelCreationContext option \" + key + \" must be a boolean\");\n }\n}\n"}, "702461070ddcab29dae7c7a2b130e323a106d020": {"t": 1786067428.7719777, "n": 4131, "h": "dd1d60ccab400576", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.compat.deepseek;\n\nimport static io.agentscope.core.model.ModelProviderSupport.firstNonBlank;\nimport static io.agentscope.core.model.ModelProviderSupport.trimToNull;\nimport static io.agentscope.extensions.model.openai.OpenAIModelProviderSupport.applyAdvancedOptions;\n\nimport io.agentscope.core.model.GenerateOptions;\nimport io.agentscope.core.model.Model;\nimport io.agentscope.core.model.ModelContextWindows;\nimport io.agentscope.core.model.ModelCreationContext;\nimport io.agentscope.core.model.spi.ModelProvider;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\n/** DeepSeek provider registered through {@link java.util.ServiceLoader}. */\npublic final class DeepSeekModelProvider implements ModelProvider {\n\n private static final String PREFIX = \"deepseek:\";\n private static final Pattern MODEL_ID = Pattern.compile(\"deepseek:.+\");\n public static final String DEFAULT_BASE_URL = \"https://api.deepseek.com\";\n\n @Override\n public String providerId() {\n return \"deepseek\";\n }\n\n @Override\n public boolean supports(String modelId) {\n return modelId != null && MODEL_ID.matcher(modelId).matches();\n }\n\n @Override\n public Model create(String modelId) {\n return create(modelId, ModelCreationContext.empty());\n }\n\n @Override\n public Model create(String modelId, ModelCreationContext context) {\n if (!supports(modelId)) {\n throw new IllegalArgumentException(\"Unsupported DeepSeek model id: \" + modelId);\n }\n\n String apiKey = firstNonBlank(context.getApiKey(), System.getenv(\"DEEPSEEK_API_KEY\"));\n if (apiKey == null) {\n throw new IllegalStateException(\n \"Environment variable DEEPSEEK_API_KEY is required to auto-create model: \"\n + modelId);\n }\n String modelName = trimToNull(modelId.substring(PREFIX.length()));\n String baseUrl = firstNonBlank(context.getBaseUrl(), DEFAULT_BASE_URL);\n String endpointPath = trimToNull(context.getEndpointPath());\n boolean stream = context.getStream() != null ? context.getStream() : true;\n\n OpenAIChatModel.Builder builder =\n OpenAIChatModel.builder().apiKey(apiKey).modelName(modelName).stream(stream)\n .baseUrl(baseUrl)\n .endpointPath(endpointPath)\n .formatter(new DeepSeekFormatter())\n .nativeStructuredOutput(false)\n .contextWindowSize(\n ModelContextWindows.lookup(\n modelName, ModelContextWindows.DEEPSEEK));\n\n GenerateOptions userOptions = context.component(GenerateOptions.class);\n GenerateOptions deepSeekDefaults = deepSeekDefaultOptions(context);\n applyAdvancedOptions(\n builder, context, GenerateOptions.mergeOptions(userOptions, deepSeekDefaults));\n return builder.build();\n }\n\n private static GenerateOptions deepSeekDefaultOptions(ModelCreationContext context) {\n Boolean thinkingEnabled = context.getEnableThinking();\n if (thinkingEnabled == null) {\n return null;\n }\n\n return GenerateOptions.builder()\n .additionalBodyParam(\n \"thinking\", Map.of(\"type\", thinkingEnabled ? \"enabled\" : \"disabled\"))\n .build();\n }\n}\n"}, "9cbac4ce574183274c37b5132ce453ffcaeafb74": {"t": 1786068300.3379424, "n": 4131, "h": "dd1d60ccab400576", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.compat.deepseek;\n\nimport static io.agentscope.core.model.ModelProviderSupport.firstNonBlank;\nimport static io.agentscope.core.model.ModelProviderSupport.trimToNull;\nimport static io.agentscope.extensions.model.openai.OpenAIModelProviderSupport.applyAdvancedOptions;\n\nimport io.agentscope.core.model.GenerateOptions;\nimport io.agentscope.core.model.Model;\nimport io.agentscope.core.model.ModelContextWindows;\nimport io.agentscope.core.model.ModelCreationContext;\nimport io.agentscope.core.model.spi.ModelProvider;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\n/** DeepSeek provider registered through {@link java.util.ServiceLoader}. */\npublic final class DeepSeekModelProvider implements ModelProvider {\n\n private static final String PREFIX = \"deepseek:\";\n private static final Pattern MODEL_ID = Pattern.compile(\"deepseek:.+\");\n public static final String DEFAULT_BASE_URL = \"https://api.deepseek.com\";\n\n @Override\n public String providerId() {\n return \"deepseek\";\n }\n\n @Override\n public boolean supports(String modelId) {\n return modelId != null && MODEL_ID.matcher(modelId).matches();\n }\n\n @Override\n public Model create(String modelId) {\n return create(modelId, ModelCreationContext.empty());\n }\n\n @Override\n public Model create(String modelId, ModelCreationContext context) {\n if (!supports(modelId)) {\n throw new IllegalArgumentException(\"Unsupported DeepSeek model id: \" + modelId);\n }\n\n String apiKey = firstNonBlank(context.getApiKey(), System.getenv(\"DEEPSEEK_API_KEY\"));\n if (apiKey == null) {\n throw new IllegalStateException(\n \"Environment variable DEEPSEEK_API_KEY is required to auto-create model: \"\n + modelId);\n }\n String modelName = trimToNull(modelId.substring(PREFIX.length()));\n String baseUrl = firstNonBlank(context.getBaseUrl(), DEFAULT_BASE_URL);\n String endpointPath = trimToNull(context.getEndpointPath());\n boolean stream = context.getStream() != null ? context.getStream() : true;\n\n OpenAIChatModel.Builder builder =\n OpenAIChatModel.builder().apiKey(apiKey).modelName(modelName).stream(stream)\n .baseUrl(baseUrl)\n .endpointPath(endpointPath)\n .formatter(new DeepSeekFormatter())\n .nativeStructuredOutput(false)\n .contextWindowSize(\n ModelContextWindows.lookup(\n modelName, ModelContextWindows.DEEPSEEK));\n\n GenerateOptions userOptions = context.component(GenerateOptions.class);\n GenerateOptions deepSeekDefaults = deepSeekDefaultOptions(context);\n applyAdvancedOptions(\n builder, context, GenerateOptions.mergeOptions(userOptions, deepSeekDefaults));\n return builder.build();\n }\n\n private static GenerateOptions deepSeekDefaultOptions(ModelCreationContext context) {\n Boolean thinkingEnabled = context.getEnableThinking();\n if (thinkingEnabled == null) {\n return null;\n }\n\n return GenerateOptions.builder()\n .additionalBodyParam(\n \"thinking\", Map.of(\"type\", thinkingEnabled ? \"enabled\" : \"disabled\"))\n .build();\n }\n}\n"}}, "agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-e2e-tests/src/test/java/io/agentscope/core/e2e/ProviderFactory.java": {"ef06c7ac978d7c61b173e47a57b8772b1dd5f216": {"t": 1785745668.2121294, "n": 14512, "h": "c8ab4d4827716b69", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.core.e2e;\n\nimport io.agentscope.core.e2e.providers.AnthropicProvider;\nimport io.agentscope.core.e2e.providers.DashScopeCompatibleProvider;\nimport io.agentscope.core.e2e.providers.DashScopeProvider;\nimport io.agentscope.core.e2e.providers.DeepSeekProvider;\nimport io.agentscope.core.e2e.providers.DeepSeekReasonerProvider;\nimport io.agentscope.core.e2e.providers.GLMProvider;\nimport io.agentscope.core.e2e.providers.GeminiProvider;\nimport io.agentscope.core.e2e.providers.ModelCapability;\nimport io.agentscope.core.e2e.providers.ModelProvider;\nimport io.agentscope.core.e2e.providers.OpenRouterProvider;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * Factory for creating ModelProvider instances based on available API keys.\n *\n * <p>Dynamically provides enabled providers based on environment variables:\n *\n * <ul>\n * <li>OPENAI_API_KEY: Enables OpenAI Native providers\n * <li>DASHSCOPE_API_KEY: Enables DashScope Native, DashScope Compatible, and Bailian providers\n * <li>DEEPSEEK_API_KEY: Enables DeepSeek Native providers\n * <li>GLM_API_KEY: Enables GLM (Zhipu AI) Native providers\n * <li>GOOGLE_API_KEY: Enables Google Gemini Native providers\n * <li>ANTHROPIC_API_KEY: Enables Anthropic Claude Native providers\n * <li>OPENROUTER_API_KEY: Enables OpenRouter providers (access to various models)\n * </ul>\n *\n * <p>Usage:\n *\n * <pre>{@code\n * // Get all basic providers\n * Stream<ModelProvider> basics = ProviderFactory.getBasicProviders();\n *\n * // Get providers with specific capabilities\n * Stream<ModelProvider> imageProviders = ProviderFactory.getProviders(\n * ModelCapability.BASIC, ModelCapability.IMAGE);\n *\n * // Check provider status\n * String status = ProviderFactory.getApiKeyStatus();\n * }</pre>\n */\npublic class ProviderFactory {\n\n private static final String OPENAI_API_KEY = \"OPENAI_API_KEY\";\n private static final String DASHSCOPE_API_KEY = \"DASHSCOPE_API_KEY\";\n private static final String DEEPSEEK_API_KEY = \"DEEPSEEK_API_KEY\";\n private static final String GLM_API_KEY = \"GLM_API_KEY\";\n private static final String GOOGLE_API_KEY = \"GOOGLE_API_KEY\";\n private static final String ANTHROPIC_API_KEY = \"ANTHROPIC_API_KEY\";\n private static final String OPENROUTER_API_KEY = \"OPENROUTER_API_KEY\";\n\n private static final List<String> ALL_KEYS =\n List.of(\n OPENAI_API_KEY,\n DASHSCOPE_API_KEY,\n DEEPSEEK_API_KEY,\n GLM_API_KEY,\n GOOGLE_API_KEY,\n ANTHROPIC_API_KEY,\n OPENROUTER_API_KEY);\n\n /**\n * Gets all registered providers (including disabled ones).\n *\n * @return List of all provider instances\n */\n private static List<ModelProvider> getAllProviders() {\n List<ModelProvider> providers = new ArrayList<>();\n\n // DashScope Compatible providers\n providers.add(new DashScopeCompatibleProvider.QwenPlusOpenAI());\n providers.add(new DashScopeCompatibleProvider.QwenPlusMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3OmniFlashOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3OmniFlashMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3VlPlusOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3VlPlusMultiAgentOpenAI());\n\n // DashScope Compatible providers (Qwen3.5)\n providers.add(new DashScopeCompatibleProvider.Qwen35PlusOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35PlusMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35FlashOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35FlashMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35_397bA17bOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35_397bA17bMultiAgentOpenAI());\n\n // DashScope Native providers\n providers.add(new DashScopeProvider.QwenPlusDashScope());\n providers.add(new DashScopeProvider.QwenPlusMultiAgentDashScope());\n providers.add(new DashScopeProvider.QwenPlusThinkingDashScope());\n providers.add(new DashScopeProvider.QwenPlusThinkingMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen3VlPlusDashScope());\n providers.add(new DashScopeProvider.Qwen3VlPlusMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen35PlusDashScope());\n providers.add(new DashScopeProvider.Qwen35PlusMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen35FlashDashScope());\n providers.add(new DashScopeProvider.Qwen35FlashMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen35_397bA17bDashScope());\n providers.add(new DashScopeProvider.Qwen35_397bA17bMultiAgentDashScope());\n\n // Gemini providers\n providers.add(new GeminiProvider.Gemini25FlashGemini());\n providers.add(new GeminiProvider.Gemini25FlashMultiAgentGemini());\n\n // Anthropic providers\n providers.add(new AnthropicProvider.ClaudeHaiku45Anthropic());\n providers.add(new AnthropicProvider.ClaudeHaiku45MultiAgentAnthropic());\n\n // DeepSeek providers\n providers.add(new DeepSeekProvider.DeepSeekChat());\n providers.add(new DeepSeekProvider.DeepSeekChatMultiAgent());\n providers.add(new DeepSeekReasonerProvider.DeepSeekR1());\n providers.add(new DeepSeekReasonerProvider.DeepSeekR1MultiAgent());\n\n // GLM providers\n providers.add(new GLMProvider.GLM46());\n providers.add(new GLMProvider.GLM46MultiAgent());\n providers.add(new GLMProvider.GLM46VPlus());\n providers.add(new GLMProvider.GLM46VMultiAgent());\n providers.add(new GLMProvider.GLM45());\n providers.add(new GLMProvider.GLM47());\n\n // OpenRouter providers\n providers.add(new OpenRouterProvider.GPT52());\n providers.add(new OpenRouterProvider.GPT52MultiAgent());\n providers.add(new OpenRouterProvider.Claude45Haiku());\n providers.add(new OpenRouterProvider.Claude45HaikuMultiAgent());\n providers.add(new OpenRouterProvider.Qwen3VL());\n providers.add(new OpenRouterProvider.Qwen3VLMultiAgent());\n providers.add(new OpenRouterProvider.Gemini3FlashPreview());\n providers.add(new OpenRouterProvider.Gemini3FlashPreviewMultiAgent());\n providers.add(new OpenRouterProvider.Gemini3ProPreview());\n providers.add(new OpenRouterProvider.Gemini3ProPreviewMultiAgent());\n providers.add(new OpenRouterProvider.DeepSeekV32());\n providers.add(new OpenRouterProvider.DeepSeekV32MultiAgent());\n providers.add(new OpenRouterProvider.DeepSeekR1());\n providers.add(new OpenRouterProvider.DeepSeekR1MultiAgent());\n providers.add(new OpenRouterProvider.GLM46());\n providers.add(new OpenRouterProvider.GLM46MultiAgent());\n\n return providers;\n }\n\n // ==========================================================================\n // API Key Helpers\n // ==========================================================================\n\n protected static boolean hasApiKey(String keyName) {\n String key = System.getenv(keyName);\n if (key == null || key.isEmpty()) {\n key = System.getProperty(keyName);\n }\n return key != null && !key.isEmpty();\n }\n\n protected static boolean hasOpenAIKey() {\n return hasApiKey(OPENAI_API_KEY);\n }\n\n protected static boolean hasDeepSeekKey() {\n return hasApiKey(DEEPSEEK_API_KEY);\n }\n\n protected static boolean hasGLMKey() {\n return hasApiKey(GLM_API_KEY);\n }\n\n protected static boolean hasDashScopeKey() {\n return hasApiKey(DASHSCOPE_API_KEY);\n }\n\n protected static boolean hasGoogleKey() {\n return hasApiKey(GOOGLE_API_KEY);\n }\n\n protected static boolean hasAnthropicKey() {\n return hasApiKey(ANTHROPIC_API_KEY);\n }\n\n protected static boolean hasOpenRouterKey() {\n return hasApiKey(OPENROUTER_API_KEY);\n }\n\n // ==========================================================================\n // Capability-based Provider Selection\n // ==========================================================================\n\n /**\n * Gets providers with all specified capabilities.\n *\n * @param required The required capabilities\n * @return Stream of enabled providers with all required capabilities\n */\n public static Stream<ModelProvider> getProviders(ModelCapability... required) {\n Set<ModelCapability> requiredSet = Set.of(required);\n return getAllProviders().stream()\n .filter(ModelProvider::isEnabled)\n .filter(p -> p.getCapabilities().containsAll(requiredSet));\n }\n\n // ==========================================================================\n // Convenience Methods (renamed from getEnabledXxxProviders to getXxxProviders)\n // ==========================================================================\n\n /**\n * Gets all enabled basic providers for core functionality testing.\n *\n * @return Stream of enabled providers with basic capability\n */\n public static Stream<ModelProvider> getBasicProviders() {\n return getProviders(ModelCapability.BASIC);\n }\n\n /**\n * Gets all enabled providers for tool functionality testing.\n *\n * @return Stream of enabled providers that support tools\n */\n public static Stream<ModelProvider> getToolProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.TOOL_CALLING);\n }\n\n /**\n * Gets all enabled providers for image functionality testing.\n *\n * @return Stream of enabled providers that support images\n */\n public static Stream<ModelProvider> getImageProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.IMAGE);\n }\n\n /**\n * Gets all enabled providers for audio functionality testing.\n *\n * @return Stream of enabled providers that support audio\n */\n public static Stream<ModelProvider> getAudioProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.AUDIO);\n }\n\n /**\n * Gets all enabled providers for multimodal functionality testing.\n *\n * @return Stream of enabled providers that support multiple modalities\n */\n public static Stream<ModelProvider> getMultimodalProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.IMAGE, ModelCapability.AUDIO);\n }\n\n /**\n * Gets all enabled providers for thinking functionality testing.\n *\n * @return Stream of enabled providers that support thinking\n */\n public static Stream<ModelProvider> getThinkingProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.THINKING);\n }\n\n /**\n * Gets all enabled providers for thinking with budget control.\n *\n * @return Stream of enabled providers that support thinking budget\n */\n public static Stream<ModelProvider> getThinkingBudgetProviders() {\n Stream.Builder<ModelProvider> builders = Stream.builder();\n\n if (hasDashScopeKey()) {\n builders.add(new DashScopeProvider.QwenPlusThinkingDashScope(1000));\n builders.add(new DashScopeProvider.QwenPlusThinkingMultiAgentDashScope(1000));\n }\n\n if (hasGLMKey()) {\n builders.add(new GLMProvider.GLM45());\n builders.add(new GLMProvider.GLM47());\n }\n\n if (hasOpenRouterKey()) {\n builders.add(new OpenRouterProvider.DeepSeekR1());\n builders.add(new OpenRouterProvider.DeepSeekR1MultiAgent());\n builders.add(new OpenRouterProvider.Claude45HaikuThinking(1024));\n }\n\n return builders.build();\n }\n\n /**\n * Gets all enabled providers for video functionality testing.\n *\n * @return Stream of enabled providers that support video\n */\n public static Stream<ModelProvider> getVideoProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.VIDEO);\n }\n\n /**\n * Gets all enabled providers for multimodal tool functionality testing.\n *\n * @return Stream of enabled providers that support multimodal tools\n */\n public static Stream<ModelProvider> getMultimodalToolProviders() {\n return getProviders(\n ModelCapability.BASIC, ModelCapability.IMAGE, ModelCapability.TOOL_CALLING);\n }\n\n /**\n * Gets all enabled providers that support multi-agent formatter for MsgHub testing.\n *\n * @return Stream of enabled providers with multi-agent formatter capability\n */\n public static Stream<ModelProvider> getMultiAgentProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.MULTI_AGENT_FORMATTER);\n }\n\n // ==========================================================================\n // Utility Methods\n // ==========================================================================\n\n /**\n * Checks if any E2E tests can be run (has at least one API key).\n *\n * @return true if at least one API key is available\n */\n public static boolean hasAnyApiKey() {\n return ALL_KEYS.stream().anyMatch(ProviderFactory::hasApiKey);\n }\n\n /**\n * Gets a comma-separated list of available API keys for debugging.\n *\n * @return String describing available API keys\n */\n public static String getApiKeyStatus() {\n String status =\n ALL_KEYS.stream()\n .filter(ProviderFactory::hasApiKey)\n .collect(Collectors.joining(\", \"));\n return status.isEmpty() ? \"None\" : status;\n }\n}\n"}, "235ffb82406b4790a7a98624c2724741a48fd584": {"t": 1785755441.5648801, "n": 14512, "h": "c8ab4d4827716b69", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.core.e2e;\n\nimport io.agentscope.core.e2e.providers.AnthropicProvider;\nimport io.agentscope.core.e2e.providers.DashScopeCompatibleProvider;\nimport io.agentscope.core.e2e.providers.DashScopeProvider;\nimport io.agentscope.core.e2e.providers.DeepSeekProvider;\nimport io.agentscope.core.e2e.providers.DeepSeekReasonerProvider;\nimport io.agentscope.core.e2e.providers.GLMProvider;\nimport io.agentscope.core.e2e.providers.GeminiProvider;\nimport io.agentscope.core.e2e.providers.ModelCapability;\nimport io.agentscope.core.e2e.providers.ModelProvider;\nimport io.agentscope.core.e2e.providers.OpenRouterProvider;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * Factory for creating ModelProvider instances based on available API keys.\n *\n * <p>Dynamically provides enabled providers based on environment variables:\n *\n * <ul>\n * <li>OPENAI_API_KEY: Enables OpenAI Native providers\n * <li>DASHSCOPE_API_KEY: Enables DashScope Native, DashScope Compatible, and Bailian providers\n * <li>DEEPSEEK_API_KEY: Enables DeepSeek Native providers\n * <li>GLM_API_KEY: Enables GLM (Zhipu AI) Native providers\n * <li>GOOGLE_API_KEY: Enables Google Gemini Native providers\n * <li>ANTHROPIC_API_KEY: Enables Anthropic Claude Native providers\n * <li>OPENROUTER_API_KEY: Enables OpenRouter providers (access to various models)\n * </ul>\n *\n * <p>Usage:\n *\n * <pre>{@code\n * // Get all basic providers\n * Stream<ModelProvider> basics = ProviderFactory.getBasicProviders();\n *\n * // Get providers with specific capabilities\n * Stream<ModelProvider> imageProviders = ProviderFactory.getProviders(\n * ModelCapability.BASIC, ModelCapability.IMAGE);\n *\n * // Check provider status\n * String status = ProviderFactory.getApiKeyStatus();\n * }</pre>\n */\npublic class ProviderFactory {\n\n private static final String OPENAI_API_KEY = \"OPENAI_API_KEY\";\n private static final String DASHSCOPE_API_KEY = \"DASHSCOPE_API_KEY\";\n private static final String DEEPSEEK_API_KEY = \"DEEPSEEK_API_KEY\";\n private static final String GLM_API_KEY = \"GLM_API_KEY\";\n private static final String GOOGLE_API_KEY = \"GOOGLE_API_KEY\";\n private static final String ANTHROPIC_API_KEY = \"ANTHROPIC_API_KEY\";\n private static final String OPENROUTER_API_KEY = \"OPENROUTER_API_KEY\";\n\n private static final List<String> ALL_KEYS =\n List.of(\n OPENAI_API_KEY,\n DASHSCOPE_API_KEY,\n DEEPSEEK_API_KEY,\n GLM_API_KEY,\n GOOGLE_API_KEY,\n ANTHROPIC_API_KEY,\n OPENROUTER_API_KEY);\n\n /**\n * Gets all registered providers (including disabled ones).\n *\n * @return List of all provider instances\n */\n private static List<ModelProvider> getAllProviders() {\n List<ModelProvider> providers = new ArrayList<>();\n\n // DashScope Compatible providers\n providers.add(new DashScopeCompatibleProvider.QwenPlusOpenAI());\n providers.add(new DashScopeCompatibleProvider.QwenPlusMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3OmniFlashOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3OmniFlashMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3VlPlusOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3VlPlusMultiAgentOpenAI());\n\n // DashScope Compatible providers (Qwen3.5)\n providers.add(new DashScopeCompatibleProvider.Qwen35PlusOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35PlusMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35FlashOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35FlashMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35_397bA17bOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35_397bA17bMultiAgentOpenAI());\n\n // DashScope Native providers\n providers.add(new DashScopeProvider.QwenPlusDashScope());\n providers.add(new DashScopeProvider.QwenPlusMultiAgentDashScope());\n providers.add(new DashScopeProvider.QwenPlusThinkingDashScope());\n providers.add(new DashScopeProvider.QwenPlusThinkingMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen3VlPlusDashScope());\n providers.add(new DashScopeProvider.Qwen3VlPlusMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen35PlusDashScope());\n providers.add(new DashScopeProvider.Qwen35PlusMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen35FlashDashScope());\n providers.add(new DashScopeProvider.Qwen35FlashMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen35_397bA17bDashScope());\n providers.add(new DashScopeProvider.Qwen35_397bA17bMultiAgentDashScope());\n\n // Gemini providers\n providers.add(new GeminiProvider.Gemini25FlashGemini());\n providers.add(new GeminiProvider.Gemini25FlashMultiAgentGemini());\n\n // Anthropic providers\n providers.add(new AnthropicProvider.ClaudeHaiku45Anthropic());\n providers.add(new AnthropicProvider.ClaudeHaiku45MultiAgentAnthropic());\n\n // DeepSeek providers\n providers.add(new DeepSeekProvider.DeepSeekChat());\n providers.add(new DeepSeekProvider.DeepSeekChatMultiAgent());\n providers.add(new DeepSeekReasonerProvider.DeepSeekR1());\n providers.add(new DeepSeekReasonerProvider.DeepSeekR1MultiAgent());\n\n // GLM providers\n providers.add(new GLMProvider.GLM46());\n providers.add(new GLMProvider.GLM46MultiAgent());\n providers.add(new GLMProvider.GLM46VPlus());\n providers.add(new GLMProvider.GLM46VMultiAgent());\n providers.add(new GLMProvider.GLM45());\n providers.add(new GLMProvider.GLM47());\n\n // OpenRouter providers\n providers.add(new OpenRouterProvider.GPT52());\n providers.add(new OpenRouterProvider.GPT52MultiAgent());\n providers.add(new OpenRouterProvider.Claude45Haiku());\n providers.add(new OpenRouterProvider.Claude45HaikuMultiAgent());\n providers.add(new OpenRouterProvider.Qwen3VL());\n providers.add(new OpenRouterProvider.Qwen3VLMultiAgent());\n providers.add(new OpenRouterProvider.Gemini3FlashPreview());\n providers.add(new OpenRouterProvider.Gemini3FlashPreviewMultiAgent());\n providers.add(new OpenRouterProvider.Gemini3ProPreview());\n providers.add(new OpenRouterProvider.Gemini3ProPreviewMultiAgent());\n providers.add(new OpenRouterProvider.DeepSeekV32());\n providers.add(new OpenRouterProvider.DeepSeekV32MultiAgent());\n providers.add(new OpenRouterProvider.DeepSeekR1());\n providers.add(new OpenRouterProvider.DeepSeekR1MultiAgent());\n providers.add(new OpenRouterProvider.GLM46());\n providers.add(new OpenRouterProvider.GLM46MultiAgent());\n\n return providers;\n }\n\n // ==========================================================================\n // API Key Helpers\n // ==========================================================================\n\n protected static boolean hasApiKey(String keyName) {\n String key = System.getenv(keyName);\n if (key == null || key.isEmpty()) {\n key = System.getProperty(keyName);\n }\n return key != null && !key.isEmpty();\n }\n\n protected static boolean hasOpenAIKey() {\n return hasApiKey(OPENAI_API_KEY);\n }\n\n protected static boolean hasDeepSeekKey() {\n return hasApiKey(DEEPSEEK_API_KEY);\n }\n\n protected static boolean hasGLMKey() {\n return hasApiKey(GLM_API_KEY);\n }\n\n protected static boolean hasDashScopeKey() {\n return hasApiKey(DASHSCOPE_API_KEY);\n }\n\n protected static boolean hasGoogleKey() {\n return hasApiKey(GOOGLE_API_KEY);\n }\n\n protected static boolean hasAnthropicKey() {\n return hasApiKey(ANTHROPIC_API_KEY);\n }\n\n protected static boolean hasOpenRouterKey() {\n return hasApiKey(OPENROUTER_API_KEY);\n }\n\n // ==========================================================================\n // Capability-based Provider Selection\n // ==========================================================================\n\n /**\n * Gets providers with all specified capabilities.\n *\n * @param required The required capabilities\n * @return Stream of enabled providers with all required capabilities\n */\n public static Stream<ModelProvider> getProviders(ModelCapability... required) {\n Set<ModelCapability> requiredSet = Set.of(required);\n return getAllProviders().stream()\n .filter(ModelProvider::isEnabled)\n .filter(p -> p.getCapabilities().containsAll(requiredSet));\n }\n\n // ==========================================================================\n // Convenience Methods (renamed from getEnabledXxxProviders to getXxxProviders)\n // ==========================================================================\n\n /**\n * Gets all enabled basic providers for core functionality testing.\n *\n * @return Stream of enabled providers with basic capability\n */\n public static Stream<ModelProvider> getBasicProviders() {\n return getProviders(ModelCapability.BASIC);\n }\n\n /**\n * Gets all enabled providers for tool functionality testing.\n *\n * @return Stream of enabled providers that support tools\n */\n public static Stream<ModelProvider> getToolProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.TOOL_CALLING);\n }\n\n /**\n * Gets all enabled providers for image functionality testing.\n *\n * @return Stream of enabled providers that support images\n */\n public static Stream<ModelProvider> getImageProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.IMAGE);\n }\n\n /**\n * Gets all enabled providers for audio functionality testing.\n *\n * @return Stream of enabled providers that support audio\n */\n public static Stream<ModelProvider> getAudioProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.AUDIO);\n }\n\n /**\n * Gets all enabled providers for multimodal functionality testing.\n *\n * @return Stream of enabled providers that support multiple modalities\n */\n public static Stream<ModelProvider> getMultimodalProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.IMAGE, ModelCapability.AUDIO);\n }\n\n /**\n * Gets all enabled providers for thinking functionality testing.\n *\n * @return Stream of enabled providers that support thinking\n */\n public static Stream<ModelProvider> getThinkingProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.THINKING);\n }\n\n /**\n * Gets all enabled providers for thinking with budget control.\n *\n * @return Stream of enabled providers that support thinking budget\n */\n public static Stream<ModelProvider> getThinkingBudgetProviders() {\n Stream.Builder<ModelProvider> builders = Stream.builder();\n\n if (hasDashScopeKey()) {\n builders.add(new DashScopeProvider.QwenPlusThinkingDashScope(1000));\n builders.add(new DashScopeProvider.QwenPlusThinkingMultiAgentDashScope(1000));\n }\n\n if (hasGLMKey()) {\n builders.add(new GLMProvider.GLM45());\n builders.add(new GLMProvider.GLM47());\n }\n\n if (hasOpenRouterKey()) {\n builders.add(new OpenRouterProvider.DeepSeekR1());\n builders.add(new OpenRouterProvider.DeepSeekR1MultiAgent());\n builders.add(new OpenRouterProvider.Claude45HaikuThinking(1024));\n }\n\n return builders.build();\n }\n\n /**\n * Gets all enabled providers for video functionality testing.\n *\n * @return Stream of enabled providers that support video\n */\n public static Stream<ModelProvider> getVideoProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.VIDEO);\n }\n\n /**\n * Gets all enabled providers for multimodal tool functionality testing.\n *\n * @return Stream of enabled providers that support multimodal tools\n */\n public static Stream<ModelProvider> getMultimodalToolProviders() {\n return getProviders(\n ModelCapability.BASIC, ModelCapability.IMAGE, ModelCapability.TOOL_CALLING);\n }\n\n /**\n * Gets all enabled providers that support multi-agent formatter for MsgHub testing.\n *\n * @return Stream of enabled providers with multi-agent formatter capability\n */\n public static Stream<ModelProvider> getMultiAgentProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.MULTI_AGENT_FORMATTER);\n }\n\n // ==========================================================================\n // Utility Methods\n // ==========================================================================\n\n /**\n * Checks if any E2E tests can be run (has at least one API key).\n *\n * @return true if at least one API key is available\n */\n public static boolean hasAnyApiKey() {\n return ALL_KEYS.stream().anyMatch(ProviderFactory::hasApiKey);\n }\n\n /**\n * Gets a comma-separated list of available API keys for debugging.\n *\n * @return String describing available API keys\n */\n public static String getApiKeyStatus() {\n String status =\n ALL_KEYS.stream()\n .filter(ProviderFactory::hasApiKey)\n .collect(Collectors.joining(\", \"));\n return status.isEmpty() ? \"None\" : status;\n }\n}\n"}, "702461070ddcab29dae7c7a2b130e323a106d020": {"t": 1786067431.9650576, "n": 14512, "h": "c8ab4d4827716b69", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.core.e2e;\n\nimport io.agentscope.core.e2e.providers.AnthropicProvider;\nimport io.agentscope.core.e2e.providers.DashScopeCompatibleProvider;\nimport io.agentscope.core.e2e.providers.DashScopeProvider;\nimport io.agentscope.core.e2e.providers.DeepSeekProvider;\nimport io.agentscope.core.e2e.providers.DeepSeekReasonerProvider;\nimport io.agentscope.core.e2e.providers.GLMProvider;\nimport io.agentscope.core.e2e.providers.GeminiProvider;\nimport io.agentscope.core.e2e.providers.ModelCapability;\nimport io.agentscope.core.e2e.providers.ModelProvider;\nimport io.agentscope.core.e2e.providers.OpenRouterProvider;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * Factory for creating ModelProvider instances based on available API keys.\n *\n * <p>Dynamically provides enabled providers based on environment variables:\n *\n * <ul>\n * <li>OPENAI_API_KEY: Enables OpenAI Native providers\n * <li>DASHSCOPE_API_KEY: Enables DashScope Native, DashScope Compatible, and Bailian providers\n * <li>DEEPSEEK_API_KEY: Enables DeepSeek Native providers\n * <li>GLM_API_KEY: Enables GLM (Zhipu AI) Native providers\n * <li>GOOGLE_API_KEY: Enables Google Gemini Native providers\n * <li>ANTHROPIC_API_KEY: Enables Anthropic Claude Native providers\n * <li>OPENROUTER_API_KEY: Enables OpenRouter providers (access to various models)\n * </ul>\n *\n * <p>Usage:\n *\n * <pre>{@code\n * // Get all basic providers\n * Stream<ModelProvider> basics = ProviderFactory.getBasicProviders();\n *\n * // Get providers with specific capabilities\n * Stream<ModelProvider> imageProviders = ProviderFactory.getProviders(\n * ModelCapability.BASIC, ModelCapability.IMAGE);\n *\n * // Check provider status\n * String status = ProviderFactory.getApiKeyStatus();\n * }</pre>\n */\npublic class ProviderFactory {\n\n private static final String OPENAI_API_KEY = \"OPENAI_API_KEY\";\n private static final String DASHSCOPE_API_KEY = \"DASHSCOPE_API_KEY\";\n private static final String DEEPSEEK_API_KEY = \"DEEPSEEK_API_KEY\";\n private static final String GLM_API_KEY = \"GLM_API_KEY\";\n private static final String GOOGLE_API_KEY = \"GOOGLE_API_KEY\";\n private static final String ANTHROPIC_API_KEY = \"ANTHROPIC_API_KEY\";\n private static final String OPENROUTER_API_KEY = \"OPENROUTER_API_KEY\";\n\n private static final List<String> ALL_KEYS =\n List.of(\n OPENAI_API_KEY,\n DASHSCOPE_API_KEY,\n DEEPSEEK_API_KEY,\n GLM_API_KEY,\n GOOGLE_API_KEY,\n ANTHROPIC_API_KEY,\n OPENROUTER_API_KEY);\n\n /**\n * Gets all registered providers (including disabled ones).\n *\n * @return List of all provider instances\n */\n private static List<ModelProvider> getAllProviders() {\n List<ModelProvider> providers = new ArrayList<>();\n\n // DashScope Compatible providers\n providers.add(new DashScopeCompatibleProvider.QwenPlusOpenAI());\n providers.add(new DashScopeCompatibleProvider.QwenPlusMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3OmniFlashOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3OmniFlashMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3VlPlusOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3VlPlusMultiAgentOpenAI());\n\n // DashScope Compatible providers (Qwen3.5)\n providers.add(new DashScopeCompatibleProvider.Qwen35PlusOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35PlusMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35FlashOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35FlashMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35_397bA17bOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35_397bA17bMultiAgentOpenAI());\n\n // DashScope Native providers\n providers.add(new DashScopeProvider.QwenPlusDashScope());\n providers.add(new DashScopeProvider.QwenPlusMultiAgentDashScope());\n providers.add(new DashScopeProvider.QwenPlusThinkingDashScope());\n providers.add(new DashScopeProvider.QwenPlusThinkingMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen3VlPlusDashScope());\n providers.add(new DashScopeProvider.Qwen3VlPlusMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen35PlusDashScope());\n providers.add(new DashScopeProvider.Qwen35PlusMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen35FlashDashScope());\n providers.add(new DashScopeProvider.Qwen35FlashMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen35_397bA17bDashScope());\n providers.add(new DashScopeProvider.Qwen35_397bA17bMultiAgentDashScope());\n\n // Gemini providers\n providers.add(new GeminiProvider.Gemini25FlashGemini());\n providers.add(new GeminiProvider.Gemini25FlashMultiAgentGemini());\n\n // Anthropic providers\n providers.add(new AnthropicProvider.ClaudeHaiku45Anthropic());\n providers.add(new AnthropicProvider.ClaudeHaiku45MultiAgentAnthropic());\n\n // DeepSeek providers\n providers.add(new DeepSeekProvider.DeepSeekChat());\n providers.add(new DeepSeekProvider.DeepSeekChatMultiAgent());\n providers.add(new DeepSeekReasonerProvider.DeepSeekR1());\n providers.add(new DeepSeekReasonerProvider.DeepSeekR1MultiAgent());\n\n // GLM providers\n providers.add(new GLMProvider.GLM46());\n providers.add(new GLMProvider.GLM46MultiAgent());\n providers.add(new GLMProvider.GLM46VPlus());\n providers.add(new GLMProvider.GLM46VMultiAgent());\n providers.add(new GLMProvider.GLM45());\n providers.add(new GLMProvider.GLM47());\n\n // OpenRouter providers\n providers.add(new OpenRouterProvider.GPT52());\n providers.add(new OpenRouterProvider.GPT52MultiAgent());\n providers.add(new OpenRouterProvider.Claude45Haiku());\n providers.add(new OpenRouterProvider.Claude45HaikuMultiAgent());\n providers.add(new OpenRouterProvider.Qwen3VL());\n providers.add(new OpenRouterProvider.Qwen3VLMultiAgent());\n providers.add(new OpenRouterProvider.Gemini3FlashPreview());\n providers.add(new OpenRouterProvider.Gemini3FlashPreviewMultiAgent());\n providers.add(new OpenRouterProvider.Gemini3ProPreview());\n providers.add(new OpenRouterProvider.Gemini3ProPreviewMultiAgent());\n providers.add(new OpenRouterProvider.DeepSeekV32());\n providers.add(new OpenRouterProvider.DeepSeekV32MultiAgent());\n providers.add(new OpenRouterProvider.DeepSeekR1());\n providers.add(new OpenRouterProvider.DeepSeekR1MultiAgent());\n providers.add(new OpenRouterProvider.GLM46());\n providers.add(new OpenRouterProvider.GLM46MultiAgent());\n\n return providers;\n }\n\n // ==========================================================================\n // API Key Helpers\n // ==========================================================================\n\n protected static boolean hasApiKey(String keyName) {\n String key = System.getenv(keyName);\n if (key == null || key.isEmpty()) {\n key = System.getProperty(keyName);\n }\n return key != null && !key.isEmpty();\n }\n\n protected static boolean hasOpenAIKey() {\n return hasApiKey(OPENAI_API_KEY);\n }\n\n protected static boolean hasDeepSeekKey() {\n return hasApiKey(DEEPSEEK_API_KEY);\n }\n\n protected static boolean hasGLMKey() {\n return hasApiKey(GLM_API_KEY);\n }\n\n protected static boolean hasDashScopeKey() {\n return hasApiKey(DASHSCOPE_API_KEY);\n }\n\n protected static boolean hasGoogleKey() {\n return hasApiKey(GOOGLE_API_KEY);\n }\n\n protected static boolean hasAnthropicKey() {\n return hasApiKey(ANTHROPIC_API_KEY);\n }\n\n protected static boolean hasOpenRouterKey() {\n return hasApiKey(OPENROUTER_API_KEY);\n }\n\n // ==========================================================================\n // Capability-based Provider Selection\n // ==========================================================================\n\n /**\n * Gets providers with all specified capabilities.\n *\n * @param required The required capabilities\n * @return Stream of enabled providers with all required capabilities\n */\n public static Stream<ModelProvider> getProviders(ModelCapability... required) {\n Set<ModelCapability> requiredSet = Set.of(required);\n return getAllProviders().stream()\n .filter(ModelProvider::isEnabled)\n .filter(p -> p.getCapabilities().containsAll(requiredSet));\n }\n\n // ==========================================================================\n // Convenience Methods (renamed from getEnabledXxxProviders to getXxxProviders)\n // ==========================================================================\n\n /**\n * Gets all enabled basic providers for core functionality testing.\n *\n * @return Stream of enabled providers with basic capability\n */\n public static Stream<ModelProvider> getBasicProviders() {\n return getProviders(ModelCapability.BASIC);\n }\n\n /**\n * Gets all enabled providers for tool functionality testing.\n *\n * @return Stream of enabled providers that support tools\n */\n public static Stream<ModelProvider> getToolProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.TOOL_CALLING);\n }\n\n /**\n * Gets all enabled providers for image functionality testing.\n *\n * @return Stream of enabled providers that support images\n */\n public static Stream<ModelProvider> getImageProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.IMAGE);\n }\n\n /**\n * Gets all enabled providers for audio functionality testing.\n *\n * @return Stream of enabled providers that support audio\n */\n public static Stream<ModelProvider> getAudioProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.AUDIO);\n }\n\n /**\n * Gets all enabled providers for multimodal functionality testing.\n *\n * @return Stream of enabled providers that support multiple modalities\n */\n public static Stream<ModelProvider> getMultimodalProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.IMAGE, ModelCapability.AUDIO);\n }\n\n /**\n * Gets all enabled providers for thinking functionality testing.\n *\n * @return Stream of enabled providers that support thinking\n */\n public static Stream<ModelProvider> getThinkingProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.THINKING);\n }\n\n /**\n * Gets all enabled providers for thinking with budget control.\n *\n * @return Stream of enabled providers that support thinking budget\n */\n public static Stream<ModelProvider> getThinkingBudgetProviders() {\n Stream.Builder<ModelProvider> builders = Stream.builder();\n\n if (hasDashScopeKey()) {\n builders.add(new DashScopeProvider.QwenPlusThinkingDashScope(1000));\n builders.add(new DashScopeProvider.QwenPlusThinkingMultiAgentDashScope(1000));\n }\n\n if (hasGLMKey()) {\n builders.add(new GLMProvider.GLM45());\n builders.add(new GLMProvider.GLM47());\n }\n\n if (hasOpenRouterKey()) {\n builders.add(new OpenRouterProvider.DeepSeekR1());\n builders.add(new OpenRouterProvider.DeepSeekR1MultiAgent());\n builders.add(new OpenRouterProvider.Claude45HaikuThinking(1024));\n }\n\n return builders.build();\n }\n\n /**\n * Gets all enabled providers for video functionality testing.\n *\n * @return Stream of enabled providers that support video\n */\n public static Stream<ModelProvider> getVideoProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.VIDEO);\n }\n\n /**\n * Gets all enabled providers for multimodal tool functionality testing.\n *\n * @return Stream of enabled providers that support multimodal tools\n */\n public static Stream<ModelProvider> getMultimodalToolProviders() {\n return getProviders(\n ModelCapability.BASIC, ModelCapability.IMAGE, ModelCapability.TOOL_CALLING);\n }\n\n /**\n * Gets all enabled providers that support multi-agent formatter for MsgHub testing.\n *\n * @return Stream of enabled providers with multi-agent formatter capability\n */\n public static Stream<ModelProvider> getMultiAgentProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.MULTI_AGENT_FORMATTER);\n }\n\n // ==========================================================================\n // Utility Methods\n // ==========================================================================\n\n /**\n * Checks if any E2E tests can be run (has at least one API key).\n *\n * @return true if at least one API key is available\n */\n public static boolean hasAnyApiKey() {\n return ALL_KEYS.stream().anyMatch(ProviderFactory::hasApiKey);\n }\n\n /**\n * Gets a comma-separated list of available API keys for debugging.\n *\n * @return String describing available API keys\n */\n public static String getApiKeyStatus() {\n String status =\n ALL_KEYS.stream()\n .filter(ProviderFactory::hasApiKey)\n .collect(Collectors.joining(\", \"));\n return status.isEmpty() ? \"None\" : status;\n }\n}\n"}, "9cbac4ce574183274c37b5132ce453ffcaeafb74": {"t": 1786068300.8015862, "n": 14512, "h": "c8ab4d4827716b69", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.core.e2e;\n\nimport io.agentscope.core.e2e.providers.AnthropicProvider;\nimport io.agentscope.core.e2e.providers.DashScopeCompatibleProvider;\nimport io.agentscope.core.e2e.providers.DashScopeProvider;\nimport io.agentscope.core.e2e.providers.DeepSeekProvider;\nimport io.agentscope.core.e2e.providers.DeepSeekReasonerProvider;\nimport io.agentscope.core.e2e.providers.GLMProvider;\nimport io.agentscope.core.e2e.providers.GeminiProvider;\nimport io.agentscope.core.e2e.providers.ModelCapability;\nimport io.agentscope.core.e2e.providers.ModelProvider;\nimport io.agentscope.core.e2e.providers.OpenRouterProvider;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * Factory for creating ModelProvider instances based on available API keys.\n *\n * <p>Dynamically provides enabled providers based on environment variables:\n *\n * <ul>\n * <li>OPENAI_API_KEY: Enables OpenAI Native providers\n * <li>DASHSCOPE_API_KEY: Enables DashScope Native, DashScope Compatible, and Bailian providers\n * <li>DEEPSEEK_API_KEY: Enables DeepSeek Native providers\n * <li>GLM_API_KEY: Enables GLM (Zhipu AI) Native providers\n * <li>GOOGLE_API_KEY: Enables Google Gemini Native providers\n * <li>ANTHROPIC_API_KEY: Enables Anthropic Claude Native providers\n * <li>OPENROUTER_API_KEY: Enables OpenRouter providers (access to various models)\n * </ul>\n *\n * <p>Usage:\n *\n * <pre>{@code\n * // Get all basic providers\n * Stream<ModelProvider> basics = ProviderFactory.getBasicProviders();\n *\n * // Get providers with specific capabilities\n * Stream<ModelProvider> imageProviders = ProviderFactory.getProviders(\n * ModelCapability.BASIC, ModelCapability.IMAGE);\n *\n * // Check provider status\n * String status = ProviderFactory.getApiKeyStatus();\n * }</pre>\n */\npublic class ProviderFactory {\n\n private static final String OPENAI_API_KEY = \"OPENAI_API_KEY\";\n private static final String DASHSCOPE_API_KEY = \"DASHSCOPE_API_KEY\";\n private static final String DEEPSEEK_API_KEY = \"DEEPSEEK_API_KEY\";\n private static final String GLM_API_KEY = \"GLM_API_KEY\";\n private static final String GOOGLE_API_KEY = \"GOOGLE_API_KEY\";\n private static final String ANTHROPIC_API_KEY = \"ANTHROPIC_API_KEY\";\n private static final String OPENROUTER_API_KEY = \"OPENROUTER_API_KEY\";\n\n private static final List<String> ALL_KEYS =\n List.of(\n OPENAI_API_KEY,\n DASHSCOPE_API_KEY,\n DEEPSEEK_API_KEY,\n GLM_API_KEY,\n GOOGLE_API_KEY,\n ANTHROPIC_API_KEY,\n OPENROUTER_API_KEY);\n\n /**\n * Gets all registered providers (including disabled ones).\n *\n * @return List of all provider instances\n */\n private static List<ModelProvider> getAllProviders() {\n List<ModelProvider> providers = new ArrayList<>();\n\n // DashScope Compatible providers\n providers.add(new DashScopeCompatibleProvider.QwenPlusOpenAI());\n providers.add(new DashScopeCompatibleProvider.QwenPlusMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3OmniFlashOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3OmniFlashMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3VlPlusOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen3VlPlusMultiAgentOpenAI());\n\n // DashScope Compatible providers (Qwen3.5)\n providers.add(new DashScopeCompatibleProvider.Qwen35PlusOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35PlusMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35FlashOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35FlashMultiAgentOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35_397bA17bOpenAI());\n providers.add(new DashScopeCompatibleProvider.Qwen35_397bA17bMultiAgentOpenAI());\n\n // DashScope Native providers\n providers.add(new DashScopeProvider.QwenPlusDashScope());\n providers.add(new DashScopeProvider.QwenPlusMultiAgentDashScope());\n providers.add(new DashScopeProvider.QwenPlusThinkingDashScope());\n providers.add(new DashScopeProvider.QwenPlusThinkingMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen3VlPlusDashScope());\n providers.add(new DashScopeProvider.Qwen3VlPlusMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen35PlusDashScope());\n providers.add(new DashScopeProvider.Qwen35PlusMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen35FlashDashScope());\n providers.add(new DashScopeProvider.Qwen35FlashMultiAgentDashScope());\n providers.add(new DashScopeProvider.Qwen35_397bA17bDashScope());\n providers.add(new DashScopeProvider.Qwen35_397bA17bMultiAgentDashScope());\n\n // Gemini providers\n providers.add(new GeminiProvider.Gemini25FlashGemini());\n providers.add(new GeminiProvider.Gemini25FlashMultiAgentGemini());\n\n // Anthropic providers\n providers.add(new AnthropicProvider.ClaudeHaiku45Anthropic());\n providers.add(new AnthropicProvider.ClaudeHaiku45MultiAgentAnthropic());\n\n // DeepSeek providers\n providers.add(new DeepSeekProvider.DeepSeekChat());\n providers.add(new DeepSeekProvider.DeepSeekChatMultiAgent());\n providers.add(new DeepSeekReasonerProvider.DeepSeekR1());\n providers.add(new DeepSeekReasonerProvider.DeepSeekR1MultiAgent());\n\n // GLM providers\n providers.add(new GLMProvider.GLM46());\n providers.add(new GLMProvider.GLM46MultiAgent());\n providers.add(new GLMProvider.GLM46VPlus());\n providers.add(new GLMProvider.GLM46VMultiAgent());\n providers.add(new GLMProvider.GLM45());\n providers.add(new GLMProvider.GLM47());\n\n // OpenRouter providers\n providers.add(new OpenRouterProvider.GPT52());\n providers.add(new OpenRouterProvider.GPT52MultiAgent());\n providers.add(new OpenRouterProvider.Claude45Haiku());\n providers.add(new OpenRouterProvider.Claude45HaikuMultiAgent());\n providers.add(new OpenRouterProvider.Qwen3VL());\n providers.add(new OpenRouterProvider.Qwen3VLMultiAgent());\n providers.add(new OpenRouterProvider.Gemini3FlashPreview());\n providers.add(new OpenRouterProvider.Gemini3FlashPreviewMultiAgent());\n providers.add(new OpenRouterProvider.Gemini3ProPreview());\n providers.add(new OpenRouterProvider.Gemini3ProPreviewMultiAgent());\n providers.add(new OpenRouterProvider.DeepSeekV32());\n providers.add(new OpenRouterProvider.DeepSeekV32MultiAgent());\n providers.add(new OpenRouterProvider.DeepSeekR1());\n providers.add(new OpenRouterProvider.DeepSeekR1MultiAgent());\n providers.add(new OpenRouterProvider.GLM46());\n providers.add(new OpenRouterProvider.GLM46MultiAgent());\n\n return providers;\n }\n\n // ==========================================================================\n // API Key Helpers\n // ==========================================================================\n\n protected static boolean hasApiKey(String keyName) {\n String key = System.getenv(keyName);\n if (key == null || key.isEmpty()) {\n key = System.getProperty(keyName);\n }\n return key != null && !key.isEmpty();\n }\n\n protected static boolean hasOpenAIKey() {\n return hasApiKey(OPENAI_API_KEY);\n }\n\n protected static boolean hasDeepSeekKey() {\n return hasApiKey(DEEPSEEK_API_KEY);\n }\n\n protected static boolean hasGLMKey() {\n return hasApiKey(GLM_API_KEY);\n }\n\n protected static boolean hasDashScopeKey() {\n return hasApiKey(DASHSCOPE_API_KEY);\n }\n\n protected static boolean hasGoogleKey() {\n return hasApiKey(GOOGLE_API_KEY);\n }\n\n protected static boolean hasAnthropicKey() {\n return hasApiKey(ANTHROPIC_API_KEY);\n }\n\n protected static boolean hasOpenRouterKey() {\n return hasApiKey(OPENROUTER_API_KEY);\n }\n\n // ==========================================================================\n // Capability-based Provider Selection\n // ==========================================================================\n\n /**\n * Gets providers with all specified capabilities.\n *\n * @param required The required capabilities\n * @return Stream of enabled providers with all required capabilities\n */\n public static Stream<ModelProvider> getProviders(ModelCapability... required) {\n Set<ModelCapability> requiredSet = Set.of(required);\n return getAllProviders().stream()\n .filter(ModelProvider::isEnabled)\n .filter(p -> p.getCapabilities().containsAll(requiredSet));\n }\n\n // ==========================================================================\n // Convenience Methods (renamed from getEnabledXxxProviders to getXxxProviders)\n // ==========================================================================\n\n /**\n * Gets all enabled basic providers for core functionality testing.\n *\n * @return Stream of enabled providers with basic capability\n */\n public static Stream<ModelProvider> getBasicProviders() {\n return getProviders(ModelCapability.BASIC);\n }\n\n /**\n * Gets all enabled providers for tool functionality testing.\n *\n * @return Stream of enabled providers that support tools\n */\n public static Stream<ModelProvider> getToolProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.TOOL_CALLING);\n }\n\n /**\n * Gets all enabled providers for image functionality testing.\n *\n * @return Stream of enabled providers that support images\n */\n public static Stream<ModelProvider> getImageProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.IMAGE);\n }\n\n /**\n * Gets all enabled providers for audio functionality testing.\n *\n * @return Stream of enabled providers that support audio\n */\n public static Stream<ModelProvider> getAudioProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.AUDIO);\n }\n\n /**\n * Gets all enabled providers for multimodal functionality testing.\n *\n * @return Stream of enabled providers that support multiple modalities\n */\n public static Stream<ModelProvider> getMultimodalProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.IMAGE, ModelCapability.AUDIO);\n }\n\n /**\n * Gets all enabled providers for thinking functionality testing.\n *\n * @return Stream of enabled providers that support thinking\n */\n public static Stream<ModelProvider> getThinkingProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.THINKING);\n }\n\n /**\n * Gets all enabled providers for thinking with budget control.\n *\n * @return Stream of enabled providers that support thinking budget\n */\n public static Stream<ModelProvider> getThinkingBudgetProviders() {\n Stream.Builder<ModelProvider> builders = Stream.builder();\n\n if (hasDashScopeKey()) {\n builders.add(new DashScopeProvider.QwenPlusThinkingDashScope(1000));\n builders.add(new DashScopeProvider.QwenPlusThinkingMultiAgentDashScope(1000));\n }\n\n if (hasGLMKey()) {\n builders.add(new GLMProvider.GLM45());\n builders.add(new GLMProvider.GLM47());\n }\n\n if (hasOpenRouterKey()) {\n builders.add(new OpenRouterProvider.DeepSeekR1());\n builders.add(new OpenRouterProvider.DeepSeekR1MultiAgent());\n builders.add(new OpenRouterProvider.Claude45HaikuThinking(1024));\n }\n\n return builders.build();\n }\n\n /**\n * Gets all enabled providers for video functionality testing.\n *\n * @return Stream of enabled providers that support video\n */\n public static Stream<ModelProvider> getVideoProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.VIDEO);\n }\n\n /**\n * Gets all enabled providers for multimodal tool functionality testing.\n *\n * @return Stream of enabled providers that support multimodal tools\n */\n public static Stream<ModelProvider> getMultimodalToolProviders() {\n return getProviders(\n ModelCapability.BASIC, ModelCapability.IMAGE, ModelCapability.TOOL_CALLING);\n }\n\n /**\n * Gets all enabled providers that support multi-agent formatter for MsgHub testing.\n *\n * @return Stream of enabled providers with multi-agent formatter capability\n */\n public static Stream<ModelProvider> getMultiAgentProviders() {\n return getProviders(ModelCapability.BASIC, ModelCapability.MULTI_AGENT_FORMATTER);\n }\n\n // ==========================================================================\n // Utility Methods\n // ==========================================================================\n\n /**\n * Checks if any E2E tests can be run (has at least one API key).\n *\n * @return true if at least one API key is available\n */\n public static boolean hasAnyApiKey() {\n return ALL_KEYS.stream().anyMatch(ProviderFactory::hasApiKey);\n }\n\n /**\n * Gets a comma-separated list of available API keys for debugging.\n *\n * @return String describing available API keys\n */\n public static String getApiKeyStatus() {\n String status =\n ALL_KEYS.stream()\n .filter(ProviderFactory::hasApiKey)\n .collect(Collectors.joining(\", \"));\n return status.isEmpty() ? \"None\" : status;\n }\n}\n"}}, "agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-e2e-tests/src/test/java/io/agentscope/core/e2e/providers/DeepSeekReasonerProvider.java": {"ef06c7ac978d7c61b173e47a57b8772b1dd5f216": {"t": 1785745675.6685283, "n": 3914, "h": "6490d60aae73835e", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.core.e2e.providers;\n\nimport io.agentscope.core.ReActAgent;\nimport io.agentscope.core.tool.Toolkit;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport io.agentscope.extensions.model.openai.compat.deepseek.DeepSeekFormatter;\nimport io.agentscope.extensions.model.openai.compat.deepseek.DeepSeekMultiAgentFormatter;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * Provider for DeepSeek API - OpenAI compatible.\n *\n * <p>Supports DeepSeek Chat (V3) and DeepSeek R1 (Reasoner) models.\n */\n@ModelCapabilities({ModelCapability.BASIC, ModelCapability.TOOL_CALLING})\npublic class DeepSeekReasonerProvider extends BaseModelProvider {\n\n private static final String API_KEY_ENV = \"DEEPSEEK_API_KEY\";\n private static final String DEEPSEEK_BASE_URL = \"https://api.deepseek.com\";\n\n public DeepSeekReasonerProvider(String modelName, boolean multiAgentFormatter) {\n super(API_KEY_ENV, modelName, multiAgentFormatter);\n }\n\n @Override\n protected ReActAgent.Builder doCreateAgentBuilder(String name, Toolkit toolkit, String apiKey) {\n OpenAIChatModel model =\n OpenAIChatModel.builder()\n .baseUrl(DEEPSEEK_BASE_URL)\n .apiKey(apiKey)\n .modelName(getModelName())\n .stream(true)\n .formatter(\n isMultiAgentFormatter()\n ? new DeepSeekMultiAgentFormatter(true)\n : new DeepSeekFormatter(true))\n .build();\n\n return ReActAgent.builder().name(name).model(model).toolkit(toolkit);\n }\n\n @Override\n public String getProviderName() {\n return \"DeepSeek\";\n }\n\n @Override\n public Set<ModelCapability> getCapabilities() {\n Set<ModelCapability> caps = new HashSet<>(super.getCapabilities());\n if (isMultiAgentFormatter()) {\n caps.add(ModelCapability.MULTI_AGENT_FORMATTER);\n }\n return caps;\n }\n\n // ==========================================================================\n // Provider Instances\n // ==========================================================================\n\n /** DeepSeek R1 (Reasoner) - Thinking model, does NOT support tool calling. */\n @ModelCapabilities({\n ModelCapability.BASIC,\n ModelCapability.TOOL_CALLING,\n ModelCapability.THINKING\n })\n public static class DeepSeekR1 extends DeepSeekReasonerProvider {\n public DeepSeekR1() {\n super(\"deepseek-reasoner\", false);\n }\n\n @Override\n public String getProviderName() {\n return \"DeepSeek R1\";\n }\n }\n\n /** DeepSeek R1 (Reasoner) with Multi-Agent Formatter. */\n @ModelCapabilities({\n ModelCapability.BASIC,\n ModelCapability.TOOL_CALLING,\n ModelCapability.THINKING,\n ModelCapability.MULTI_AGENT_FORMATTER\n })\n public static class DeepSeekR1MultiAgent extends DeepSeekReasonerProvider {\n public DeepSeekR1MultiAgent() {\n super(\"deepseek-reasoner\", true);\n }\n\n @Override\n public String getProviderName() {\n return \"DeepSeek R1 (MultiAgent)\";\n }\n }\n}\n"}, "74db7572f20f07f05439a0e1b0d6992f9f7c6a38": {"t": 1785755444.8396182, "n": 3914, "h": "6490d60aae73835e", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.core.e2e.providers;\n\nimport io.agentscope.core.ReActAgent;\nimport io.agentscope.core.tool.Toolkit;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport io.agentscope.extensions.model.openai.compat.deepseek.DeepSeekFormatter;\nimport io.agentscope.extensions.model.openai.compat.deepseek.DeepSeekMultiAgentFormatter;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * Provider for DeepSeek API - OpenAI compatible.\n *\n * <p>Supports DeepSeek Chat (V3) and DeepSeek R1 (Reasoner) models.\n */\n@ModelCapabilities({ModelCapability.BASIC, ModelCapability.TOOL_CALLING})\npublic class DeepSeekReasonerProvider extends BaseModelProvider {\n\n private static final String API_KEY_ENV = \"DEEPSEEK_API_KEY\";\n private static final String DEEPSEEK_BASE_URL = \"https://api.deepseek.com\";\n\n public DeepSeekReasonerProvider(String modelName, boolean multiAgentFormatter) {\n super(API_KEY_ENV, modelName, multiAgentFormatter);\n }\n\n @Override\n protected ReActAgent.Builder doCreateAgentBuilder(String name, Toolkit toolkit, String apiKey) {\n OpenAIChatModel model =\n OpenAIChatModel.builder()\n .baseUrl(DEEPSEEK_BASE_URL)\n .apiKey(apiKey)\n .modelName(getModelName())\n .stream(true)\n .formatter(\n isMultiAgentFormatter()\n ? new DeepSeekMultiAgentFormatter(true)\n : new DeepSeekFormatter(true))\n .build();\n\n return ReActAgent.builder().name(name).model(model).toolkit(toolkit);\n }\n\n @Override\n public String getProviderName() {\n return \"DeepSeek\";\n }\n\n @Override\n public Set<ModelCapability> getCapabilities() {\n Set<ModelCapability> caps = new HashSet<>(super.getCapabilities());\n if (isMultiAgentFormatter()) {\n caps.add(ModelCapability.MULTI_AGENT_FORMATTER);\n }\n return caps;\n }\n\n // ==========================================================================\n // Provider Instances\n // ==========================================================================\n\n /** DeepSeek R1 (Reasoner) - Thinking model, does NOT support tool calling. */\n @ModelCapabilities({\n ModelCapability.BASIC,\n ModelCapability.TOOL_CALLING,\n ModelCapability.THINKING\n })\n public static class DeepSeekR1 extends DeepSeekReasonerProvider {\n public DeepSeekR1() {\n super(\"deepseek-reasoner\", false);\n }\n\n @Override\n public String getProviderName() {\n return \"DeepSeek R1\";\n }\n }\n\n /** DeepSeek R1 (Reasoner) with Multi-Agent Formatter. */\n @ModelCapabilities({\n ModelCapability.BASIC,\n ModelCapability.TOOL_CALLING,\n ModelCapability.THINKING,\n ModelCapability.MULTI_AGENT_FORMATTER\n })\n public static class DeepSeekR1MultiAgent extends DeepSeekReasonerProvider {\n public DeepSeekR1MultiAgent() {\n super(\"deepseek-reasoner\", true);\n }\n\n @Override\n public String getProviderName() {\n return \"DeepSeek R1 (MultiAgent)\";\n }\n }\n}\n"}, "235ffb82406b4790a7a98624c2724741a48fd584": {"t": 1785755446.2843535, "n": 3902, "h": "3c547dbab650ddf7", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.core.e2e.providers;\n\nimport io.agentscope.core.ReActAgent;\nimport io.agentscope.core.tool.Toolkit;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport io.agentscope.extensions.model.openai.formatter.DeepSeekFormatter;\nimport io.agentscope.extensions.model.openai.formatter.DeepSeekMultiAgentFormatter;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * Provider for DeepSeek API - OpenAI compatible.\n *\n * <p>Supports DeepSeek Chat (V3) and DeepSeek R1 (Reasoner) models.\n */\n@ModelCapabilities({ModelCapability.BASIC, ModelCapability.TOOL_CALLING})\npublic class DeepSeekReasonerProvider extends BaseModelProvider {\n\n private static final String API_KEY_ENV = \"DEEPSEEK_API_KEY\";\n private static final String DEEPSEEK_BASE_URL = \"https://api.deepseek.com\";\n\n public DeepSeekReasonerProvider(String modelName, boolean multiAgentFormatter) {\n super(API_KEY_ENV, modelName, multiAgentFormatter);\n }\n\n @Override\n protected ReActAgent.Builder doCreateAgentBuilder(String name, Toolkit toolkit, String apiKey) {\n OpenAIChatModel model =\n OpenAIChatModel.builder()\n .baseUrl(DEEPSEEK_BASE_URL)\n .apiKey(apiKey)\n .modelName(getModelName())\n .stream(true)\n .formatter(\n isMultiAgentFormatter()\n ? new DeepSeekMultiAgentFormatter(true)\n : new DeepSeekFormatter(true))\n .build();\n\n return ReActAgent.builder().name(name).model(model).toolkit(toolkit);\n }\n\n @Override\n public String getProviderName() {\n return \"DeepSeek\";\n }\n\n @Override\n public Set<ModelCapability> getCapabilities() {\n Set<ModelCapability> caps = new HashSet<>(super.getCapabilities());\n if (isMultiAgentFormatter()) {\n caps.add(ModelCapability.MULTI_AGENT_FORMATTER);\n }\n return caps;\n }\n\n // ==========================================================================\n // Provider Instances\n // ==========================================================================\n\n /** DeepSeek R1 (Reasoner) - Thinking model, does NOT support tool calling. */\n @ModelCapabilities({\n ModelCapability.BASIC,\n ModelCapability.TOOL_CALLING,\n ModelCapability.THINKING\n })\n public static class DeepSeekR1 extends DeepSeekReasonerProvider {\n public DeepSeekR1() {\n super(\"deepseek-reasoner\", false);\n }\n\n @Override\n public String getProviderName() {\n return \"DeepSeek R1\";\n }\n }\n\n /** DeepSeek R1 (Reasoner) with Multi-Agent Formatter. */\n @ModelCapabilities({\n ModelCapability.BASIC,\n ModelCapability.TOOL_CALLING,\n ModelCapability.THINKING,\n ModelCapability.MULTI_AGENT_FORMATTER\n })\n public static class DeepSeekR1MultiAgent extends DeepSeekReasonerProvider {\n public DeepSeekR1MultiAgent() {\n super(\"deepseek-reasoner\", true);\n }\n\n @Override\n public String getProviderName() {\n return \"DeepSeek R1 (MultiAgent)\";\n }\n }\n}\n"}, "702461070ddcab29dae7c7a2b130e323a106d020": {"t": 1786067433.6021929, "n": 3914, "h": "6490d60aae73835e", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.core.e2e.providers;\n\nimport io.agentscope.core.ReActAgent;\nimport io.agentscope.core.tool.Toolkit;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport io.agentscope.extensions.model.openai.compat.deepseek.DeepSeekFormatter;\nimport io.agentscope.extensions.model.openai.compat.deepseek.DeepSeekMultiAgentFormatter;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * Provider for DeepSeek API - OpenAI compatible.\n *\n * <p>Supports DeepSeek Chat (V3) and DeepSeek R1 (Reasoner) models.\n */\n@ModelCapabilities({ModelCapability.BASIC, ModelCapability.TOOL_CALLING})\npublic class DeepSeekReasonerProvider extends BaseModelProvider {\n\n private static final String API_KEY_ENV = \"DEEPSEEK_API_KEY\";\n private static final String DEEPSEEK_BASE_URL = \"https://api.deepseek.com\";\n\n public DeepSeekReasonerProvider(String modelName, boolean multiAgentFormatter) {\n super(API_KEY_ENV, modelName, multiAgentFormatter);\n }\n\n @Override\n protected ReActAgent.Builder doCreateAgentBuilder(String name, Toolkit toolkit, String apiKey) {\n OpenAIChatModel model =\n OpenAIChatModel.builder()\n .baseUrl(DEEPSEEK_BASE_URL)\n .apiKey(apiKey)\n .modelName(getModelName())\n .stream(true)\n .formatter(\n isMultiAgentFormatter()\n ? new DeepSeekMultiAgentFormatter(true)\n : new DeepSeekFormatter(true))\n .build();\n\n return ReActAgent.builder().name(name).model(model).toolkit(toolkit);\n }\n\n @Override\n public String getProviderName() {\n return \"DeepSeek\";\n }\n\n @Override\n public Set<ModelCapability> getCapabilities() {\n Set<ModelCapability> caps = new HashSet<>(super.getCapabilities());\n if (isMultiAgentFormatter()) {\n caps.add(ModelCapability.MULTI_AGENT_FORMATTER);\n }\n return caps;\n }\n\n // ==========================================================================\n // Provider Instances\n // ==========================================================================\n\n /** DeepSeek R1 (Reasoner) - Thinking model, does NOT support tool calling. */\n @ModelCapabilities({\n ModelCapability.BASIC,\n ModelCapability.TOOL_CALLING,\n ModelCapability.THINKING\n })\n public static class DeepSeekR1 extends DeepSeekReasonerProvider {\n public DeepSeekR1() {\n super(\"deepseek-reasoner\", false);\n }\n\n @Override\n public String getProviderName() {\n return \"DeepSeek R1\";\n }\n }\n\n /** DeepSeek R1 (Reasoner) with Multi-Agent Formatter. */\n @ModelCapabilities({\n ModelCapability.BASIC,\n ModelCapability.TOOL_CALLING,\n ModelCapability.THINKING,\n ModelCapability.MULTI_AGENT_FORMATTER\n })\n public static class DeepSeekR1MultiAgent extends DeepSeekReasonerProvider {\n public DeepSeekR1MultiAgent() {\n super(\"deepseek-reasoner\", true);\n }\n\n @Override\n public String getProviderName() {\n return \"DeepSeek R1 (MultiAgent)\";\n }\n }\n}\n"}, "9cbac4ce574183274c37b5132ce453ffcaeafb74": {"t": 1786068300.9711885, "n": 3914, "h": "6490d60aae73835e", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.core.e2e.providers;\n\nimport io.agentscope.core.ReActAgent;\nimport io.agentscope.core.tool.Toolkit;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport io.agentscope.extensions.model.openai.compat.deepseek.DeepSeekFormatter;\nimport io.agentscope.extensions.model.openai.compat.deepseek.DeepSeekMultiAgentFormatter;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * Provider for DeepSeek API - OpenAI compatible.\n *\n * <p>Supports DeepSeek Chat (V3) and DeepSeek R1 (Reasoner) models.\n */\n@ModelCapabilities({ModelCapability.BASIC, ModelCapability.TOOL_CALLING})\npublic class DeepSeekReasonerProvider extends BaseModelProvider {\n\n private static final String API_KEY_ENV = \"DEEPSEEK_API_KEY\";\n private static final String DEEPSEEK_BASE_URL = \"https://api.deepseek.com\";\n\n public DeepSeekReasonerProvider(String modelName, boolean multiAgentFormatter) {\n super(API_KEY_ENV, modelName, multiAgentFormatter);\n }\n\n @Override\n protected ReActAgent.Builder doCreateAgentBuilder(String name, Toolkit toolkit, String apiKey) {\n OpenAIChatModel model =\n OpenAIChatModel.builder()\n .baseUrl(DEEPSEEK_BASE_URL)\n .apiKey(apiKey)\n .modelName(getModelName())\n .stream(true)\n .formatter(\n isMultiAgentFormatter()\n ? new DeepSeekMultiAgentFormatter(true)\n : new DeepSeekFormatter(true))\n .build();\n\n return ReActAgent.builder().name(name).model(model).toolkit(toolkit);\n }\n\n @Override\n public String getProviderName() {\n return \"DeepSeek\";\n }\n\n @Override\n public Set<ModelCapability> getCapabilities() {\n Set<ModelCapability> caps = new HashSet<>(super.getCapabilities());\n if (isMultiAgentFormatter()) {\n caps.add(ModelCapability.MULTI_AGENT_FORMATTER);\n }\n return caps;\n }\n\n // ==========================================================================\n // Provider Instances\n // ==========================================================================\n\n /** DeepSeek R1 (Reasoner) - Thinking model, does NOT support tool calling. */\n @ModelCapabilities({\n ModelCapability.BASIC,\n ModelCapability.TOOL_CALLING,\n ModelCapability.THINKING\n })\n public static class DeepSeekR1 extends DeepSeekReasonerProvider {\n public DeepSeekR1() {\n super(\"deepseek-reasoner\", false);\n }\n\n @Override\n public String getProviderName() {\n return \"DeepSeek R1\";\n }\n }\n\n /** DeepSeek R1 (Reasoner) with Multi-Agent Formatter. */\n @ModelCapabilities({\n ModelCapability.BASIC,\n ModelCapability.TOOL_CALLING,\n ModelCapability.THINKING,\n ModelCapability.MULTI_AGENT_FORMATTER\n })\n public static class DeepSeekR1MultiAgent extends DeepSeekReasonerProvider {\n public DeepSeekR1MultiAgent() {\n super(\"deepseek-reasoner\", true);\n }\n\n @Override\n public String getProviderName() {\n return \"DeepSeek R1 (MultiAgent)\";\n }\n }\n}\n"}}, "agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-openai/src/test/java/io/agentscope/extensions/model/openai/compat/deepseek/DeepSeekModelProviderTest.java": {"ef06c7ac978d7c61b173e47a57b8772b1dd5f216": {"t": 1785745676.8949254, "n": 15839, "h": "7cd4da1397ca0705", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.compat.deepseek;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertInstanceOf;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assumptions.assumeTrue;\n\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport io.agentscope.core.formatter.Formatter;\nimport io.agentscope.core.message.Msg;\nimport io.agentscope.core.message.MsgRole;\nimport io.agentscope.core.message.TextBlock;\nimport io.agentscope.core.model.GenerateOptions;\nimport io.agentscope.core.model.Model;\nimport io.agentscope.core.model.ModelCreationContext;\nimport io.agentscope.core.model.ModelRegistry;\nimport io.agentscope.core.model.transport.HttpRequest;\nimport io.agentscope.core.model.transport.HttpResponse;\nimport io.agentscope.core.model.transport.HttpTransport;\nimport io.agentscope.core.model.transport.ProxyConfig;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport io.agentscope.extensions.model.openai.dto.OpenAIRequest;\nimport io.agentscope.extensions.model.openai.dto.OpenAIResponse;\nimport io.agentscope.extensions.model.openai.formatter.OpenAIChatFormatter;\nimport java.util.List;\nimport java.util.Map;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Flux;\n\n@DisplayName(\"DeepSeekModelProvider Unit Tests\")\nclass DeepSeekModelProviderTest {\n\n private static final ObjectMapper MAPPER = new ObjectMapper();\n\n @AfterEach\n void tearDown() {\n ModelRegistry.reset();\n }\n\n @Test\n @DisplayName(\"Returns provider id\")\n void returnsProviderId() {\n assertEquals(\"deepseek\", new DeepSeekModelProvider().providerId());\n }\n\n @Test\n @DisplayName(\"Supports DeepSeek model ids\")\n void supportsDeepSeekModelIds() {\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n assertTrue(provider.supports(\"deepseek:deepseek-v4-flash\"));\n assertTrue(provider.supports(\"deepseek:deepseek-v4-pro\"));\n assertFalse(provider.supports(\"deepseek:\"));\n assertFalse(provider.supports(\"openai:gpt-4o-mini\"));\n assertFalse(provider.supports(null));\n }\n\n @Test\n @DisplayName(\"Rejects unsupported model ids before reading environment\")\n void createRejectsUnsupportedModelIdsBeforeReadingEnvironment() {\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n assertThrows(IllegalArgumentException.class, () -> provider.create(\"deepseek:\"));\n assertThrows(IllegalArgumentException.class, () -> provider.create(\"openai:gpt-4o-mini\"));\n assertThrows(IllegalArgumentException.class, () -> provider.create(null));\n }\n\n @Test\n @DisplayName(\"Fails clearly when DEEPSEEK_API_KEY is missing\")\n void createFailsClearlyWhenApiKeyMissing() {\n assumeTrue(System.getenv(\"DEEPSEEK_API_KEY\") == null);\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n IllegalStateException error =\n assertThrows(\n IllegalStateException.class,\n () -> provider.create(\"deepseek:deepseek-v4-flash\"));\n\n assertTrue(error.getMessage().contains(\"DEEPSEEK_API_KEY\"));\n }\n\n @Test\n @DisplayName(\"Uses DeepSeek defaults for context window and structured output\")\n void createUsesDeepSeekContextWindowAndStructuredOutputDefaults() {\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").build();\n\n Model model = new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-pro\", context);\n\n assertEquals(1_000_000, model.getContextWindowSize());\n assertFalse(model.supportsNativeStructuredOutput());\n assertFalse(model.supportsNativeStructuredOutputWithTools());\n }\n\n @Test\n @DisplayName(\"Creates model from context and applies default request settings\")\n void createUsesModelCreationContextAndDefaultDeepSeekRequestSettings() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n ModelCreationContext context =\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .baseUrl(\" \")\n .endpointPath(\" \")\n .stream(false)\n .enableThinking(true)\n .component(HttpTransport.class, transport)\n .component(ProxyConfig.class, ProxyConfig.http(\"proxy.example.com\", 8080))\n .option(\"contextWindowSize\", 128000)\n .option(\"nativeStructuredOutput\", false)\n .build();\n\n Model model = new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-flash\", context);\n\n assertInstanceOf(OpenAIChatModel.class, model);\n assertEquals(\"deepseek-v4-flash\", model.getModelName());\n assertEquals(128000, model.getContextWindowSize());\n assertFalse(model.supportsNativeStructuredOutput());\n\n ((OpenAIChatModel) model).stream(userMessages(), null, null).blockLast();\n\n assertEquals(\"https://api.deepseek.com/v1/chat/completions\", transport.request.getUrl());\n assertEquals(\n \"Bearer test-deepseek-key\", transport.request.getHeaders().get(\"Authorization\"));\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(\"deepseek-v4-flash\", body.get(\"model\"));\n assertEquals(false, body.get(\"stream\"));\n assertEquals(Map.of(\"type\", \"enabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Applies base URL, endpoint path, and structured-output overrides\")\n void createAppliesEndpointAndStructuredOutputOverrides() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n ModelCreationContext context =\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .baseUrl(\"https://deepseek.example.com\")\n .endpointPath(\"/custom/chat\")\n .stream(false)\n .enableThinking(false)\n .component(HttpTransport.class, transport)\n .option(\"nativeStructuredOutput\", true)\n .option(\"nativeStructuredOutputWithTools\", true)\n .build();\n\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-flash\", context);\n\n assertTrue(model.supportsNativeStructuredOutput());\n assertTrue(model.supportsNativeStructuredOutputWithTools());\n\n model.stream(userMessages(), null, null).blockLast();\n\n assertEquals(\"https://deepseek.example.com/custom/chat\", transport.request.getUrl());\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(Map.of(\"type\", \"disabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Rejects invalid advanced option types\")\n void createRejectsInvalidAdvancedOptionTypes() {\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n assertThrows(\n IllegalArgumentException.class,\n () ->\n provider.create(\n \"deepseek:deepseek-v4-flash\",\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .option(\"contextWindowSize\", \"large\")\n .build()));\n assertThrows(\n IllegalArgumentException.class,\n () ->\n provider.create(\n \"deepseek:deepseek-v4-flash\",\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .option(\"nativeStructuredOutput\", \"true\")\n .build()));\n }\n\n @Test\n @DisplayName(\"Keeps reasoning_effort independent from thinking\")\n void generateOptionsControlReasoningEffortIndependentlyFromThinking() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n GenerateOptions generateOptions = GenerateOptions.builder().reasoningEffort(\"high\").build();\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").stream(false)\n .enableThinking(true)\n .component(GenerateOptions.class, generateOptions)\n .component(HttpTransport.class, transport)\n .build();\n\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-pro\", context);\n\n model.stream(userMessages(), null, null).blockLast();\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(\"high\", body.get(\"reasoning_effort\"));\n assertEquals(Map.of(\"type\", \"enabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Allows user options to override default thinking\")\n void userGenerateOptionsCanOverrideDeepSeekThinkingDefault() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n GenerateOptions generateOptions =\n GenerateOptions.builder()\n .additionalBodyParam(\"thinking\", Map.of(\"type\", \"disabled\"))\n .build();\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").stream(false)\n .enableThinking(true)\n .component(GenerateOptions.class, generateOptions)\n .component(HttpTransport.class, transport)\n .build();\n\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-pro\", context);\n\n model.stream(userMessages(), null, null).blockLast();\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(Map.of(\"type\", \"disabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Allows context formatter to override provider default\")\n void contextFormatterOverrideTakesPrecedenceOverProviderDefault() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n Formatter<OpenAIMessage, OpenAIResponse, OpenAIRequest> formatter =\n new OpenAIChatFormatter();\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").stream(false)\n .component(Formatter.class, formatter)\n .component(HttpTransport.class, transport)\n .build();\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-flash\", context);\n\n model.stream(\n List.of(\n Msg.builder()\n .role(MsgRole.SYSTEM)\n .content(\n List.of(\n TextBlock.builder()\n .text(\"System instruction\")\n .build()))\n .build(),\n Msg.builder()\n .role(MsgRole.USER)\n .content(\n List.of(\n TextBlock.builder()\n .text(\"Reply with pong\")\n .build()))\n .build()),\n null,\n null)\n .blockLast();\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n @SuppressWarnings(\"unchecked\")\n List<Map<String, Object>> messages = (List<Map<String, Object>>) body.get(\"messages\");\n assertEquals(\"system\", messages.get(0).get(\"role\"));\n }\n\n @Test\n @DisplayName(\"Registers through ServiceLoader\")\n void modelRegistryFindsDeepSeekProviderFromServiceLoader() {\n assertTrue(ModelRegistry.canResolve(\"deepseek:deepseek-v4-flash\"));\n }\n\n private static List<Msg> userMessages() {\n return List.of(\n Msg.builder()\n .role(MsgRole.USER)\n .content(List.of(TextBlock.builder().text(\"Reply with pong\").build()))\n .build());\n }\n\n private static Map<String, Object> parseBody(String body) throws Exception {\n return MAPPER.readValue(body, new TypeReference<Map<String, Object>>() {});\n }\n\n private static final class CapturingTransport implements HttpTransport {\n private HttpRequest request;\n\n @Override\n public HttpResponse execute(HttpRequest request) {\n this.request = request;\n return HttpResponse.builder()\n .statusCode(200)\n .body(\n \"\"\"\n {\n \"id\": \"deepseek-test\",\n \"object\": \"chat.completion\",\n \"created\": 1,\n \"model\": \"deepseek-v4-flash\",\n \"choices\": [{\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"pong\"\n },\n \"finish_reason\": \"stop\"\n }],\n \"usage\": {\n \"prompt_tokens\": 1,\n \"completion_tokens\": 1,\n \"total_tokens\": 2\n }\n }\n \"\"\")\n .build();\n }\n\n @Override\n public Flux<String> stream(HttpRequest request) {\n this.request = request;\n return Flux.empty();\n }\n\n @Override\n public void close() {}\n }\n}\n"}, "74db7572f20f07f05439a0e1b0d6992f9f7c6a38": {"t": 1785755442.6193123, "n": 15839, "h": "7cd4da1397ca0705", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.compat.deepseek;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertInstanceOf;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assumptions.assumeTrue;\n\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport io.agentscope.core.formatter.Formatter;\nimport io.agentscope.core.message.Msg;\nimport io.agentscope.core.message.MsgRole;\nimport io.agentscope.core.message.TextBlock;\nimport io.agentscope.core.model.GenerateOptions;\nimport io.agentscope.core.model.Model;\nimport io.agentscope.core.model.ModelCreationContext;\nimport io.agentscope.core.model.ModelRegistry;\nimport io.agentscope.core.model.transport.HttpRequest;\nimport io.agentscope.core.model.transport.HttpResponse;\nimport io.agentscope.core.model.transport.HttpTransport;\nimport io.agentscope.core.model.transport.ProxyConfig;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport io.agentscope.extensions.model.openai.dto.OpenAIRequest;\nimport io.agentscope.extensions.model.openai.dto.OpenAIResponse;\nimport io.agentscope.extensions.model.openai.formatter.OpenAIChatFormatter;\nimport java.util.List;\nimport java.util.Map;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Flux;\n\n@DisplayName(\"DeepSeekModelProvider Unit Tests\")\nclass DeepSeekModelProviderTest {\n\n private static final ObjectMapper MAPPER = new ObjectMapper();\n\n @AfterEach\n void tearDown() {\n ModelRegistry.reset();\n }\n\n @Test\n @DisplayName(\"Returns provider id\")\n void returnsProviderId() {\n assertEquals(\"deepseek\", new DeepSeekModelProvider().providerId());\n }\n\n @Test\n @DisplayName(\"Supports DeepSeek model ids\")\n void supportsDeepSeekModelIds() {\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n assertTrue(provider.supports(\"deepseek:deepseek-v4-flash\"));\n assertTrue(provider.supports(\"deepseek:deepseek-v4-pro\"));\n assertFalse(provider.supports(\"deepseek:\"));\n assertFalse(provider.supports(\"openai:gpt-4o-mini\"));\n assertFalse(provider.supports(null));\n }\n\n @Test\n @DisplayName(\"Rejects unsupported model ids before reading environment\")\n void createRejectsUnsupportedModelIdsBeforeReadingEnvironment() {\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n assertThrows(IllegalArgumentException.class, () -> provider.create(\"deepseek:\"));\n assertThrows(IllegalArgumentException.class, () -> provider.create(\"openai:gpt-4o-mini\"));\n assertThrows(IllegalArgumentException.class, () -> provider.create(null));\n }\n\n @Test\n @DisplayName(\"Fails clearly when DEEPSEEK_API_KEY is missing\")\n void createFailsClearlyWhenApiKeyMissing() {\n assumeTrue(System.getenv(\"DEEPSEEK_API_KEY\") == null);\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n IllegalStateException error =\n assertThrows(\n IllegalStateException.class,\n () -> provider.create(\"deepseek:deepseek-v4-flash\"));\n\n assertTrue(error.getMessage().contains(\"DEEPSEEK_API_KEY\"));\n }\n\n @Test\n @DisplayName(\"Uses DeepSeek defaults for context window and structured output\")\n void createUsesDeepSeekContextWindowAndStructuredOutputDefaults() {\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").build();\n\n Model model = new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-pro\", context);\n\n assertEquals(1_000_000, model.getContextWindowSize());\n assertFalse(model.supportsNativeStructuredOutput());\n assertFalse(model.supportsNativeStructuredOutputWithTools());\n }\n\n @Test\n @DisplayName(\"Creates model from context and applies default request settings\")\n void createUsesModelCreationContextAndDefaultDeepSeekRequestSettings() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n ModelCreationContext context =\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .baseUrl(\" \")\n .endpointPath(\" \")\n .stream(false)\n .enableThinking(true)\n .component(HttpTransport.class, transport)\n .component(ProxyConfig.class, ProxyConfig.http(\"proxy.example.com\", 8080))\n .option(\"contextWindowSize\", 128000)\n .option(\"nativeStructuredOutput\", false)\n .build();\n\n Model model = new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-flash\", context);\n\n assertInstanceOf(OpenAIChatModel.class, model);\n assertEquals(\"deepseek-v4-flash\", model.getModelName());\n assertEquals(128000, model.getContextWindowSize());\n assertFalse(model.supportsNativeStructuredOutput());\n\n ((OpenAIChatModel) model).stream(userMessages(), null, null).blockLast();\n\n assertEquals(\"https://api.deepseek.com/v1/chat/completions\", transport.request.getUrl());\n assertEquals(\n \"Bearer test-deepseek-key\", transport.request.getHeaders().get(\"Authorization\"));\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(\"deepseek-v4-flash\", body.get(\"model\"));\n assertEquals(false, body.get(\"stream\"));\n assertEquals(Map.of(\"type\", \"enabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Applies base URL, endpoint path, and structured-output overrides\")\n void createAppliesEndpointAndStructuredOutputOverrides() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n ModelCreationContext context =\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .baseUrl(\"https://deepseek.example.com\")\n .endpointPath(\"/custom/chat\")\n .stream(false)\n .enableThinking(false)\n .component(HttpTransport.class, transport)\n .option(\"nativeStructuredOutput\", true)\n .option(\"nativeStructuredOutputWithTools\", true)\n .build();\n\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-flash\", context);\n\n assertTrue(model.supportsNativeStructuredOutput());\n assertTrue(model.supportsNativeStructuredOutputWithTools());\n\n model.stream(userMessages(), null, null).blockLast();\n\n assertEquals(\"https://deepseek.example.com/custom/chat\", transport.request.getUrl());\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(Map.of(\"type\", \"disabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Rejects invalid advanced option types\")\n void createRejectsInvalidAdvancedOptionTypes() {\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n assertThrows(\n IllegalArgumentException.class,\n () ->\n provider.create(\n \"deepseek:deepseek-v4-flash\",\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .option(\"contextWindowSize\", \"large\")\n .build()));\n assertThrows(\n IllegalArgumentException.class,\n () ->\n provider.create(\n \"deepseek:deepseek-v4-flash\",\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .option(\"nativeStructuredOutput\", \"true\")\n .build()));\n }\n\n @Test\n @DisplayName(\"Keeps reasoning_effort independent from thinking\")\n void generateOptionsControlReasoningEffortIndependentlyFromThinking() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n GenerateOptions generateOptions = GenerateOptions.builder().reasoningEffort(\"high\").build();\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").stream(false)\n .enableThinking(true)\n .component(GenerateOptions.class, generateOptions)\n .component(HttpTransport.class, transport)\n .build();\n\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-pro\", context);\n\n model.stream(userMessages(), null, null).blockLast();\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(\"high\", body.get(\"reasoning_effort\"));\n assertEquals(Map.of(\"type\", \"enabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Allows user options to override default thinking\")\n void userGenerateOptionsCanOverrideDeepSeekThinkingDefault() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n GenerateOptions generateOptions =\n GenerateOptions.builder()\n .additionalBodyParam(\"thinking\", Map.of(\"type\", \"disabled\"))\n .build();\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").stream(false)\n .enableThinking(true)\n .component(GenerateOptions.class, generateOptions)\n .component(HttpTransport.class, transport)\n .build();\n\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-pro\", context);\n\n model.stream(userMessages(), null, null).blockLast();\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(Map.of(\"type\", \"disabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Allows context formatter to override provider default\")\n void contextFormatterOverrideTakesPrecedenceOverProviderDefault() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n Formatter<OpenAIMessage, OpenAIResponse, OpenAIRequest> formatter =\n new OpenAIChatFormatter();\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").stream(false)\n .component(Formatter.class, formatter)\n .component(HttpTransport.class, transport)\n .build();\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-flash\", context);\n\n model.stream(\n List.of(\n Msg.builder()\n .role(MsgRole.SYSTEM)\n .content(\n List.of(\n TextBlock.builder()\n .text(\"System instruction\")\n .build()))\n .build(),\n Msg.builder()\n .role(MsgRole.USER)\n .content(\n List.of(\n TextBlock.builder()\n .text(\"Reply with pong\")\n .build()))\n .build()),\n null,\n null)\n .blockLast();\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n @SuppressWarnings(\"unchecked\")\n List<Map<String, Object>> messages = (List<Map<String, Object>>) body.get(\"messages\");\n assertEquals(\"system\", messages.get(0).get(\"role\"));\n }\n\n @Test\n @DisplayName(\"Registers through ServiceLoader\")\n void modelRegistryFindsDeepSeekProviderFromServiceLoader() {\n assertTrue(ModelRegistry.canResolve(\"deepseek:deepseek-v4-flash\"));\n }\n\n private static List<Msg> userMessages() {\n return List.of(\n Msg.builder()\n .role(MsgRole.USER)\n .content(List.of(TextBlock.builder().text(\"Reply with pong\").build()))\n .build());\n }\n\n private static Map<String, Object> parseBody(String body) throws Exception {\n return MAPPER.readValue(body, new TypeReference<Map<String, Object>>() {});\n }\n\n private static final class CapturingTransport implements HttpTransport {\n private HttpRequest request;\n\n @Override\n public HttpResponse execute(HttpRequest request) {\n this.request = request;\n return HttpResponse.builder()\n .statusCode(200)\n .body(\n \"\"\"\n {\n \"id\": \"deepseek-test\",\n \"object\": \"chat.completion\",\n \"created\": 1,\n \"model\": \"deepseek-v4-flash\",\n \"choices\": [{\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"pong\"\n },\n \"finish_reason\": \"stop\"\n }],\n \"usage\": {\n \"prompt_tokens\": 1,\n \"completion_tokens\": 1,\n \"total_tokens\": 2\n }\n }\n \"\"\")\n .build();\n }\n\n @Override\n public Flux<String> stream(HttpRequest request) {\n this.request = request;\n return Flux.empty();\n }\n\n @Override\n public void close() {}\n }\n}\n"}, "702461070ddcab29dae7c7a2b130e323a106d020": {"t": 1786067433.6664312, "n": 15839, "h": "7cd4da1397ca0705", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.compat.deepseek;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertInstanceOf;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assumptions.assumeTrue;\n\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport io.agentscope.core.formatter.Formatter;\nimport io.agentscope.core.message.Msg;\nimport io.agentscope.core.message.MsgRole;\nimport io.agentscope.core.message.TextBlock;\nimport io.agentscope.core.model.GenerateOptions;\nimport io.agentscope.core.model.Model;\nimport io.agentscope.core.model.ModelCreationContext;\nimport io.agentscope.core.model.ModelRegistry;\nimport io.agentscope.core.model.transport.HttpRequest;\nimport io.agentscope.core.model.transport.HttpResponse;\nimport io.agentscope.core.model.transport.HttpTransport;\nimport io.agentscope.core.model.transport.ProxyConfig;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport io.agentscope.extensions.model.openai.dto.OpenAIRequest;\nimport io.agentscope.extensions.model.openai.dto.OpenAIResponse;\nimport io.agentscope.extensions.model.openai.formatter.OpenAIChatFormatter;\nimport java.util.List;\nimport java.util.Map;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Flux;\n\n@DisplayName(\"DeepSeekModelProvider Unit Tests\")\nclass DeepSeekModelProviderTest {\n\n private static final ObjectMapper MAPPER = new ObjectMapper();\n\n @AfterEach\n void tearDown() {\n ModelRegistry.reset();\n }\n\n @Test\n @DisplayName(\"Returns provider id\")\n void returnsProviderId() {\n assertEquals(\"deepseek\", new DeepSeekModelProvider().providerId());\n }\n\n @Test\n @DisplayName(\"Supports DeepSeek model ids\")\n void supportsDeepSeekModelIds() {\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n assertTrue(provider.supports(\"deepseek:deepseek-v4-flash\"));\n assertTrue(provider.supports(\"deepseek:deepseek-v4-pro\"));\n assertFalse(provider.supports(\"deepseek:\"));\n assertFalse(provider.supports(\"openai:gpt-4o-mini\"));\n assertFalse(provider.supports(null));\n }\n\n @Test\n @DisplayName(\"Rejects unsupported model ids before reading environment\")\n void createRejectsUnsupportedModelIdsBeforeReadingEnvironment() {\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n assertThrows(IllegalArgumentException.class, () -> provider.create(\"deepseek:\"));\n assertThrows(IllegalArgumentException.class, () -> provider.create(\"openai:gpt-4o-mini\"));\n assertThrows(IllegalArgumentException.class, () -> provider.create(null));\n }\n\n @Test\n @DisplayName(\"Fails clearly when DEEPSEEK_API_KEY is missing\")\n void createFailsClearlyWhenApiKeyMissing() {\n assumeTrue(System.getenv(\"DEEPSEEK_API_KEY\") == null);\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n IllegalStateException error =\n assertThrows(\n IllegalStateException.class,\n () -> provider.create(\"deepseek:deepseek-v4-flash\"));\n\n assertTrue(error.getMessage().contains(\"DEEPSEEK_API_KEY\"));\n }\n\n @Test\n @DisplayName(\"Uses DeepSeek defaults for context window and structured output\")\n void createUsesDeepSeekContextWindowAndStructuredOutputDefaults() {\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").build();\n\n Model model = new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-pro\", context);\n\n assertEquals(1_000_000, model.getContextWindowSize());\n assertFalse(model.supportsNativeStructuredOutput());\n assertFalse(model.supportsNativeStructuredOutputWithTools());\n }\n\n @Test\n @DisplayName(\"Creates model from context and applies default request settings\")\n void createUsesModelCreationContextAndDefaultDeepSeekRequestSettings() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n ModelCreationContext context =\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .baseUrl(\" \")\n .endpointPath(\" \")\n .stream(false)\n .enableThinking(true)\n .component(HttpTransport.class, transport)\n .component(ProxyConfig.class, ProxyConfig.http(\"proxy.example.com\", 8080))\n .option(\"contextWindowSize\", 128000)\n .option(\"nativeStructuredOutput\", false)\n .build();\n\n Model model = new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-flash\", context);\n\n assertInstanceOf(OpenAIChatModel.class, model);\n assertEquals(\"deepseek-v4-flash\", model.getModelName());\n assertEquals(128000, model.getContextWindowSize());\n assertFalse(model.supportsNativeStructuredOutput());\n\n ((OpenAIChatModel) model).stream(userMessages(), null, null).blockLast();\n\n assertEquals(\"https://api.deepseek.com/v1/chat/completions\", transport.request.getUrl());\n assertEquals(\n \"Bearer test-deepseek-key\", transport.request.getHeaders().get(\"Authorization\"));\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(\"deepseek-v4-flash\", body.get(\"model\"));\n assertEquals(false, body.get(\"stream\"));\n assertEquals(Map.of(\"type\", \"enabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Applies base URL, endpoint path, and structured-output overrides\")\n void createAppliesEndpointAndStructuredOutputOverrides() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n ModelCreationContext context =\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .baseUrl(\"https://deepseek.example.com\")\n .endpointPath(\"/custom/chat\")\n .stream(false)\n .enableThinking(false)\n .component(HttpTransport.class, transport)\n .option(\"nativeStructuredOutput\", true)\n .option(\"nativeStructuredOutputWithTools\", true)\n .build();\n\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-flash\", context);\n\n assertTrue(model.supportsNativeStructuredOutput());\n assertTrue(model.supportsNativeStructuredOutputWithTools());\n\n model.stream(userMessages(), null, null).blockLast();\n\n assertEquals(\"https://deepseek.example.com/custom/chat\", transport.request.getUrl());\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(Map.of(\"type\", \"disabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Rejects invalid advanced option types\")\n void createRejectsInvalidAdvancedOptionTypes() {\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n assertThrows(\n IllegalArgumentException.class,\n () ->\n provider.create(\n \"deepseek:deepseek-v4-flash\",\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .option(\"contextWindowSize\", \"large\")\n .build()));\n assertThrows(\n IllegalArgumentException.class,\n () ->\n provider.create(\n \"deepseek:deepseek-v4-flash\",\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .option(\"nativeStructuredOutput\", \"true\")\n .build()));\n }\n\n @Test\n @DisplayName(\"Keeps reasoning_effort independent from thinking\")\n void generateOptionsControlReasoningEffortIndependentlyFromThinking() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n GenerateOptions generateOptions = GenerateOptions.builder().reasoningEffort(\"high\").build();\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").stream(false)\n .enableThinking(true)\n .component(GenerateOptions.class, generateOptions)\n .component(HttpTransport.class, transport)\n .build();\n\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-pro\", context);\n\n model.stream(userMessages(), null, null).blockLast();\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(\"high\", body.get(\"reasoning_effort\"));\n assertEquals(Map.of(\"type\", \"enabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Allows user options to override default thinking\")\n void userGenerateOptionsCanOverrideDeepSeekThinkingDefault() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n GenerateOptions generateOptions =\n GenerateOptions.builder()\n .additionalBodyParam(\"thinking\", Map.of(\"type\", \"disabled\"))\n .build();\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").stream(false)\n .enableThinking(true)\n .component(GenerateOptions.class, generateOptions)\n .component(HttpTransport.class, transport)\n .build();\n\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-pro\", context);\n\n model.stream(userMessages(), null, null).blockLast();\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(Map.of(\"type\", \"disabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Allows context formatter to override provider default\")\n void contextFormatterOverrideTakesPrecedenceOverProviderDefault() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n Formatter<OpenAIMessage, OpenAIResponse, OpenAIRequest> formatter =\n new OpenAIChatFormatter();\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").stream(false)\n .component(Formatter.class, formatter)\n .component(HttpTransport.class, transport)\n .build();\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-flash\", context);\n\n model.stream(\n List.of(\n Msg.builder()\n .role(MsgRole.SYSTEM)\n .content(\n List.of(\n TextBlock.builder()\n .text(\"System instruction\")\n .build()))\n .build(),\n Msg.builder()\n .role(MsgRole.USER)\n .content(\n List.of(\n TextBlock.builder()\n .text(\"Reply with pong\")\n .build()))\n .build()),\n null,\n null)\n .blockLast();\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n @SuppressWarnings(\"unchecked\")\n List<Map<String, Object>> messages = (List<Map<String, Object>>) body.get(\"messages\");\n assertEquals(\"system\", messages.get(0).get(\"role\"));\n }\n\n @Test\n @DisplayName(\"Registers through ServiceLoader\")\n void modelRegistryFindsDeepSeekProviderFromServiceLoader() {\n assertTrue(ModelRegistry.canResolve(\"deepseek:deepseek-v4-flash\"));\n }\n\n private static List<Msg> userMessages() {\n return List.of(\n Msg.builder()\n .role(MsgRole.USER)\n .content(List.of(TextBlock.builder().text(\"Reply with pong\").build()))\n .build());\n }\n\n private static Map<String, Object> parseBody(String body) throws Exception {\n return MAPPER.readValue(body, new TypeReference<Map<String, Object>>() {});\n }\n\n private static final class CapturingTransport implements HttpTransport {\n private HttpRequest request;\n\n @Override\n public HttpResponse execute(HttpRequest request) {\n this.request = request;\n return HttpResponse.builder()\n .statusCode(200)\n .body(\n \"\"\"\n {\n \"id\": \"deepseek-test\",\n \"object\": \"chat.completion\",\n \"created\": 1,\n \"model\": \"deepseek-v4-flash\",\n \"choices\": [{\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"pong\"\n },\n \"finish_reason\": \"stop\"\n }],\n \"usage\": {\n \"prompt_tokens\": 1,\n \"completion_tokens\": 1,\n \"total_tokens\": 2\n }\n }\n \"\"\")\n .build();\n }\n\n @Override\n public Flux<String> stream(HttpRequest request) {\n this.request = request;\n return Flux.empty();\n }\n\n @Override\n public void close() {}\n }\n}\n"}, "9cbac4ce574183274c37b5132ce453ffcaeafb74": {"t": 1786068300.979883, "n": 15839, "h": "7cd4da1397ca0705", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.compat.deepseek;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertInstanceOf;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assumptions.assumeTrue;\n\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport io.agentscope.core.formatter.Formatter;\nimport io.agentscope.core.message.Msg;\nimport io.agentscope.core.message.MsgRole;\nimport io.agentscope.core.message.TextBlock;\nimport io.agentscope.core.model.GenerateOptions;\nimport io.agentscope.core.model.Model;\nimport io.agentscope.core.model.ModelCreationContext;\nimport io.agentscope.core.model.ModelRegistry;\nimport io.agentscope.core.model.transport.HttpRequest;\nimport io.agentscope.core.model.transport.HttpResponse;\nimport io.agentscope.core.model.transport.HttpTransport;\nimport io.agentscope.core.model.transport.ProxyConfig;\nimport io.agentscope.extensions.model.openai.OpenAIChatModel;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport io.agentscope.extensions.model.openai.dto.OpenAIRequest;\nimport io.agentscope.extensions.model.openai.dto.OpenAIResponse;\nimport io.agentscope.extensions.model.openai.formatter.OpenAIChatFormatter;\nimport java.util.List;\nimport java.util.Map;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Flux;\n\n@DisplayName(\"DeepSeekModelProvider Unit Tests\")\nclass DeepSeekModelProviderTest {\n\n private static final ObjectMapper MAPPER = new ObjectMapper();\n\n @AfterEach\n void tearDown() {\n ModelRegistry.reset();\n }\n\n @Test\n @DisplayName(\"Returns provider id\")\n void returnsProviderId() {\n assertEquals(\"deepseek\", new DeepSeekModelProvider().providerId());\n }\n\n @Test\n @DisplayName(\"Supports DeepSeek model ids\")\n void supportsDeepSeekModelIds() {\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n assertTrue(provider.supports(\"deepseek:deepseek-v4-flash\"));\n assertTrue(provider.supports(\"deepseek:deepseek-v4-pro\"));\n assertFalse(provider.supports(\"deepseek:\"));\n assertFalse(provider.supports(\"openai:gpt-4o-mini\"));\n assertFalse(provider.supports(null));\n }\n\n @Test\n @DisplayName(\"Rejects unsupported model ids before reading environment\")\n void createRejectsUnsupportedModelIdsBeforeReadingEnvironment() {\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n assertThrows(IllegalArgumentException.class, () -> provider.create(\"deepseek:\"));\n assertThrows(IllegalArgumentException.class, () -> provider.create(\"openai:gpt-4o-mini\"));\n assertThrows(IllegalArgumentException.class, () -> provider.create(null));\n }\n\n @Test\n @DisplayName(\"Fails clearly when DEEPSEEK_API_KEY is missing\")\n void createFailsClearlyWhenApiKeyMissing() {\n assumeTrue(System.getenv(\"DEEPSEEK_API_KEY\") == null);\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n IllegalStateException error =\n assertThrows(\n IllegalStateException.class,\n () -> provider.create(\"deepseek:deepseek-v4-flash\"));\n\n assertTrue(error.getMessage().contains(\"DEEPSEEK_API_KEY\"));\n }\n\n @Test\n @DisplayName(\"Uses DeepSeek defaults for context window and structured output\")\n void createUsesDeepSeekContextWindowAndStructuredOutputDefaults() {\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").build();\n\n Model model = new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-pro\", context);\n\n assertEquals(1_000_000, model.getContextWindowSize());\n assertFalse(model.supportsNativeStructuredOutput());\n assertFalse(model.supportsNativeStructuredOutputWithTools());\n }\n\n @Test\n @DisplayName(\"Creates model from context and applies default request settings\")\n void createUsesModelCreationContextAndDefaultDeepSeekRequestSettings() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n ModelCreationContext context =\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .baseUrl(\" \")\n .endpointPath(\" \")\n .stream(false)\n .enableThinking(true)\n .component(HttpTransport.class, transport)\n .component(ProxyConfig.class, ProxyConfig.http(\"proxy.example.com\", 8080))\n .option(\"contextWindowSize\", 128000)\n .option(\"nativeStructuredOutput\", false)\n .build();\n\n Model model = new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-flash\", context);\n\n assertInstanceOf(OpenAIChatModel.class, model);\n assertEquals(\"deepseek-v4-flash\", model.getModelName());\n assertEquals(128000, model.getContextWindowSize());\n assertFalse(model.supportsNativeStructuredOutput());\n\n ((OpenAIChatModel) model).stream(userMessages(), null, null).blockLast();\n\n assertEquals(\"https://api.deepseek.com/v1/chat/completions\", transport.request.getUrl());\n assertEquals(\n \"Bearer test-deepseek-key\", transport.request.getHeaders().get(\"Authorization\"));\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(\"deepseek-v4-flash\", body.get(\"model\"));\n assertEquals(false, body.get(\"stream\"));\n assertEquals(Map.of(\"type\", \"enabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Applies base URL, endpoint path, and structured-output overrides\")\n void createAppliesEndpointAndStructuredOutputOverrides() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n ModelCreationContext context =\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .baseUrl(\"https://deepseek.example.com\")\n .endpointPath(\"/custom/chat\")\n .stream(false)\n .enableThinking(false)\n .component(HttpTransport.class, transport)\n .option(\"nativeStructuredOutput\", true)\n .option(\"nativeStructuredOutputWithTools\", true)\n .build();\n\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-flash\", context);\n\n assertTrue(model.supportsNativeStructuredOutput());\n assertTrue(model.supportsNativeStructuredOutputWithTools());\n\n model.stream(userMessages(), null, null).blockLast();\n\n assertEquals(\"https://deepseek.example.com/custom/chat\", transport.request.getUrl());\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(Map.of(\"type\", \"disabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Rejects invalid advanced option types\")\n void createRejectsInvalidAdvancedOptionTypes() {\n DeepSeekModelProvider provider = new DeepSeekModelProvider();\n\n assertThrows(\n IllegalArgumentException.class,\n () ->\n provider.create(\n \"deepseek:deepseek-v4-flash\",\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .option(\"contextWindowSize\", \"large\")\n .build()));\n assertThrows(\n IllegalArgumentException.class,\n () ->\n provider.create(\n \"deepseek:deepseek-v4-flash\",\n ModelCreationContext.builder()\n .apiKey(\"test-deepseek-key\")\n .option(\"nativeStructuredOutput\", \"true\")\n .build()));\n }\n\n @Test\n @DisplayName(\"Keeps reasoning_effort independent from thinking\")\n void generateOptionsControlReasoningEffortIndependentlyFromThinking() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n GenerateOptions generateOptions = GenerateOptions.builder().reasoningEffort(\"high\").build();\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").stream(false)\n .enableThinking(true)\n .component(GenerateOptions.class, generateOptions)\n .component(HttpTransport.class, transport)\n .build();\n\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-pro\", context);\n\n model.stream(userMessages(), null, null).blockLast();\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(\"high\", body.get(\"reasoning_effort\"));\n assertEquals(Map.of(\"type\", \"enabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Allows user options to override default thinking\")\n void userGenerateOptionsCanOverrideDeepSeekThinkingDefault() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n GenerateOptions generateOptions =\n GenerateOptions.builder()\n .additionalBodyParam(\"thinking\", Map.of(\"type\", \"disabled\"))\n .build();\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").stream(false)\n .enableThinking(true)\n .component(GenerateOptions.class, generateOptions)\n .component(HttpTransport.class, transport)\n .build();\n\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-pro\", context);\n\n model.stream(userMessages(), null, null).blockLast();\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n assertEquals(Map.of(\"type\", \"disabled\"), body.get(\"thinking\"));\n }\n\n @Test\n @DisplayName(\"Allows context formatter to override provider default\")\n void contextFormatterOverrideTakesPrecedenceOverProviderDefault() throws Exception {\n CapturingTransport transport = new CapturingTransport();\n Formatter<OpenAIMessage, OpenAIResponse, OpenAIRequest> formatter =\n new OpenAIChatFormatter();\n ModelCreationContext context =\n ModelCreationContext.builder().apiKey(\"test-deepseek-key\").stream(false)\n .component(Formatter.class, formatter)\n .component(HttpTransport.class, transport)\n .build();\n OpenAIChatModel model =\n (OpenAIChatModel)\n new DeepSeekModelProvider().create(\"deepseek:deepseek-v4-flash\", context);\n\n model.stream(\n List.of(\n Msg.builder()\n .role(MsgRole.SYSTEM)\n .content(\n List.of(\n TextBlock.builder()\n .text(\"System instruction\")\n .build()))\n .build(),\n Msg.builder()\n .role(MsgRole.USER)\n .content(\n List.of(\n TextBlock.builder()\n .text(\"Reply with pong\")\n .build()))\n .build()),\n null,\n null)\n .blockLast();\n\n Map<String, Object> body = parseBody(transport.request.getBody());\n @SuppressWarnings(\"unchecked\")\n List<Map<String, Object>> messages = (List<Map<String, Object>>) body.get(\"messages\");\n assertEquals(\"system\", messages.get(0).get(\"role\"));\n }\n\n @Test\n @DisplayName(\"Registers through ServiceLoader\")\n void modelRegistryFindsDeepSeekProviderFromServiceLoader() {\n assertTrue(ModelRegistry.canResolve(\"deepseek:deepseek-v4-flash\"));\n }\n\n private static List<Msg> userMessages() {\n return List.of(\n Msg.builder()\n .role(MsgRole.USER)\n .content(List.of(TextBlock.builder().text(\"Reply with pong\").build()))\n .build());\n }\n\n private static Map<String, Object> parseBody(String body) throws Exception {\n return MAPPER.readValue(body, new TypeReference<Map<String, Object>>() {});\n }\n\n private static final class CapturingTransport implements HttpTransport {\n private HttpRequest request;\n\n @Override\n public HttpResponse execute(HttpRequest request) {\n this.request = request;\n return HttpResponse.builder()\n .statusCode(200)\n .body(\n \"\"\"\n {\n \"id\": \"deepseek-test\",\n \"object\": \"chat.completion\",\n \"created\": 1,\n \"model\": \"deepseek-v4-flash\",\n \"choices\": [{\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"pong\"\n },\n \"finish_reason\": \"stop\"\n }],\n \"usage\": {\n \"prompt_tokens\": 1,\n \"completion_tokens\": 1,\n \"total_tokens\": 2\n }\n }\n \"\"\")\n .build();\n }\n\n @Override\n public Flux<String> stream(HttpRequest request) {\n this.request = request;\n return Flux.empty();\n }\n\n @Override\n public void close() {}\n }\n}\n"}}, "agentscope-core/src/main/java/io/agentscope/core/credential/DeepSeekCredential.java": {"ef06c7ac978d7c61b173e47a57b8772b1dd5f216": {"t": 1785746105.6582396, "n": 3648, "h": "0d9878289be33546", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.core.credential;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonPropertyOrder;\nimport io.agentscope.core.model.ChatModelBase;\nimport java.util.Objects;\n\n/**\n * Credential for the DeepSeek API.\n *\n * <p>No {@code DeepSeekChatModel} class ships in agentscope-core yet, so {@link\n * #getChatModelClass()} throws {@link UnsupportedOperationException}. The credential itself\n * (id/api-key/base-url) round-trips through JSON for storage compatibility.\n */\n@JsonIgnoreProperties(ignoreUnknown = true)\n@JsonPropertyOrder({\"id\", \"type\", \"api_key\", \"base_url\"})\npublic final class DeepSeekCredential extends CredentialBase {\n\n public static final String TYPE = \"deepseek_credential\";\n\n public static final String DEFAULT_BASE_URL = \"https://api.deepseek.com\";\n\n private final String apiKey;\n private final String baseUrl;\n\n private DeepSeekCredential(String id, String apiKey, String baseUrl) {\n super(id);\n this.apiKey = Objects.requireNonNull(apiKey, \"apiKey must not be null\");\n this.baseUrl = baseUrl == null ? DEFAULT_BASE_URL : baseUrl;\n }\n\n @JsonCreator\n static DeepSeekCredential fromJson(\n @JsonProperty(\"id\") String id,\n @JsonProperty(\"api_key\") String apiKey,\n @JsonProperty(\"base_url\") String baseUrl) {\n return new DeepSeekCredential(id, apiKey, baseUrl);\n }\n\n @JsonProperty(\"type\")\n public String getType() {\n return TYPE;\n }\n\n @JsonProperty(\"api_key\")\n public String getApiKey() {\n return apiKey;\n }\n\n @JsonProperty(\"base_url\")\n public String getBaseUrl() {\n return baseUrl;\n }\n\n @Override\n public Class<? extends ChatModelBase> getChatModelClass() {\n throw new UnsupportedOperationException(\n \"DeepSeekChatModel is not implemented in agentscope-core yet.\"\n + \" Use io.agentscope.extensions.model.openai.OpenAIChatModel against the\"\n + \" DeepSeek base URL instead.\");\n }\n\n @Override\n public String toString() {\n return \"DeepSeekCredential{id=\" + getId() + \", baseUrl=\" + baseUrl + \", apiKey=***}\";\n }\n\n public static Builder builder() {\n return new Builder();\n }\n\n public static final class Builder {\n private String id;\n private String apiKey;\n private String baseUrl;\n\n private Builder() {}\n\n public Builder id(String id) {\n this.id = id;\n return this;\n }\n\n public Builder apiKey(String apiKey) {\n this.apiKey = apiKey;\n return this;\n }\n\n public Builder baseUrl(String baseUrl) {\n this.baseUrl = baseUrl;\n return this;\n }\n\n public DeepSeekCredential build() {\n return new DeepSeekCredential(id, apiKey, baseUrl);\n }\n }\n}\n"}, "7f18ce50ab9e5048f6d4be72809c9bb1e3e6dd59": {"t": 1785755445.8496945, "n": 3648, "h": "0d9878289be33546", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.core.credential;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonPropertyOrder;\nimport io.agentscope.core.model.ChatModelBase;\nimport java.util.Objects;\n\n/**\n * Credential for the DeepSeek API.\n *\n * <p>No {@code DeepSeekChatModel} class ships in agentscope-core yet, so {@link\n * #getChatModelClass()} throws {@link UnsupportedOperationException}. The credential itself\n * (id/api-key/base-url) round-trips through JSON for storage compatibility.\n */\n@JsonIgnoreProperties(ignoreUnknown = true)\n@JsonPropertyOrder({\"id\", \"type\", \"api_key\", \"base_url\"})\npublic final class DeepSeekCredential extends CredentialBase {\n\n public static final String TYPE = \"deepseek_credential\";\n\n public static final String DEFAULT_BASE_URL = \"https://api.deepseek.com\";\n\n private final String apiKey;\n private final String baseUrl;\n\n private DeepSeekCredential(String id, String apiKey, String baseUrl) {\n super(id);\n this.apiKey = Objects.requireNonNull(apiKey, \"apiKey must not be null\");\n this.baseUrl = baseUrl == null ? DEFAULT_BASE_URL : baseUrl;\n }\n\n @JsonCreator\n static DeepSeekCredential fromJson(\n @JsonProperty(\"id\") String id,\n @JsonProperty(\"api_key\") String apiKey,\n @JsonProperty(\"base_url\") String baseUrl) {\n return new DeepSeekCredential(id, apiKey, baseUrl);\n }\n\n @JsonProperty(\"type\")\n public String getType() {\n return TYPE;\n }\n\n @JsonProperty(\"api_key\")\n public String getApiKey() {\n return apiKey;\n }\n\n @JsonProperty(\"base_url\")\n public String getBaseUrl() {\n return baseUrl;\n }\n\n @Override\n public Class<? extends ChatModelBase> getChatModelClass() {\n throw new UnsupportedOperationException(\n \"DeepSeekChatModel is not implemented in agentscope-core yet.\"\n + \" Use io.agentscope.extensions.model.openai.OpenAIChatModel against the\"\n + \" DeepSeek base URL instead.\");\n }\n\n @Override\n public String toString() {\n return \"DeepSeekCredential{id=\" + getId() + \", baseUrl=\" + baseUrl + \", apiKey=***}\";\n }\n\n public static Builder builder() {\n return new Builder();\n }\n\n public static final class Builder {\n private String id;\n private String apiKey;\n private String baseUrl;\n\n private Builder() {}\n\n public Builder id(String id) {\n this.id = id;\n return this;\n }\n\n public Builder apiKey(String apiKey) {\n this.apiKey = apiKey;\n return this;\n }\n\n public Builder baseUrl(String baseUrl) {\n this.baseUrl = baseUrl;\n return this;\n }\n\n public DeepSeekCredential build() {\n return new DeepSeekCredential(id, apiKey, baseUrl);\n }\n }\n}\n"}, "dc30978808c72308d9762ed972f3d62c5074cd8a": {"t": 1785755447.2798102, "n": 3585, "h": "ab44a9b8bea3b7ad", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.core.credential;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonPropertyOrder;\nimport io.agentscope.core.model.ChatModelBase;\nimport java.util.Objects;\n\n/**\n * Credential for the DeepSeek API.\n *\n * <p>No {@code DeepSeekChatModel} class ships in agentscope-core yet, so {@link\n * #getChatModelClass()} throws {@link UnsupportedOperationException}. The credential itself\n * (id/api-key/base-url) round-trips through JSON for storage compatibility.\n */\n@JsonIgnoreProperties(ignoreUnknown = true)\n@JsonPropertyOrder({\"id\", \"type\", \"api_key\", \"base_url\"})\npublic final class DeepSeekCredential extends CredentialBase {\n\n public static final String TYPE = \"deepseek_credential\";\n\n public static final String DEFAULT_BASE_URL = \"https://api.deepseek.com\";\n\n private final String apiKey;\n private final String baseUrl;\n\n private DeepSeekCredential(String id, String apiKey, String baseUrl) {\n super(id);\n this.apiKey = Objects.requireNonNull(apiKey, \"apiKey must not be null\");\n this.baseUrl = baseUrl == null ? DEFAULT_BASE_URL : baseUrl;\n }\n\n @JsonCreator\n static DeepSeekCredential fromJson(\n @JsonProperty(\"id\") String id,\n @JsonProperty(\"api_key\") String apiKey,\n @JsonProperty(\"base_url\") String baseUrl) {\n return new DeepSeekCredential(id, apiKey, baseUrl);\n }\n\n @JsonProperty(\"type\")\n public String getType() {\n return TYPE;\n }\n\n @JsonProperty(\"api_key\")\n public String getApiKey() {\n return apiKey;\n }\n\n @JsonProperty(\"base_url\")\n public String getBaseUrl() {\n return baseUrl;\n }\n\n @Override\n public Class<? extends ChatModelBase> getChatModelClass() {\n throw new UnsupportedOperationException(\n \"DeepSeekChatModel is not implemented in agentscope-core yet.\"\n + \" Use the OpenAIChatModel against the DeepSeek base URL instead.\");\n }\n\n @Override\n public String toString() {\n return \"DeepSeekCredential{id=\" + getId() + \", baseUrl=\" + baseUrl + \", apiKey=***}\";\n }\n\n public static Builder builder() {\n return new Builder();\n }\n\n public static final class Builder {\n private String id;\n private String apiKey;\n private String baseUrl;\n\n private Builder() {}\n\n public Builder id(String id) {\n this.id = id;\n return this;\n }\n\n public Builder apiKey(String apiKey) {\n this.apiKey = apiKey;\n return this;\n }\n\n public Builder baseUrl(String baseUrl) {\n this.baseUrl = baseUrl;\n return this;\n }\n\n public DeepSeekCredential build() {\n return new DeepSeekCredential(id, apiKey, baseUrl);\n }\n }\n}\n"}, "702461070ddcab29dae7c7a2b130e323a106d020": {"t": 1786067582.0871792, "n": 3648, "h": "0d9878289be33546", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.core.credential;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonPropertyOrder;\nimport io.agentscope.core.model.ChatModelBase;\nimport java.util.Objects;\n\n/**\n * Credential for the DeepSeek API.\n *\n * <p>No {@code DeepSeekChatModel} class ships in agentscope-core yet, so {@link\n * #getChatModelClass()} throws {@link UnsupportedOperationException}. The credential itself\n * (id/api-key/base-url) round-trips through JSON for storage compatibility.\n */\n@JsonIgnoreProperties(ignoreUnknown = true)\n@JsonPropertyOrder({\"id\", \"type\", \"api_key\", \"base_url\"})\npublic final class DeepSeekCredential extends CredentialBase {\n\n public static final String TYPE = \"deepseek_credential\";\n\n public static final String DEFAULT_BASE_URL = \"https://api.deepseek.com\";\n\n private final String apiKey;\n private final String baseUrl;\n\n private DeepSeekCredential(String id, String apiKey, String baseUrl) {\n super(id);\n this.apiKey = Objects.requireNonNull(apiKey, \"apiKey must not be null\");\n this.baseUrl = baseUrl == null ? DEFAULT_BASE_URL : baseUrl;\n }\n\n @JsonCreator\n static DeepSeekCredential fromJson(\n @JsonProperty(\"id\") String id,\n @JsonProperty(\"api_key\") String apiKey,\n @JsonProperty(\"base_url\") String baseUrl) {\n return new DeepSeekCredential(id, apiKey, baseUrl);\n }\n\n @JsonProperty(\"type\")\n public String getType() {\n return TYPE;\n }\n\n @JsonProperty(\"api_key\")\n public String getApiKey() {\n return apiKey;\n }\n\n @JsonProperty(\"base_url\")\n public String getBaseUrl() {\n return baseUrl;\n }\n\n @Override\n public Class<? extends ChatModelBase> getChatModelClass() {\n throw new UnsupportedOperationException(\n \"DeepSeekChatModel is not implemented in agentscope-core yet.\"\n + \" Use io.agentscope.extensions.model.openai.OpenAIChatModel against the\"\n + \" DeepSeek base URL instead.\");\n }\n\n @Override\n public String toString() {\n return \"DeepSeekCredential{id=\" + getId() + \", baseUrl=\" + baseUrl + \", apiKey=***}\";\n }\n\n public static Builder builder() {\n return new Builder();\n }\n\n public static final class Builder {\n private String id;\n private String apiKey;\n private String baseUrl;\n\n private Builder() {}\n\n public Builder id(String id) {\n this.id = id;\n return this;\n }\n\n public Builder apiKey(String apiKey) {\n this.apiKey = apiKey;\n return this;\n }\n\n public Builder baseUrl(String baseUrl) {\n this.baseUrl = baseUrl;\n return this;\n }\n\n public DeepSeekCredential build() {\n return new DeepSeekCredential(id, apiKey, baseUrl);\n }\n }\n}\n"}, "9cbac4ce574183274c37b5132ce453ffcaeafb74": {"t": 1786068377.0512295, "n": 3648, "h": "0d9878289be33546", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.core.credential;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonPropertyOrder;\nimport io.agentscope.core.model.ChatModelBase;\nimport java.util.Objects;\n\n/**\n * Credential for the DeepSeek API.\n *\n * <p>No {@code DeepSeekChatModel} class ships in agentscope-core yet, so {@link\n * #getChatModelClass()} throws {@link UnsupportedOperationException}. The credential itself\n * (id/api-key/base-url) round-trips through JSON for storage compatibility.\n */\n@JsonIgnoreProperties(ignoreUnknown = true)\n@JsonPropertyOrder({\"id\", \"type\", \"api_key\", \"base_url\"})\npublic final class DeepSeekCredential extends CredentialBase {\n\n public static final String TYPE = \"deepseek_credential\";\n\n public static final String DEFAULT_BASE_URL = \"https://api.deepseek.com\";\n\n private final String apiKey;\n private final String baseUrl;\n\n private DeepSeekCredential(String id, String apiKey, String baseUrl) {\n super(id);\n this.apiKey = Objects.requireNonNull(apiKey, \"apiKey must not be null\");\n this.baseUrl = baseUrl == null ? DEFAULT_BASE_URL : baseUrl;\n }\n\n @JsonCreator\n static DeepSeekCredential fromJson(\n @JsonProperty(\"id\") String id,\n @JsonProperty(\"api_key\") String apiKey,\n @JsonProperty(\"base_url\") String baseUrl) {\n return new DeepSeekCredential(id, apiKey, baseUrl);\n }\n\n @JsonProperty(\"type\")\n public String getType() {\n return TYPE;\n }\n\n @JsonProperty(\"api_key\")\n public String getApiKey() {\n return apiKey;\n }\n\n @JsonProperty(\"base_url\")\n public String getBaseUrl() {\n return baseUrl;\n }\n\n @Override\n public Class<? extends ChatModelBase> getChatModelClass() {\n throw new UnsupportedOperationException(\n \"DeepSeekChatModel is not implemented in agentscope-core yet.\"\n + \" Use io.agentscope.extensions.model.openai.OpenAIChatModel against the\"\n + \" DeepSeek base URL instead.\");\n }\n\n @Override\n public String toString() {\n return \"DeepSeekCredential{id=\" + getId() + \", baseUrl=\" + baseUrl + \", apiKey=***}\";\n }\n\n public static Builder builder() {\n return new Builder();\n }\n\n public static final class Builder {\n private String id;\n private String apiKey;\n private String baseUrl;\n\n private Builder() {}\n\n public Builder id(String id) {\n this.id = id;\n return this;\n }\n\n public Builder apiKey(String apiKey) {\n this.apiKey = apiKey;\n return this;\n }\n\n public Builder baseUrl(String baseUrl) {\n this.baseUrl = baseUrl;\n return this;\n }\n\n public DeepSeekCredential build() {\n return new DeepSeekCredential(id, apiKey, baseUrl);\n }\n }\n}\n"}}, "agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-openai/src/main/java/io/agentscope/extensions/model/openai/formatter/DeepSeekMultiAgentFormatter.java": {"ef06c7ac978d7c61b173e47a57b8772b1dd5f216": {"t": 1785746117.4517128, "n": 3608, "h": "c274a1a080d492d9", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.formatter;\n\nimport io.agentscope.core.message.Msg;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport java.util.List;\n\n/**\n * Multi-agent formatter for DeepSeek Chat models.\n *\n * <p>This formatter extends {@link OpenAIMultiAgentFormatter} with DeepSeek-specific handling:\n * <ul>\n * <li>No name field in messages (returns HTTP 400 if present)</li>\n * <li>Does NOT support strict parameter in tool definitions</li>\n * <li>reasoning_content handling for thinking mode</li>\n * </ul>\n *\n * <p>Usage:\n * <pre>{@code\n * OpenAIChatModel.builder()\n * .formatter(new DeepSeekMultiAgentFormatter())\n * .modelName(\"deepseek-chat\")\n * .baseUrl(\"https://api.deepseek.com/v1\")\n * .apiKey(apiKey)\n * .build();\n * }</pre>\n *\n * @deprecated use {@link io.agentscope.extensions.model.openai.compat.deepseek.DeepSeekMultiAgentFormatter}.\n */\n@Deprecated\npublic class DeepSeekMultiAgentFormatter extends OpenAIMultiAgentFormatter {\n\n private final boolean appendEmptyUserIfEndsWithAssistant;\n\n public DeepSeekMultiAgentFormatter() {\n super();\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n public DeepSeekMultiAgentFormatter(String conversationHistoryPrompt) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with optional empty user message appending.\n *\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(boolean appendEmptyUserIfEndsWithAssistant) {\n super();\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with custom conversation history prompt and optional\n * empty user message appending.\n *\n * @param conversationHistoryPrompt custom prompt for conversation history\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(\n String conversationHistoryPrompt, boolean appendEmptyUserIfEndsWithAssistant) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n @Override\n protected List<OpenAIMessage> doFormat(List<Msg> msgs) {\n List<OpenAIMessage> messages = super.doFormat(msgs);\n messages = DeepSeekFormatter.applyDeepSeekFixes(messages);\n if (appendEmptyUserIfEndsWithAssistant) {\n messages = DeepSeekFormatter.appendEmptyUserIfNeeded(messages);\n }\n return messages;\n }\n\n @Override\n protected boolean supportsStrict() {\n return false;\n }\n}\n"}, "74db7572f20f07f05439a0e1b0d6992f9f7c6a38": {"t": 1785755445.378143, "n": 3608, "h": "c274a1a080d492d9", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.formatter;\n\nimport io.agentscope.core.message.Msg;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport java.util.List;\n\n/**\n * Multi-agent formatter for DeepSeek Chat models.\n *\n * <p>This formatter extends {@link OpenAIMultiAgentFormatter} with DeepSeek-specific handling:\n * <ul>\n * <li>No name field in messages (returns HTTP 400 if present)</li>\n * <li>Does NOT support strict parameter in tool definitions</li>\n * <li>reasoning_content handling for thinking mode</li>\n * </ul>\n *\n * <p>Usage:\n * <pre>{@code\n * OpenAIChatModel.builder()\n * .formatter(new DeepSeekMultiAgentFormatter())\n * .modelName(\"deepseek-chat\")\n * .baseUrl(\"https://api.deepseek.com/v1\")\n * .apiKey(apiKey)\n * .build();\n * }</pre>\n *\n * @deprecated use {@link io.agentscope.extensions.model.openai.compat.deepseek.DeepSeekMultiAgentFormatter}.\n */\n@Deprecated\npublic class DeepSeekMultiAgentFormatter extends OpenAIMultiAgentFormatter {\n\n private final boolean appendEmptyUserIfEndsWithAssistant;\n\n public DeepSeekMultiAgentFormatter() {\n super();\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n public DeepSeekMultiAgentFormatter(String conversationHistoryPrompt) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with optional empty user message appending.\n *\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(boolean appendEmptyUserIfEndsWithAssistant) {\n super();\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with custom conversation history prompt and optional\n * empty user message appending.\n *\n * @param conversationHistoryPrompt custom prompt for conversation history\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(\n String conversationHistoryPrompt, boolean appendEmptyUserIfEndsWithAssistant) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n @Override\n protected List<OpenAIMessage> doFormat(List<Msg> msgs) {\n List<OpenAIMessage> messages = super.doFormat(msgs);\n messages = DeepSeekFormatter.applyDeepSeekFixes(messages);\n if (appendEmptyUserIfEndsWithAssistant) {\n messages = DeepSeekFormatter.appendEmptyUserIfNeeded(messages);\n }\n return messages;\n }\n\n @Override\n protected boolean supportsStrict() {\n return false;\n }\n}\n"}, "2c929ffa5abf5e045b87c5876a4a27861f9d205d": {"t": 1785755447.034383, "n": 3483, "h": "507bd1542010afed", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.formatter;\n\nimport io.agentscope.core.message.Msg;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport java.util.List;\n\n/**\n * Multi-agent formatter for DeepSeek Chat models.\n *\n * <p>This formatter extends {@link OpenAIMultiAgentFormatter} with DeepSeek-specific handling:\n * <ul>\n * <li>No name field in messages (returns HTTP 400 if present)</li>\n * <li>Does NOT support strict parameter in tool definitions</li>\n * <li>reasoning_content handling for thinking mode</li>\n * </ul>\n *\n * <p>Usage:\n * <pre>{@code\n * OpenAIChatModel.builder()\n * .formatter(new DeepSeekMultiAgentFormatter())\n * .modelName(\"deepseek-chat\")\n * .baseUrl(\"https://api.deepseek.com/v1\")\n * .apiKey(apiKey)\n * .build();\n * }</pre>\n */\npublic class DeepSeekMultiAgentFormatter extends OpenAIMultiAgentFormatter {\n\n private final boolean appendEmptyUserIfEndsWithAssistant;\n\n public DeepSeekMultiAgentFormatter() {\n super();\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n public DeepSeekMultiAgentFormatter(String conversationHistoryPrompt) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with optional empty user message appending.\n *\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(boolean appendEmptyUserIfEndsWithAssistant) {\n super();\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with custom conversation history prompt and optional\n * empty user message appending.\n *\n * @param conversationHistoryPrompt custom prompt for conversation history\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(\n String conversationHistoryPrompt, boolean appendEmptyUserIfEndsWithAssistant) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n @Override\n protected List<OpenAIMessage> doFormat(List<Msg> msgs) {\n List<OpenAIMessage> messages = super.doFormat(msgs);\n messages = DeepSeekFormatter.applyDeepSeekFixes(messages);\n if (appendEmptyUserIfEndsWithAssistant) {\n messages = DeepSeekFormatter.appendEmptyUserIfNeeded(messages);\n }\n return messages;\n }\n\n @Override\n protected boolean supportsStrict() {\n return false;\n }\n}\n"}, "59c5cf7c67362bc2bc078f5e7904730352bf715f": {"t": 1785755448.4836988, "n": 3540, "h": "845c7e2050d00b4f", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.formatter;\n\nimport io.agentscope.core.message.Msg;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport java.util.List;\n\n/**\n * Multi-agent formatter for DeepSeek Chat models.\n *\n * <p>This formatter extends {@link OpenAIMultiAgentFormatter} with DeepSeek-specific handling:\n * <ul>\n * <li>No name field in messages (returns HTTP 400 if present)</li>\n * <li>System messages converted to user messages</li>\n * <li>Does NOT support strict parameter in tool definitions</li>\n * <li>reasoning_content handling for thinking mode</li>\n * </ul>\n *\n * <p>Usage:\n * <pre>{@code\n * OpenAIChatModel.builder()\n * .formatter(new DeepSeekMultiAgentFormatter())\n * .modelName(\"deepseek-chat\")\n * .baseUrl(\"https://api.deepseek.com/v1\")\n * .apiKey(apiKey)\n * .build();\n * }</pre>\n */\npublic class DeepSeekMultiAgentFormatter extends OpenAIMultiAgentFormatter {\n\n private final boolean appendEmptyUserIfEndsWithAssistant;\n\n public DeepSeekMultiAgentFormatter() {\n super();\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n public DeepSeekMultiAgentFormatter(String conversationHistoryPrompt) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with optional empty user message appending.\n *\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(boolean appendEmptyUserIfEndsWithAssistant) {\n super();\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with custom conversation history prompt and optional\n * empty user message appending.\n *\n * @param conversationHistoryPrompt custom prompt for conversation history\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(\n String conversationHistoryPrompt, boolean appendEmptyUserIfEndsWithAssistant) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n @Override\n protected List<OpenAIMessage> doFormat(List<Msg> msgs) {\n List<OpenAIMessage> messages = super.doFormat(msgs);\n messages = DeepSeekFormatter.applyDeepSeekFixes(messages);\n if (appendEmptyUserIfEndsWithAssistant) {\n messages = DeepSeekFormatter.appendEmptyUserIfNeeded(messages);\n }\n return messages;\n }\n\n @Override\n protected boolean supportsStrict() {\n return false;\n }\n}\n"}, "702461070ddcab29dae7c7a2b130e323a106d020": {"t": 1786067586.9009535, "n": 3608, "h": "c274a1a080d492d9", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.formatter;\n\nimport io.agentscope.core.message.Msg;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport java.util.List;\n\n/**\n * Multi-agent formatter for DeepSeek Chat models.\n *\n * <p>This formatter extends {@link OpenAIMultiAgentFormatter} with DeepSeek-specific handling:\n * <ul>\n * <li>No name field in messages (returns HTTP 400 if present)</li>\n * <li>Does NOT support strict parameter in tool definitions</li>\n * <li>reasoning_content handling for thinking mode</li>\n * </ul>\n *\n * <p>Usage:\n * <pre>{@code\n * OpenAIChatModel.builder()\n * .formatter(new DeepSeekMultiAgentFormatter())\n * .modelName(\"deepseek-chat\")\n * .baseUrl(\"https://api.deepseek.com/v1\")\n * .apiKey(apiKey)\n * .build();\n * }</pre>\n *\n * @deprecated use {@link io.agentscope.extensions.model.openai.compat.deepseek.DeepSeekMultiAgentFormatter}.\n */\n@Deprecated\npublic class DeepSeekMultiAgentFormatter extends OpenAIMultiAgentFormatter {\n\n private final boolean appendEmptyUserIfEndsWithAssistant;\n\n public DeepSeekMultiAgentFormatter() {\n super();\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n public DeepSeekMultiAgentFormatter(String conversationHistoryPrompt) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with optional empty user message appending.\n *\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(boolean appendEmptyUserIfEndsWithAssistant) {\n super();\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with custom conversation history prompt and optional\n * empty user message appending.\n *\n * @param conversationHistoryPrompt custom prompt for conversation history\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(\n String conversationHistoryPrompt, boolean appendEmptyUserIfEndsWithAssistant) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n @Override\n protected List<OpenAIMessage> doFormat(List<Msg> msgs) {\n List<OpenAIMessage> messages = super.doFormat(msgs);\n messages = DeepSeekFormatter.applyDeepSeekFixes(messages);\n if (appendEmptyUserIfEndsWithAssistant) {\n messages = DeepSeekFormatter.appendEmptyUserIfNeeded(messages);\n }\n return messages;\n }\n\n @Override\n protected boolean supportsStrict() {\n return false;\n }\n}\n"}, "9cbac4ce574183274c37b5132ce453ffcaeafb74": {"t": 1786068378.4657676, "n": 3608, "h": "c274a1a080d492d9", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.formatter;\n\nimport io.agentscope.core.message.Msg;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport java.util.List;\n\n/**\n * Multi-agent formatter for DeepSeek Chat models.\n *\n * <p>This formatter extends {@link OpenAIMultiAgentFormatter} with DeepSeek-specific handling:\n * <ul>\n * <li>No name field in messages (returns HTTP 400 if present)</li>\n * <li>Does NOT support strict parameter in tool definitions</li>\n * <li>reasoning_content handling for thinking mode</li>\n * </ul>\n *\n * <p>Usage:\n * <pre>{@code\n * OpenAIChatModel.builder()\n * .formatter(new DeepSeekMultiAgentFormatter())\n * .modelName(\"deepseek-chat\")\n * .baseUrl(\"https://api.deepseek.com/v1\")\n * .apiKey(apiKey)\n * .build();\n * }</pre>\n *\n * @deprecated use {@link io.agentscope.extensions.model.openai.compat.deepseek.DeepSeekMultiAgentFormatter}.\n */\n@Deprecated\npublic class DeepSeekMultiAgentFormatter extends OpenAIMultiAgentFormatter {\n\n private final boolean appendEmptyUserIfEndsWithAssistant;\n\n public DeepSeekMultiAgentFormatter() {\n super();\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n public DeepSeekMultiAgentFormatter(String conversationHistoryPrompt) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with optional empty user message appending.\n *\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(boolean appendEmptyUserIfEndsWithAssistant) {\n super();\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with custom conversation history prompt and optional\n * empty user message appending.\n *\n * @param conversationHistoryPrompt custom prompt for conversation history\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(\n String conversationHistoryPrompt, boolean appendEmptyUserIfEndsWithAssistant) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n @Override\n protected List<OpenAIMessage> doFormat(List<Msg> msgs) {\n List<OpenAIMessage> messages = super.doFormat(msgs);\n messages = DeepSeekFormatter.applyDeepSeekFixes(messages);\n if (appendEmptyUserIfEndsWithAssistant) {\n messages = DeepSeekFormatter.appendEmptyUserIfNeeded(messages);\n }\n return messages;\n }\n\n @Override\n protected boolean supportsStrict() {\n return false;\n }\n}\n"}}, "agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-openai/src/main/java/io/agentscope/extensions/model/openai/compat/deepseek/DeepSeekMultiAgentFormatter.java": {"ef06c7ac978d7c61b173e47a57b8772b1dd5f216": {"t": 1785746134.4522555, "n": 3594, "h": "09a225d093476ab6", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.compat.deepseek;\n\nimport io.agentscope.core.message.Msg;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport io.agentscope.extensions.model.openai.formatter.OpenAIMultiAgentFormatter;\nimport java.util.List;\n\n/**\n * Multi-agent formatter for DeepSeek Chat models (deepseek-v4-flash, deepseek-v4-pro).\n *\n * <p>This formatter extends {@link OpenAIMultiAgentFormatter} with DeepSeek-specific handling:\n * <ul>\n * <li>System/user/assistant {@code name} fields are allowed</li>\n * <li>Omits strict parameter in tool definitions</li>\n * <li>reasoning_content handling for thinking mode</li>\n * </ul>\n *\n * <p>Usage:\n * <pre>{@code\n * OpenAIChatModel.builder()\n * .formatter(new DeepSeekMultiAgentFormatter())\n * .modelName(\"deepseek-v4-pro\")\n * .baseUrl(\"https://api.deepseek.com\")\n * .apiKey(apiKey)\n * .build();\n * }</pre>\n */\npublic class DeepSeekMultiAgentFormatter extends OpenAIMultiAgentFormatter {\n\n private final boolean appendEmptyUserIfEndsWithAssistant;\n\n public DeepSeekMultiAgentFormatter() {\n super();\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n public DeepSeekMultiAgentFormatter(String conversationHistoryPrompt) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with optional empty user message appending.\n *\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(boolean appendEmptyUserIfEndsWithAssistant) {\n super();\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with custom conversation history prompt and optional\n * empty user message appending.\n *\n * @param conversationHistoryPrompt custom prompt for conversation history\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(\n String conversationHistoryPrompt, boolean appendEmptyUserIfEndsWithAssistant) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n @Override\n protected List<OpenAIMessage> doFormat(List<Msg> msgs) {\n List<OpenAIMessage> messages = super.doFormat(msgs);\n messages = DeepSeekFormatter.applyDeepSeekFixes(messages);\n if (appendEmptyUserIfEndsWithAssistant) {\n messages = DeepSeekFormatter.appendEmptyUserIfNeeded(messages);\n }\n return messages;\n }\n\n @Override\n protected boolean supportsStrict() {\n return false;\n }\n}\n"}, "74db7572f20f07f05439a0e1b0d6992f9f7c6a38": {"t": 1785755439.4700792, "n": 3594, "h": "09a225d093476ab6", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.compat.deepseek;\n\nimport io.agentscope.core.message.Msg;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport io.agentscope.extensions.model.openai.formatter.OpenAIMultiAgentFormatter;\nimport java.util.List;\n\n/**\n * Multi-agent formatter for DeepSeek Chat models (deepseek-v4-flash, deepseek-v4-pro).\n *\n * <p>This formatter extends {@link OpenAIMultiAgentFormatter} with DeepSeek-specific handling:\n * <ul>\n * <li>System/user/assistant {@code name} fields are allowed</li>\n * <li>Omits strict parameter in tool definitions</li>\n * <li>reasoning_content handling for thinking mode</li>\n * </ul>\n *\n * <p>Usage:\n * <pre>{@code\n * OpenAIChatModel.builder()\n * .formatter(new DeepSeekMultiAgentFormatter())\n * .modelName(\"deepseek-v4-pro\")\n * .baseUrl(\"https://api.deepseek.com\")\n * .apiKey(apiKey)\n * .build();\n * }</pre>\n */\npublic class DeepSeekMultiAgentFormatter extends OpenAIMultiAgentFormatter {\n\n private final boolean appendEmptyUserIfEndsWithAssistant;\n\n public DeepSeekMultiAgentFormatter() {\n super();\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n public DeepSeekMultiAgentFormatter(String conversationHistoryPrompt) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with optional empty user message appending.\n *\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(boolean appendEmptyUserIfEndsWithAssistant) {\n super();\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with custom conversation history prompt and optional\n * empty user message appending.\n *\n * @param conversationHistoryPrompt custom prompt for conversation history\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(\n String conversationHistoryPrompt, boolean appendEmptyUserIfEndsWithAssistant) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n @Override\n protected List<OpenAIMessage> doFormat(List<Msg> msgs) {\n List<OpenAIMessage> messages = super.doFormat(msgs);\n messages = DeepSeekFormatter.applyDeepSeekFixes(messages);\n if (appendEmptyUserIfEndsWithAssistant) {\n messages = DeepSeekFormatter.appendEmptyUserIfNeeded(messages);\n }\n return messages;\n }\n\n @Override\n protected boolean supportsStrict() {\n return false;\n }\n}\n"}, "702461070ddcab29dae7c7a2b130e323a106d020": {"t": 1786067595.752852, "n": 3594, "h": "09a225d093476ab6", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.compat.deepseek;\n\nimport io.agentscope.core.message.Msg;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport io.agentscope.extensions.model.openai.formatter.OpenAIMultiAgentFormatter;\nimport java.util.List;\n\n/**\n * Multi-agent formatter for DeepSeek Chat models (deepseek-v4-flash, deepseek-v4-pro).\n *\n * <p>This formatter extends {@link OpenAIMultiAgentFormatter} with DeepSeek-specific handling:\n * <ul>\n * <li>System/user/assistant {@code name} fields are allowed</li>\n * <li>Omits strict parameter in tool definitions</li>\n * <li>reasoning_content handling for thinking mode</li>\n * </ul>\n *\n * <p>Usage:\n * <pre>{@code\n * OpenAIChatModel.builder()\n * .formatter(new DeepSeekMultiAgentFormatter())\n * .modelName(\"deepseek-v4-pro\")\n * .baseUrl(\"https://api.deepseek.com\")\n * .apiKey(apiKey)\n * .build();\n * }</pre>\n */\npublic class DeepSeekMultiAgentFormatter extends OpenAIMultiAgentFormatter {\n\n private final boolean appendEmptyUserIfEndsWithAssistant;\n\n public DeepSeekMultiAgentFormatter() {\n super();\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n public DeepSeekMultiAgentFormatter(String conversationHistoryPrompt) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with optional empty user message appending.\n *\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(boolean appendEmptyUserIfEndsWithAssistant) {\n super();\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with custom conversation history prompt and optional\n * empty user message appending.\n *\n * @param conversationHistoryPrompt custom prompt for conversation history\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(\n String conversationHistoryPrompt, boolean appendEmptyUserIfEndsWithAssistant) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n @Override\n protected List<OpenAIMessage> doFormat(List<Msg> msgs) {\n List<OpenAIMessage> messages = super.doFormat(msgs);\n messages = DeepSeekFormatter.applyDeepSeekFixes(messages);\n if (appendEmptyUserIfEndsWithAssistant) {\n messages = DeepSeekFormatter.appendEmptyUserIfNeeded(messages);\n }\n return messages;\n }\n\n @Override\n protected boolean supportsStrict() {\n return false;\n }\n}\n"}, "9cbac4ce574183274c37b5132ce453ffcaeafb74": {"t": 1786068379.2098303, "n": 3594, "h": "09a225d093476ab6", "c": "/*\n * Copyright 2024-2026 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.agentscope.extensions.model.openai.compat.deepseek;\n\nimport io.agentscope.core.message.Msg;\nimport io.agentscope.extensions.model.openai.dto.OpenAIMessage;\nimport io.agentscope.extensions.model.openai.formatter.OpenAIMultiAgentFormatter;\nimport java.util.List;\n\n/**\n * Multi-agent formatter for DeepSeek Chat models (deepseek-v4-flash, deepseek-v4-pro).\n *\n * <p>This formatter extends {@link OpenAIMultiAgentFormatter} with DeepSeek-specific handling:\n * <ul>\n * <li>System/user/assistant {@code name} fields are allowed</li>\n * <li>Omits strict parameter in tool definitions</li>\n * <li>reasoning_content handling for thinking mode</li>\n * </ul>\n *\n * <p>Usage:\n * <pre>{@code\n * OpenAIChatModel.builder()\n * .formatter(new DeepSeekMultiAgentFormatter())\n * .modelName(\"deepseek-v4-pro\")\n * .baseUrl(\"https://api.deepseek.com\")\n * .apiKey(apiKey)\n * .build();\n * }</pre>\n */\npublic class DeepSeekMultiAgentFormatter extends OpenAIMultiAgentFormatter {\n\n private final boolean appendEmptyUserIfEndsWithAssistant;\n\n public DeepSeekMultiAgentFormatter() {\n super();\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n public DeepSeekMultiAgentFormatter(String conversationHistoryPrompt) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = false;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with optional empty user message appending.\n *\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(boolean appendEmptyUserIfEndsWithAssistant) {\n super();\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n /**\n * Create a DeepSeek multi-agent formatter with custom conversation history prompt and optional\n * empty user message appending.\n *\n * @param conversationHistoryPrompt custom prompt for conversation history\n * @param appendEmptyUserIfEndsWithAssistant if true, append an empty user message when the\n * conversation ends with an assistant message to avoid API errors\n */\n public DeepSeekMultiAgentFormatter(\n String conversationHistoryPrompt, boolean appendEmptyUserIfEndsWithAssistant) {\n super(conversationHistoryPrompt);\n this.appendEmptyUserIfEndsWithAssistant = appendEmptyUserIfEndsWithAssistant;\n }\n\n @Override\n protected List<OpenAIMessage> doFormat(List<Msg> msgs) {\n List<OpenAIMessage> messages = super.doFormat(msgs);\n messages = DeepSeekFormatter.applyDeepSeekFixes(messages);\n if (appendEmptyUserIfEndsWithAssistant) {\n messages = DeepSeekFormatter.appendEmptyUserIfNeeded(messages);\n }\n return messages;\n }\n\n @Override\n protected boolean supportsStrict() {\n return false;\n }\n}\n"}}}}