Add observability for the regular imperative call in VertexAi Gemini chat model
- Integrate ObservationRegistry for instrumentation in VertexAiGeminiChatModel's non-streaming call - Add ChatModelObservationContext and related classes for observation - Refactor createGeminiRequest method to support runtime options for imperative calls - Implement observation for non-streaming chat operations in VertexAiChatModelObservationIT - Add VertexAiGeminiConstants class for provider name This commit enhances the VertexAiGeminiChatModel with observability features for the non-streaming, imperative call() method. It allows for better monitoring and instrumentation of synchronous chat operations. The changes include refactoring to improve runtime option handling for these calls and adds integration tests for the new observability features.
This commit is contained in:
committed by
Mark Pollack
parent
9678ea53e8
commit
0763d78fef
@@ -84,6 +84,12 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-observation-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -37,6 +37,10 @@ import org.springframework.ai.chat.model.AbstractToolCallSupport;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.model.Generation;
|
||||
import org.springframework.ai.chat.observation.ChatModelObservationContext;
|
||||
import org.springframework.ai.chat.observation.ChatModelObservationConvention;
|
||||
import org.springframework.ai.chat.observation.ChatModelObservationDocumentation;
|
||||
import org.springframework.ai.chat.observation.DefaultChatModelObservationConvention;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.model.ChatModelDescription;
|
||||
@@ -46,6 +50,7 @@ import org.springframework.ai.model.function.FunctionCallback;
|
||||
import org.springframework.ai.model.function.FunctionCallbackContext;
|
||||
import org.springframework.ai.model.function.FunctionCallingOptions;
|
||||
import org.springframework.ai.retry.RetryUtils;
|
||||
import org.springframework.ai.vertexai.gemini.common.VertexAiGeminiConstants;
|
||||
import org.springframework.ai.vertexai.gemini.metadata.VertexAiUsage;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.lang.NonNull;
|
||||
@@ -53,6 +58,8 @@ import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -68,10 +75,13 @@ import java.util.Set;
|
||||
* @author luocongqiu
|
||||
* @author Chris Turchin
|
||||
* @author Mark Pollack
|
||||
* @author Soby Chacko
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public class VertexAiGeminiChatModel extends AbstractToolCallSupport implements ChatModel, DisposableBean {
|
||||
|
||||
private static final ChatModelObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultChatModelObservationConvention();
|
||||
|
||||
private final VertexAI vertexAI;
|
||||
|
||||
private final VertexAiGeminiChatOptions defaultOptions;
|
||||
@@ -83,6 +93,16 @@ public class VertexAiGeminiChatModel extends AbstractToolCallSupport implements
|
||||
|
||||
private final GenerationConfig generationConfig;
|
||||
|
||||
/**
|
||||
* Observation registry used for instrumentation.
|
||||
*/
|
||||
private final ObservationRegistry observationRegistry;
|
||||
|
||||
/**
|
||||
* Conventions to use for generating observations.
|
||||
*/
|
||||
private final ChatModelObservationConvention observationConvention = DEFAULT_OBSERVATION_CONVENTION;
|
||||
|
||||
public enum GeminiMessageType {
|
||||
|
||||
USER("user"),
|
||||
@@ -153,6 +173,13 @@ public class VertexAiGeminiChatModel extends AbstractToolCallSupport implements
|
||||
public VertexAiGeminiChatModel(VertexAI vertexAI, VertexAiGeminiChatOptions options,
|
||||
FunctionCallbackContext functionCallbackContext, List<FunctionCallback> toolFunctionCallbacks,
|
||||
RetryTemplate retryTemplate) {
|
||||
this(vertexAI, options, functionCallbackContext, toolFunctionCallbacks, retryTemplate,
|
||||
ObservationRegistry.NOOP);
|
||||
}
|
||||
|
||||
public VertexAiGeminiChatModel(VertexAI vertexAI, VertexAiGeminiChatOptions options,
|
||||
FunctionCallbackContext functionCallbackContext, List<FunctionCallback> toolFunctionCallbacks,
|
||||
RetryTemplate retryTemplate, ObservationRegistry observationRegistry) {
|
||||
|
||||
super(functionCallbackContext, options, toolFunctionCallbacks);
|
||||
|
||||
@@ -165,41 +192,60 @@ public class VertexAiGeminiChatModel extends AbstractToolCallSupport implements
|
||||
this.defaultOptions = options;
|
||||
this.generationConfig = toGenerationConfig(options);
|
||||
this.retryTemplate = retryTemplate;
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
// https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini
|
||||
@Override
|
||||
public ChatResponse call(Prompt prompt) {
|
||||
return retryTemplate.execute(context -> {
|
||||
var geminiRequest = createGeminiRequest(prompt);
|
||||
|
||||
GenerateContentResponse response = this.getContentResponse(geminiRequest);
|
||||
VertexAiGeminiChatOptions vertexAiGeminiChatOptions = vertexAiGeminiChatOptions(prompt);
|
||||
|
||||
List<Generation> generations = response.getCandidatesList()
|
||||
.stream()
|
||||
.map(this::responseCandiateToGeneration)
|
||||
.flatMap(List::stream)
|
||||
.toList();
|
||||
ChatModelObservationContext observationContext = ChatModelObservationContext.builder()
|
||||
.prompt(prompt)
|
||||
.provider(VertexAiGeminiConstants.PROVIDER_NAME)
|
||||
.requestOptions(vertexAiGeminiChatOptions)
|
||||
.build();
|
||||
|
||||
ChatResponse chatResponse = new ChatResponse(generations, toChatResponseMetadata(response));
|
||||
ChatResponse response = ChatModelObservationDocumentation.CHAT_MODEL_OPERATION
|
||||
.observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
|
||||
this.observationRegistry)
|
||||
.observe(() -> this.retryTemplate.execute(context -> {
|
||||
|
||||
if (!isProxyToolCalls(prompt, this.defaultOptions)
|
||||
&& isToolCall(chatResponse, Set.of(FinishReason.STOP.name()))) {
|
||||
var toolCallConversation = handleToolCalls(prompt, chatResponse);
|
||||
// Recursively call the call method with the tool call message
|
||||
// conversation that contains the call responses.
|
||||
return this.call(new Prompt(toolCallConversation, prompt.getOptions()));
|
||||
}
|
||||
var geminiRequest = createGeminiRequest(prompt, vertexAiGeminiChatOptions);
|
||||
|
||||
GenerateContentResponse generateContentResponse = this.getContentResponse(geminiRequest);
|
||||
|
||||
List<Generation> generations = generateContentResponse.getCandidatesList()
|
||||
.stream()
|
||||
.map(this::responseCandiateToGeneration)
|
||||
.flatMap(List::stream)
|
||||
.toList();
|
||||
|
||||
ChatResponse chatResponse = new ChatResponse(generations,
|
||||
toChatResponseMetadata(generateContentResponse));
|
||||
|
||||
observationContext.setResponse(chatResponse);
|
||||
return chatResponse;
|
||||
}));
|
||||
|
||||
if (!isProxyToolCalls(prompt, this.defaultOptions) && isToolCall(response, Set.of(FinishReason.STOP.name()))) {
|
||||
var toolCallConversation = handleToolCalls(prompt, response);
|
||||
// Recursively call the call method with the tool call message
|
||||
// conversation that contains the call responses.
|
||||
return this.call(new Prompt(toolCallConversation, prompt.getOptions()));
|
||||
}
|
||||
|
||||
return response;
|
||||
|
||||
return chatResponse;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ChatResponse> stream(Prompt prompt) {
|
||||
try {
|
||||
|
||||
var request = createGeminiRequest(prompt);
|
||||
VertexAiGeminiChatOptions vertexAiGeminiChatOptions = vertexAiGeminiChatOptions(prompt);
|
||||
var request = createGeminiRequest(prompt, vertexAiGeminiChatOptions);
|
||||
|
||||
ResponseStream<GenerateContentResponse> responseStream = request.model
|
||||
.generateContentStream(request.contents);
|
||||
@@ -281,10 +327,26 @@ public class VertexAiGeminiChatModel extends AbstractToolCallSupport implements
|
||||
public record GeminiRequest(List<Content> contents, GenerativeModel model) {
|
||||
}
|
||||
|
||||
private VertexAiGeminiChatOptions vertexAiGeminiChatOptions(Prompt prompt) {
|
||||
VertexAiGeminiChatOptions updatedRuntimeOptions = VertexAiGeminiChatOptions.builder().build();
|
||||
if (prompt.getOptions() != null) {
|
||||
updatedRuntimeOptions = ModelOptionsUtils.copyToTarget(prompt.getOptions(), ChatOptions.class,
|
||||
VertexAiGeminiChatOptions.class);
|
||||
|
||||
}
|
||||
|
||||
updatedRuntimeOptions = ModelOptionsUtils.merge(updatedRuntimeOptions, this.defaultOptions,
|
||||
VertexAiGeminiChatOptions.class);
|
||||
|
||||
return updatedRuntimeOptions;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests access to the {@link #createGeminiRequest(Prompt)} method.
|
||||
* Tests access to the {@link #createGeminiRequest(Prompt, VertexAiGeminiChatOptions)}
|
||||
* method.
|
||||
*/
|
||||
GeminiRequest createGeminiRequest(Prompt prompt) {
|
||||
GeminiRequest createGeminiRequest(Prompt prompt, VertexAiGeminiChatOptions updatedRuntimeOptions) {
|
||||
|
||||
Set<String> functionsForThisRequest = new HashSet<>();
|
||||
|
||||
@@ -293,13 +355,10 @@ public class VertexAiGeminiChatModel extends AbstractToolCallSupport implements
|
||||
var generativeModelBuilder = new GenerativeModel.Builder().setModelName(this.defaultOptions.getModel())
|
||||
.setVertexAi(this.vertexAI);
|
||||
|
||||
VertexAiGeminiChatOptions updatedRuntimeOptions = VertexAiGeminiChatOptions.builder().build();
|
||||
|
||||
if (prompt.getOptions() != null) {
|
||||
if (prompt.getOptions() instanceof FunctionCallingOptions functionCallingOptions) {
|
||||
updatedRuntimeOptions = ModelOptionsUtils.copyToTarget(functionCallingOptions,
|
||||
FunctionCallingOptions.class, VertexAiGeminiChatOptions.class);
|
||||
|
||||
}
|
||||
else {
|
||||
updatedRuntimeOptions = ModelOptionsUtils.copyToTarget(prompt.getOptions(), ChatOptions.class,
|
||||
@@ -312,9 +371,6 @@ public class VertexAiGeminiChatModel extends AbstractToolCallSupport implements
|
||||
functionsForThisRequest.addAll(this.defaultOptions.getFunctions());
|
||||
}
|
||||
|
||||
updatedRuntimeOptions = ModelOptionsUtils.merge(updatedRuntimeOptions, this.defaultOptions,
|
||||
VertexAiGeminiChatOptions.class);
|
||||
|
||||
if (updatedRuntimeOptions != null) {
|
||||
|
||||
if (StringUtils.hasText(updatedRuntimeOptions.getModel())
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.vertexai.gemini.common;
|
||||
|
||||
import org.springframework.ai.observation.conventions.AiProvider;
|
||||
|
||||
/**
|
||||
* @author Soby Chacko
|
||||
*/
|
||||
public class VertexAiGeminiConstants {
|
||||
|
||||
public static final String PROVIDER_NAME = AiProvider.VERTEX_AI.value();
|
||||
|
||||
}
|
||||
@@ -53,7 +53,7 @@ public class CreateGeminiRequestTests {
|
||||
var client = new VertexAiGeminiChatModel(vertexAI,
|
||||
VertexAiGeminiChatOptions.builder().withModel("DEFAULT_MODEL").withTemperature(66.6).build());
|
||||
|
||||
GeminiRequest request = client.createGeminiRequest(new Prompt("Test message content"));
|
||||
GeminiRequest request = client.createGeminiRequest(new Prompt("Test message content"), null);
|
||||
|
||||
assertThat(request.contents()).hasSize(1);
|
||||
|
||||
@@ -61,8 +61,10 @@ public class CreateGeminiRequestTests {
|
||||
assertThat(request.model().getModelName()).isEqualTo("DEFAULT_MODEL");
|
||||
assertThat(request.model().getGenerationConfig().getTemperature()).isEqualTo(66.6f);
|
||||
|
||||
request = client.createGeminiRequest(new Prompt("Test message content",
|
||||
VertexAiGeminiChatOptions.builder().withModel("PROMPT_MODEL").withTemperature(99.9).build()));
|
||||
request = client.createGeminiRequest(
|
||||
new Prompt("Test message content",
|
||||
VertexAiGeminiChatOptions.builder().withModel("PROMPT_MODEL").withTemperature(99.9).build()),
|
||||
null);
|
||||
|
||||
assertThat(request.contents()).hasSize(1);
|
||||
|
||||
@@ -82,7 +84,7 @@ public class CreateGeminiRequestTests {
|
||||
var client = new VertexAiGeminiChatModel(vertexAI,
|
||||
VertexAiGeminiChatOptions.builder().withModel("DEFAULT_MODEL").withTemperature(66.6).build());
|
||||
|
||||
GeminiRequest request = client.createGeminiRequest(new Prompt(List.of(systemMessage, userMessage)));
|
||||
GeminiRequest request = client.createGeminiRequest(new Prompt(List.of(systemMessage, userMessage)), null);
|
||||
|
||||
assertThat(request.model().getModelName()).isEqualTo("DEFAULT_MODEL");
|
||||
assertThat(request.model().getGenerationConfig().getTemperature()).isEqualTo(66.6f);
|
||||
@@ -119,7 +121,8 @@ public class CreateGeminiRequestTests {
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.build()))
|
||||
.build()));
|
||||
.build()),
|
||||
null);
|
||||
|
||||
assertThat(client.getFunctionCallbackRegister()).hasSize(1);
|
||||
assertThat(client.getFunctionCallbackRegister()).containsKeys(TOOL_FUNCTION_NAME);
|
||||
@@ -148,7 +151,7 @@ public class CreateGeminiRequestTests {
|
||||
.build()))
|
||||
.build());
|
||||
|
||||
var request = client.createGeminiRequest(new Prompt("Test message content"));
|
||||
var request = client.createGeminiRequest(new Prompt("Test message content"), null);
|
||||
|
||||
assertThat(client.getFunctionCallbackRegister()).hasSize(1);
|
||||
assertThat(client.getFunctionCallbackRegister()).containsKeys(TOOL_FUNCTION_NAME);
|
||||
@@ -164,7 +167,7 @@ public class CreateGeminiRequestTests {
|
||||
|
||||
// Explicitly enable the function
|
||||
request = client.createGeminiRequest(new Prompt("Test message content",
|
||||
VertexAiGeminiChatOptions.builder().withFunction(TOOL_FUNCTION_NAME).build()));
|
||||
VertexAiGeminiChatOptions.builder().withFunction(TOOL_FUNCTION_NAME).build()), null);
|
||||
|
||||
assertThat(request.model().getTools()).hasSize(1);
|
||||
assertThat(request.model().getTools().get(0).getFunctionDeclarations(0).getName())
|
||||
@@ -178,7 +181,8 @@ public class CreateGeminiRequestTests {
|
||||
.withName(TOOL_FUNCTION_NAME)
|
||||
.withDescription("Overridden function description")
|
||||
.build()))
|
||||
.build()));
|
||||
.build()),
|
||||
null);
|
||||
|
||||
assertThat(request.model().getTools()).hasSize(1);
|
||||
assertThat(request.model().getTools().get(0).getFunctionDeclarations(0).getName())
|
||||
@@ -206,7 +210,7 @@ public class CreateGeminiRequestTests {
|
||||
.withResponseMimeType("application/json")
|
||||
.build());
|
||||
|
||||
GeminiRequest request = client.createGeminiRequest(new Prompt("Test message content"));
|
||||
GeminiRequest request = client.createGeminiRequest(new Prompt("Test message content"), null);
|
||||
|
||||
assertThat(request.contents()).hasSize(1);
|
||||
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 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.vertexai.gemini;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||
|
||||
import org.springframework.ai.chat.metadata.ChatResponseMetadata;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.observation.ChatModelObservationDocumentation;
|
||||
import org.springframework.ai.chat.observation.DefaultChatModelObservationConvention;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.observation.conventions.AiOperationType;
|
||||
import org.springframework.ai.observation.conventions.AiProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
|
||||
import com.google.cloud.vertexai.Transport;
|
||||
import com.google.cloud.vertexai.VertexAI;
|
||||
import io.micrometer.observation.tck.TestObservationRegistry;
|
||||
import io.micrometer.observation.tck.TestObservationRegistryAssert;
|
||||
|
||||
/**
|
||||
* @author Soby Chacko
|
||||
*/
|
||||
@SpringBootTest
|
||||
@EnabledIfEnvironmentVariable(named = "VERTEX_AI_GEMINI_PROJECT_ID", matches = ".*")
|
||||
@EnabledIfEnvironmentVariable(named = "VERTEX_AI_GEMINI_LOCATION", matches = ".*")
|
||||
public class VertexAiChatModelObservationIT {
|
||||
|
||||
@Autowired
|
||||
TestObservationRegistry observationRegistry;
|
||||
|
||||
@Autowired
|
||||
VertexAiGeminiChatModel chatModel;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
observationRegistry.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
void observationForChatOperation() {
|
||||
|
||||
var options = VertexAiGeminiChatOptions.builder()
|
||||
.withModel(VertexAiGeminiChatModel.ChatModel.GEMINI_1_5_PRO.getValue())
|
||||
.withTemperature(0.7)
|
||||
.withStopSequences(List.of("this-is-the-end"))
|
||||
.withMaxOutputTokens(2048)
|
||||
.withTopP(1.0)
|
||||
.build();
|
||||
|
||||
Prompt prompt = new Prompt("Why does a raven look like a desk?", options);
|
||||
|
||||
ChatResponse chatResponse = chatModel.call(prompt);
|
||||
assertThat(chatResponse.getResult().getOutput().getContent()).isNotEmpty();
|
||||
|
||||
ChatResponseMetadata responseMetadata = chatResponse.getMetadata();
|
||||
assertThat(responseMetadata).isNotNull();
|
||||
|
||||
validate(responseMetadata);
|
||||
}
|
||||
|
||||
private void validate(ChatResponseMetadata responseMetadata) {
|
||||
TestObservationRegistryAssert.assertThat(observationRegistry)
|
||||
.doesNotHaveAnyRemainingCurrentObservation()
|
||||
.hasObservationWithNameEqualTo(DefaultChatModelObservationConvention.DEFAULT_NAME)
|
||||
.that()
|
||||
.hasLowCardinalityKeyValue(
|
||||
ChatModelObservationDocumentation.LowCardinalityKeyNames.AI_OPERATION_TYPE.asString(),
|
||||
AiOperationType.CHAT.value())
|
||||
.hasLowCardinalityKeyValue(ChatModelObservationDocumentation.LowCardinalityKeyNames.AI_PROVIDER.asString(),
|
||||
AiProvider.VERTEX_AI.value())
|
||||
.hasLowCardinalityKeyValue(
|
||||
ChatModelObservationDocumentation.LowCardinalityKeyNames.REQUEST_MODEL.asString(),
|
||||
VertexAiGeminiChatModel.ChatModel.GEMINI_1_5_PRO.getValue())
|
||||
.hasHighCardinalityKeyValue(
|
||||
ChatModelObservationDocumentation.HighCardinalityKeyNames.REQUEST_MAX_TOKENS.asString(), "2048")
|
||||
.hasHighCardinalityKeyValue(
|
||||
ChatModelObservationDocumentation.HighCardinalityKeyNames.REQUEST_STOP_SEQUENCES.asString(),
|
||||
"[\"this-is-the-end\"]")
|
||||
.hasHighCardinalityKeyValue(
|
||||
ChatModelObservationDocumentation.HighCardinalityKeyNames.REQUEST_TEMPERATURE.asString(), "0.7")
|
||||
.doesNotHaveHighCardinalityKeyValueWithKey(
|
||||
ChatModelObservationDocumentation.HighCardinalityKeyNames.REQUEST_TOP_K.asString())
|
||||
.hasHighCardinalityKeyValue(
|
||||
ChatModelObservationDocumentation.HighCardinalityKeyNames.REQUEST_TOP_P.asString(), "1.0")
|
||||
.hasHighCardinalityKeyValue(
|
||||
ChatModelObservationDocumentation.HighCardinalityKeyNames.RESPONSE_FINISH_REASONS.asString(),
|
||||
"[\"STOP\"]")
|
||||
.hasHighCardinalityKeyValue(
|
||||
ChatModelObservationDocumentation.HighCardinalityKeyNames.USAGE_INPUT_TOKENS.asString(),
|
||||
String.valueOf(responseMetadata.getUsage().getPromptTokens()))
|
||||
.hasHighCardinalityKeyValue(
|
||||
ChatModelObservationDocumentation.HighCardinalityKeyNames.USAGE_OUTPUT_TOKENS.asString(),
|
||||
String.valueOf(responseMetadata.getUsage().getGenerationTokens()))
|
||||
.hasHighCardinalityKeyValue(
|
||||
ChatModelObservationDocumentation.HighCardinalityKeyNames.USAGE_TOTAL_TOKENS.asString(),
|
||||
String.valueOf(responseMetadata.getUsage().getTotalTokens()))
|
||||
.hasBeenStarted()
|
||||
.hasBeenStopped();
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public TestObservationRegistry observationRegistry() {
|
||||
return TestObservationRegistry.create();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VertexAI vertexAiApi() {
|
||||
String projectId = System.getenv("VERTEX_AI_GEMINI_PROJECT_ID");
|
||||
String location = System.getenv("VERTEX_AI_GEMINI_LOCATION");
|
||||
return new VertexAI.Builder().setProjectId(projectId)
|
||||
.setLocation(location)
|
||||
.setTransport(Transport.REST)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VertexAiGeminiChatModel vertexAiEmbedding(VertexAI vertexAi,
|
||||
TestObservationRegistry observationRegistry) {
|
||||
return new VertexAiGeminiChatModel(vertexAi,
|
||||
VertexAiGeminiChatOptions.builder()
|
||||
.withModel(VertexAiGeminiChatModel.ChatModel.GEMINI_1_5_PRO)
|
||||
.build(),
|
||||
null, List.of(), RetryTemplate.defaultInstance(), observationRegistry);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user