diff --git a/api/build.gradle b/api/build.gradle index ed797da8c47..e142ec96186 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -1081,6 +1081,19 @@ dependencies { ) ) + BuildUtils.addExternalDependency( + project, + new ExternalDependency( + "org.springframework.ai:spring-ai-openai:${springAiVersion}", + "spring-ai-openai", + "spring-ai", + "https://github.com/spring-projects/spring-ai", + ExternalDependency.APACHE_2_LICENSE_NAME, + ExternalDependency.APACHE_2_LICENSE_URL, + "LLM Chat Client integration for ChatGPT" + ) + ) + BuildUtils.addExternalDependency( project, new ExternalDependency( diff --git a/core/src/org/labkey/core/mpc/McpServiceImpl.java b/core/src/org/labkey/core/mpc/McpServiceImpl.java index 422d0140223..c1f3f98d7fd 100644 --- a/core/src/org/labkey/core/mpc/McpServiceImpl.java +++ b/core/src/org/labkey/core/mpc/McpServiceImpl.java @@ -35,6 +35,12 @@ import org.labkey.api.util.logging.LogHelper; import org.springframework.ai.anthropic.AnthropicChatModel; import org.springframework.ai.anthropic.AnthropicChatOptions; +import org.springframework.ai.openai.OpenAiChatModel; +import org.springframework.ai.openai.OpenAiChatOptions; +import org.springframework.ai.openai.OpenAiEmbeddingModel; +import org.springframework.ai.openai.OpenAiEmbeddingOptions; +import org.springframework.ai.openai.api.OpenAiApi; +import org.springframework.ai.document.MetadataMode; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; import org.springframework.ai.chat.client.advisor.api.Advisor; @@ -108,11 +114,21 @@ public McpServiceImpl() { model = new _ClaudeProvider(); } + if (isNotBlank(System.getenv("OPENAI_API_KEY"))) + { + var openai = new _ChatGptProvider(); + if (null == embedding) + embedding = openai; + if (null == model) + model = openai; + } if (isNotBlank(System.getenv("GEMINI_API_KEY"))) { - embedding = new _GeminiProvider(); + var gemini = new _GeminiProvider(); + if (null == embedding) + embedding = gemini; if (null == model) - model = new _GeminiProvider(); + model = gemini; } modelProvider = model; embeddingProvider = embedding; @@ -603,4 +619,55 @@ public EmbeddingModel createEmbeddingModel() return null; } } + + class _ChatGptProvider implements _ModelProvider + { + @Override + public String getModel() + { + return "gpt-4o"; + } + + @Override + public String getEmbeddingModel() + { + return "text-embedding-3-small"; + } + + @Override + public OpenAiChatOptions getChatOptions() + { + return OpenAiChatOptions.builder() + .model(getModel()) + .toolCallbacks(getToolCallbacks()) + .build(); + } + + @Override + public OpenAiChatModel getChatModel() + { + OpenAiApi openAiApi = OpenAiApi.builder() + .apiKey(System.getenv("OPENAI_API_KEY")) + .build(); + + return OpenAiChatModel.builder() + .openAiApi(openAiApi) + .defaultOptions(getChatOptions()) + .build(); + } + + @Override + public EmbeddingModel createEmbeddingModel() + { + OpenAiApi openAiApi = OpenAiApi.builder() + .apiKey(System.getenv("OPENAI_API_KEY")) + .build(); + + OpenAiEmbeddingOptions embeddingOptions = OpenAiEmbeddingOptions.builder() + .model(getEmbeddingModel()) + .build(); + + return new OpenAiEmbeddingModel(openAiApi, MetadataMode.EMBED, embeddingOptions); + } + } }