Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Spring Boot Project Starter for Google Gemini API model - ChatLangauge, Streaming model and Embedding Model #74

Open
wants to merge 48 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
89fa967
Base Commit for Google Gemini AI Model
Suhas-Koheda Nov 13, 2024
cb8544f
Added Configuration files for Google Gemini AI - ChatModel
Suhas-Koheda Nov 13, 2024
3a8765c
Added Configuration files for Google Gemini AI - ChatModel
Suhas-Koheda Nov 13, 2024
0dfbc0d
Check
Suhas-Koheda Nov 13, 2024
8335e67
Adding Streaming Google AI Chat Model and Removing Lombok uses
Suhas-Koheda Nov 13, 2024
84bc5e3
Adding application.properties.local for easy usage
Suhas-Koheda Nov 13, 2024
6d7c4cf
Ignore all the .idea folders in subfolders
Suhas-Koheda Nov 13, 2024
300cfa2
Ignore all the .idea folders in subfolders
Suhas-Koheda Nov 13, 2024
9618410
Ignore all the .idea folders in subfolders
Suhas-Koheda Nov 13, 2024
7ee36c6
Changing pom.xml headers
Suhas-Koheda Nov 14, 2024
f696e82
Resolving API_KEY to apikey
Suhas-Koheda Nov 14, 2024
4b8f2ec
Resolving API_KEY to apikey
Suhas-Koheda Nov 14, 2024
9453ced
Resolving API_KEY to apikey
Suhas-Koheda Nov 14, 2024
4f267ff
Change Response Format to User Defined Props
Suhas-Koheda Nov 15, 2024
affefe3
Change Response Format to User Defined Props
Suhas-Koheda Nov 15, 2024
76053d9
Write test to check working of ChatLangaugeModel and StreamingCHatLan…
Suhas-Koheda Nov 15, 2024
6ee971e
Complete Test for Chat Language Model
Suhas-Koheda Nov 15, 2024
637cd0e
Complete Test for Chat Language Model
Suhas-Koheda Nov 15, 2024
3e5fca5
Implement test for Streaming Chat Language Model
Suhas-Koheda Nov 15, 2024
cfc5cae
Delete langchain4j-google-ai-gemini-spring-boot-starter/src/main/reso…
Suhas-Koheda Nov 15, 2024
66c828e
Changing Java Version in pom.xml from 21 to 17 to support maven (mvn)…
Suhas-Koheda Nov 16, 2024
11d7989
Merge remote-tracking branch 'refs/remotes/origin/main'
Suhas-Koheda Nov 16, 2024
ec1275b
Changing Java version back to 21 :) in pom.xml
Suhas-Koheda Nov 16, 2024
0fd6868
Resolving other issues except Embedding model
Suhas-Koheda Nov 18, 2024
070a71e
Adding env variable to main.yaml and release.yaml
Suhas-Koheda Nov 18, 2024
7aa79cb
Merge branch 'langchain4j:main' into main
Suhas-Koheda Nov 18, 2024
889aa2f
Update release.yaml to add Google AI Gemini API Key
Suhas-Koheda Nov 18, 2024
db889db
Update main.yaml to add Google AI Gemini API Key
Suhas-Koheda Nov 18, 2024
b626e12
Merge branch 'main' of https://github.com/Suhas-Koheda/langchain4j-sp…
Suhas-Koheda Nov 18, 2024
4ae025a
Changing the version of dependencies to project version
Suhas-Koheda Nov 18, 2024
9aad39f
adding GeminiSafetyProperties and GeminiSafetySettings to googleGemin…
Suhas-Koheda Nov 19, 2024
3c32174
Add tests for chatlanguagemodel with updated properties
Suhas-Koheda Nov 19, 2024
c4ba686
Adding Embedding model configuration and test for Embedding model
Suhas-Koheda Nov 20, 2024
ef71908
Merge branch 'langchain4j:main' into main
Suhas-Koheda Nov 20, 2024
b0a8f9a
Resolving issues in spring boot starter for google ai gemini
Suhas-Koheda Nov 26, 2024
89694f5
Update main.yaml for google ai gemini key
Suhas-Koheda Nov 26, 2024
907424b
Update release.yaml for google ai gemini key
Suhas-Koheda Nov 26, 2024
6159165
refactoring resources
Suhas-Koheda Nov 26, 2024
4a27ef1
Merge remote-tracking branch 'refs/remotes/origin/main'
Suhas-Koheda Nov 26, 2024
7554b25
Changing the safteySetting and toolConfig and writing test
Suhas-Koheda Nov 30, 2024
4afe606
Merge branch 'langchain4j:main' into main
Suhas-Koheda Dec 2, 2024
69660a4
Resolving silly mistake
Suhas-Koheda Dec 3, 2024
3107cb9
Resolving issues
Suhas-Koheda Dec 7, 2024
56d7546
Changing bean properties
Suhas-Koheda Dec 9, 2024
ed5afe0
cosmetics
langchain4j Dec 16, 2024
9b52b6e
Merge branch 'langchain4j:main' into main
Suhas-Koheda Dec 21, 2024
9c167d1
Adding NPE check for Safety Setting and Tool Config
Suhas-Koheda Dec 21, 2024
816652c
Changing safetySettings to map of harm category and harm threshold
Suhas-Koheda Jan 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ jobs:
AZURE_SEARCH_ENDPOINT: ${{ secrets.AZURE_SEARCH_ENDPOINT }}
AZURE_SEARCH_KEY: ${{ secrets.AZURE_SEARCH_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GOOGLE_AI_GEMINI_API_KEY: ${{secrets.GOOGLE_AI_GEMINI_API_KEY}}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
VOYAGE_API_KEY: ${{ secrets.VOYAGE_API_KEY }}
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
Expand All @@ -50,4 +51,4 @@ jobs:
distribution: 'temurin'
cache: 'maven'
- name: License Compliance
run: mvn -U -P compliance org.honton.chas:license-maven-plugin:compliance
run: mvn -U -P compliance org.honton.chas:license-maven-plugin:compliance
1 change: 1 addition & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:
AZURE_SEARCH_ENDPOINT: ${{ secrets.AZURE_SEARCH_ENDPOINT }}
AZURE_SEARCH_KEY: ${{ secrets.AZURE_SEARCH_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GOOGLE_AI_GEMINI_API_KEY: ${{secrets.GOOGLE_AI_GEMINI_API_KEY}}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
VOYAGE_API_KEY: ${{ secrets.VOYAGE_API_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ target/

### IntelliJ IDEA ###
.idea/*
**/.idea/
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
Expand Down
63 changes: 63 additions & 0 deletions langchain4j-google-ai-gemini-spring-boot-starter/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring</artifactId>
<version>0.37.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>langchain4j-google-ai-gemini-spring-boot-starter</artifactId>
Suhas-Koheda marked this conversation as resolved.
Show resolved Hide resolved
<name>LangChain4j Spring Boot starter for Google AI Gemini</name>
<packaging>jar</packaging>

<dependencies>

<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-google-ai-gemini</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

Suhas-Koheda marked this conversation as resolved.
Show resolved Hide resolved
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>

<!-- needed to generate automatic metadata about available config properties -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>



</dependencies>

<licenses>
<license>
<name>Apache-2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
<comments>A business-friendly OSS license</comments>
</license>
</licenses>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package dev.langchain4j.googleaigemini.spring;

import dev.langchain4j.model.googleai.GoogleAiEmbeddingModel;
import dev.langchain4j.model.googleai.GoogleAiGeminiChatModel;
import dev.langchain4j.model.googleai.GoogleAiGeminiStreamingChatModel;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

import java.util.Map;

import static dev.langchain4j.googleaigemini.spring.Properties.PREFIX;

@AutoConfiguration
@EnableConfigurationProperties(Properties.class)
public class AutoConfig {

@Bean
@ConditionalOnProperty({
PREFIX + ".chat-model.api-key",
PREFIX + ".chat-model.model-name"
})
GoogleAiGeminiChatModel googleAiGeminiChatModel(Properties properties) {
ChatModelProperties chatModelProperties = properties.getChatModel();
return GoogleAiGeminiChatModel.builder()
.apiKey(chatModelProperties.apiKey())
.modelName(chatModelProperties.modelName())
.temperature(chatModelProperties.temperature())
.topP(chatModelProperties.topP())
.topK(chatModelProperties.topK())
.maxOutputTokens(chatModelProperties.maxOutputTokens())
.responseFormat(chatModelProperties.responseFormat())
.logRequestsAndResponses(chatModelProperties.logRequestsAndResponses())
.safetySettings(Map.of(
// TODO NPE?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please rewrite it to avoid NPE

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@langchain4j hey how can we avoid NPE like i am writing a function that checks if it is empty or not
if it is not empty the map is returned or else i wanted to return a defautl value
but what can the default value be
@ddobrin if you can help me with this :-)
Thank you

chatModelProperties.safetySetting().geminiHarmCategory(),
chatModelProperties.safetySetting().geminiHarmBlockThreshold()
))
.toolConfig(
// TODO NPE?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, please check all other model beans as well

chatModelProperties.functionCallingConfig().geminiMode(),
chatModelProperties.functionCallingConfig().allowedFunctionNames().toArray(new String[0])
)
.build();
}

@Bean
@ConditionalOnProperty({
PREFIX + ".streaming-chat-model.api-key",
PREFIX + ".streaming-chat-model.model-name"
})
GoogleAiGeminiStreamingChatModel googleAiGeminiStreamingChatModel(Properties properties) {
ChatModelProperties chatModelProperties = properties.getStreamingChatModel();
return GoogleAiGeminiStreamingChatModel.builder()
.apiKey(chatModelProperties.apiKey())
.modelName(chatModelProperties.modelName())
.temperature(chatModelProperties.temperature())
.topP(chatModelProperties.topP())
.topK(chatModelProperties.topK())
.responseFormat(chatModelProperties.responseFormat())
.logRequestsAndResponses(chatModelProperties.logRequestsAndResponses())
.safetySettings(Map.of(
// TODO NPE?
chatModelProperties.safetySetting().geminiHarmCategory(),
chatModelProperties.safetySetting().geminiHarmBlockThreshold()
))
.toolConfig(
// TODO NPE?
chatModelProperties.functionCallingConfig().geminiMode(),
chatModelProperties.functionCallingConfig().allowedFunctionNames().toArray(new String[0])
)
.build();
}

@Bean
@ConditionalOnProperty({
PREFIX + ".embedding-model.api-key",
PREFIX + ".embedding-model.model-name"
})
GoogleAiEmbeddingModel googleAiEmbeddingModel(Properties properties) {
EmbeddingModelProperties embeddingModelProperties = properties.getEmbeddingModel();
return GoogleAiEmbeddingModel.builder()
.apiKey(embeddingModelProperties.apiKey())
.modelName(embeddingModelProperties.modelName())
.logRequestsAndResponses(embeddingModelProperties.logRequestsAndResponses())
.maxRetries(embeddingModelProperties.maxRetries())
.outputDimensionality(embeddingModelProperties.outputDimensionality())
.taskType(embeddingModelProperties.taskType())
.timeout(embeddingModelProperties.timeout())
.titleMetadataKey(embeddingModelProperties.titleMetadataKey())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package dev.langchain4j.googleaigemini.spring;

import dev.langchain4j.model.chat.request.ResponseFormat;

import java.time.Duration;

public record ChatModelProperties(
String apiKey,
String modelName,
Double temperature,
Double topP,
Integer topK,
Integer maxOutputTokens,
ResponseFormat responseFormat,
Boolean logRequestsAndResponses,
Integer maxRetries,
Duration timeout,
GeminiSafetySetting safetySetting,
GeminiFunctionCallingConfig functionCallingConfig
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package dev.langchain4j.googleaigemini.spring;

import dev.langchain4j.model.googleai.GoogleAiEmbeddingModel.TaskType;

import java.time.Duration;

public record EmbeddingModelProperties(
String apiKey,
String modelName,
String titleMetadataKey,
Boolean logRequestsAndResponses,
Integer maxRetries,
Integer outputDimensionality,
TaskType taskType,
Duration timeout
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package dev.langchain4j.googleaigemini.spring;

import dev.langchain4j.model.googleai.GeminiMode;

import java.util.List;

public record GeminiFunctionCallingConfig(
GeminiMode geminiMode,
List<String> allowedFunctionNames
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package dev.langchain4j.googleaigemini.spring;

import dev.langchain4j.model.googleai.GeminiHarmBlockThreshold;
import dev.langchain4j.model.googleai.GeminiHarmCategory;

public record GeminiSafetySetting(
GeminiHarmCategory geminiHarmCategory,
GeminiHarmBlockThreshold geminiHarmBlockThreshold
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package dev.langchain4j.googleaigemini.spring;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

@ConfigurationProperties(prefix = Properties.PREFIX)
public class Properties {

static final String PREFIX = "langchain4j.google-ai-gemini";

@NestedConfigurationProperty
private ChatModelProperties chatModel;

@NestedConfigurationProperty
private ChatModelProperties streamingChatModel;

@NestedConfigurationProperty
private EmbeddingModelProperties embeddingModel;

public EmbeddingModelProperties getEmbeddingModel() {
return embeddingModel;
}

public void setEmbeddingModel(EmbeddingModelProperties embeddingModel) {
this.embeddingModel = embeddingModel;
}

public ChatModelProperties getStreamingChatModel() {
return streamingChatModel;
}

public void setStreamingChatModel(ChatModelProperties streamingChatModel) {
this.streamingChatModel = streamingChatModel;
}

public ChatModelProperties getChatModel() {
return chatModel;
}

public void setChatModel(ChatModelProperties chatModel) {
this.chatModel = chatModel;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dev.langchain4j.googleaigemini.spring.AutoConfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package dev.langchain4j.googleaigemini.spring;

import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.StreamingResponseHandler;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.googleai.GoogleAiEmbeddingModel;
import dev.langchain4j.model.googleai.GoogleAiGeminiChatModel;
import dev.langchain4j.model.googleai.GoogleAiGeminiStreamingChatModel;
import dev.langchain4j.model.output.Response;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;

import java.util.concurrent.CompletableFuture;

import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.Assertions.assertThat;

class AutoConfigIT {

private static final String API_KEY = System.getenv("GOOGLE_AI_GEMINI_API_KEY");

ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(AutoConfig.class));

@Test
void provide_chat_model() {
Suhas-Koheda marked this conversation as resolved.
Show resolved Hide resolved
contextRunner.withPropertyValues(
"langchain4j.google-ai-gemini.chat-model.api-key=" + API_KEY,
"langchain4j.google-ai-gemini.chat-model.model-name=gemini-1.5-flash"
)
.run(context -> {
ChatLanguageModel chatLanguageModel = context.getBean(ChatLanguageModel.class);
assertThat(context.getBean(GoogleAiGeminiChatModel.class)).isSameAs(chatLanguageModel);

String response = chatLanguageModel.generate("What is the capital of India");
assertThat(response).contains("Delhi");

String newResponse = chatLanguageModel.generate("Calculate the Fibonacci of 22 and give me the result as an integer value along with the code. ");
assertThat(newResponse).contains("17711");
});
}

@Test
void provide_streaming_chat_model() {
contextRunner.withPropertyValues(
"langchain4j.google-ai-gemini.streaming-chat-model.api-key=" + API_KEY,
"langchain4j.google-ai-gemini.streaming-chat-model.model-name=gemini-1.5-flash"
)
.run(context -> {
StreamingChatLanguageModel streamingChatLanguageModel = context.getBean(StreamingChatLanguageModel.class);
assertThat(context.getBean(GoogleAiGeminiStreamingChatModel.class)).isSameAs(streamingChatLanguageModel);

CompletableFuture<Response<AiMessage>> future = new CompletableFuture<>();
streamingChatLanguageModel.generate("What is the capital of India", new StreamingResponseHandler<>() {
@Override
public void onNext(String s) {
}

@Override
public void onComplete(Response<AiMessage> response) {
future.complete(response);
}

@Override
public void onError(Throwable throwable) {
future.completeExceptionally(throwable);
}
});

Response<AiMessage> response = future.get(60, SECONDS);
assertThat(response.content().text()).contains("Delhi");
});
}

@Test
void provide_embedding_model() {
contextRunner.withPropertyValues(
"langchain4j.google-ai-gemini.embedding-model.apiKey=" + API_KEY,
"langchain4j.google-ai-gemini.embedding-model.model-name=text-embedding-004"
).run(context -> {
EmbeddingModel embeddingModel = context.getBean(EmbeddingModel.class);
assertThat(context.getBean(GoogleAiEmbeddingModel.class)).isSameAs(embeddingModel);

Response<Embedding> response = embeddingModel.embed("Hi, I live in India");
assertThat(response.content().dimension()).isEqualTo(768);
});
}
}
Loading