diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java index 63458b559d3..22e22ced28d 100644 --- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java +++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java @@ -28,6 +28,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; +import org.springframework.ai.chat.metadata.Usage; +import org.springframework.ai.embedding.Embedding; +import org.springframework.ai.model.security.ApiKey; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -75,13 +78,15 @@ public class OpenAiApi { private final WebClient webClient; + private final ApiKey apiKey; + private OpenAiStreamFunctionCallingHelper chunkMerger = new OpenAiStreamFunctionCallingHelper(); /** * Create a new chat completion api with base URL set to https://api.openai.com * @param apiKey OpenAI apiKey. */ - public OpenAiApi(String apiKey) { + public OpenAiApi(ApiKey apiKey) { this(OpenAiApiConstants.DEFAULT_BASE_URL, apiKey); } @@ -90,7 +95,7 @@ public OpenAiApi(String apiKey) { * @param baseUrl api base URL. * @param apiKey OpenAI apiKey. */ - public OpenAiApi(String baseUrl, String apiKey) { + public OpenAiApi(String baseUrl, ApiKey apiKey) { this(baseUrl, apiKey, RestClient.builder(), WebClient.builder()); } @@ -101,7 +106,7 @@ public OpenAiApi(String baseUrl, String apiKey) { * @param restClientBuilder RestClient builder. * @param webClientBuilder WebClient builder. */ - public OpenAiApi(String baseUrl, String apiKey, RestClient.Builder restClientBuilder, + public OpenAiApi(String baseUrl, ApiKey apiKey, RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder) { this(baseUrl, apiKey, restClientBuilder, webClientBuilder, RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER); } @@ -114,7 +119,7 @@ public OpenAiApi(String baseUrl, String apiKey, RestClient.Builder restClientBui * @param webClientBuilder WebClient builder. * @param responseErrorHandler Response error handler. */ - public OpenAiApi(String baseUrl, String apiKey, RestClient.Builder restClientBuilder, + public OpenAiApi(String baseUrl, ApiKey apiKey, RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder, ResponseErrorHandler responseErrorHandler) { this(baseUrl, apiKey, "/v1/chat/completions", "/v1/embeddings", restClientBuilder, webClientBuilder, responseErrorHandler); @@ -130,7 +135,7 @@ public OpenAiApi(String baseUrl, String apiKey, RestClient.Builder restClientBui * @param webClientBuilder WebClient builder. * @param responseErrorHandler Response error handler. */ - public OpenAiApi(String baseUrl, String apiKey, String completionsPath, String embeddingsPath, + public OpenAiApi(String baseUrl, ApiKey apiKey, String completionsPath, String embeddingsPath, RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder, ResponseErrorHandler responseErrorHandler) { @@ -149,7 +154,7 @@ public OpenAiApi(String baseUrl, String apiKey, String completionsPath, String e * @param webClientBuilder WebClient builder. * @param responseErrorHandler Response error handler. */ - public OpenAiApi(String baseUrl, String apiKey, MultiValueMap headers, String completionsPath, + public OpenAiApi(String baseUrl, ApiKey apiKey, MultiValueMap headers, String completionsPath, String embeddingsPath, RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder, ResponseErrorHandler responseErrorHandler) { @@ -157,11 +162,11 @@ public OpenAiApi(String baseUrl, String apiKey, MultiValueMap he Assert.hasText(embeddingsPath, "Embeddings Path must not be null"); Assert.notNull(headers, "Headers must not be null"); + this.apiKey = apiKey; this.completionsPath = completionsPath; this.embeddingsPath = embeddingsPath; // @formatter:off Consumer finalHeaders = h -> { - h.setBearerAuth(apiKey); h.setContentType(MediaType.APPLICATION_JSON); h.addAll(headers); }; @@ -208,12 +213,12 @@ public ResponseEntity chatCompletionEntity(ChatCompletionRequest Assert.isTrue(!chatRequest.stream(), "Request must set the stream property to false."); Assert.notNull(additionalHttpHeader, "The additional HTTP headers can not be null."); - return this.restClient.post() - .uri(this.completionsPath) - .headers(headers -> headers.addAll(additionalHttpHeader)) - .body(chatRequest) - .retrieve() - .toEntity(ChatCompletion.class); + return this.restClient.post().uri(this.completionsPath).headers(headers -> { + headers.addAll(additionalHttpHeader); + if (!additionalHttpHeader.containsKey(HttpHeaders.AUTHORIZATION)) { + headers.setBearerAuth(apiKey.getValue()); + } + }).body(chatRequest).retrieve().toEntity(ChatCompletion.class); } /** @@ -242,9 +247,12 @@ public Flux chatCompletionStream(ChatCompletionRequest chat AtomicBoolean isInsideTool = new AtomicBoolean(false); - return this.webClient.post() - .uri(this.completionsPath) - .headers(headers -> headers.addAll(additionalHttpHeader)) + return this.webClient.post().uri(this.completionsPath).headers(headers -> { + headers.addAll(additionalHttpHeader); + if (!additionalHttpHeader.containsKey(HttpHeaders.AUTHORIZATION)) { + headers.setBearerAuth(apiKey.getValue()); + } + }) .body(Mono.just(chatRequest), ChatCompletionRequest.class) .retrieve() .bodyToFlux(String.class) @@ -318,6 +326,7 @@ public ResponseEntity> embeddings(EmbeddingRequest< return this.restClient.post() .uri(this.embeddingsPath) + .headers(headers -> headers.setBearerAuth(apiKey.getValue())) .body(embeddingRequest) .retrieve() .toEntity(new ParameterizedTypeReference<>() { diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/ChatCompletionRequestTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/ChatCompletionRequestTests.java index d948080cbae..aa9f32c1d44 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/ChatCompletionRequestTests.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/ChatCompletionRequestTests.java @@ -22,6 +22,7 @@ import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.model.function.FunctionCallbackWrapper; +import org.springframework.ai.model.security.StaticApiKey; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.openai.api.tool.MockWeatherService; @@ -35,7 +36,7 @@ public class ChatCompletionRequestTests { @Test public void createRequestWithChatOptions() { - var client = new OpenAiChatModel(new OpenAiApi("TEST"), + var client = new OpenAiChatModel(new OpenAiApi(new StaticApiKey("TEST")), OpenAiChatOptions.builder().withModel("DEFAULT_MODEL").withTemperature(66.6).build()); var request = client.createRequest(new Prompt("Test message content"), false); @@ -61,7 +62,7 @@ public void promptOptionsTools() { final String TOOL_FUNCTION_NAME = "CurrentWeather"; - var client = new OpenAiChatModel(new OpenAiApi("TEST"), + var client = new OpenAiChatModel(new OpenAiApi(new StaticApiKey("TEST")), OpenAiChatOptions.builder().withModel("DEFAULT_MODEL").build()); var request = client.createRequest(new Prompt("Test message content", @@ -91,7 +92,7 @@ public void defaultOptionsTools() { final String TOOL_FUNCTION_NAME = "CurrentWeather"; - var client = new OpenAiChatModel(new OpenAiApi("TEST"), + var client = new OpenAiChatModel(new OpenAiApi(new StaticApiKey("TEST")), OpenAiChatOptions.builder() .withModel("DEFAULT_MODEL") .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService()) diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/OpenAiTestConfiguration.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/OpenAiTestConfiguration.java index d9e6b6ca513..7f9ba5ced34 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/OpenAiTestConfiguration.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/OpenAiTestConfiguration.java @@ -16,6 +16,7 @@ package org.springframework.ai.openai; +import org.springframework.ai.model.security.StaticApiKey; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.openai.api.OpenAiApi.ChatModel; import org.springframework.ai.openai.api.OpenAiAudioApi; @@ -30,7 +31,7 @@ public class OpenAiTestConfiguration { @Bean public OpenAiApi openAiApi() { - return new OpenAiApi(getApiKey()); + return new OpenAiApi(new StaticApiKey(getApiKey())); } @Bean diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiApiIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiApiIT.java index a07d400e1d4..94ace617499 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiApiIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/OpenAiApiIT.java @@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.model.security.StaticApiKey; import reactor.core.publisher.Flux; import org.springframework.ai.openai.api.OpenAiApi.ChatCompletion; @@ -39,7 +40,7 @@ @EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+") public class OpenAiApiIT { - OpenAiApi openAiApi = new OpenAiApi(System.getenv("OPENAI_API_KEY")); + OpenAiApi openAiApi = new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY"))); @Test void chatCompletionEntity() { diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/tool/OpenAiApiToolFunctionCallIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/tool/OpenAiApiToolFunctionCallIT.java index a7b1cb48aa9..fd43c73f1af 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/tool/OpenAiApiToolFunctionCallIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/api/tool/OpenAiApiToolFunctionCallIT.java @@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory; import org.springframework.ai.model.ModelOptionsUtils; +import org.springframework.ai.model.security.StaticApiKey; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.openai.api.OpenAiApi.ChatCompletion; import org.springframework.ai.openai.api.OpenAiApi.ChatCompletionMessage; @@ -51,7 +52,7 @@ public class OpenAiApiToolFunctionCallIT { MockWeatherService weatherService = new MockWeatherService(); - OpenAiApi completionApi = new OpenAiApi(System.getenv("OPENAI_API_KEY")); + OpenAiApi completionApi = new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY"))); private static T fromJson(String json, Class targetClass) { try { diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModeAdditionalHttpHeadersIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModeAdditionalHttpHeadersIT.java index 21c5b7e81b5..6fd223a4e37 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModeAdditionalHttpHeadersIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModeAdditionalHttpHeadersIT.java @@ -23,6 +23,7 @@ import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.model.security.StaticApiKey; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.openai.api.OpenAiApi; @@ -67,7 +68,7 @@ static class Config { @Bean public OpenAiApi chatCompletionApi() { - return new OpenAiApi("Invalid API Key"); + return new OpenAiApi(new StaticApiKey("Invalid API Key")); } @Bean diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelFunctionCallingIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelFunctionCallingIT.java index 281f86e4688..d94e8c77921 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelFunctionCallingIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelFunctionCallingIT.java @@ -26,6 +26,7 @@ import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ai.model.security.StaticApiKey; import reactor.core.publisher.Flux; import org.springframework.ai.chat.messages.AssistantMessage; @@ -197,7 +198,7 @@ static class Config { @Bean public OpenAiApi chatCompletionApi() { - return new OpenAiApi(System.getenv("OPENAI_API_KEY")); + return new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY"))); } @Bean diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelObservationIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelObservationIT.java index 03bbc1c7e7c..009a5f77dcd 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelObservationIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelObservationIT.java @@ -24,6 +24,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.model.security.StaticApiKey; import reactor.core.publisher.Flux; import org.springframework.ai.chat.metadata.ChatResponseMetadata; @@ -169,7 +170,7 @@ public TestObservationRegistry observationRegistry() { @Bean public OpenAiApi openAiApi() { - return new OpenAiApi(System.getenv("OPENAI_API_KEY")); + return new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY"))); } @Bean diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelProxyToolCallsIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelProxyToolCallsIT.java index b6e182559ce..a590a345ed0 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelProxyToolCallsIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelProxyToolCallsIT.java @@ -32,6 +32,7 @@ import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ai.model.security.StaticApiKey; import reactor.core.publisher.Flux; import org.springframework.ai.chat.messages.AssistantMessage; @@ -355,7 +356,7 @@ static class Config { @Bean public OpenAiApi chatCompletionApi() { - return new OpenAiApi(System.getenv("OPENAI_API_KEY")); + return new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY"))); } @Bean diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelResponseFormatIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelResponseFormatIT.java index bb0db5ce3a2..d43896ada3c 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelResponseFormatIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelResponseFormatIT.java @@ -31,6 +31,7 @@ import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.converter.BeanOutputConverter; +import org.springframework.ai.model.security.StaticApiKey; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.openai.api.OpenAiApi; @@ -234,7 +235,7 @@ static class Config { @Bean public OpenAiApi chatCompletionApi() { - return new OpenAiApi(System.getenv("OPENAI_API_KEY")); + return new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY"))); } @Bean diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelWithChatResponseMetadataTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelWithChatResponseMetadataTests.java index ae598fcaad1..c570bd83649 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelWithChatResponseMetadataTests.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelWithChatResponseMetadataTests.java @@ -28,6 +28,8 @@ import org.springframework.ai.chat.metadata.Usage; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.model.security.ApiKey; +import org.springframework.ai.model.security.StaticApiKey; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.openai.metadata.support.OpenAiApiResponseHeaders; @@ -56,7 +58,7 @@ @RestClientTest(OpenAiChatModelWithChatResponseMetadataTests.Config.class) public class OpenAiChatModelWithChatResponseMetadataTests { - private static String TEST_API_KEY = "sk-1234567890"; + private static ApiKey TEST_API_KEY = new StaticApiKey("sk-1234567890"); @Autowired private OpenAiChatModel openAiChatClient; @@ -135,7 +137,7 @@ private void prepareMock() { this.server.expect(requestTo("/v1/chat/completions")) .andExpect(method(HttpMethod.POST)) - .andExpect(header(HttpHeaders.AUTHORIZATION, "Bearer " + TEST_API_KEY)) + .andExpect(header(HttpHeaders.AUTHORIZATION, "Bearer " + TEST_API_KEY.getValue())) .andRespond(withSuccess(getJson(), MediaType.APPLICATION_JSON).headers(httpHeaders)); } diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiCompatibleChatModelIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiCompatibleChatModelIT.java index 997fb02c3a7..3419da2f8b6 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiCompatibleChatModelIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiCompatibleChatModelIT.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.ai.model.security.StaticApiKey; import reactor.core.publisher.Flux; import org.springframework.ai.chat.messages.AssistantMessage; @@ -55,16 +56,18 @@ static OpenAiChatOptions forModelName(String modelName) { static Stream openAiCompatibleApis() { Stream.Builder builder = Stream.builder(); - builder.add(new OpenAiChatModel(new OpenAiApi(System.getenv("OPENAI_API_KEY")), forModelName("gpt-3.5-turbo"))); + builder.add(new OpenAiChatModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY"))), + forModelName("gpt-3.5-turbo"))); if (System.getenv("GROQ_API_KEY") != null) { - builder.add(new OpenAiChatModel(new OpenAiApi("https://api.groq.com/openai", System.getenv("GROQ_API_KEY")), + builder.add(new OpenAiChatModel( + new OpenAiApi("https://api.groq.com/openai", new StaticApiKey(System.getenv("GROQ_API_KEY"))), forModelName("llama3-8b-8192"))); } if (System.getenv("OPEN_ROUTER_API_KEY") != null) { builder.add(new OpenAiChatModel( - new OpenAiApi("https://openrouter.ai/api", System.getenv("OPEN_ROUTER_API_KEY")), + new OpenAiApi("https://openrouter.ai/api", new StaticApiKey(System.getenv("OPEN_ROUTER_API_KEY"))), forModelName("meta-llama/llama-3-8b-instruct"))); } diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiPaymentTransactionIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiPaymentTransactionIT.java index 98a2892d182..05aafeb2c8d 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiPaymentTransactionIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiPaymentTransactionIT.java @@ -26,6 +26,7 @@ import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ai.model.security.StaticApiKey; import reactor.core.publisher.Flux; import org.springframework.ai.chat.client.ChatClient; @@ -216,7 +217,7 @@ public ChatClient chatClient(OpenAiChatModel chatModel) { @Bean public OpenAiApi chatCompletionApi() { - return new OpenAiApi(System.getenv("OPENAI_API_KEY")); + return new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY"))); } @Bean diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/GroqWithOpenAiChatModelIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/GroqWithOpenAiChatModelIT.java index a3d45486b22..0e98cd7d3e9 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/GroqWithOpenAiChatModelIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/GroqWithOpenAiChatModelIT.java @@ -31,6 +31,7 @@ import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ai.model.security.StaticApiKey; import reactor.core.publisher.Flux; import org.springframework.ai.chat.client.ChatClient; @@ -380,7 +381,7 @@ static class Config { @Bean public OpenAiApi chatCompletionApi() { - return new OpenAiApi(GROQ_BASE_URL, System.getenv("GROQ_API_KEY")); + return new OpenAiApi(GROQ_BASE_URL, new StaticApiKey(System.getenv("GROQ_API_KEY"))); } @Bean diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/MistralWithOpenAiChatModelIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/MistralWithOpenAiChatModelIT.java index 80ec34b995b..e1d654e7dbb 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/MistralWithOpenAiChatModelIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/MistralWithOpenAiChatModelIT.java @@ -31,6 +31,8 @@ import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ai.model.security.ApiKey; +import org.springframework.ai.model.security.StaticApiKey; import reactor.core.publisher.Flux; import org.springframework.ai.chat.client.ChatClient; @@ -385,7 +387,7 @@ static class Config { @Bean public OpenAiApi chatCompletionApi() { - return new OpenAiApi(MISTRAL_BASE_URL, System.getenv("MISTRAL_AI_API_KEY")); + return new OpenAiApi(MISTRAL_BASE_URL, new StaticApiKey(System.getenv("MISTRAL_AI_API_KEY"))); } @Bean diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/NvidiaWithOpenAiChatModelIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/NvidiaWithOpenAiChatModelIT.java index 4bbe60e6889..a132dab1bdc 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/NvidiaWithOpenAiChatModelIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/NvidiaWithOpenAiChatModelIT.java @@ -27,6 +27,7 @@ import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ai.model.security.StaticApiKey; import reactor.core.publisher.Flux; import org.springframework.ai.chat.client.ChatClient; @@ -318,7 +319,7 @@ static class Config { @Bean public OpenAiApi chatCompletionApi() { - return new OpenAiApi(NVIDIA_BASE_URL, System.getenv("NVIDIA_API_KEY")); + return new OpenAiApi(NVIDIA_BASE_URL, new StaticApiKey(System.getenv("NVIDIA_API_KEY"))); } @Bean diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/OllamaWithOpenAiChatModelIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/OllamaWithOpenAiChatModelIT.java index 81df8ac03e3..d7e11ea61e4 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/OllamaWithOpenAiChatModelIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/OllamaWithOpenAiChatModelIT.java @@ -31,6 +31,7 @@ import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.ollama.OllamaContainer; @@ -401,7 +402,8 @@ static class Config { @Bean public OpenAiApi chatCompletionApi() { - return new OpenAiApi(baseUrl, ""); + return new OpenAiApi(baseUrl, new StaticApiKey("")); + } @Bean diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/embedding/OpenAiEmbeddingModelObservationIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/embedding/OpenAiEmbeddingModelObservationIT.java index f5f0b046288..c3b67111299 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/embedding/OpenAiEmbeddingModelObservationIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/embedding/OpenAiEmbeddingModelObservationIT.java @@ -28,6 +28,7 @@ import org.springframework.ai.embedding.EmbeddingResponse; import org.springframework.ai.embedding.EmbeddingResponseMetadata; import org.springframework.ai.embedding.observation.DefaultEmbeddingModelObservationConvention; +import org.springframework.ai.model.security.StaticApiKey; import org.springframework.ai.observation.conventions.AiOperationType; import org.springframework.ai.observation.conventions.AiProvider; import org.springframework.ai.openai.OpenAiEmbeddingModel; @@ -104,7 +105,7 @@ public TestObservationRegistry observationRegistry() { @Bean public OpenAiApi openAiApi() { - return new OpenAiApi(System.getenv("OPENAI_API_KEY")); + return new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY"))); } @Bean diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/transformer/MetadataTransformerIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/transformer/MetadataTransformerIT.java index 66a8d3d8eda..496c6a9fb25 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/transformer/MetadataTransformerIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/transformer/MetadataTransformerIT.java @@ -26,6 +26,7 @@ import org.springframework.ai.document.DefaultContentFormatter; import org.springframework.ai.document.Document; +import org.springframework.ai.model.security.StaticApiKey; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.transformer.ContentFormatTransformer; @@ -163,7 +164,7 @@ public OpenAiApi openAiApi() throws IOException { throw new IllegalArgumentException( "You must provide an API key. Put it in an environment variable under the name OPENAI_API_KEY"); } - return new OpenAiApi(apiKey); + return new OpenAiApi(new StaticApiKey(apiKey)); } @Bean diff --git a/spring-ai-core/src/main/java/org/springframework/ai/model/security/ApiKey.java b/spring-ai-core/src/main/java/org/springframework/ai/model/security/ApiKey.java new file mode 100644 index 00000000000..2cae822c06d --- /dev/null +++ b/spring-ai-core/src/main/java/org/springframework/ai/model/security/ApiKey.java @@ -0,0 +1,40 @@ +/* + * Copyright 2023-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.model.security; + +/** + * Some model providers API leverage short-lived api keys which must be renewed at regular + * intervals using another credential. For example, a GCP service account can be exchanged + * for an api key to call Vertex AI. + * + * Model clients use the ApiKey interface to get an api key before they make any request + * to the model provider. Implementations of this interface can cache the api key and + * perform a key refresh when it is required. + * + * @author Adib Saikali + */ +public interface ApiKey { + + /** + * Returns an api key to use for a making request. Users of this method should NOT + * cache the returned api key, instead call this method whenever you need an api key. + * Implementors of this method MUST ensure that the returned key is not expired. + * @return the current value of the api key + */ + String getValue(); + +} diff --git a/spring-ai-core/src/main/java/org/springframework/ai/model/security/StaticApiKey.java b/spring-ai-core/src/main/java/org/springframework/ai/model/security/StaticApiKey.java new file mode 100644 index 00000000000..f3bf8cd5549 --- /dev/null +++ b/spring-ai-core/src/main/java/org/springframework/ai/model/security/StaticApiKey.java @@ -0,0 +1,38 @@ +/* + * Copyright 2023-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.model.security; + +/** + * The StaticApiKey class implements the ApiKey interface, representing a static, + * non-expiring API key. + * + * @author Adib Saikali + */ +public class StaticApiKey implements ApiKey { + + private final String value; + + public StaticApiKey(String value) { + this.value = value; + } + + @Override + public String getValue() { + return this.value; + } + +} diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfiguration.java index 3436ca32696..e75b29728a9 100644 --- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfiguration.java +++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/openai/OpenAiAutoConfiguration.java @@ -29,6 +29,7 @@ import org.springframework.ai.image.observation.ImageModelObservationConvention; import org.springframework.ai.model.function.FunctionCallback; import org.springframework.ai.model.function.FunctionCallbackContext; +import org.springframework.ai.model.security.StaticApiKey; import org.springframework.ai.openai.OpenAiAudioSpeechModel; import org.springframework.ai.openai.OpenAiAudioTranscriptionModel; import org.springframework.ai.openai.OpenAiChatModel; @@ -157,7 +158,7 @@ private OpenAiApi openAiApi(OpenAiChatProperties chatProperties, OpenAiConnectio ResolvedConnectionProperties resolved = resolveConnectionProperties(commonProperties, chatProperties, modelType); - return new OpenAiApi(resolved.baseUrl(), resolved.apiKey(), resolved.headers(), + return new OpenAiApi(resolved.baseUrl(), new StaticApiKey(resolved.apiKey()), resolved.headers(), chatProperties.getCompletionsPath(), OpenAiEmbeddingProperties.DEFAULT_EMBEDDINGS_PATH, restClientBuilder, webClientBuilder, responseErrorHandler); } @@ -169,7 +170,7 @@ private OpenAiApi openAiApi(OpenAiEmbeddingProperties embeddingProperties, ResolvedConnectionProperties resolved = resolveConnectionProperties(commonProperties, embeddingProperties, modelType); - return new OpenAiApi(resolved.baseUrl(), resolved.apiKey(), resolved.headers(), + return new OpenAiApi(resolved.baseUrl(), new StaticApiKey(resolved.apiKey()), resolved.headers(), OpenAiChatProperties.DEFAULT_COMPLETIONS_PATH, embeddingProperties.getEmbeddingsPath(), restClientBuilder, webClientBuilder, responseErrorHandler); } diff --git a/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactoryIT.java b/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactoryIT.java index a6ce317ed67..ace7e4943f6 100644 --- a/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactoryIT.java +++ b/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/mongo/MongoDbAtlasLocalContainerConnectionDetailsFactoryIT.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.mongodb.MongoDBAtlasLocalContainer; @@ -98,7 +99,7 @@ static class Config { @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } } diff --git a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/BasicAuthChromaWhereIT.java b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/BasicAuthChromaWhereIT.java index d9cbc560d9f..61b0bce5b02 100644 --- a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/BasicAuthChromaWhereIT.java +++ b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/BasicAuthChromaWhereIT.java @@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.chromadb.ChromaDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -115,7 +116,7 @@ public VectorStore chromaVectorStore(EmbeddingModel embeddingModel, ChromaApi ch @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } } diff --git a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreIT.java b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreIT.java index 966eb06baf2..e7446b820e2 100644 --- a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreIT.java +++ b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreIT.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.chromadb.ChromaDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -224,7 +225,7 @@ public VectorStore chromaVectorStore(EmbeddingModel embeddingModel, ChromaApi ch @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } } diff --git a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreObservationIT.java b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreObservationIT.java index cd9e973b543..39ae29d2730 100644 --- a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreObservationIT.java @@ -26,6 +26,7 @@ import io.micrometer.observation.tck.TestObservationRegistryAssert; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.chromadb.ChromaDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -182,7 +183,7 @@ public VectorStore chromaVectorStore(EmbeddingModel embeddingModel, ChromaApi ch @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } } diff --git a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/TokenSecuredChromaWhereIT.java b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/TokenSecuredChromaWhereIT.java index 2aea2f6a3b6..632eb7e902a 100644 --- a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/TokenSecuredChromaWhereIT.java +++ b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/TokenSecuredChromaWhereIT.java @@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.chromadb.ChromaDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -148,7 +149,7 @@ public VectorStore chromaVectorStore(EmbeddingModel embeddingModel, ChromaApi ch @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } } diff --git a/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/ElasticsearchVectorStoreIT.java b/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/ElasticsearchVectorStoreIT.java index c262c9a4af8..774ccc3bbbc 100644 --- a/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/ElasticsearchVectorStoreIT.java +++ b/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/ElasticsearchVectorStoreIT.java @@ -42,6 +42,7 @@ import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.elasticsearch.ElasticsearchContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -395,7 +396,7 @@ public ElasticsearchVectorStore vectorStoreDotProduct(EmbeddingModel embeddingMo @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } @Bean diff --git a/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/ElasticsearchVectorStoreObservationIT.java b/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/ElasticsearchVectorStoreObservationIT.java index 1d074fba087..98967c38624 100644 --- a/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/ElasticsearchVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/ElasticsearchVectorStoreObservationIT.java @@ -39,6 +39,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.elasticsearch.ElasticsearchContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -211,7 +212,7 @@ public ElasticsearchVectorStore vectorStoreDefault(EmbeddingModel embeddingModel @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } @Bean diff --git a/vector-stores/spring-ai-hanadb-store/src/test/java/org/springframework/ai/vectorstore/HanaCloudVectorStoreIT.java b/vector-stores/spring-ai-hanadb-store/src/test/java/org/springframework/ai/vectorstore/HanaCloudVectorStoreIT.java index c55f52ceca5..e607d726aff 100644 --- a/vector-stores/spring-ai-hanadb-store/src/test/java/org/springframework/ai/vectorstore/HanaCloudVectorStoreIT.java +++ b/vector-stores/spring-ai-hanadb-store/src/test/java/org/springframework/ai/vectorstore/HanaCloudVectorStoreIT.java @@ -30,6 +30,7 @@ import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingModel; +import org.springframework.ai.model.security.StaticApiKey; import org.springframework.ai.openai.OpenAiEmbeddingModel; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.reader.pdf.PagePdfDocumentReader; @@ -124,7 +125,7 @@ public LocalContainerEntityManagerFactoryBean entityManagerFactory() { @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } } diff --git a/vector-stores/spring-ai-hanadb-store/src/test/java/org/springframework/ai/vectorstore/HanaVectorStoreObservationIT.java b/vector-stores/spring-ai-hanadb-store/src/test/java/org/springframework/ai/vectorstore/HanaVectorStoreObservationIT.java index c61fb2aaf4e..382bc5bb8fb 100644 --- a/vector-stores/spring-ai-hanadb-store/src/test/java/org/springframework/ai/vectorstore/HanaVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-hanadb-store/src/test/java/org/springframework/ai/vectorstore/HanaVectorStoreObservationIT.java @@ -31,6 +31,7 @@ import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingModel; +import org.springframework.ai.model.security.StaticApiKey; import org.springframework.ai.observation.conventions.SpringAiKind; import org.springframework.ai.observation.conventions.VectorStoreProvider; import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric; @@ -200,7 +201,7 @@ public LocalContainerEntityManagerFactoryBean entityManagerFactory() { @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } } diff --git a/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/MilvusVectorStoreIT.java b/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/MilvusVectorStoreIT.java index 98a88e7b7d1..29b34a87068 100644 --- a/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/MilvusVectorStoreIT.java +++ b/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/MilvusVectorStoreIT.java @@ -30,6 +30,7 @@ import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.milvus.MilvusContainer; @@ -287,7 +288,7 @@ public MilvusServiceClient milvusClient() { @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); // return new OpenAiEmbeddingModel(new // OpenAiApi(System.getenv("OPENAI_API_KEY")), MetadataMode.EMBED, // OpenAiEmbeddingOptions.builder().withModel("text-embedding-ada-002").build()); diff --git a/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/MilvusVectorStoreObservationIT.java b/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/MilvusVectorStoreObservationIT.java index 7303e4d3cda..ec882d319d7 100644 --- a/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/MilvusVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/MilvusVectorStoreObservationIT.java @@ -30,6 +30,7 @@ import io.milvus.param.MetricType; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.milvus.MilvusContainer; @@ -188,7 +189,7 @@ public MilvusServiceClient milvusClient() { @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } } diff --git a/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStoreIT.java b/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStoreIT.java index d908e843648..ebb7ae95c6f 100644 --- a/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStoreIT.java +++ b/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStoreIT.java @@ -27,6 +27,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.mongodb.MongoDBAtlasLocalContainer; @@ -224,7 +225,7 @@ public MongoTemplate mongoTemplate(MongoClient mongoClient, MongoCustomConversio @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } @Bean diff --git a/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDbVectorStoreObservationIT.java b/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDbVectorStoreObservationIT.java index b8ddad7b43d..8a14f3e5a5c 100644 --- a/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDbVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDbVectorStoreObservationIT.java @@ -29,6 +29,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.mongodb.MongoDBAtlasLocalContainer; @@ -205,7 +206,7 @@ public MongoTemplate mongoTemplate(MongoClient mongoClient, MongoCustomConversio @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } @Bean diff --git a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreIT.java b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreIT.java index a707a30622a..30b6080872e 100644 --- a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreIT.java +++ b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreIT.java @@ -28,6 +28,7 @@ import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Driver; import org.neo4j.driver.GraphDatabase; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.containers.Neo4jContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -300,7 +301,7 @@ public Driver driver() { @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } } diff --git a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreObservationIT.java b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreObservationIT.java index 1c841b1c1da..62286232af1 100644 --- a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreObservationIT.java @@ -31,6 +31,7 @@ import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Driver; import org.neo4j.driver.GraphDatabase; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.containers.Neo4jContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -188,7 +189,7 @@ public Driver driver() { @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } } diff --git a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreIT.java b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreIT.java index 26cf006b817..8c8533f8fd9 100644 --- a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreIT.java +++ b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreIT.java @@ -39,6 +39,7 @@ import org.opensearch.client.opensearch.OpenSearchClient; import org.opensearch.client.transport.httpclient5.ApacheHttpClient5TransportBuilder; import org.opensearch.testcontainers.OpensearchContainer; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -421,7 +422,7 @@ public OpenSearchVectorStore anotherVectorStore(EmbeddingModel embeddingModel) { @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } } diff --git a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreObservationIT.java b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreObservationIT.java index 7ce5101a110..780a3d381b4 100644 --- a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreObservationIT.java @@ -36,6 +36,7 @@ import org.opensearch.client.opensearch.OpenSearchClient; import org.opensearch.client.transport.httpclient5.ApacheHttpClient5TransportBuilder; import org.opensearch.testcontainers.OpensearchContainer; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -219,7 +220,7 @@ public OpenSearchVectorStore vectorStore(EmbeddingModel embeddingModel, @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } } diff --git a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreCustomNamesIT.java b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreCustomNamesIT.java index 3741cf5a453..f77dcc32e2b 100644 --- a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreCustomNamesIT.java +++ b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreCustomNamesIT.java @@ -23,6 +23,7 @@ import com.zaxxer.hikari.HikariDataSource; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -243,7 +244,7 @@ public HikariDataSource dataSource(DataSourceProperties dataSourceProperties) { @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } } diff --git a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreIT.java b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreIT.java index 3366c88296b..4d54b8aa900 100644 --- a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreIT.java +++ b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreIT.java @@ -34,6 +34,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -377,7 +378,7 @@ public HikariDataSource dataSource(DataSourceProperties dataSourceProperties) { @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } } diff --git a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreObservationIT.java b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreObservationIT.java index 963b256e915..b46aca077c0 100644 --- a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreObservationIT.java @@ -29,6 +29,7 @@ import io.micrometer.observation.tck.TestObservationRegistryAssert; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.model.security.StaticApiKey; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -212,7 +213,7 @@ public HikariDataSource dataSource(DataSourceProperties dataSourceProperties) { @Bean public EmbeddingModel embeddingModel() { - return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); + return new OpenAiEmbeddingModel(new OpenAiApi(new StaticApiKey(System.getenv("OPENAI_API_KEY")))); } }