diff --git a/jakartaee-microprofile-example/README.md b/jakartaee-microprofile-example/README.md index 3dbacf65..98b04f57 100644 --- a/jakartaee-microprofile-example/README.md +++ b/jakartaee-microprofile-example/README.md @@ -1,14 +1,29 @@ # LangChain4j in Jakarta EE and MicroProfile -This example demonstrates LangChain4J in a Jakarta EE / MicroProfile application on Open Liberty. The application is a chatbot built with LangChain4J and uses Jakarta CDI, Jakarta RESTful Web Services, Jakarta WebSocket, MicroProfile Config, MicroProfile Metrics, and MicroProfile OpenAPI features. +This example demonstrates LangChain4J in a Jakarta EE / MicroProfile application on Open Liberty. The application is a chatbot built with LangChain4J and uses Jakarta CDI, Jakarta RESTful Web Services, Jakarta WebSocket, MicroProfile Config, MicroProfile Metrics, and MicroProfile OpenAPI features. The application allows to use models from either Github, Ollama, or Hugging Face. ## Prerequisites: - [Java 21](https://developer.ibm.com/languages/java/semeru-runtimes/downloads) -- Hugging Face API Key - - Sign up and log in to https://huggingface.co. - - Go to [Access Tokens](https://huggingface.co/settings/tokens). - - Create a new access token with `read` role. - +- Either one of the following model providers: + - Github + - Sign up and sign in to https://github.com. + - Go to your [Settings](https://github.com/settings/profile)/[Developer Settings](https://github.com/settings/developers)/[Persional access tokens](https://github.com/settings/personal-access-tokens). + - Generate a new token + - Ollama + - Download and install [Ollama](https://ollama.com/download) + - see the [README.md](https://github.com/ollama/ollama/blob/main/README.md#ollama) + - Pull the following models + - `ollama pull llama3.2` + - `ollama pull all-minilm` + - `ollama pull tinydolphin` + - Mistral AI + - Sign up and log in to https://console.mistral.ai/home. + - Go to [Your API keys](https://console.mistral.ai/api-keys). + - Create a new key. + - Hugging Face + - Sign up and log in to https://huggingface.co. + - Go to [Access Tokens](https://huggingface.co/settings/tokens). + - Create a new access token with `read` role. ## Environment Set Up @@ -18,10 +33,40 @@ To run this example application, navigate to the `jakartaee-microprofile-exampl cd langchain4j-examples/jakartaee-microprofile-example ``` -Set the following environment variables: - +Set the `JAVA_HOME` environment variable: ``` export JAVA_HOME= +``` + +Set the `GITHUB_API_KEY` environment variable if using Github. +``` +unset HUGGING_FACE_API_KEY +unset OLLAMA_BASE_URL +unset MISTRAL_AI_API_KEY +export GITHUB_API_KEY= +``` + +Set the `OLLAMA_BASE_URL` environment variable if using Ollama. Use your Ollama URL if not using the default. +``` +unset HUGGING_FACE_API_KEY +unset GITHUB_API_KEY +unset MISTRAL_AI_API_KEY +export OLLAMA_BASE_URL=http://localhost:11434 +``` + +Set the `MISTRAL_AI_API_KEY` environment variable if using Mistral AI. +``` +unset GITHUB_API_KEY +unset OLLAMA_BASE_URL +unset MISTRAL_AI_API_KEY +export MISTRAL_AI_API_KEY= +``` + +Set the `HUGGING_FACE_API_KEY` environment variable if using Hugging Face. +``` +unset GITHUB_API_KEY +unset OLLAMA_BASE_URL +unset MISTRAL_AI_API_KEY export HUGGING_FACE_API_KEY= ``` @@ -52,7 +97,7 @@ Use the Maven wrapper to start the application by using the [Liberty dev mode](h Navigate to the the [OpenAPI UI](http://localhost:9080/openapi/ui) URL for the following 3 REST APIs: -- [HuggingFaceLanguageModel](https://github.com/langchain4j/langchain4j/blob/main/langchain4j-hugging-face/src/main/java/dev/langchain4j/model/huggingface/HuggingFaceLanguageModel.java) +- [LanguageModel](https://javadoc.io/doc/dev.langchain4j/langchain4j-core/latest/dev/langchain4j/model/language/LanguageModel.html) - Expand the GET `/api/model/language` API. 1. Click the **Try it out** button. 2. Type `When was Hugging Face launched?`, or any question, in the question field. @@ -60,9 +105,8 @@ Navigate to the the [OpenAPI UI](http://localhost:9080/openapi/ui) URL for the f - Alternatively, run the following `curl` command from a command-line session: - ``` curl 'http://localhost:9080/api/model/language?question=When%20was%20Hugging%20Face%20launched%3F' - ``` -- [HuggingFaceChatModel](https://github.com/langchain4j/langchain4j/blob/main/langchain4j-hugging-face/src/main/java/dev/langchain4j/model/huggingface/HuggingFaceChatModel.java) +- [ChatModel](https://javadoc.io/doc/dev.langchain4j/langchain4j-core/latest/dev/langchain4j/model/chat/ChatModel.html) - expand the GET `/api/model/chat` API 1. Click the **Try it out** button. 2. Type `Which are the most used Large Language Models?`, or any question, in the question field. @@ -71,7 +115,7 @@ Navigate to the the [OpenAPI UI](http://localhost:9080/openapi/ui) URL for the f - ``` curl 'http://localhost:9080/api/model/chat?userMessage=Which%20are%20the%20most%20used%20Large%20Language%20Models%3F' | jq ``` -- [InProcessEmbeddingModel](https://github.com/langchain4j/langchain4j-embeddings) +- [EmbeddingModel](https://javadoc.io/doc/dev.langchain4j/langchain4j-core/latest/dev/langchain4j/model/embedding/EmbeddingModel.html) - expand the GET `/api/model/similarity` API 1. Click the **Try it out** button. 2. Type `I like Jakarta EE and MicroProfile.`, or any text, in the the **text1** field. diff --git a/jakartaee-microprofile-example/pom.xml b/jakartaee-microprofile-example/pom.xml index 33abfad0..15e50755 100644 --- a/jakartaee-microprofile-example/pom.xml +++ b/jakartaee-microprofile-example/pom.xml @@ -39,6 +39,21 @@ langchain4j-hugging-face 1.8.0-beta15 + + dev.langchain4j + langchain4j-github-models + 1.5.0-beta11 + + + dev.langchain4j + langchain4j-ollama + 1.5.0 + + + dev.langchain4j + langchain4j-mistral-ai + 1.5.0 + org.slf4j slf4j-reload4j @@ -53,7 +68,7 @@ org.junit.jupiter junit-jupiter - 5.12.2 + 5.13.4 test @@ -77,7 +92,7 @@ org.eclipse.jetty.websocket websocket-jakarta-client - 11.0.25 + 11.0.26 test @@ -94,7 +109,7 @@ io.openliberty.tools liberty-maven-plugin - 3.11.3 + 3.11.5 diff --git a/jakartaee-microprofile-example/src/main/java/dev/langchain4j/example/chat/ChatAgent.java b/jakartaee-microprofile-example/src/main/java/dev/langchain4j/example/chat/ChatAgent.java index cdb1bb08..e85d190e 100644 --- a/jakartaee-microprofile-example/src/main/java/dev/langchain4j/example/chat/ChatAgent.java +++ b/jakartaee-microprofile-example/src/main/java/dev/langchain4j/example/chat/ChatAgent.java @@ -1,11 +1,10 @@ package dev.langchain4j.example.chat; -import static java.time.Duration.ofSeconds; - import org.eclipse.microprofile.config.inject.ConfigProperty; +import dev.langchain4j.example.chat.util.ModelBuilder; import dev.langchain4j.memory.chat.MessageWindowChatMemory; -import dev.langchain4j.model.huggingface.HuggingFaceChatModel; +import dev.langchain4j.model.chat.ChatModel; import dev.langchain4j.service.AiServices; import dev.langchain4j.service.MemoryId; import dev.langchain4j.service.UserMessage; @@ -16,24 +15,7 @@ public class ChatAgent { @Inject - @ConfigProperty(name = "hugging.face.api.key") - private String HUGGING_FACE_API_KEY; - - @Inject - @ConfigProperty(name = "chat.model.id") - private String CHAT_MODEL_ID; - - @Inject - @ConfigProperty(name = "chat.model.timeout") - private Integer TIMEOUT; - - @Inject - @ConfigProperty(name = "chat.model.max.token") - private Integer MAX_NEW_TOKEN; - - @Inject - @ConfigProperty(name = "chat.model.temperature") - private Double TEMPERATURE; + private ModelBuilder modelBuilder; @Inject @ConfigProperty(name = "chat.memory.max.messages") @@ -45,16 +27,9 @@ interface Assistant { private Assistant assistant = null; - public Assistant getAssistant() { + public Assistant getAssistant() throws Exception { if (assistant == null) { - HuggingFaceChatModel model = HuggingFaceChatModel.builder() - .accessToken(HUGGING_FACE_API_KEY) - .modelId(CHAT_MODEL_ID) - .timeout(ofSeconds(TIMEOUT)) - .temperature(TEMPERATURE) - .maxNewTokens(MAX_NEW_TOKEN) - .waitForModel(true) - .build(); + ChatModel model = modelBuilder.getChatModelForWeb(); assistant = AiServices.builder(Assistant.class) .chatModel(model) .chatMemoryProvider( @@ -64,10 +39,8 @@ public Assistant getAssistant() { return assistant; } - public String chat(String sessionId, String message) { - String reply = getAssistant().chat(sessionId, message).trim(); - int i = reply.lastIndexOf(message); - return i > 0 ? reply.substring(i) : reply; + public String chat(String sessionId, String message) throws Exception { + return getAssistant().chat(sessionId, message).trim(); } } diff --git a/jakartaee-microprofile-example/src/main/java/dev/langchain4j/example/chat/ChatMessageEncoder.java b/jakartaee-microprofile-example/src/main/java/dev/langchain4j/example/chat/ChatMessageEncoder.java index 52cfd867..16b150d6 100644 --- a/jakartaee-microprofile-example/src/main/java/dev/langchain4j/example/chat/ChatMessageEncoder.java +++ b/jakartaee-microprofile-example/src/main/java/dev/langchain4j/example/chat/ChatMessageEncoder.java @@ -8,7 +8,7 @@ public class ChatMessageEncoder implements Encoder.Text { @Override public String encode(String message) throws EncodeException { - if (!message.endsWith(".")) { + if (!message.endsWith(".") && !message.endsWith("!") && !message.endsWith("?")) { message += " ..."; } diff --git a/jakartaee-microprofile-example/src/main/java/dev/langchain4j/example/chat/util/ModelBuilder.java b/jakartaee-microprofile-example/src/main/java/dev/langchain4j/example/chat/util/ModelBuilder.java new file mode 100644 index 00000000..311e1edd --- /dev/null +++ b/jakartaee-microprofile-example/src/main/java/dev/langchain4j/example/chat/util/ModelBuilder.java @@ -0,0 +1,263 @@ +package dev.langchain4j.example.chat.util; + +import static dev.langchain4j.model.github.GitHubModelsEmbeddingModelName.TEXT_EMBEDDING_3_SMALL; +import static dev.langchain4j.model.huggingface.HuggingFaceModelName.SENTENCE_TRANSFORMERS_ALL_MINI_LM_L6_V2; +import static dev.langchain4j.model.mistralai.MistralAiChatModelName.MISTRAL_SMALL_LATEST; +import static dev.langchain4j.model.mistralai.MistralAiEmbeddingModelName.MISTRAL_EMBED; +import static java.time.Duration.ofSeconds; + +import java.util.logging.Logger; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import dev.langchain4j.model.chat.ChatModel; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.model.github.GitHubModelsChatModel; +import dev.langchain4j.model.github.GitHubModelsEmbeddingModel; +import dev.langchain4j.model.huggingface.HuggingFaceChatModel; +import dev.langchain4j.model.huggingface.HuggingFaceEmbeddingModel; +import dev.langchain4j.model.huggingface.HuggingFaceLanguageModel; +import dev.langchain4j.model.language.LanguageModel; +import dev.langchain4j.model.mistralai.MistralAiChatModel; +import dev.langchain4j.model.mistralai.MistralAiEmbeddingModel; +import dev.langchain4j.model.ollama.OllamaChatModel; +import dev.langchain4j.model.ollama.OllamaEmbeddingModel; +import dev.langchain4j.model.ollama.OllamaLanguageModel; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class ModelBuilder { + + private static Logger logger = Logger.getLogger(ModelBuilder.class.getName()); + + @Inject + @ConfigProperty(name = "hugging.face.api.key") + private String HUGGING_FACE_API_KEY; + + @Inject + @ConfigProperty(name = "hugging.face.language.model.id") + private String HUGGING_FACE_LANGUAGE_MODEL_ID; + + @Inject + @ConfigProperty(name = "hugging.face.chat.model.id") + private String HUGGING_FACE_CHAT_MODEL_ID; + + @Inject + @ConfigProperty(name = "github.api.key") + private String GITHUB_API_KEY; + + @Inject + @ConfigProperty(name = "github.chat.model.id") + private String GITHUB_CHAT_MODEL_ID; + + @Inject + @ConfigProperty(name = "ollama.base.url") + private String OLLAMA_BASE_URL; + + @Inject + @ConfigProperty(name = "ollama.chat.model.id") + private String OLLAMA_CHAT_MODEL_ID; + + @Inject + @ConfigProperty(name = "mistral.ai.api.key") + private String MISTRAL_AI_API_KEY; + + @Inject + @ConfigProperty(name = "mistral.ai.chat.model.id") + private String MISTRAL_AI_MISTRAL_CHAT_MODEL_ID; + + @Inject + @ConfigProperty(name = "chat.model.timeout") + private Integer TIMEOUT; + + @Inject + @ConfigProperty(name = "chat.model.max.token") + private Integer MAX_NEW_TOKEN; + + @Inject + @ConfigProperty(name = "chat.model.temperature") + private Double TEMPERATURE; + + private LanguageModel languageModel = null; + private EmbeddingModel embeddingModel = null; + private ChatModel chatModelForResource = null; + private ChatModel chatModelForWeb = null; + + public boolean usingGithub() { + return GITHUB_API_KEY.startsWith("ghp_") || + GITHUB_API_KEY.startsWith("github_pat_"); + } + + public boolean usingOllama() { + return OLLAMA_BASE_URL.startsWith("http"); + } + + public boolean usingMistralAi() { + return MISTRAL_AI_API_KEY.length() > 30; + } + + public boolean usingHuggingFace() { + return HUGGING_FACE_API_KEY.startsWith("hf_"); + } + + public LanguageModel getLanguageModel() throws Exception { + if (languageModel == null) { + if (usingGithub()) { + throw new Exception("LangChain4J Github APIs do not support language model"); + } else if (usingOllama()) { + languageModel = OllamaLanguageModel.builder() + .baseUrl(OLLAMA_BASE_URL) + .modelName("tinydolphin") + .temperature(1.0) + .build(); + logger.info("using Ollama tinydolphin language model"); + } else if (usingMistralAi()) { + throw new Exception("LangChain4J Mistral AI APIs do not support language model"); + } else if (usingHuggingFace()) { + languageModel = HuggingFaceLanguageModel.builder() + .accessToken(HUGGING_FACE_API_KEY) + .modelId(HUGGING_FACE_LANGUAGE_MODEL_ID) + .timeout(ofSeconds(120)) + .temperature(1.0) + .maxNewTokens(30) + .waitForModel(true) + .build(); + logger.info("using Hugging Face " + HUGGING_FACE_LANGUAGE_MODEL_ID + " language model"); + } else { + throw new Exception("No available platform to access model"); + } + } + return languageModel; + } + + public EmbeddingModel getEmbeddingModel() throws Exception { + if (embeddingModel == null) { + if (usingGithub()) { + embeddingModel = GitHubModelsEmbeddingModel.builder() + .gitHubToken(GITHUB_API_KEY) + .modelName(TEXT_EMBEDDING_3_SMALL) + .timeout(ofSeconds(120)) + .build(); + logger.info("using Github " + TEXT_EMBEDDING_3_SMALL + " embedding model"); + } else if (usingOllama()) { + embeddingModel = OllamaEmbeddingModel.builder() + .baseUrl(OLLAMA_BASE_URL) + .modelName("all-minilm") + .timeout(ofSeconds(120)) + .build(); + logger.info("using Ollama all-minilm embedding model"); + } else if (usingMistralAi()) { + embeddingModel = MistralAiEmbeddingModel.builder() + .apiKey(MISTRAL_AI_API_KEY) + .modelName(MISTRAL_EMBED) + .timeout(ofSeconds(120)) + .build(); + logger.info("using Mistral AI " + MISTRAL_EMBED + " embedding model"); + } else if (usingHuggingFace()) { + embeddingModel = HuggingFaceEmbeddingModel.builder() + .accessToken(HUGGING_FACE_API_KEY) + .modelId(SENTENCE_TRANSFORMERS_ALL_MINI_LM_L6_V2) + .timeout(ofSeconds(120)) + .waitForModel(true) + .build(); + logger.info("using Hugging Face " + SENTENCE_TRANSFORMERS_ALL_MINI_LM_L6_V2 + " embedding model"); + } else { + throw new Exception("No available platform to access model"); + } + } + return embeddingModel; + } + + public ChatModel getChatModelForResource() throws Exception { + if (chatModelForResource == null) { + if (usingGithub()) { + chatModelForResource = GitHubModelsChatModel.builder() + .gitHubToken(GITHUB_API_KEY) + .modelName("Phi-4-mini-instruct") + .timeout(ofSeconds(120)) + .temperature(1.0) + .maxTokens(200) + .build(); + logger.info("using Github Phi-4-mini-instruct chat model"); + } else if (usingOllama()) { + chatModelForResource = OllamaChatModel.builder() + .baseUrl(OLLAMA_BASE_URL) + .modelName(OLLAMA_CHAT_MODEL_ID) + .timeout(ofSeconds(TIMEOUT)) + .temperature(TEMPERATURE) + .build(); + logger.info("using Ollama " + OLLAMA_CHAT_MODEL_ID + " chat model"); + } else if (usingMistralAi()) { + chatModelForResource = MistralAiChatModel.builder() + .apiKey(MISTRAL_AI_API_KEY) + .modelName(MISTRAL_SMALL_LATEST) + .timeout(ofSeconds(TIMEOUT)) + .temperature(TEMPERATURE) + .maxTokens(MAX_NEW_TOKEN) + .build(); + logger.info("using Mistral AI " + MISTRAL_SMALL_LATEST + " chat model"); + } else if (usingHuggingFace()) { + chatModelForResource = HuggingFaceChatModel.builder() + .accessToken(HUGGING_FACE_API_KEY) + .modelId(HUGGING_FACE_LANGUAGE_MODEL_ID) + .timeout(ofSeconds(120)) + .temperature(1.0) + .maxNewTokens(200) + .waitForModel(true) + .build(); + logger.info("using Hugging Face " + HUGGING_FACE_LANGUAGE_MODEL_ID + " chat model"); + } else { + throw new Exception("No available platform to access model"); + } + } + return chatModelForResource; + } + + public ChatModel getChatModelForWeb() throws Exception { + if (chatModelForWeb == null) { + if (usingGithub()) { + chatModelForWeb = GitHubModelsChatModel.builder() + .gitHubToken(GITHUB_API_KEY) + .modelName(GITHUB_CHAT_MODEL_ID) + .timeout(ofSeconds(TIMEOUT)) + .temperature(TEMPERATURE) + .maxTokens(MAX_NEW_TOKEN) + .build(); + logger.info("using Github " + GITHUB_CHAT_MODEL_ID + " chat model for the web"); + } else if (usingOllama()) { + chatModelForWeb = OllamaChatModel.builder() + .baseUrl(OLLAMA_BASE_URL) + .modelName(OLLAMA_CHAT_MODEL_ID) + .timeout(ofSeconds(TIMEOUT)) + .temperature(TEMPERATURE) + .build(); + logger.info("using Ollama " + OLLAMA_CHAT_MODEL_ID + " chat model for the web"); + } else if (usingMistralAi()) { + chatModelForWeb = MistralAiChatModel.builder() + .apiKey(MISTRAL_AI_API_KEY) + .modelName(MISTRAL_AI_MISTRAL_CHAT_MODEL_ID) + .timeout(ofSeconds(TIMEOUT)) + .temperature(TEMPERATURE) + .maxTokens(MAX_NEW_TOKEN) + .build(); + logger.info("using Mistral AI " + MISTRAL_AI_MISTRAL_CHAT_MODEL_ID + " chat model for the web"); + } else if (usingHuggingFace()) { + chatModelForWeb = HuggingFaceChatModel.builder() + .accessToken(HUGGING_FACE_API_KEY) + .modelId(HUGGING_FACE_CHAT_MODEL_ID) + .timeout(ofSeconds(TIMEOUT)) + .temperature(TEMPERATURE) + .maxNewTokens(MAX_NEW_TOKEN) + .waitForModel(true) + .build(); + logger.info("using Hugging Face " + HUGGING_FACE_CHAT_MODEL_ID + " chat model for the web"); + + } else { + throw new Exception("No available platform to access model"); + } + } + return chatModelForWeb; + } + +} diff --git a/jakartaee-microprofile-example/src/main/java/dev/langchain4j/example/rest/ModelResource.java b/jakartaee-microprofile-example/src/main/java/dev/langchain4j/example/rest/ModelResource.java index 1a3409ac..b6c761c4 100644 --- a/jakartaee-microprofile-example/src/main/java/dev/langchain4j/example/rest/ModelResource.java +++ b/jakartaee-microprofile-example/src/main/java/dev/langchain4j/example/rest/ModelResource.java @@ -1,13 +1,23 @@ package dev.langchain4j.example.rest; +import static dev.langchain4j.data.segment.TextSegment.textSegment; +import static dev.langchain4j.store.embedding.CosineSimilarity.between; +import static dev.langchain4j.store.embedding.RelevanceScore.fromCosineSimilarity; + +import java.util.List; +import java.util.Properties; + +import org.eclipse.microprofile.openapi.annotations.Operation; + import dev.langchain4j.data.embedding.Embedding; import dev.langchain4j.data.message.AiMessage; import dev.langchain4j.data.message.SystemMessage; import dev.langchain4j.data.message.UserMessage; import dev.langchain4j.data.segment.TextSegment; -import dev.langchain4j.model.huggingface.HuggingFaceChatModel; -import dev.langchain4j.model.huggingface.HuggingFaceEmbeddingModel; -import dev.langchain4j.model.huggingface.HuggingFaceLanguageModel; +import dev.langchain4j.example.chat.util.ModelBuilder; +import dev.langchain4j.model.chat.ChatModel; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.model.language.LanguageModel; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.ws.rs.GET; @@ -15,58 +25,13 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; -import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.eclipse.microprofile.openapi.annotations.Operation; - -import java.util.List; -import java.util.Properties; - -import static dev.langchain4j.data.segment.TextSegment.textSegment; -import static dev.langchain4j.model.huggingface.HuggingFaceModelName.SENTENCE_TRANSFORMERS_ALL_MINI_LM_L6_V2; -import static dev.langchain4j.store.embedding.CosineSimilarity.between; -import static dev.langchain4j.store.embedding.RelevanceScore.fromCosineSimilarity; -import static java.time.Duration.ofSeconds; @ApplicationScoped @Path("model") public class ModelResource { @Inject - @ConfigProperty(name = "hugging.face.api.key") - private String HUGGING_FACE_API_KEY; - - @Inject - @ConfigProperty(name = "language.model.id") - private String LANGUAGE_MODEL_ID; - - private HuggingFaceLanguageModel languageModel = null; - private HuggingFaceEmbeddingModel embeddingModel = null; - - private HuggingFaceLanguageModel getLanguageModel() { - if (languageModel == null) { - languageModel = HuggingFaceLanguageModel.builder() - .accessToken(HUGGING_FACE_API_KEY) - .modelId(LANGUAGE_MODEL_ID) - .timeout(ofSeconds(120)) - .temperature(1.0) - .maxNewTokens(30) - .waitForModel(true) - .build(); - } - return languageModel; - } - - private HuggingFaceEmbeddingModel getEmbeddingModel() { - if (embeddingModel == null) { - embeddingModel = HuggingFaceEmbeddingModel.builder() - .accessToken(HUGGING_FACE_API_KEY) - .modelId(SENTENCE_TRANSFORMERS_ALL_MINI_LM_L6_V2) - .timeout(ofSeconds(120)) - .waitForModel(true) - .build(); - } - return embeddingModel; - } + private ModelBuilder modelBuilder; @GET @Produces(MediaType.TEXT_PLAIN) @@ -75,9 +40,9 @@ private HuggingFaceEmbeddingModel getEmbeddingModel() { summary = "Use the language model.", description = "Provide a sequence of words to a large language model.", operationId = "languageModelAsk") - public String languageModelAsk(@QueryParam("question") String question) { + public String languageModelAsk(@QueryParam("question") String question) throws Exception { - HuggingFaceLanguageModel model = getLanguageModel(); + LanguageModel model = modelBuilder.getLanguageModel(); String answer; try { @@ -98,16 +63,9 @@ public String languageModelAsk(@QueryParam("question") String question) { description = "Assume you are talking with an agent that is knowledgeable about " + "Large Language Models. Ask any question about it.", operationId = "chatModelAsk") - public List chatModelAsk(@QueryParam("userMessage") String userMessage) { + public List chatModelAsk(@QueryParam("userMessage") String userMessage) throws Exception { - HuggingFaceChatModel model = HuggingFaceChatModel.builder() - .accessToken(HUGGING_FACE_API_KEY) - .modelId(LANGUAGE_MODEL_ID) - .timeout(ofSeconds(120)) - .temperature(1.0) - .maxNewTokens(200) - .waitForModel(true) - .build(); + ChatModel model = modelBuilder.getChatModelForResource(); SystemMessage systemMessage = SystemMessage.from( "You are very knowledgeable about Large Language Models. Be friendly. Give concise answers."); @@ -137,9 +95,9 @@ private Properties getProperties(String value, Embedding embedding) { operationId = "similarity") public Properties similarity( @QueryParam("text1") String text1, - @QueryParam("text2") String text2) { + @QueryParam("text2") String text2) throws Exception { - HuggingFaceEmbeddingModel model = getEmbeddingModel(); + EmbeddingModel model = modelBuilder.getEmbeddingModel(); List textSegments = List.of(textSegment(text1), textSegment(text2)); List embeddings = model.embedAll(textSegments).content(); diff --git a/jakartaee-microprofile-example/src/main/resources/META-INF/microprofile-config.properties b/jakartaee-microprofile-example/src/main/resources/META-INF/microprofile-config.properties index c606861f..264d4bed 100644 --- a/jakartaee-microprofile-example/src/main/resources/META-INF/microprofile-config.properties +++ b/jakartaee-microprofile-example/src/main/resources/META-INF/microprofile-config.properties @@ -1,8 +1,17 @@ +github.api.key=set it by env variable +github.chat.model.id=gpt-4o-mini + +ollama.base.url=set it by env variable +ollama.chat.model.id=llama3.2 + +mistral.ai.api.key=set it by env variable +mistral.ai.chat.model.id=mistral-small-latest + hugging.face.api.key=set it by env variable -#chat.model.id=meta-llama/Llama-3.2-1B-Instruct -chat.model.id=mistralai/Mistral-Nemo-Instruct-2407 +hugging.face.chat.model.id=HuggingFaceH4/zephyr-7b-beta +hugging.face.language.model.id=microsoft/Phi-3-mini-4k-instruct + chat.model.timeout=120 chat.model.max.token=200 chat.model.temperature=1.0 chat.memory.max.messages=20 -language.model.id=microsoft/Phi-3-mini-4k-instruct \ No newline at end of file diff --git a/jakartaee-microprofile-example/src/test/java/it/dev/langchan4j/example/ChatServiceIT.java b/jakartaee-microprofile-example/src/test/java/it/dev/langchan4j/example/ChatServiceIT.java index 3c87269d..3f96c6de 100644 --- a/jakartaee-microprofile-example/src/test/java/it/dev/langchan4j/example/ChatServiceIT.java +++ b/jakartaee-microprofile-example/src/test/java/it/dev/langchan4j/example/ChatServiceIT.java @@ -28,7 +28,9 @@ public void testChat() throws Exception { public static void verify(String message) { assertNotNull(message); - assertTrue(message.contains("2023"), message); + assertTrue(message.contains("2020") || message.contains("2021") || + message.contains("2022") || message.contains("2023"), + message); countDown.countDown(); } diff --git a/jakartaee-microprofile-example/src/test/java/it/dev/langchan4j/example/ModelResourceIT.java b/jakartaee-microprofile-example/src/test/java/it/dev/langchan4j/example/ModelResourceIT.java index 0fea238f..23ae43b0 100644 --- a/jakartaee-microprofile-example/src/test/java/it/dev/langchan4j/example/ModelResourceIT.java +++ b/jakartaee-microprofile-example/src/test/java/it/dev/langchan4j/example/ModelResourceIT.java @@ -29,10 +29,14 @@ public void teardown() { @Test public void testLanguageMode() { + if (Util.usingGithub() || Util.usingMistralAi()) { + return; + } String url = baseUrl + "language?question=When was Hugging Face launched?"; Response response = client.target(url).request().get(); String answer = response.readEntity(String.class); - assertTrue(answer.contains("2018"), "actual: " + answer); + assertTrue(answer.contains("2015") || answer.contains("2016") || + answer.contains("2017") || answer.contains("2018"), "actual: " + answer); } @Test @@ -40,7 +44,8 @@ public void testChatMode() { String url = baseUrl + "chat?userMessage=Which are the most used Large Language Models?"; Response response = client.target(url).request().get(); String answer = response.readEntity(String.class); - assertTrue(answer.contains("BERT"), "actual: " + answer); + assertTrue(answer.contains("BERT") || answer.contains("GPT") || answer.contains("LaMDA"), + "actual: " + answer); } @Test @@ -52,10 +57,10 @@ public void testEmbeddingMode() { JsonObject json = response.readEntity(JsonObject.class); double score = json.getJsonNumber("relevance-score").doubleValue(); - assertTrue(score > 0.69 && score < 0.70, "actual score: " + score); + assertTrue(score > 0.63 && score < 0.89, "actual score: " + score); double similarity = json.getJsonNumber("similarity").doubleValue(); - assertTrue(similarity > 0.38 && similarity < 0.39, + assertTrue(similarity > 0.27 && similarity < 0.79, "actual similarity: " + similarity); } diff --git a/jakartaee-microprofile-example/src/test/java/it/dev/langchan4j/example/Util.java b/jakartaee-microprofile-example/src/test/java/it/dev/langchan4j/example/Util.java new file mode 100644 index 00000000..434e9fa1 --- /dev/null +++ b/jakartaee-microprofile-example/src/test/java/it/dev/langchan4j/example/Util.java @@ -0,0 +1,28 @@ +package it.dev.langchan4j.example; + +public class Util { + + private static String hfApiKey = System.getenv("HUGGING_FACE_API_KEY"); + private static String githubApiKey = System.getenv("GITHUB_API_KEY"); + private static String ollamaBaseUrl = System.getenv("OLLAMA_BASE_URL"); + private static String mistralAiApiKey = System.getenv("MISTRAL_AI_API_KEY"); + + public static boolean usingHuggingFace() { + return hfApiKey != null && hfApiKey.startsWith("hf_"); + } + + public static boolean usingGithub() { + return githubApiKey != null && + (githubApiKey.startsWith("ghp_") || + githubApiKey.startsWith("github_pat_")); + } + + public static boolean usingOllama() { + return ollamaBaseUrl != null && ollamaBaseUrl.startsWith("http"); + } + + public static boolean usingMistralAi() { + return mistralAiApiKey != null && mistralAiApiKey.length() > 30; + } + +}