From 497f47f2c26683cffc6c0ddbcf82cdd26ca59c96 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Fri, 7 Feb 2025 20:17:47 +0100 Subject: [PATCH 1/3] Adds support for Langchain4j Cohere provider. --- all/pom.xml | 4 + bom/pom.xml | 5 + .../helidon/integrations/langchain4j/Ai.java | 4 + .../langchain4j/providers/cohere/pom.xml | 166 ++++++++++++++++++ .../langchain4j/providers/cohere/Cohere.java | 31 ++++ .../providers/cohere/CohereCommonConfig.java | 90 ++++++++++ .../CohereEmbeddingModelConfigBlueprint.java | 52 ++++++ .../cohere/CohereEmbeddingModelFactory.java | 92 ++++++++++ .../CohereScoringModelConfigBlueprint.java | 52 ++++++ .../cohere/CohereScoringModelFactory.java | 104 +++++++++++ .../providers/cohere/package-info.java | 20 +++ .../cohere/src/main/java/module-info.java | 41 +++++ .../cohere/EmbeddingModelConfigTest.java | 56 ++++++ .../cohere/ScoringModelConfigTest.java | 55 ++++++ .../src/test/resources/application.yaml | 36 ++++ .../openai/OpenAiChatModelFactory.java | 4 - integrations/langchain4j/providers/pom.xml | 1 + 17 files changed, 809 insertions(+), 4 deletions(-) create mode 100644 integrations/langchain4j/providers/cohere/pom.xml create mode 100644 integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/Cohere.java create mode 100644 integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereCommonConfig.java create mode 100644 integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereEmbeddingModelConfigBlueprint.java create mode 100644 integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereEmbeddingModelFactory.java create mode 100644 integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereScoringModelConfigBlueprint.java create mode 100644 integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereScoringModelFactory.java create mode 100644 integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/package-info.java create mode 100644 integrations/langchain4j/providers/cohere/src/main/java/module-info.java create mode 100644 integrations/langchain4j/providers/cohere/src/test/java/io/helidon/integrations/langchain4j/providers/cohere/EmbeddingModelConfigTest.java create mode 100644 integrations/langchain4j/providers/cohere/src/test/java/io/helidon/integrations/langchain4j/providers/cohere/ScoringModelConfigTest.java create mode 100644 integrations/langchain4j/providers/cohere/src/test/resources/application.yaml diff --git a/all/pom.xml b/all/pom.xml index 86805b5e59d..ba193e5bf29 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -795,6 +795,10 @@ io.helidon.integrations.langchain4j.providers helidon-integrations-langchain4j-providers-open-ai + + io.helidon.integrations.langchain4j.providers + helidon-integrations-langchain4j-providers-cohere + io.helidon.openapi helidon-openapi diff --git a/bom/pom.xml b/bom/pom.xml index a35df57ea75..732563f1610 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -1053,6 +1053,11 @@ helidon-integrations-langchain4j-providers-open-ai ${helidon.version} + + io.helidon.integrations.langchain4j.providers + helidon-integrations-langchain4j-providers-cohere + ${helidon.version} + diff --git a/integrations/langchain4j/langchain4j/src/main/java/io/helidon/integrations/langchain4j/Ai.java b/integrations/langchain4j/langchain4j/src/main/java/io/helidon/integrations/langchain4j/Ai.java index bd40205ce43..35382819ff1 100644 --- a/integrations/langchain4j/langchain4j/src/main/java/io/helidon/integrations/langchain4j/Ai.java +++ b/integrations/langchain4j/langchain4j/src/main/java/io/helidon/integrations/langchain4j/Ai.java @@ -51,6 +51,10 @@ public final class Ai { * Name that is used to qualify moderation models. */ public static final String MODERATION_MODEL_NAME = "moderation-model"; + /** + * Name that is used to qualify scoring models. + */ + public static final String SCORING_MODEL_NAME = "scoring-model"; private Ai() { } diff --git a/integrations/langchain4j/providers/cohere/pom.xml b/integrations/langchain4j/providers/cohere/pom.xml new file mode 100644 index 00000000000..7c42374c8c0 --- /dev/null +++ b/integrations/langchain4j/providers/cohere/pom.xml @@ -0,0 +1,166 @@ + + + + 4.0.0 + + io.helidon.integrations.langchain4j.providers + helidon-integrations-langchain4j-providers-project + 4.2.0-SNAPSHOT + ../pom.xml + + + helidon-integrations-langchain4j-providers-cohere + + + + io.helidon.common + helidon-common-config + + + io.helidon.common + helidon-common-types + + + io.helidon.common + helidon-common + + + io.helidon.service + helidon-service-registry + + + io.helidon.builder + helidon-builder-api + + + io.helidon.integrations.langchain4j + helidon-integrations-langchain4j + + + dev.langchain4j + langchain4j-core + + + dev.langchain4j + langchain4j-cohere + + + io.helidon.common.features + helidon-common-features-api + true + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + io.helidon.config + helidon-config + test + + + io.helidon.config + helidon-config-yaml + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.helidon.config.metadata + helidon-config-metadata-codegen + ${helidon.version} + + + io.helidon.codegen + helidon-codegen-apt + ${helidon.version} + + + io.helidon.builder + helidon-builder-codegen + ${helidon.version} + + + io.helidon.service + helidon-service-codegen + ${helidon.version} + + + io.helidon.codegen + helidon-codegen-helidon-copyright + ${helidon.version} + + + io.helidon.common.features + helidon-common-features-processor + ${helidon.version} + + + + + + io.helidon.config.metadata + helidon-config-metadata-codegen + ${helidon.version} + + + io.helidon.codegen + helidon-codegen-apt + ${helidon.version} + + + io.helidon.builder + helidon-builder-codegen + ${helidon.version} + + + io.helidon.service + helidon-service-codegen + ${helidon.version} + + + io.helidon.codegen + helidon-codegen-helidon-copyright + ${helidon.version} + + + io.helidon.common.features + helidon-common-features-processor + ${helidon.version} + + + + + + diff --git a/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/Cohere.java b/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/Cohere.java new file mode 100644 index 00000000000..4cfacdd6bb3 --- /dev/null +++ b/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/Cohere.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.integrations.langchain4j.providers.cohere; + +import io.helidon.common.Weighted; +import io.helidon.integrations.langchain4j.Ai; +import io.helidon.service.registry.Qualifier; + +final class Cohere { + static final String COHERE = "cohere"; + static final String SCORING_MODEL = COHERE + "." + Ai.SCORING_MODEL_NAME; + static final Qualifier COHERE_QUALIFIER = Qualifier.createNamed(COHERE); + static final double WEIGHT = Weighted.DEFAULT_WEIGHT - 20; + + private Cohere() { + } +} diff --git a/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereCommonConfig.java b/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereCommonConfig.java new file mode 100644 index 00000000000..3fd217614ee --- /dev/null +++ b/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereCommonConfig.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2025 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.integrations.langchain4j.providers.cohere; + +import java.time.Duration; +import java.util.Map; +import java.util.Optional; + +import io.helidon.builder.api.Option; + +interface CohereCommonConfig { + /** + * If set to {@code false} (default), Cohere model will not be available even if configured. + * + * @return whether Cohere model is enabled, defaults to {@code false} + */ + @Option.Configured + boolean enabled(); + + /** + * The base URL for the Cohere API. + * + * @return the base URL + */ + @Option.Configured + Optional baseUrl(); + + /** + * The API key used to authenticate requests to the Cohere API. + * + * @return an {@link java.util.Optional} containing the API key + */ + @Option.Configured + Optional apiKey(); + + /** + * Whether to log API requests. + * + * @return an {@link java.util.Optional} containing true if requests should be logged, false otherwise + */ + @Option.Configured + Optional logRequests(); + + /** + * Whether to log API responses. + * + * @return an {@link java.util.Optional} containing true if responses should be logged, false otherwise + */ + @Option.Configured + Optional logResponses(); + + /** + * A map containing custom headers. + * + * @return custom headers map + */ + @Option.Configured + @Option.Singular + Map customHeaders(); + + /** + * The timeout setting for API requests. + * + * @return the timeout setting in {@link java.time.Duration#parse} format + */ + @Option.Configured + Optional timeout(); + + /** + * The model name to use (e.g., "gpt-3.5-turbo"). + * + * @return the model name + */ + @Option.Configured + Optional modelName(); +} diff --git a/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereEmbeddingModelConfigBlueprint.java b/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereEmbeddingModelConfigBlueprint.java new file mode 100644 index 00000000000..c963fa862b6 --- /dev/null +++ b/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereEmbeddingModelConfigBlueprint.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2025 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.integrations.langchain4j.providers.cohere; + +import java.util.Optional; + +import io.helidon.builder.api.Option; +import io.helidon.builder.api.Prototype; + +/** + * Configuration for the Cohere embedding model, {@link dev.langchain4j.model.cohere.CohereEmbeddingModel}. + * + * @see dev.langchain4j.model.cohere.CohereEmbeddingModel + */ +@Prototype.Configured(CohereEmbeddingModelConfigBlueprint.CONFIG_ROOT) +@Prototype.Blueprint +interface CohereEmbeddingModelConfigBlueprint extends CohereCommonConfig { + /** + * Default configuration prefix. + */ + String CONFIG_ROOT = "langchain4j.cohere.embedding-model"; + + /** + * Gets the input type. + * + * @return an {@link java.util.Optional} containing the input type + */ + @Option.Configured + Optional inputType(); + + /** + * Gets the maximum number of segments per batch. + * + * @return an {@link java.util.Optional} containing the maximum number of segments per batch + */ + @Option.Configured + Optional maxSegmentsPerBatch(); +} diff --git a/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereEmbeddingModelFactory.java b/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereEmbeddingModelFactory.java new file mode 100644 index 00000000000..1db9df5bea7 --- /dev/null +++ b/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereEmbeddingModelFactory.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2025 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.integrations.langchain4j.providers.cohere; + +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; + +import io.helidon.common.Weight; +import io.helidon.common.config.Config; +import io.helidon.service.registry.Service; + +import dev.langchain4j.model.cohere.CohereEmbeddingModel; + +/** + * Factory for a configured {@link CohereEmbeddingModel}. + * + * @see CohereEmbeddingModel + * @see io.helidon.integrations.langchain4j.providers.cohere.CohereEmbeddingModelConfig + * @see #create + */ +@Service.Singleton +@Service.Named(Service.Named.WILDCARD_NAME) +@Weight(Cohere.WEIGHT) +public class CohereEmbeddingModelFactory implements Service.ServicesFactory { + private final Supplier> model; + + CohereEmbeddingModelFactory(Config config) { + var configBuilder = CohereEmbeddingModelConfig.builder() + .config(config.get(CohereEmbeddingModelConfigBlueprint.CONFIG_ROOT)); + + this.model = () -> buildModel(configBuilder); + } + + /** + * Create the Cohere model from its configuration. + * + * @param config configuration to use + * @return a new model instance + * @throws IllegalStateException in case the configuration is not enabled + */ + public static CohereEmbeddingModel create(CohereEmbeddingModelConfig config) { + if (!config.enabled()) { + throw new IllegalStateException("Cannot create a model when the configuration is disabled."); + } + + var builder = CohereEmbeddingModel.builder(); + config.baseUrl().ifPresent(builder::baseUrl); + config.apiKey().ifPresent(builder::apiKey); + config.modelName().ifPresent(builder::modelName); + config.inputType().ifPresent(builder::inputType); + config.timeout().ifPresent(builder::timeout); + config.logRequests().ifPresent(builder::logRequests); + config.logResponses().ifPresent(builder::logResponses); + config.maxSegmentsPerBatch().ifPresent(builder::maxSegmentsPerBatch); + return builder.build(); + } + + @Override + public List> services() { + var modelOptional = model.get(); + if (modelOptional.isEmpty()) { + return List.of(); + } + + var theModel = modelOptional.get(); + return List.of(Service.QualifiedInstance.create(theModel), + Service.QualifiedInstance.create(theModel, Cohere.COHERE_QUALIFIER)); + } + + private static Optional buildModel(CohereEmbeddingModelConfig.Builder configBuilder) { + if (!configBuilder.enabled()) { + return Optional.empty(); + } + + return Optional.of(create(configBuilder.build())); + } +} diff --git a/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereScoringModelConfigBlueprint.java b/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereScoringModelConfigBlueprint.java new file mode 100644 index 00000000000..5026634e5f6 --- /dev/null +++ b/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereScoringModelConfigBlueprint.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2025 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.integrations.langchain4j.providers.cohere; + +import java.net.Proxy; +import java.util.Optional; + +import io.helidon.builder.api.Option; +import io.helidon.builder.api.Prototype; + +/** + * Configuration for the Cohere embedding model, {@link dev.langchain4j.model.cohere.CohereScoringModel}. + * + * @see dev.langchain4j.model.cohere.CohereScoringModel + */ +@Prototype.Configured(CohereScoringModelConfigBlueprint.CONFIG_ROOT) +@Prototype.Blueprint +interface CohereScoringModelConfigBlueprint extends CohereCommonConfig { + /** + * Default configuration prefix. + */ + String CONFIG_ROOT = "langchain4j.cohere.scoring-model"; + + /** + * Gets the maximum number of retries for failed API requests. + * + * @return an {@link java.util.Optional} containing the maximum number of retries + */ + @Option.Configured + Optional maxRetries(); + + /** + * Proxy to use. + * + * @return an {@link java.util.Optional} containing HTTP proxy to use + */ + Optional proxy(); +} diff --git a/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereScoringModelFactory.java b/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereScoringModelFactory.java new file mode 100644 index 00000000000..a0391d92dc7 --- /dev/null +++ b/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/CohereScoringModelFactory.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2025 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.integrations.langchain4j.providers.cohere; + +import java.net.Proxy; +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; + +import io.helidon.common.Weight; +import io.helidon.common.config.Config; +import io.helidon.service.registry.Service; + +import dev.langchain4j.model.cohere.CohereScoringModel; + +/** + * Factory for a configured {@link dev.langchain4j.model.cohere.CohereScoringModel}. + * + * @see dev.langchain4j.model.cohere.CohereScoringModel + * @see io.helidon.integrations.langchain4j.providers.cohere.CohereScoringModelConfig + * @see #create + */ +@Service.Singleton +@Service.Named(Service.Named.WILDCARD_NAME) +@Weight(Cohere.WEIGHT) +public class CohereScoringModelFactory implements Service.ServicesFactory { + private final Supplier> model; + + CohereScoringModelFactory(@Service.Named(Cohere.SCORING_MODEL) Supplier> cohereScoringModelProxy, + @Service.Named(Cohere.COHERE) Supplier> cohereProxy, + Supplier> proxy, + Config config) { + var configBuilder = CohereScoringModelConfig.builder() + .config(config.get(CohereScoringModelConfigBlueprint.CONFIG_ROOT)); + + this.model = () -> buildModel(configBuilder, cohereScoringModelProxy, cohereProxy, proxy); + } + + /** + * Create the Cohere model from its configuration. + * + * @param config configuration to use + * @return a new model instance + * @throws IllegalStateException in case the configuration is not enabled + */ + public static CohereScoringModel create(CohereScoringModelConfig config) { + if (!config.enabled()) { + throw new IllegalStateException("Cannot create a model when the configuration is disabled."); + } + + var builder = CohereScoringModel.builder(); + config.baseUrl().ifPresent(builder::baseUrl); + config.apiKey().ifPresent(builder::apiKey); + config.modelName().ifPresent(builder::modelName); + config.timeout().ifPresent(builder::timeout); + config.maxRetries().ifPresent(builder::maxRetries); + config.logRequests().ifPresent(builder::logRequests); + config.logResponses().ifPresent(builder::logResponses); + config.proxy().ifPresent(builder::proxy); + return builder.build(); + } + + @Override + public List> services() { + var modelOptional = model.get(); + if (modelOptional.isEmpty()) { + return List.of(); + } + + var theModel = modelOptional.get(); + return List.of(Service.QualifiedInstance.create(theModel), + Service.QualifiedInstance.create(theModel, Cohere.COHERE_QUALIFIER)); + } + + private static Optional buildModel(CohereScoringModelConfig.Builder configBuilder, + Supplier> cohereScoringModelProxy, + Supplier> cohereProxy, + Supplier> proxy) { + if (!configBuilder.enabled()) { + return Optional.empty(); + } + + cohereScoringModelProxy.get() + .or(cohereProxy) + .or(proxy) + .ifPresent(configBuilder::proxy); + + return Optional.of(create(configBuilder.build())); + } +} diff --git a/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/package-info.java b/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/package-info.java new file mode 100644 index 00000000000..0a2c9a9cde7 --- /dev/null +++ b/integrations/langchain4j/providers/cohere/src/main/java/io/helidon/integrations/langchain4j/providers/cohere/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Provides integration with Cohere models. + */ +package io.helidon.integrations.langchain4j.providers.cohere; diff --git a/integrations/langchain4j/providers/cohere/src/main/java/module-info.java b/integrations/langchain4j/providers/cohere/src/main/java/module-info.java new file mode 100644 index 00000000000..e374791c5c3 --- /dev/null +++ b/integrations/langchain4j/providers/cohere/src/main/java/module-info.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import io.helidon.common.features.api.Feature; +import io.helidon.common.features.api.HelidonFlavor; +import io.helidon.common.features.api.Preview; + +/** + * Provides integration with Cohere models. + */ +@Feature(value = "Langchain4j Cohere", + description = "Langchain4j Cohere Provider Integration", + in = {HelidonFlavor.SE, HelidonFlavor.MP}, + path = {"Langchain4j", "Cohere"} +) +@Preview +module io.helidon.integrations.langchain4j.providers.ollama { + requires static io.helidon.common.features.api; + + requires langchain4j.cohere; + requires langchain4j.core; + + requires transitive io.helidon.service.registry; + requires transitive io.helidon.integrations.langchain4j; + requires transitive io.helidon.common.config; + + exports io.helidon.integrations.langchain4j.providers.cohere; +} \ No newline at end of file diff --git a/integrations/langchain4j/providers/cohere/src/test/java/io/helidon/integrations/langchain4j/providers/cohere/EmbeddingModelConfigTest.java b/integrations/langchain4j/providers/cohere/src/test/java/io/helidon/integrations/langchain4j/providers/cohere/EmbeddingModelConfigTest.java new file mode 100644 index 00000000000..c142f975669 --- /dev/null +++ b/integrations/langchain4j/providers/cohere/src/test/java/io/helidon/integrations/langchain4j/providers/cohere/EmbeddingModelConfigTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.integrations.langchain4j.providers.cohere; + +import java.time.Duration; + +import io.helidon.config.Config; +import io.helidon.config.ConfigSources; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +class EmbeddingModelConfigTest { + + @Test + void testDefaultRoot() { + var config = CohereEmbeddingModelConfig.create(Config.just(ConfigSources.classpath("application.yaml")) + .get(CohereEmbeddingModelConfig.CONFIG_ROOT)); + + assertThat(config, is(notNullValue())); + assertThat(config.apiKey().isPresent(), equalTo(true)); + assertThat(config.apiKey().get(), equalTo("api-key")); + assertThat(config.modelName().isPresent(), equalTo(true)); + assertThat(config.modelName().get(), equalTo("model-name")); + assertThat(config.baseUrl().isPresent(), equalTo(true)); + assertThat(config.baseUrl().get(), equalTo("base-url")); + assertThat(config.inputType().isPresent(), is(true)); + assertThat(config.inputType().get(), is("inputType")); + assertThat(config.timeout().isPresent(), is(true)); + assertThat(config.timeout().get(), equalTo(Duration.parse("PT10M"))); + assertThat(config.logRequests().isPresent(), is(true)); + assertThat(config.logRequests().get(), is(true)); + assertThat(config.logResponses().isPresent(), is(true)); + assertThat(config.logResponses().get(), is(true)); + assertThat(config.maxSegmentsPerBatch().isPresent(), is(true)); + assertThat(config.maxSegmentsPerBatch().get(), is(10)); + } +} diff --git a/integrations/langchain4j/providers/cohere/src/test/java/io/helidon/integrations/langchain4j/providers/cohere/ScoringModelConfigTest.java b/integrations/langchain4j/providers/cohere/src/test/java/io/helidon/integrations/langchain4j/providers/cohere/ScoringModelConfigTest.java new file mode 100644 index 00000000000..34125795378 --- /dev/null +++ b/integrations/langchain4j/providers/cohere/src/test/java/io/helidon/integrations/langchain4j/providers/cohere/ScoringModelConfigTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.integrations.langchain4j.providers.cohere; + +import java.time.Duration; + +import io.helidon.config.Config; +import io.helidon.config.ConfigSources; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +class ScoringModelConfigTest { + + @Test + void testDefaultRoot() { + var config = CohereScoringModelConfig.create(Config.just(ConfigSources.classpath("application.yaml")) + .get(CohereScoringModelConfig.CONFIG_ROOT)); + + assertThat(config, is(notNullValue())); + assertThat(config.apiKey().isPresent(), equalTo(true)); + assertThat(config.apiKey().get(), equalTo("api-key")); + assertThat(config.modelName().isPresent(), equalTo(true)); + assertThat(config.modelName().get(), equalTo("model-name")); + assertThat(config.baseUrl().isPresent(), equalTo(true)); + assertThat(config.baseUrl().get(), equalTo("base-url")); + assertThat(config.timeout().isPresent(), is(true)); + assertThat(config.timeout().get(), equalTo(Duration.parse("PT10M"))); + assertThat(config.maxRetries().isPresent(), is(true)); + assertThat(config.maxRetries().get(), is(5)); + assertThat(config.logRequests().isPresent(), is(true)); + assertThat(config.logRequests().get(), is(true)); + assertThat(config.logResponses().isPresent(), is(true)); + assertThat(config.logResponses().get(), is(true)); + assertThat(config.proxy().isPresent(), is(false)); + } +} diff --git a/integrations/langchain4j/providers/cohere/src/test/resources/application.yaml b/integrations/langchain4j/providers/cohere/src/test/resources/application.yaml new file mode 100644 index 00000000000..a01e78a0359 --- /dev/null +++ b/integrations/langchain4j/providers/cohere/src/test/resources/application.yaml @@ -0,0 +1,36 @@ +# +# Copyright (c) 2025 Oracle and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +langchain4j.cohere: + # EmbeddingModel + embedding-model: + api-key: api-key + model-name: model-name + base-url: base-url + input-type: inputType + timeout: PT10M + log-requests: true + log-responses: true + max-segments-per-batch: 10 + # ScoringModel + scoring-model: + api-key: api-key + model-name: model-name + base-url: base-url + timeout: PT10M + max-retries: 5 + log-requests: true + log-responses: true diff --git a/integrations/langchain4j/providers/open-ai/src/main/java/io/helidon/integrations/langchain4j/providers/openai/OpenAiChatModelFactory.java b/integrations/langchain4j/providers/open-ai/src/main/java/io/helidon/integrations/langchain4j/providers/openai/OpenAiChatModelFactory.java index 9a40a2809c1..8ece582714e 100644 --- a/integrations/langchain4j/providers/open-ai/src/main/java/io/helidon/integrations/langchain4j/providers/openai/OpenAiChatModelFactory.java +++ b/integrations/langchain4j/providers/open-ai/src/main/java/io/helidon/integrations/langchain4j/providers/openai/OpenAiChatModelFactory.java @@ -138,10 +138,6 @@ private static Optional buildModel(OpenAiChatModelConfig.Builde .or(proxy) .ifPresent(configBuilder::proxy); - openAiModelProxy.get() - .or(openAiProxy) - .or(proxy) - .ifPresent(configBuilder::proxy); return Optional.of(create(configBuilder.build())); } } diff --git a/integrations/langchain4j/providers/pom.xml b/integrations/langchain4j/providers/pom.xml index 33e0e75e1f7..096406eef22 100644 --- a/integrations/langchain4j/providers/pom.xml +++ b/integrations/langchain4j/providers/pom.xml @@ -36,5 +36,6 @@ open-ai oracle ollama + cohere From f662bf8ff542c2bde21e5fb9332e5dd60ca8c0da Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Fri, 7 Feb 2025 23:29:33 +0100 Subject: [PATCH 2/3] Update pom.xml - add missing version --- bom/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/bom/pom.xml b/bom/pom.xml index 599d878b657..58fdddb9f61 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -1056,6 +1056,7 @@ io.helidon.integrations.langchain4j.providers helidon-integrations-langchain4j-providers-cohere + ${helidon.version} io.helidon.integrations.langchain4j.providers From 5b026660470143342cf74b4e3d982149d9f9e2e2 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Tue, 11 Feb 2025 17:39:33 +0100 Subject: [PATCH 3/3] Fixed module name. --- .../langchain4j/providers/cohere/src/main/java/module-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrations/langchain4j/providers/cohere/src/main/java/module-info.java b/integrations/langchain4j/providers/cohere/src/main/java/module-info.java index e374791c5c3..882c3c09778 100644 --- a/integrations/langchain4j/providers/cohere/src/main/java/module-info.java +++ b/integrations/langchain4j/providers/cohere/src/main/java/module-info.java @@ -27,7 +27,7 @@ path = {"Langchain4j", "Cohere"} ) @Preview -module io.helidon.integrations.langchain4j.providers.ollama { +module io.helidon.integrations.langchain4j.providers.cohere { requires static io.helidon.common.features.api; requires langchain4j.cohere;