Add VertexAi Chat Optios Support + Docs
This commit is contained in:
@@ -20,9 +20,11 @@ import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ChatOptions;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.model.ModelOptionsUtils;
|
||||
import org.springframework.ai.chat.messages.MessageType;
|
||||
import org.springframework.ai.vertex.api.VertexAiApi;
|
||||
import org.springframework.ai.vertex.api.VertexAiApi.GenerateMessageRequest;
|
||||
@@ -38,41 +40,41 @@ public class VertexAiChatClient implements ChatClient {
|
||||
|
||||
private final VertexAiApi vertexAiApi;
|
||||
|
||||
private Float temperature;
|
||||
|
||||
private Float topP;
|
||||
|
||||
private Integer topK;
|
||||
|
||||
private Integer candidateCount;
|
||||
private final VertexAiChatOptions defaultOptions;
|
||||
|
||||
public VertexAiChatClient(VertexAiApi vertexAiApi) {
|
||||
this(vertexAiApi,
|
||||
VertexAiChatOptions.builder().withTemperature(0.7f).withCandidateCount(1).withTopK(20).build());
|
||||
}
|
||||
|
||||
public VertexAiChatClient(VertexAiApi vertexAiApi, VertexAiChatOptions defaultOptions) {
|
||||
Assert.notNull(defaultOptions, "Default options must not be null!");
|
||||
Assert.notNull(vertexAiApi, "VertexAiApi must not be null!");
|
||||
|
||||
this.vertexAiApi = vertexAiApi;
|
||||
}
|
||||
|
||||
public VertexAiChatClient withTemperature(Float temperature) {
|
||||
this.temperature = temperature;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VertexAiChatClient withTopP(Float topP) {
|
||||
this.topP = topP;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VertexAiChatClient withTopK(Integer topK) {
|
||||
this.topK = topK;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VertexAiChatClient withCandidateCount(Integer maxTokens) {
|
||||
this.candidateCount = maxTokens;
|
||||
return this;
|
||||
this.defaultOptions = defaultOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatResponse call(Prompt prompt) {
|
||||
|
||||
GenerateMessageRequest request = createRequest(prompt);
|
||||
|
||||
GenerateMessageResponse response = this.vertexAiApi.generateMessage(request);
|
||||
|
||||
List<Generation> generations = response.candidates()
|
||||
.stream()
|
||||
.map(vmsg -> new Generation(vmsg.content()))
|
||||
.toList();
|
||||
|
||||
return new ChatResponse(generations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessible for testing.
|
||||
*/
|
||||
GenerateMessageRequest createRequest(Prompt prompt) {
|
||||
|
||||
String vertexContext = prompt.getInstructions()
|
||||
.stream()
|
||||
.filter(m -> m.getMessageType() == MessageType.SYSTEM)
|
||||
@@ -89,17 +91,25 @@ public class VertexAiChatClient implements ChatClient {
|
||||
|
||||
var vertexPrompt = new MessagePrompt(vertexContext, vertexMessages);
|
||||
|
||||
GenerateMessageRequest request = new GenerateMessageRequest(vertexPrompt, this.temperature, this.candidateCount,
|
||||
this.topP, this.topK);
|
||||
GenerateMessageRequest request = new GenerateMessageRequest(vertexPrompt);
|
||||
|
||||
GenerateMessageResponse response = this.vertexAiApi.generateMessage(request);
|
||||
if (this.defaultOptions != null) {
|
||||
request = ModelOptionsUtils.merge(request, this.defaultOptions, GenerateMessageRequest.class);
|
||||
}
|
||||
|
||||
List<Generation> generations = response.candidates()
|
||||
.stream()
|
||||
.map(vmsg -> new Generation(vmsg.content()))
|
||||
.toList();
|
||||
if (prompt.getOptions() != null) {
|
||||
if (prompt.getOptions() instanceof ChatOptions runtimeOptions) {
|
||||
VertexAiChatOptions updatedRuntimeOptions = ModelOptionsUtils.copyToTarget(runtimeOptions,
|
||||
ChatOptions.class, VertexAiChatOptions.class);
|
||||
request = ModelOptionsUtils.merge(updatedRuntimeOptions, request, GenerateMessageRequest.class);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Prompt options are not of type ChatOptions: "
|
||||
+ prompt.getOptions().getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
return new ChatResponse(generations);
|
||||
return request;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2024-2024 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.springframework.ai.vertex;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.springframework.ai.chat.ChatOptions;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
public class VertexAiChatOptions implements ChatOptions {
|
||||
|
||||
// @formatter:off
|
||||
/**
|
||||
* Controls the randomness of the output. Values can range over [0.0,1.0], inclusive.
|
||||
* A value closer to 1.0 will produce responses that are more varied, while a value
|
||||
* closer to 0.0 will typically result in less surprising responses from the
|
||||
* generative. This value specifies default to be used by the backend while making the
|
||||
* call to the generative.
|
||||
*/
|
||||
private @JsonProperty("temperature") Float temperature;
|
||||
|
||||
/**
|
||||
* The number of generated response messages to return. This value must be between [1,
|
||||
* 8], inclusive. Defaults to 1.
|
||||
*/
|
||||
private @JsonProperty("candidateCount") Integer candidateCount;
|
||||
|
||||
/**
|
||||
* The maximum cumulative probability of tokens to consider when sampling. The
|
||||
* generative uses combined Top-k and nucleus sampling. Nucleus sampling considers the
|
||||
* smallest set of tokens whose probability sum is at least topP.
|
||||
*/
|
||||
private @JsonProperty("topP") Float topP;
|
||||
|
||||
/**
|
||||
* The maximum number of tokens to consider when sampling. The generative uses
|
||||
* combined Top-k and nucleus sampling. Top-k sampling considers the set of topK most
|
||||
* probable tokens.
|
||||
*/
|
||||
private @JsonProperty("topK") Integer topK;
|
||||
// @formatter:on
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private VertexAiChatOptions options = new VertexAiChatOptions();
|
||||
|
||||
public Builder withTemperature(Float temperature) {
|
||||
this.options.temperature = temperature;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withCandidateCount(Integer candidateCount) {
|
||||
this.options.candidateCount = candidateCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withTopP(Float topP) {
|
||||
this.options.topP = topP;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withTopK(Integer topK) {
|
||||
this.options.topK = topK;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VertexAiChatOptions build() {
|
||||
return this.options;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getTemperature() {
|
||||
return this.temperature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTemperature(Float temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
public Integer getCandidateCount() {
|
||||
return this.candidateCount;
|
||||
}
|
||||
|
||||
public void setCandidateCount(Integer candidateCount) {
|
||||
this.candidateCount = candidateCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getTopP() {
|
||||
return this.topP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTopP(Float topP) {
|
||||
this.topP = topP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getTopK() {
|
||||
return this.topK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTopK(Integer topK) {
|
||||
this.topK = topK;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -107,7 +107,7 @@ public class VertexAiApi {
|
||||
|
||||
private final String apiKey;
|
||||
|
||||
private final String generateModel;
|
||||
private final String chatModel;
|
||||
|
||||
private final String embeddingModel;
|
||||
|
||||
@@ -130,7 +130,7 @@ public class VertexAiApi {
|
||||
public VertexAiApi(String baseUrl, String apiKey, String model, String embeddingModel,
|
||||
RestClient.Builder restClientBuilder) {
|
||||
|
||||
this.generateModel = model;
|
||||
this.chatModel = model;
|
||||
this.embeddingModel = embeddingModel;
|
||||
this.apiKey = apiKey;
|
||||
|
||||
@@ -165,11 +165,12 @@ public class VertexAiApi {
|
||||
* @param request Request body.
|
||||
* @return Response body.
|
||||
*/
|
||||
@SuppressWarnings("null")
|
||||
public GenerateMessageResponse generateMessage(GenerateMessageRequest request) {
|
||||
Assert.notNull(request, "The request body can not be null.");
|
||||
|
||||
return this.restClient.post()
|
||||
.uri("/models/{model}:generateMessage?key={apiKey}", this.generateModel, this.apiKey)
|
||||
.uri("/models/{model}:generateMessage?key={apiKey}", this.chatModel, this.apiKey)
|
||||
.body(request)
|
||||
.retrieve()
|
||||
.body(GenerateMessageResponse.class);
|
||||
@@ -231,7 +232,7 @@ public class VertexAiApi {
|
||||
}
|
||||
|
||||
TokenCount tokenCountResponse = this.restClient.post()
|
||||
.uri("/models/{model}:countMessageTokens?key={apiKey}", this.generateModel, this.apiKey)
|
||||
.uri("/models/{model}:countMessageTokens?key={apiKey}", this.chatModel, this.apiKey)
|
||||
.body(Map.of("prompt", prompt))
|
||||
.retrieve()
|
||||
.body(TokenCount.class);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.ai.vertex.generation;
|
||||
package org.springframework.ai.vertex;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -9,15 +9,14 @@ import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.parser.BeanOutputParser;
|
||||
import org.springframework.ai.parser.ListOutputParser;
|
||||
import org.springframework.ai.parser.MapOutputParser;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.chat.prompt.PromptTemplate;
|
||||
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.vertex.VertexAiChatClient;
|
||||
import org.springframework.ai.parser.BeanOutputParser;
|
||||
import org.springframework.ai.parser.ListOutputParser;
|
||||
import org.springframework.ai.parser.MapOutputParser;
|
||||
import org.springframework.ai.vertex.api.VertexAiApi;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2024-2024 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.springframework.ai.vertex;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.ai.chat.ChatOptions;
|
||||
import org.springframework.ai.chat.ChatOptionsBuilder;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.vertex.api.VertexAiApi;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
public class VertexAiChatRequestTests {
|
||||
|
||||
VertexAiChatClient client = new VertexAiChatClient(new VertexAiApi("bla"));
|
||||
|
||||
@Test
|
||||
public void createRequestWithDefaultOptions() {
|
||||
|
||||
var request = client.createRequest(new Prompt("Test message content"));
|
||||
|
||||
assertThat(request.prompt().messages()).hasSize(1);
|
||||
|
||||
assertThat(request.candidateCount()).isEqualTo(1);
|
||||
assertThat(request.temperature()).isEqualTo(0.7f);
|
||||
assertThat(request.topK()).isEqualTo(20);
|
||||
assertThat(request.topP()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createRequestWithPromptVertexAiOptions() {
|
||||
|
||||
// Runtime options should override the default options.
|
||||
VertexAiChatOptions promptOptions = VertexAiChatOptions.builder()
|
||||
.withTemperature(0.8f)
|
||||
.withTopP(0.5f)
|
||||
.withTopK(99)
|
||||
// .withCandidateCount(2)
|
||||
.build();
|
||||
|
||||
var request = client.createRequest(new Prompt("Test message content", promptOptions));
|
||||
|
||||
assertThat(request.prompt().messages()).hasSize(1);
|
||||
|
||||
assertThat(request.candidateCount()).isEqualTo(1);
|
||||
assertThat(request.temperature()).isEqualTo(0.8f);
|
||||
assertThat(request.topK()).isEqualTo(99);
|
||||
assertThat(request.topP()).isEqualTo(0.5f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createRequestWithPromptPortableChatOptions() {
|
||||
|
||||
// runtime options.
|
||||
ChatOptions portablePromptOptions = ChatOptionsBuilder.builder()
|
||||
.withTemperature(0.9f)
|
||||
.withTopK(100)
|
||||
.withTopP(0.6f)
|
||||
.build();
|
||||
|
||||
var request = client.createRequest(new Prompt("Test message content", portablePromptOptions));
|
||||
|
||||
assertThat(request.prompt().messages()).hasSize(1);
|
||||
|
||||
assertThat(request.candidateCount()).isEqualTo(1);
|
||||
assertThat(request.temperature()).isEqualTo(0.9f);
|
||||
assertThat(request.topK()).isEqualTo(100);
|
||||
assertThat(request.topP()).isEqualTo(0.6f);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.ai.vertex.embedding;
|
||||
package org.springframework.ai.vertex;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 291 KiB |
@@ -14,13 +14,14 @@
|
||||
** xref:api/chatclient.adoc[]
|
||||
*** xref:api/clients/openai-chat.adoc[]
|
||||
*** xref:api/clients/azure-openai-chat.adoc[]
|
||||
*** xref:api/clients/ollama-chat.adoc[]
|
||||
*** xref:api/bedrock.adoc[Amazon Bedrock Chat]
|
||||
**** xref:api/clients/bedrock/bedrock-anthropic.adoc[]
|
||||
**** xref:api/clients/bedrock/bedrock-llama2.adoc[]
|
||||
**** xref:api/clients/bedrock/bedrock-cohere.adoc[]
|
||||
**** xref:api/clients/bedrock/bedrock-titan.adoc[]
|
||||
*** xref:api/clients/huggingface.adoc[]
|
||||
*** xref:api/clients/ollama-chat.adoc[]
|
||||
*** xref:api/clients/vertexai-chat.adoc[]
|
||||
** xref:api/prompt.adoc[]
|
||||
** xref:api/output-parser.adoc[]
|
||||
** xref:api/etl-pipeline.adoc[]
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
= VertexAI Chat
|
||||
|
||||
The link:https://developers.generativeai.google/api/rest/generativelanguage[Generative Language] PaLM API allows developers to build generative AI applications using the PaLM model. Large Language Models (LLMs) are a powerful, versatile type of machine learning model that enables computers to comprehend and generate natural language through a series of prompts. The PaLM API is based on Google's next generation LLM, PaLM. It excels at a variety of different tasks like code generation, reasoning, and writing. You can use the PaLM API to build generative AI applications for use cases like content generation, dialogue agents, summarization and classification systems, and more.
|
||||
|
||||
Based on the link:https://developers.generativeai.google/api/rest/generativelanguage/models[Models REST API].
|
||||
|
||||
== Prerequisites
|
||||
|
||||
To access the PaLM2 REST API you need to obtain an access API KEY form link:https://makersuite.google.com/app/apikey[makersuite].
|
||||
|
||||
NOTE: Currently the PaLM API it is not available outside US, but you can use VPN for testing.
|
||||
|
||||
The Spring AI project defines a configuration property named `spring.ai.vertex.ai.api-key` that you should set to the value of the `API Key` obtained from openai.com.
|
||||
Exporting an environment variable is one way to set that configuration property:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
export SPRING_AI_VERTEX_AI_API_KEY=<INSERT KEY HERE>
|
||||
----
|
||||
|
||||
== Auto-configuration
|
||||
|
||||
Spring AI provides Spring Boot auto-configuration for the VertexAI Chat Client.
|
||||
To enable it add the following dependency to your project's Maven `pom.xml` file:
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-vertex-ai-spring-boot-starter</artifactId>
|
||||
<version>0.8.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
or to your Gradle `build.gradle` build file.
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
dependencies {
|
||||
implementation 'org.springframework.ai:spring-ai-vertex-ai-spring-boot-starter:0.8.0-SNAPSHOT'
|
||||
}
|
||||
----
|
||||
|
||||
TIP: Refer to the xref:getting-started.adoc#_dependency_management[Dependency Management] section to add Milestone and/or Snapshot Repositories to your build file.
|
||||
|
||||
=== Chat Properties
|
||||
|
||||
The prefix `spring.ai.vertex.ai` is used as the property prefix that lets you connect to OpenAI.
|
||||
|
||||
[cols="3,5,1"]
|
||||
|====
|
||||
| Property | Description | Default
|
||||
|
||||
| spring.ai.vertex.ai.ai.base-url | The URL to connect to | https://generativelanguage.googleapis.com/v1beta3
|
||||
| spring.ai.vertex.ai.api-key | The API Key | -
|
||||
|====
|
||||
|
||||
The prefix `spring.ai.vertex.ai.chat` is the property prefix that lets you configure the chat client implementation for VertexAI Chat.
|
||||
|
||||
[cols="3,5,1"]
|
||||
|====
|
||||
| Property | Description | Default
|
||||
|
||||
| spring.ai.vertex.ai.chat.model | This is the https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text-chat[Vertex Chat model] to use | chat-bison-001
|
||||
| spring.ai.vertex.ai.chat.options.temperature | Controls the randomness of the output. Values can range over [0.0,1.0], inclusive. A value closer to 1.0 will produce responses that are more varied, while a value closer to 0.0 will typically result in less surprising responses from the generative. This value specifies default to be used by the backend while making the call to the generative. | 0.7
|
||||
| spring.ai.vertex.ai.chat.options.topK | The maximum number of tokens to consider when sampling. The generative uses combined Top-k and nucleus sampling. Top-k sampling considers the set of topK most probable tokens. | 20
|
||||
| spring.ai.vertex.ai.chat.options.topP | The maximum cumulative probability of tokens to consider when sampling. The generative uses combined Top-k and nucleus sampling. Nucleus sampling considers the smallest set of tokens whose probability sum is at least topP. | -
|
||||
| spring.ai.vertex.ai.chat.options.candidateCount | The number of generated response messages to return. This value must be between [1, 8], inclusive. Defaults to 1. | 1
|
||||
|====
|
||||
|
||||
TIP: All properties prefixed with `spring.ai.vertex.ai.chat.options` can be overridden at runtime by adding a request specific <<chat-options>> to the `Prompt` call.
|
||||
|
||||
=== Chat Options [[chat-options]]
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-openai/src/main/java/org/springframework/ai/vertex/VertexAiChatOptions.java[VertexAiChatOptions.java] provides model configurations, such as the temperature, the topK, etc.
|
||||
|
||||
On start-up, the default options can be configured with the `VertexAiChatClient(api, options)` constructor or the `spring.ai.vertex.ai.chat.options.*` properties.
|
||||
|
||||
At run-time you can override the default options by adding new, request specific, options to the `Prompt` call.
|
||||
For example to override the default temperature for a specific request:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
ChatResponse response = chatClient.call(
|
||||
new Prompt(
|
||||
"Generate the names of 5 famous pirates.",
|
||||
VertexAiChatOptions.builder()
|
||||
.withTemperature(0.4)
|
||||
.build()
|
||||
));
|
||||
----
|
||||
|
||||
TIP: In addition to the model specific `VertexAiChatOptions` you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()].
|
||||
|
||||
=== Sample Controller (Auto-configuration)
|
||||
|
||||
https://start.spring.io/[Create] a new Spring Boot project and add the `spring-ai-openai-spring-boot-starter` to your pom (or gradle) dependencies.
|
||||
|
||||
Add a `application.properties` file, under the `src/main/resources` directory, to enable and configure the VertexAi Chat client:
|
||||
|
||||
[source,application.properties]
|
||||
----
|
||||
spring.ai.vertex.ai.api-key=YOUR_API_KEY
|
||||
spring.ai.vertex.ai.chat.model=chat-bison-001
|
||||
spring.ai.vertex.ai.chat.options.temperature=0.5
|
||||
----
|
||||
|
||||
TIP: replace the `api-key` with your VertexAI credentials.
|
||||
|
||||
This will create a `VertexAiChatClient` implementation that you can inject into your class.
|
||||
Here is an example of a simple `@Controller` class that uses the chat client for text generations.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@RestController
|
||||
public class ChatController {
|
||||
|
||||
private final VertexAiChatClient chatClient;
|
||||
|
||||
@Autowired
|
||||
public ChatController(VertexAiChatClient chatClient) {
|
||||
this.chatClient = chatClient;
|
||||
}
|
||||
|
||||
@GetMapping("/open-ai/generate")
|
||||
public Map generate(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
|
||||
return Map.of("generation", chatClient.call(message));
|
||||
}
|
||||
|
||||
@GetMapping("/open-ai/generateStream")
|
||||
public Flux<ChatResponse> generateStream(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
|
||||
Prompt prompt = new Prompt(new UserMessage(message));
|
||||
return chatClient.stream(prompt);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
== Manual Configuration
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-openai/src/main/java/org/springframework/ai/vertex/VertexAiChatClient.java[OpenAiChatClient] implements the `ChatClient` and uses the <<low-level-api>> to connect to the VertexAI service.
|
||||
|
||||
Add the `spring-ai-vertex-ai` dependency to your project's Maven `pom.xml` file:
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-vertex-ai</artifactId>
|
||||
<version>0.8.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
or to your Gradle `build.gradle` build file.
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
dependencies {
|
||||
implementation 'org.springframework.ai:spring-ai-vertex-ai:0.8.0-SNAPSHOT'
|
||||
}
|
||||
----
|
||||
|
||||
TIP: Refer to the xref:getting-started.adoc#_dependency_management[Dependency Management] section to add Milestone and/or Snapshot Repositories to your build file.
|
||||
|
||||
Next, create a `VertexAiChatClient` and use it for text generations:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
VertexAiApi vertexAiApi = new VertexAiApi(< YOUR PALM_API_KEY>);
|
||||
|
||||
var chatClient = new VertexAiChatClient(vertexAiApi,
|
||||
VertexAiChatOptions.builder()
|
||||
.withTemperature(0.4)
|
||||
.build());
|
||||
|
||||
ChatResponse response = chatClient.call(
|
||||
new Prompt("Generate the names of 5 famous pirates."));
|
||||
----
|
||||
|
||||
The `VertexAiChatOptions` provides the configuration information for the chat requests.
|
||||
The `VertexAiChatOptions.Builder` is fluent options builder.
|
||||
|
||||
=== Low-level VertexAiApi Client [[low-level-api]]
|
||||
|
||||
The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-openai/src/main/java/org/springframework/ai/vertex/api/VertexAiApi.java[VertexAiApi] provides is lightweight Java client for VertexAiApi Chat API.
|
||||
|
||||
Following class diagram illustrates the `VertexAiApi` chat interfaces and building blocks:
|
||||
|
||||
image::vertex-ai-chat-low-level-api.jpg[w=800,align="center"]
|
||||
|
||||
Here is a simple snippet how to use the api programmatically:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
VertexAiApi vertexAiApi = new VertexAiApi(< YOUR PALM_API_KEY>);
|
||||
|
||||
// Generate
|
||||
var prompt = new MessagePrompt(List.of(new Message("0", "Hello, how are you?")));
|
||||
|
||||
GenerateMessageRequest request = new GenerateMessageRequest(prompt);
|
||||
|
||||
GenerateMessageResponse response = vertexAiApi.generateMessage(request);
|
||||
|
||||
// Embed text
|
||||
Embedding embedding = vertexAiApi.embedText("Hello, how are you?");
|
||||
|
||||
// Batch embedding
|
||||
List<Embedding> embeddings = vertexAiApi.batchEmbedText(List.of("Hello, how are you?", "I am fine, thank you!"));
|
||||
----
|
||||
|
||||
|
||||
|
||||
@@ -36,24 +36,6 @@ import org.springframework.web.client.RestClient;
|
||||
VertexAiEmbeddingProperties.class })
|
||||
public class VertexAiAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public VertexAiChatClient vertexAiChatClient(VertexAiApi vertexAiApi, VertexAiChatProperties chatProperties) {
|
||||
|
||||
VertexAiChatClient client = new VertexAiChatClient(vertexAiApi).withTemperature(chatProperties.getTemperature())
|
||||
.withTopP(chatProperties.getTopP())
|
||||
.withTopK(chatProperties.getTopK())
|
||||
.withCandidateCount(chatProperties.getCandidateCount());
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public VertexAiEmbeddingClient vertexAiEmbeddingClient(VertexAiApi vertexAiApi) {
|
||||
return new VertexAiEmbeddingClient(vertexAiApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public VertexAiApi vertexAiApi(VertexAiConnectionProperties connectionProperties,
|
||||
@@ -64,4 +46,16 @@ public class VertexAiAutoConfiguration {
|
||||
chatProperties.getModel(), embeddingAiProperties.getModel(), restClientBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public VertexAiChatClient vertexAiChatClient(VertexAiApi vertexAiApi, VertexAiChatProperties chatProperties) {
|
||||
return new VertexAiChatClient(vertexAiApi, chatProperties.getOptions());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public VertexAiEmbeddingClient vertexAiEmbeddingClient(VertexAiApi vertexAiApi) {
|
||||
return new VertexAiEmbeddingClient(vertexAiApi);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.ai.autoconfigure.vertexai;
|
||||
|
||||
import org.springframework.ai.vertex.VertexAiChatOptions;
|
||||
import org.springframework.ai.vertex.api.VertexAiApi;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@@ -24,40 +25,21 @@ public class VertexAiChatProperties {
|
||||
|
||||
public static final String CONFIG_PREFIX = "spring.ai.vertex.ai.chat";
|
||||
|
||||
/**
|
||||
* Controls the randomness of the output. Values can range over [0.0,1.0], inclusive.
|
||||
* A value closer to 1.0 will produce responses that are more varied, while a value
|
||||
* closer to 0.0 will typically result in less surprising responses from the
|
||||
* generative. This value specifies default to be used by the backend while making the
|
||||
* call to the generative.
|
||||
*/
|
||||
private Float temperature = 0.7f;
|
||||
|
||||
/**
|
||||
* The maximum cumulative probability of tokens to consider when sampling. The
|
||||
* generative uses combined Top-k and nucleus sampling. Nucleus sampling considers the
|
||||
* smallest set of tokens whose probability sum is at least topP.
|
||||
*/
|
||||
private Float topP = null;
|
||||
|
||||
/**
|
||||
* The number of generated response messages to return. This value must be between [1,
|
||||
* 8], inclusive. Defaults to 1.
|
||||
*/
|
||||
private Integer candidateCount = 1;
|
||||
|
||||
/**
|
||||
* The maximum number of tokens to consider when sampling. The generative uses
|
||||
* combined Top-k and nucleus sampling. Top-k sampling considers the set of topK most
|
||||
* probable tokens.
|
||||
*/
|
||||
private Integer topK = 20;
|
||||
|
||||
/**
|
||||
* Vertex AI PaLM API generative name. Defaults to chat-bison-001
|
||||
*/
|
||||
private String model = VertexAiApi.DEFAULT_GENERATE_MODEL;
|
||||
|
||||
/**
|
||||
* Vertex AI PaLM API generative options.
|
||||
*/
|
||||
private VertexAiChatOptions options = VertexAiChatOptions.builder()
|
||||
.withTemperature(0.7f)
|
||||
.withTopP(null)
|
||||
.withCandidateCount(1)
|
||||
.withTopK(20)
|
||||
.build();
|
||||
|
||||
public String getModel() {
|
||||
return this.model;
|
||||
}
|
||||
@@ -66,36 +48,12 @@ public class VertexAiChatProperties {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public Float getTemperature() {
|
||||
return this.temperature;
|
||||
public VertexAiChatOptions getOptions() {
|
||||
return this.options;
|
||||
}
|
||||
|
||||
public void setTemperature(Float temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
public Float getTopP() {
|
||||
return this.topP;
|
||||
}
|
||||
|
||||
public void setTopP(Float topP) {
|
||||
this.topP = topP;
|
||||
}
|
||||
|
||||
public Integer getCandidateCount() {
|
||||
return this.candidateCount;
|
||||
}
|
||||
|
||||
public void setCandidateCount(Integer candidateCount) {
|
||||
this.candidateCount = candidateCount;
|
||||
}
|
||||
|
||||
public Integer getTopK() {
|
||||
return this.topK;
|
||||
}
|
||||
|
||||
public void setTopK(Integer topK) {
|
||||
this.topK = topK;
|
||||
public void setOptions(VertexAiChatOptions options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class VertexAiAutoConfigurationIT {
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withPropertyValues("spring.ai.vertex.ai.baseUrl=https://generativelanguage.googleapis.com/v1beta3",
|
||||
"spring.ai.vertex.ai.apiKey=" + System.getenv("PALM_API_KEY"),
|
||||
"spring.ai.vertex.ai.chat.model=chat-bison-001",
|
||||
"spring.ai.vertex.ai.chat.model=chat-bison-001", "spring.ai.vertex.ai.chat.options.temperature=0.8",
|
||||
"spring.ai.vertex.ai.embedding.model=embedding-gecko-001")
|
||||
.withConfiguration(AutoConfigurations.of(RestClientAutoConfiguration.class, VertexAiAutoConfiguration.class));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user