34
34
import reactor .core .publisher .Mono ;
35
35
36
36
import org .springframework .ai .anthropic .api .StreamHelper .ChatCompletionResponseBuilder ;
37
+ import org .springframework .ai .model .ApiKey ;
37
38
import org .springframework .ai .model .ChatModelDescription ;
38
39
import org .springframework .ai .model .ModelOptionsUtils ;
40
+ import org .springframework .ai .model .SimpleApiKey ;
39
41
import org .springframework .ai .observation .conventions .AiProvider ;
40
42
import org .springframework .ai .retry .RetryUtils ;
41
43
import org .springframework .http .HttpHeaders ;
60
62
* @author Alexandros Pappas
61
63
* @author Jonghoon Park
62
64
* @author Claudio Silva Junior
65
+ * @author Filip Hrisafov
63
66
* @since 1.0.0
64
67
*/
65
68
public final class AnthropicApi {
@@ -96,6 +99,8 @@ public static Builder builder() {
96
99
97
100
private final WebClient webClient ;
98
101
102
+ private final ApiKey apiKey ;
103
+
99
104
/**
100
105
* Create a new client api.
101
106
* @param baseUrl api base URL.
@@ -107,18 +112,18 @@ public static Builder builder() {
107
112
* @param responseErrorHandler Response error handler.
108
113
* @param anthropicBetaFeatures Anthropic beta features.
109
114
*/
110
- private AnthropicApi (String baseUrl , String completionsPath , String anthropicApiKey , String anthropicVersion ,
115
+ private AnthropicApi (String baseUrl , String completionsPath , ApiKey anthropicApiKey , String anthropicVersion ,
111
116
RestClient .Builder restClientBuilder , WebClient .Builder webClientBuilder ,
112
117
ResponseErrorHandler responseErrorHandler , String anthropicBetaFeatures ) {
113
118
114
119
Consumer <HttpHeaders > jsonContentHeaders = headers -> {
115
- headers .add (HEADER_X_API_KEY , anthropicApiKey );
116
120
headers .add (HEADER_ANTHROPIC_VERSION , anthropicVersion );
117
121
headers .add (HEADER_ANTHROPIC_BETA , anthropicBetaFeatures );
118
122
headers .setContentType (MediaType .APPLICATION_JSON );
119
123
};
120
124
121
125
this .completionsPath = completionsPath ;
126
+ this .apiKey = anthropicApiKey ;
122
127
123
128
this .restClient = restClientBuilder .clone ()
124
129
.baseUrl (baseUrl )
@@ -160,12 +165,17 @@ public ResponseEntity<ChatCompletionResponse> chatCompletionEntity(ChatCompletio
160
165
Assert .isTrue (!chatRequest .stream (), "Request must set the stream property to false." );
161
166
Assert .notNull (additionalHttpHeader , "The additional HTTP headers can not be null." );
162
167
168
+ // @formatter:off
163
169
return this .restClient .post ()
164
170
.uri (this .completionsPath )
165
- .headers (headers -> headers .addAll (additionalHttpHeader ))
171
+ .headers (headers -> {
172
+ headers .addAll (additionalHttpHeader );
173
+ addDefaultHeadersIfMissing (headers );
174
+ })
166
175
.body (chatRequest )
167
176
.retrieve ()
168
177
.toEntity (ChatCompletionResponse .class );
178
+ // @formatter:on
169
179
}
170
180
171
181
/**
@@ -196,9 +206,13 @@ public Flux<ChatCompletionResponse> chatCompletionStream(ChatCompletionRequest c
196
206
197
207
AtomicReference <ChatCompletionResponseBuilder > chatCompletionReference = new AtomicReference <>();
198
208
209
+ // @formatter:off
199
210
return this .webClient .post ()
200
211
.uri (this .completionsPath )
201
- .headers (headers -> headers .addAll (additionalHttpHeader ))
212
+ .headers (headers -> {
213
+ headers .addAll (additionalHttpHeader );
214
+ addDefaultHeadersIfMissing (headers );
215
+ }) // @formatter:off
202
216
.body (Mono .just (chatRequest ), ChatCompletionRequest .class )
203
217
.retrieve ()
204
218
.bodyToFlux (String .class )
@@ -232,6 +246,15 @@ public Flux<ChatCompletionResponse> chatCompletionStream(ChatCompletionRequest c
232
246
.filter (chatCompletionResponse -> chatCompletionResponse .type () != null );
233
247
}
234
248
249
+ private void addDefaultHeadersIfMissing (HttpHeaders headers ) {
250
+ if (!headers .containsKey (HEADER_X_API_KEY )) {
251
+ String apiKeyValue = this .apiKey .getValue ();
252
+ if (StringUtils .hasText (apiKeyValue )) {
253
+ headers .add (HEADER_X_API_KEY , apiKeyValue );
254
+ }
255
+ }
256
+ }
257
+
235
258
/**
236
259
* Check the <a href="https://docs.anthropic.com/claude/docs/models-overview">Models
237
260
* overview</a> and <a href=
@@ -1349,7 +1372,7 @@ public static class Builder {
1349
1372
1350
1373
private String completionsPath = DEFAULT_MESSAGE_COMPLETIONS_PATH ;
1351
1374
1352
- private String apiKey ;
1375
+ private ApiKey apiKey ;
1353
1376
1354
1377
private String anthropicVersion = DEFAULT_ANTHROPIC_VERSION ;
1355
1378
@@ -1373,12 +1396,18 @@ public Builder completionsPath(String completionsPath) {
1373
1396
return this ;
1374
1397
}
1375
1398
1376
- public Builder apiKey (String apiKey ) {
1399
+ public Builder apiKey (ApiKey apiKey ) {
1377
1400
Assert .notNull (apiKey , "apiKey cannot be null" );
1378
1401
this .apiKey = apiKey ;
1379
1402
return this ;
1380
1403
}
1381
1404
1405
+ public Builder apiKey (String simpleApiKey ) {
1406
+ Assert .notNull (simpleApiKey , "simpleApiKey cannot be null" );
1407
+ this .apiKey = new SimpleApiKey (simpleApiKey );
1408
+ return this ;
1409
+ }
1410
+
1382
1411
public Builder anthropicVersion (String anthropicVersion ) {
1383
1412
Assert .notNull (anthropicVersion , "anthropicVersion cannot be null" );
1384
1413
this .anthropicVersion = anthropicVersion ;
0 commit comments