| | |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactoryImpl; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.grok.GrokChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax.MiniMaxChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot.MoonshotChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.stepfun.StepFunChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel; |
| | | import com.openai.client.okhttp.OpenAIOkHttpClient; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan.YiYanChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu.ZhiPuChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha.AiBoChaWebSearchClient; |
| | | import cn.iocoder.yudao.module.ai.tool.method.PersonService; |
| | | import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; |
| | | import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; |
| | | import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; |
| | | import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; |
| | | import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingModel; |
| | | import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingOptions; |
| | | import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; |
| | | import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions; |
| | | import io.micrometer.observation.ObservationRegistry; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.ai.chat.model.ChatModel; |
| | | import org.springframework.ai.deepseek.DeepSeekChatModel; |
| | | import org.springframework.ai.deepseek.DeepSeekChatOptions; |
| | | import org.springframework.ai.deepseek.api.DeepSeekApi; |
| | | import org.springframework.ai.document.MetadataMode; |
| | | import org.springframework.ai.embedding.BatchingStrategy; |
| | | import org.springframework.ai.embedding.TokenCountBatchingStrategy; |
| | | import org.springframework.ai.model.tool.ToolCallingManager; |
| | | import org.springframework.ai.openai.OpenAiChatModel; |
| | | import org.springframework.ai.openai.OpenAiChatOptions; |
| | | import org.springframework.ai.retry.RetryUtils; |
| | | import org.springframework.ai.support.ToolCallbacks; |
| | | import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; |
| | | import org.springframework.ai.tokenizer.TokenCountEstimator; |
| | |
| | | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
| | | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| | | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * 芋道 AI 自动配置 |
| | |
| | | // ========== 各种 AI Client 创建 ========== |
| | | |
| | | @Bean |
| | | @ConditionalOnProperty(value = "yudao.ai.gemini.enable", havingValue = "true") |
| | | public GeminiChatModel geminiChatModel(YudaoAiProperties yudaoAiProperties) { |
| | | YudaoAiProperties.Gemini properties = yudaoAiProperties.getGemini(); |
| | | return buildGeminiChatClient(properties); |
| | | @ConditionalOnMissingBean |
| | | @ConditionalOnProperty(value = "spring.ai.dashscope.api-key") |
| | | public DashScopeChatModel dashScopeChatModel(@Value("${spring.ai.dashscope.api-key}") String apiKey, |
| | | ToolCallingManager toolCallingManager, |
| | | ObservationRegistry observationRegistry) { |
| | | return buildTongYiChatModel(apiKey, toolCallingManager, observationRegistry); |
| | | } |
| | | |
| | | public GeminiChatModel buildGeminiChatClient(YudaoAiProperties.Gemini properties) { |
| | | if (StrUtil.isEmpty(properties.getModel())) { |
| | | properties.setModel(GeminiChatModel.MODEL_DEFAULT); |
| | | @Bean |
| | | @ConditionalOnMissingBean |
| | | @ConditionalOnProperty(value = "spring.ai.dashscope.api-key") |
| | | public DashScopeImageModel dashScopeImageModel(@Value("${spring.ai.dashscope.api-key}") String apiKey, |
| | | ObservationRegistry observationRegistry) { |
| | | return buildTongYiImagesModel(apiKey, observationRegistry); |
| | | } |
| | | OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() |
| | | .openAiClient(OpenAIOkHttpClient.builder() |
| | | .baseUrl(GeminiChatModel.BASE_URL) |
| | | .apiKey(properties.getApiKey()) |
| | | .build()) |
| | | .options(OpenAiChatOptions.builder() |
| | | .model(properties.getModel()) |
| | | .temperature(properties.getTemperature()) |
| | | .maxTokens(properties.getMaxTokens()) |
| | | .topP(properties.getTopP()) |
| | | .build()) |
| | | .toolCallingManager(getToolCallingManager()) |
| | | |
| | | @Bean |
| | | @ConditionalOnMissingBean |
| | | @ConditionalOnProperty(value = "spring.ai.dashscope.api-key") |
| | | public DashScopeEmbeddingModel dashScopeEmbeddingModel(@Value("${spring.ai.dashscope.api-key}") String apiKey, |
| | | ObservationRegistry observationRegistry) { |
| | | return buildTongYiEmbeddingModel(apiKey, null, observationRegistry); |
| | | } |
| | | |
| | | public static DashScopeChatModel buildTongYiChatModel(String apiKey) { |
| | | return buildTongYiChatModel(apiKey, getToolCallingManager(), getObservationRegistry()); |
| | | } |
| | | |
| | | private static DashScopeChatModel buildTongYiChatModel(String apiKey, ToolCallingManager toolCallingManager, |
| | | ObservationRegistry observationRegistry) { |
| | | DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build(); |
| | | DashScopeChatOptions options = DashScopeChatOptions.builder() |
| | | .model(DashScopeApi.DEFAULT_CHAT_MODEL) |
| | | .temperature(0.7) |
| | | .build(); |
| | | return new GeminiChatModel(openAiChatModel); |
| | | return new DashScopeChatModel(dashScopeApi, options, toolCallingManager, RetryUtils.DEFAULT_RETRY_TEMPLATE, |
| | | observationRegistry); |
| | | } |
| | | |
| | | public static DashScopeImageModel buildTongYiImagesModel(String apiKey) { |
| | | return buildTongYiImagesModel(apiKey, getObservationRegistry()); |
| | | } |
| | | |
| | | private static DashScopeImageModel buildTongYiImagesModel(String apiKey, ObservationRegistry observationRegistry) { |
| | | DashScopeImageApi dashScopeImageApi = DashScopeImageApi.builder().apiKey(apiKey).build(); |
| | | DashScopeImageOptions options = DashScopeImageOptions.builder() |
| | | .model(DashScopeImageApi.DEFAULT_IMAGE_MODEL) |
| | | .build(); |
| | | return new DashScopeImageModel(dashScopeImageApi, options, RetryUtils.DEFAULT_RETRY_TEMPLATE, |
| | | observationRegistry); |
| | | } |
| | | |
| | | public static DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) { |
| | | return buildTongYiEmbeddingModel(apiKey, model, getObservationRegistry()); |
| | | } |
| | | |
| | | private static DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model, |
| | | ObservationRegistry observationRegistry) { |
| | | DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build(); |
| | | DashScopeEmbeddingOptions options = DashScopeEmbeddingOptions.builder() |
| | | .model(StrUtil.blankToDefault(model, DashScopeApi.DEFAULT_EMBEDDING_MODEL)) |
| | | .build(); |
| | | return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, options, RetryUtils.DEFAULT_RETRY_TEMPLATE, |
| | | observationRegistry); |
| | | } |
| | | |
| | | @Bean |
| | |
| | | if (StrUtil.isEmpty(properties.getModel())) { |
| | | properties.setModel(DouBaoChatModel.MODEL_DEFAULT); |
| | | } |
| | | OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() |
| | | .openAiClient(OpenAIOkHttpClient.builder() |
| | | DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder() |
| | | .deepSeekApi(DeepSeekApi.builder() |
| | | .baseUrl(DouBaoChatModel.BASE_URL) |
| | | .completionsPath(DouBaoChatModel.COMPLETE_PATH) |
| | | .apiKey(properties.getApiKey()) |
| | | .build()) |
| | | .options(OpenAiChatOptions.builder() |
| | | .options(DeepSeekChatOptions.builder() |
| | | .model(properties.getModel()) |
| | | .temperature(properties.getTemperature()) |
| | | .maxTokens(properties.getMaxTokens()) |
| | | .topP(properties.getTopP()) |
| | | .build()) |
| | | .toolCallingManager(getToolCallingManager()) |
| | | .build(); |
| | | return new DouBaoChatModel(openAiChatModel); |
| | | } |
| | |
| | | .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL) |
| | | .apiKey(properties.getApiKey()) |
| | | .build()) |
| | | .defaultOptions(DeepSeekChatOptions.builder() |
| | | .options(DeepSeekChatOptions.builder() |
| | | .model(properties.getModel()) |
| | | .temperature(properties.getTemperature()) |
| | | .maxTokens(properties.getMaxTokens()) |
| | | .topP(properties.getTopP()) |
| | | .build()) |
| | | .toolCallingManager(getToolCallingManager()) |
| | | .build(); |
| | | return new SiliconFlowChatModel(openAiChatModel); |
| | | } |
| | |
| | | if (StrUtil.isEmpty(properties.getModel())) { |
| | | properties.setModel(HunYuanChatModel.MODEL_DEFAULT); |
| | | } |
| | | // 特殊:由于混元大模型不提供 deepseek,而是通过知识引擎,所以需要区分下 URL |
| | | if (StrUtil.isEmpty(properties.getBaseUrl())) { |
| | | properties.setBaseUrl( |
| | | StrUtil.startWithIgnoreCase(properties.getModel(), "deepseek") ? HunYuanChatModel.DEEP_SEEK_BASE_URL |
| | | : HunYuanChatModel.BASE_URL); |
| | | properties.setBaseUrl(HunYuanChatModel.BASE_URL); |
| | | } |
| | | // 创建 DeepSeekChatModel、HunYuanChatModel 对象 |
| | | DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder() |
| | |
| | | .completionsPath(HunYuanChatModel.COMPLETE_PATH) |
| | | .apiKey(properties.getApiKey()) |
| | | .build()) |
| | | .defaultOptions(DeepSeekChatOptions.builder() |
| | | .options(DeepSeekChatOptions.builder() |
| | | .model(properties.getModel()) |
| | | .temperature(properties.getTemperature()) |
| | | .maxTokens(properties.getMaxTokens()) |
| | | .topP(properties.getTopP()) |
| | | .build()) |
| | | .toolCallingManager(getToolCallingManager()) |
| | | .build(); |
| | | return new HunYuanChatModel(openAiChatModel); |
| | | } |
| | |
| | | if (StrUtil.isEmpty(properties.getModel())) { |
| | | properties.setModel(XingHuoChatModel.MODEL_DEFAULT); |
| | | } |
| | | OpenAIOkHttpClient.Builder builder = OpenAIOkHttpClient.builder() |
| | | .baseUrl(XingHuoChatModel.BASE_URL_V1) |
| | | .apiKey(properties.getAppKey() + ":" + properties.getSecretKey()); |
| | | if ("x1".equals(properties.getModel())) { |
| | | builder.baseUrl(XingHuoChatModel.BASE_URL_V2); |
| | | } |
| | | OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() |
| | | .openAiClient(builder.build()) |
| | | .options(OpenAiChatOptions.builder() |
| | | return XingHuoChatModel.builder() |
| | | .apiKey(properties.getApiKey()) |
| | | .options(DeepSeekChatOptions.builder() |
| | | .model(properties.getModel()) |
| | | .temperature(properties.getTemperature()) |
| | | .maxTokens(properties.getMaxTokens()) |
| | | .topP(properties.getTopP()) |
| | | .build()) |
| | | // TODO @芋艿:星火的 function call 有 bug,会报 ToolResponseMessage must have an id 错误!!! |
| | | .toolCallingManager(getToolCallingManager()) |
| | | .build(); |
| | | return new XingHuoChatModel(openAiChatModel); |
| | | } |
| | | |
| | | @Bean |
| | |
| | | if (StrUtil.isEmpty(properties.getModel())) { |
| | | properties.setModel(BaiChuanChatModel.MODEL_DEFAULT); |
| | | } |
| | | OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() |
| | | .openAiClient(OpenAIOkHttpClient.builder() |
| | | DeepSeekChatModel deepSeekChatModel = DeepSeekChatModel.builder() |
| | | .deepSeekApi(DeepSeekApi.builder() |
| | | .baseUrl(BaiChuanChatModel.BASE_URL) |
| | | .apiKey(properties.getApiKey()) |
| | | .completionsPath(BaiChuanChatModel.COMPLETE_PATH) |
| | | .build()) |
| | | .options(OpenAiChatOptions.builder() |
| | | .options(DeepSeekChatOptions.builder() |
| | | .model(properties.getModel()) |
| | | .temperature(properties.getTemperature()) |
| | | .maxTokens(properties.getMaxTokens()) |
| | | .topP(properties.getTopP()) |
| | | .build()) |
| | | .toolCallingManager(getToolCallingManager()) |
| | | .build(); |
| | | return new BaiChuanChatModel(openAiChatModel); |
| | | return new BaiChuanChatModel(deepSeekChatModel); |
| | | } |
| | | |
| | | @Bean |
| | | @ConditionalOnProperty(value = "yudao.ai.yiyan.enable", havingValue = "true") |
| | | public YiYanChatModel yiYanChatClient(YudaoAiProperties yudaoAiProperties) { |
| | | YudaoAiProperties.YiYan properties = yudaoAiProperties.getYiyan(); |
| | | return buildYiYanChatClient(properties); |
| | | } |
| | | |
| | | public YiYanChatModel buildYiYanChatClient(YudaoAiProperties.YiYan properties) { |
| | | if (StrUtil.isEmpty(properties.getModel())) { |
| | | properties.setModel(YiYanChatModel.MODEL_DEFAULT); |
| | | } |
| | | return new YiYanChatModel(buildDeepSeekCompatibleChatModel( |
| | | StrUtil.blankToDefault(properties.getBaseUrl(), YiYanChatModel.BASE_URL), |
| | | null, properties.getApiKey(), properties.getModel(), properties.getTemperature(), |
| | | properties.getMaxTokens(), properties.getTopP())); |
| | | } |
| | | |
| | | @Bean |
| | | @ConditionalOnProperty(value = "yudao.ai.zhipu.enable", havingValue = "true") |
| | | public ZhiPuChatModel zhiPuChatClient(YudaoAiProperties yudaoAiProperties) { |
| | | YudaoAiProperties.ZhiPu properties = yudaoAiProperties.getZhipu(); |
| | | return buildZhiPuChatClient(properties); |
| | | } |
| | | |
| | | public ZhiPuChatModel buildZhiPuChatClient(YudaoAiProperties.ZhiPu properties) { |
| | | if (StrUtil.isEmpty(properties.getModel())) { |
| | | properties.setModel(ZhiPuChatModel.MODEL_DEFAULT); |
| | | } |
| | | DeepSeekChatModel deepSeekChatModel = DeepSeekChatModel.builder() |
| | | .deepSeekApi(DeepSeekApi.builder() |
| | | .baseUrl(StrUtil.blankToDefault(properties.getBaseUrl(), ZhiPuChatModel.BASE_URL)) |
| | | .apiKey(properties.getApiKey()) |
| | | .build()) |
| | | .options(DeepSeekChatOptions.builder() |
| | | .model(properties.getModel()) |
| | | .temperature(properties.getTemperature()) |
| | | .maxTokens(properties.getMaxTokens()) |
| | | .topP(properties.getTopP()) |
| | | .build()) |
| | | .build(); |
| | | return new ZhiPuChatModel(deepSeekChatModel); |
| | | } |
| | | |
| | | @Bean |
| | | @ConditionalOnProperty(value = "yudao.ai.minimax.enable", havingValue = "true") |
| | | public MiniMaxChatModel miniMaxChatClient(YudaoAiProperties yudaoAiProperties) { |
| | | YudaoAiProperties.MiniMax properties = yudaoAiProperties.getMinimax(); |
| | | return buildMiniMaxChatClient(properties); |
| | | } |
| | | |
| | | public MiniMaxChatModel buildMiniMaxChatClient(YudaoAiProperties.MiniMax properties) { |
| | | if (StrUtil.isEmpty(properties.getModel())) { |
| | | properties.setModel(MiniMaxChatModel.MODEL_DEFAULT); |
| | | } |
| | | return new MiniMaxChatModel(buildDeepSeekCompatibleChatModel( |
| | | StrUtil.blankToDefault(properties.getBaseUrl(), MiniMaxChatModel.BASE_URL), |
| | | null, properties.getApiKey(), properties.getModel(), properties.getTemperature(), |
| | | properties.getMaxTokens(), properties.getTopP())); |
| | | } |
| | | |
| | | @Bean |
| | | @ConditionalOnProperty(value = "yudao.ai.moonshot.enable", havingValue = "true") |
| | | public MoonshotChatModel moonshotChatClient(YudaoAiProperties yudaoAiProperties) { |
| | | YudaoAiProperties.Moonshot properties = yudaoAiProperties.getMoonshot(); |
| | | return buildMoonshotChatClient(properties); |
| | | } |
| | | |
| | | public MoonshotChatModel buildMoonshotChatClient(YudaoAiProperties.Moonshot properties) { |
| | | if (StrUtil.isEmpty(properties.getModel())) { |
| | | properties.setModel(MoonshotChatModel.MODEL_DEFAULT); |
| | | } |
| | | return new MoonshotChatModel(buildDeepSeekCompatibleChatModel( |
| | | StrUtil.blankToDefault(properties.getBaseUrl(), MoonshotChatModel.BASE_URL), |
| | | MoonshotChatModel.COMPLETE_PATH, properties.getApiKey(), properties.getModel(), |
| | | properties.getTemperature(), properties.getMaxTokens(), properties.getTopP())); |
| | | } |
| | | |
| | | @Bean |
| | | @ConditionalOnProperty(value = "yudao.ai.stepfun.enable", havingValue = "true") |
| | | public StepFunChatModel stepFunChatClient(YudaoAiProperties yudaoAiProperties) { |
| | | YudaoAiProperties.StepFun properties = yudaoAiProperties.getStepfun(); |
| | | return buildStepFunChatClient(properties); |
| | | } |
| | | |
| | | public StepFunChatModel buildStepFunChatClient(YudaoAiProperties.StepFun properties) { |
| | | if (StrUtil.isEmpty(properties.getModel())) { |
| | | properties.setModel(StepFunChatModel.MODEL_DEFAULT); |
| | | } |
| | | return new StepFunChatModel(buildDeepSeekCompatibleChatModel( |
| | | StrUtil.blankToDefault(properties.getBaseUrl(), StepFunChatModel.BASE_URL), |
| | | StepFunChatModel.COMPLETE_PATH, properties.getApiKey(), properties.getModel(), |
| | | properties.getTemperature(), properties.getMaxTokens(), properties.getTopP())); |
| | | } |
| | | |
| | | private static DeepSeekChatModel buildDeepSeekCompatibleChatModel(String baseUrl, String completionsPath, |
| | | String apiKey, String model, |
| | | Double temperature, Integer maxTokens, |
| | | Double topP) { |
| | | DeepSeekApi.Builder apiBuilder = DeepSeekApi.builder() |
| | | .baseUrl(baseUrl) |
| | | .apiKey(apiKey); |
| | | if (StrUtil.isNotEmpty(completionsPath)) { |
| | | apiBuilder.completionsPath(completionsPath); |
| | | } |
| | | return DeepSeekChatModel.builder() |
| | | .deepSeekApi(apiBuilder.build()) |
| | | .options(DeepSeekChatOptions.builder() |
| | | .model(model) |
| | | .temperature(temperature) |
| | | .maxTokens(maxTokens) |
| | | .topP(topP) |
| | | .build()) |
| | | .build(); |
| | | } |
| | | |
| | | @Bean |
| | |
| | | return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl()); |
| | | } |
| | | |
| | | public ChatModel buildGrokChatClient(YudaoAiProperties.Grok properties) { |
| | | @Bean |
| | | @ConditionalOnProperty(value = "yudao.ai.grok.enable", havingValue = "true") |
| | | public GrokChatModel grokChatClient(YudaoAiProperties yudaoAiProperties) { |
| | | YudaoAiProperties.Grok properties = yudaoAiProperties.getGrok(); |
| | | return buildGrokChatClient(properties); |
| | | } |
| | | |
| | | public GrokChatModel buildGrokChatClient(YudaoAiProperties.Grok properties) { |
| | | if (StrUtil.isEmpty(properties.getModel())) { |
| | | properties.setModel(GrokChatModel.MODEL_DEFAULT); |
| | | } |
| | | OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() |
| | | .openAiClient(OpenAIOkHttpClient.builder() |
| | | .baseUrl(Optional.ofNullable(properties.getBaseUrl()) |
| | | .orElse(GrokChatModel.BASE_URL)) |
| | | .apiKey(properties.getApiKey()) |
| | | .build()) |
| | | .options(OpenAiChatOptions.builder() |
| | | .baseUrl(StrUtil.blankToDefault(properties.getBaseUrl(), GrokChatModel.BASE_URL)) |
| | | .apiKey(properties.getApiKey()) |
| | | .model(properties.getModel()) |
| | | .temperature(properties.getTemperature()) |
| | | .maxTokens(properties.getMaxTokens()) |
| | | .topP(properties.getTopP()) |
| | | .build()) |
| | | .toolCallingManager(getToolCallingManager()) |
| | | .build(); |
| | | return new DouBaoChatModel(openAiChatModel); |
| | | return new GrokChatModel(openAiChatModel); |
| | | } |
| | | |
| | | // ========== RAG 相关 ========== |
| | |
| | | return SpringUtil.getBean(ToolCallingManager.class); |
| | | } |
| | | |
| | | private static ObservationRegistry getObservationRegistry() { |
| | | return SpringUtil.getBean(ObservationRegistry.class); |
| | | } |
| | | |
| | | // ========== Web Search 相关 ========== |
| | | |
| | | @Bean |