Skip to content

Commit 146d109

Browse files
Make allow image url for Anthropic API
Signed-off-by: jonghoonpark <[email protected]>
1 parent c0bc623 commit 146d109

File tree

3 files changed

+27
-10
lines changed

3 files changed

+27
-10
lines changed

models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
* @author Thomas Vitale
8989
* @author Claudio Silva Junior
9090
* @author Alexandros Pappas
91+
* @author Jonghoon Park
9192
* @since 1.0.0
9293
*/
9394
public class AnthropicChatModel implements ChatModel {
@@ -357,6 +358,18 @@ private ChatResponseMetadata from(AnthropicApi.ChatCompletionResponse result, Us
357358
.build();
358359
}
359360

361+
private Source getSourceByMedia(Media media) {
362+
String data = this.fromMediaData(media.getData());
363+
364+
// http is not allowed and redirect not allowed
365+
if (data.startsWith("https://")) {
366+
return new Source(data);
367+
}
368+
else {
369+
return new Source(media.getMimeType().toString(), data);
370+
}
371+
}
372+
360373
private String fromMediaData(Object mediaData) {
361374
if (mediaData instanceof byte[] bytes) {
362375
return Base64.getEncoder().encodeToString(bytes);
@@ -457,8 +470,7 @@ ChatCompletionRequest createRequest(Prompt prompt, boolean stream) {
457470
if (!CollectionUtils.isEmpty(userMessage.getMedia())) {
458471
List<ContentBlock> mediaContent = userMessage.getMedia().stream().map(media -> {
459472
Type contentBlockType = getContentBlockTypeByMedia(media);
460-
var source = new Source(media.getMimeType().toString(),
461-
this.fromMediaData(media.getData()));
473+
var source = getSourceByMedia(media);
462474
return new ContentBlock(contentBlockType, source);
463475
}).toList();
464476
contents.addAll(mediaContent);

models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/api/AnthropicApi.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 the original author or authors.
2+
* Copyright 2023-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -57,6 +57,7 @@
5757
* @author Thomas Vitale
5858
* @author Jihoon Kim
5959
* @author Alexandros Pappas
60+
* @author Jonghoon Park
6061
* @since 1.0.0
6162
*/
6263
public class AnthropicApi {
@@ -1038,13 +1039,15 @@ public String getValue() {
10381039
* @param mediaType The media type of the content. For example, "image/png" or
10391040
* "image/jpeg".
10401041
* @param data The base64-encoded data of the content.
1042+
* @param url The url of the content. (image only)
10411043
*/
10421044
@JsonInclude(Include.NON_NULL)
10431045
public record Source(
10441046
// @formatter:off
10451047
@JsonProperty("type") String type,
10461048
@JsonProperty("media_type") String mediaType,
1047-
@JsonProperty("data") String data) {
1049+
@JsonProperty("data") String data,
1050+
@JsonProperty("url") String url) {
10481051
// @formatter:on
10491052

10501053
/**
@@ -1053,7 +1056,11 @@ public record Source(
10531056
* @param data The content data.
10541057
*/
10551058
public Source(String mediaType, String data) {
1056-
this("base64", mediaType, data);
1059+
this("base64", mediaType, data, null);
1060+
}
1061+
1062+
public Source(String url) {
1063+
this("url", null, null, url);
10571064
}
10581065

10591066
}

models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/client/AnthropicChatClientIT.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 the original author or authors.
2+
* Copyright 2023-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -301,14 +301,12 @@ void multiModalityEmbeddedImage(String modelName) throws IOException {
301301
assertThat(response).containsAnyOf("bananas", "apple", "bowl", "basket", "fruit stand");
302302
}
303303

304-
@Disabled("Currently Anthropic API does not support external image URLs")
305304
@ParameterizedTest(name = "{0} : {displayName} ")
306-
@ValueSource(strings = { "claude-3-opus-latest", "claude-3-5-sonnet-latest", "claude-3-haiku-latest",
307-
"claude-3-7-sonnet-latest" })
305+
@ValueSource(strings = { "claude-3-opus-latest", "claude-3-5-sonnet-latest", "claude-3-7-sonnet-latest" })
308306
void multiModalityImageUrl(String modelName) throws IOException {
309307

310308
// TODO: add url method that wrapps the checked exception.
311-
URL url = new URL("https://docs.spring.io/spring-ai/reference/1.0.0-SNAPSHOT/_images/multimodal.test.png");
309+
URL url = new URL("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png");
312310

313311
// @formatter:off
314312
String response = ChatClient.create(this.chatModel).prompt()

0 commit comments

Comments
 (0)