Remove deprecated methods from Media

- Remove deprecated constructor method Media(MimeType mimeType, URL url) from Media
 - Remove deprecated builder method data(URL url) from Media builder
 - Update references to use the builder and the data(URI) methods

Signed-off-by: Ilayaperumal Gopinathan <ilayaperumal.gopinathan@broadcom.com>
This commit is contained in:
Ilayaperumal Gopinathan
2025-05-05 01:38:00 +01:00
parent 06edee4069
commit 228ef10bfb
15 changed files with 59 additions and 182 deletions

View File

@@ -100,7 +100,7 @@ class Neo4jChatMemoryAutoConfigurationIT {
.mimeType(textPlain)
.data("hello".getBytes(StandardCharsets.UTF_8))
.build(),
Media.builder().data(URI.create("http://www.google.com").toURL()).mimeType(textPlain).build());
Media.builder().data(URI.create("http://www.google.com")).mimeType(textPlain).build());
UserMessage userMessageWithMedia = UserMessage.builder().text("Message with media").media(media).build();
memory.add(sessionId, userMessageWithMedia);

View File

@@ -16,7 +16,6 @@
package org.springframework.ai.chat.memory.neo4j;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
@@ -282,12 +281,7 @@ public class Neo4jChatMemory implements ChatMemory {
.id(Optional.ofNullable(mediaMap.get(MediaAttributes.ID.getValue())).map(Object::toString).orElse(null))
.mimeType(MimeType.valueOf(mediaMap.get(MediaAttributes.MIME_TYPE.getValue()).toString()));
if (mediaMap.get(MediaAttributes.DATA.getValue()) instanceof String stringData) {
try {
mediaBuilder.data(URI.create(stringData).toURL());
}
catch (MalformedURLException e) {
throw new IllegalArgumentException("Media data contains an invalid URL");
}
mediaBuilder.data(URI.create(stringData));
}
else if (mediaMap.get(MediaAttributes.DATA.getValue()).getClass().isArray()) {
mediaBuilder.data(mediaMap.get(MediaAttributes.DATA.getValue()));

View File

@@ -17,7 +17,7 @@
package org.springframework.ai.mistralai;
import java.io.IOException;
import java.net.URL;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -35,9 +35,9 @@ import reactor.core.publisher.Flux;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
@@ -296,7 +296,7 @@ class MistralAiChatModelIT {
.text("Explain what do you see on this picture?")
.media(List.of(Media.builder()
.mimeType(MimeTypeUtils.IMAGE_PNG)
.data(new URL("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.data(URI.create("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.build()))
.build();
@@ -314,7 +314,7 @@ class MistralAiChatModelIT {
.text("Explain what do you see on this picture?")
.media(List.of(Media.builder()
.mimeType(MimeTypeUtils.IMAGE_PNG)
.data(new URL("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.data(URI.create("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.build()))
.build();

View File

@@ -127,10 +127,10 @@ public class MessageTypeContentTests {
given(this.openAiApi.chatCompletionEntity(this.pomptCaptor.capture(), this.headersCaptor.capture()))
.willReturn(Mockito.mock(ResponseEntity.class));
URL mediaUrl = new URL("http://test");
URI mediaUri = URI.create("http://test");
this.chatModel.call(new Prompt(List.of(UserMessage.builder()
.text("test message")
.media(List.of(Media.builder().mimeType(MimeTypeUtils.IMAGE_JPEG).data(mediaUrl).build()))
.media(List.of(Media.builder().mimeType(MimeTypeUtils.IMAGE_JPEG).data(mediaUri).build()))
.build())));
validateComplexContent(this.pomptCaptor.getValue());

View File

@@ -17,7 +17,7 @@
package org.springframework.ai.openai.chat;
import java.io.IOException;
import java.net.URL;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -49,12 +49,6 @@ import org.springframework.ai.chat.metadata.EmptyUsage;
import org.springframework.ai.chat.metadata.Usage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.model.Generation;
import org.springframework.ai.model.tool.DefaultToolCallingManager;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.model.tool.ToolExecutionResult;
import org.springframework.ai.tool.ToolCallbacks;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
@@ -63,6 +57,10 @@ import org.springframework.ai.content.Media;
import org.springframework.ai.converter.BeanOutputConverter;
import org.springframework.ai.converter.ListOutputConverter;
import org.springframework.ai.converter.MapOutputConverter;
import org.springframework.ai.model.tool.DefaultToolCallingManager;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.model.tool.ToolExecutionResult;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.OpenAiTestConfiguration;
import org.springframework.ai.openai.api.OpenAiApi;
@@ -71,6 +69,8 @@ import org.springframework.ai.openai.api.OpenAiApi.ChatCompletionRequest.AudioPa
import org.springframework.ai.openai.api.OpenAiApi.ChatCompletionRequest.AudioParameters.Voice;
import org.springframework.ai.openai.api.tool.MockWeatherService;
import org.springframework.ai.openai.testutils.AbstractIT;
import org.springframework.ai.tool.ToolCallbacks;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
@@ -491,7 +491,7 @@ public class OpenAiChatModelIT extends AbstractIT {
.text("Explain what do you see on this picture?")
.media(List.of(Media.builder()
.mimeType(MimeTypeUtils.IMAGE_PNG)
.data(new URL("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.data(URI.create("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.build()))
.build();
@@ -510,7 +510,7 @@ public class OpenAiChatModelIT extends AbstractIT {
.text("Explain what do you see on this picture?")
.media(List.of(Media.builder()
.mimeType(MimeTypeUtils.IMAGE_PNG)
.data(new URL("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.data(URI.create("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.build()))
.build();

View File

@@ -17,7 +17,7 @@
package org.springframework.ai.openai.chat.proxy;
import java.io.IOException;
import java.net.URL;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -334,7 +334,7 @@ class GroqWithOpenAiChatModelIT {
.text("Explain what do you see on this picture?")
.media(List.of(Media.builder()
.mimeType(MimeTypeUtils.IMAGE_PNG)
.data(new URL("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.data(URI.create("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.build()))
.build();
@@ -354,7 +354,7 @@ class GroqWithOpenAiChatModelIT {
.text("Explain what do you see on this picture?")
.media(List.of(Media.builder()
.mimeType(MimeTypeUtils.IMAGE_PNG)
.data(new URL("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.data(URI.create("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.build()))
.build();

View File

@@ -18,7 +18,6 @@ package org.springframework.ai.openai.chat.proxy;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -341,7 +340,7 @@ class MistralWithOpenAiChatModelIT {
.text("Explain what do you see on this picture?")
.media(List.of(Media.builder()
.mimeType(MimeTypeUtils.IMAGE_PNG)
.data(new URL("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.data(URI.create("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.build()))
.build();

View File

@@ -18,7 +18,6 @@ package org.springframework.ai.openai.chat.proxy;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -385,7 +384,7 @@ class OllamaWithOpenAiChatModelIT {
.text("Explain what do you see on this picture?")
.media(List.of(Media.builder()
.mimeType(MimeTypeUtils.IMAGE_PNG)
.data(new URL("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.data(URI.create("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png"))
.build()))
.build();

View File

@@ -114,7 +114,7 @@ class VertexAiMultimodalEmbeddingModelIT {
var document = Document.builder()
.media(Media.builder()
.mimeType(MimeTypeUtils.TEXT_PLAIN)
.data(URI.create("http://example.com/image.png").toURL())
.data(URI.create("http://example.com/image.png"))
.build())
.build();

View File

@@ -17,6 +17,7 @@
package org.springframework.ai.chat.client;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
@@ -202,7 +203,12 @@ public class DefaultChatClient implements ChatClient {
public PromptUserSpec media(MimeType mimeType, URL url) {
Assert.notNull(mimeType, "mimeType cannot be null");
Assert.notNull(url, "url cannot be null");
this.media.add(Media.builder().mimeType(mimeType).data(url).build());
try {
this.media.add(Media.builder().mimeType(mimeType).data(url.toURI()).build());
}
catch (URISyntaxException e) {
throw new RuntimeException(e);
}
return this;
}

View File

@@ -169,12 +169,12 @@ class DefaultChatClientTests {
@Test
void whenUserMediaThenReturn() throws MalformedURLException {
DefaultChatClient.DefaultPromptUserSpec spec = new DefaultChatClient.DefaultPromptUserSpec();
URL mediaUrl = URI.create("http://example.com/image.png").toURL();
URI mediaUri = URI.create("http://example.com/image.png");
spec = (DefaultChatClient.DefaultPromptUserSpec) spec
.media(Media.builder().mimeType(MimeTypeUtils.IMAGE_PNG).data(mediaUrl).build());
.media(Media.builder().mimeType(MimeTypeUtils.IMAGE_PNG).data(mediaUri).build());
assertThat(spec.media()).hasSize(1);
assertThat(spec.media().get(0).getMimeType()).isEqualTo(MimeTypeUtils.IMAGE_PNG);
assertThat(spec.media().get(0).getData()).isEqualTo(mediaUrl.toString());
assertThat(spec.media().get(0).getData()).isEqualTo(mediaUri.toString());
}
@Test

View File

@@ -18,7 +18,6 @@ package org.springframework.ai.content;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
@@ -116,22 +115,6 @@ public class Media {
this.name = generateDefaultName(mimeType);
}
/**
* Create a new Media instance.
* @param mimeType the media MIME type
* @param url the URL for the media data
* @deprecated in favour of {@link #Media(MimeType, URI)}
*/
@Deprecated
public Media(MimeType mimeType, URL url) {
Assert.notNull(mimeType, "MimeType must not be null");
Assert.notNull(url, "URL must not be null");
this.mimeType = mimeType;
this.id = null;
this.data = url.toString();
this.name = generateDefaultName(mimeType);
}
/**
* Create a new Media instance.
* @param mimeType the media MIME type
@@ -291,20 +274,6 @@ public class Media {
return this;
}
/**
* Sets the media data from a URL.
* @param url the media URL, must not be null
* @return the builder instance
* @throws IllegalArgumentException if url is null
* @deprecated in favour of {@link #data(URI)}
*/
@Deprecated
public Builder data(URL url) {
Assert.notNull(url, "URL must not be null");
this.data = url.toString();
return this;
}
/**
* Sets the ID for the media object. The ID is typically assigned by AI models
* when they return a reference to previously provided media content.

View File

@@ -16,8 +16,7 @@
package org.springframework.ai.document;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
@@ -36,14 +35,7 @@ public class DocumentBuilderTests {
private Document.Builder builder;
private static Media getMedia() {
try {
URL mediaUrl1 = new URL("http://type1");
Media media1 = new Media(MimeTypeUtils.IMAGE_JPEG, mediaUrl1);
return media1;
}
catch (MalformedURLException e) {
throw new RuntimeException(e);
}
return Media.builder().data(URI.create("http://type1")).mimeType(MimeTypeUtils.IMAGE_JPEG).build();
}
@BeforeEach
@@ -97,9 +89,8 @@ public class DocumentBuilderTests {
}
@Test
void testWithMediaSingle() throws MalformedURLException {
URL mediaUrl = new URL("http://test");
Media media = Media.builder().mimeType(MimeTypeUtils.IMAGE_JPEG).data(mediaUrl).build();
void testWithMediaSingle() {
Media media = Media.builder().mimeType(MimeTypeUtils.IMAGE_JPEG).data(URI.create("http://test")).build();
Document.Builder result = this.builder.media(media);

View File

@@ -17,6 +17,7 @@
package org.springframework.ai.document;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
@@ -211,14 +212,7 @@ public class DocumentTests {
}
private static Media getMedia() {
try {
URL mediaUrl1 = new URL("http://type1");
Media media1 = new Media(MimeTypeUtils.IMAGE_JPEG, mediaUrl1);
return media1;
}
catch (MalformedURLException e) {
throw new RuntimeException(e);
}
return Media.builder().mimeType(MimeTypeUtils.IMAGE_JPEG).data(URI.create("http://type1")).build();
}
}

View File

@@ -19,7 +19,6 @@ package org.springframework.ai.model;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.UUID;
import org.junit.jupiter.api.Test;
@@ -67,17 +66,17 @@ class MediaTests {
}
@Test
void testMediaBuilderWithURL() throws MalformedURLException {
void testMediaBuilderWithURI() throws MalformedURLException {
MimeType mimeType = MimeType.valueOf("image/png");
URL url = new URL("http://example.com/image.png");
URI uri = URI.create("http://example.com/image.png");
String id = "123";
String name = "test-media";
Media media = Media.builder().mimeType(mimeType).data(url).id(id).name(name).build();
Media media = Media.builder().mimeType(mimeType).data(uri).id(id).name(name).build();
assertThat(media.getMimeType()).isEqualTo(mimeType);
assertThat(media.getData()).isInstanceOf(String.class);
assertThat(media.getData()).isEqualTo(url.toString());
assertThat(media.getData()).isEqualTo(uri.toString());
assertThat(media.getId()).isEqualTo(id);
assertThat(media.getName()).isEqualTo(name);
}
@@ -116,10 +115,10 @@ class MediaTests {
}
@Test
void testMediaBuilderWithNullURL() {
assertThatThrownBy(() -> Media.builder().mimeType(MimeType.valueOf("image/png")).data((URL) null).build())
void testMediaBuilderWithNullURI() {
assertThatThrownBy(() -> Media.builder().mimeType(MimeType.valueOf("image/png")).data((URI) null).build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("URL must not be null");
.hasMessageContaining("URI must not be null");
}
@Test
@@ -177,11 +176,11 @@ class MediaTests {
}
@Test
void testLastDataMethodWins() throws MalformedURLException {
URL url = new URL("http://example.com/image.png");
void testLastDataMethodWins() {
URI uri = URI.create("http://example.com/image.png");
byte[] bytes = new byte[] { 1, 2, 3 };
Media media = Media.builder().mimeType(Media.Format.IMAGE_PNG).data(url).data(bytes).build();
Media media = Media.builder().mimeType(Media.Format.IMAGE_PNG).data(uri).data(bytes).build();
assertThat(media.getData()).isSameAs(bytes);
}
@@ -203,13 +202,13 @@ class MediaTests {
@Test
void testMediaConstructorWithUrl() throws MalformedURLException {
MimeType mimeType = MimeType.valueOf("image/png");
URL url = new URL("http://example.com/image.png");
String url = "http://example.com/image.png";
Media media = new Media(mimeType, url);
Media media = Media.builder().mimeType(mimeType).data(URI.create(url)).build();
assertThat(media.getMimeType()).isEqualTo(mimeType);
assertThat(media.getData()).isInstanceOf(String.class);
assertThat(media.getData()).isEqualTo(url.toString());
assertThat(media.getData()).isEqualTo(url);
assertThat(media.getId()).isNull();
String name = media.getName();
assertValidMediaName(media.getName(), "png");
@@ -292,81 +291,6 @@ class MediaTests {
.hasCauseInstanceOf(IOException.class);
}
/// Tests to ensure two arg ctors behave identically to the builder
@Test
void testUriConstructorMatchesBuilder() {
// Given
MimeType mimeType = MimeType.valueOf("image/png");
URI uri = URI.create("http://example.com/image.png");
// When
Media mediaFromCtor = new Media(mimeType, uri);
Media mediaFromBuilder = Media.builder().mimeType(mimeType).data(uri).build();
// Then - verify all properties match
assertThat(mediaFromCtor.getMimeType()).isEqualTo(mediaFromBuilder.getMimeType());
assertThat(mediaFromCtor.getData()).isEqualTo(mediaFromBuilder.getData());
assertThat(mediaFromCtor.getId()).isEqualTo(mediaFromBuilder.getId());
// Verify name structure for both instances
assertValidMediaName(mediaFromCtor.getName(), "png");
assertValidMediaName(mediaFromBuilder.getName(), "png");
// Data type consistency
assertThat(mediaFromCtor.getData()).isInstanceOf(String.class);
assertThat(mediaFromBuilder.getData()).isInstanceOf(String.class);
}
@Test
void testURLConstructorMatchesBuilder() throws MalformedURLException {
// Given
MimeType mimeType = MimeType.valueOf("image/png");
URL url = new URL("http://example.com/image.png");
// When
Media mediaFromCtor = new Media(mimeType, url);
Media mediaFromBuilder = Media.builder().mimeType(mimeType).data(url).build();
// Then - verify all properties match
assertThat(mediaFromCtor.getMimeType()).isEqualTo(mediaFromBuilder.getMimeType());
assertThat(mediaFromCtor.getData()).isEqualTo(mediaFromBuilder.getData());
assertThat(mediaFromCtor.getId()).isEqualTo(mediaFromBuilder.getId());
// Verify name structure for both instances
assertValidMediaName(mediaFromCtor.getName(), "png");
assertValidMediaName(mediaFromBuilder.getName(), "png");
// Data type consistency
assertThat(mediaFromCtor.getData()).isInstanceOf(String.class);
assertThat(mediaFromBuilder.getData()).isInstanceOf(String.class);
}
@Test
void testResourceConstructorMatchesBuilder() throws IOException {
// Given
MimeType mimeType = MimeType.valueOf("image/png");
byte[] content = new byte[] { 1, 2, 3, 4, 5 };
Resource resource = new ByteArrayResource(content);
// When
Media mediaFromCtor = new Media(mimeType, resource);
Media mediaFromBuilder = Media.builder().mimeType(mimeType).data(resource).build();
// Then - verify all properties match
assertThat(mediaFromCtor.getMimeType()).isEqualTo(mediaFromBuilder.getMimeType());
assertThat(mediaFromCtor.getDataAsByteArray()).isEqualTo(mediaFromBuilder.getDataAsByteArray());
assertThat(mediaFromCtor.getId()).isEqualTo(mediaFromBuilder.getId());
// Verify name structure for both instances
assertValidMediaName(mediaFromCtor.getName(), "png");
assertValidMediaName(mediaFromBuilder.getName(), "png");
// Data type consistency
assertThat(mediaFromCtor.getData()).isInstanceOf(byte[].class);
assertThat(mediaFromBuilder.getData()).isInstanceOf(byte[].class);
}
@Test
void testUriConstructorNullValidation() {
MimeType mimeType = MimeType.valueOf("image/png");
@@ -396,22 +320,23 @@ class MediaTests {
MimeType mimeType = MimeType.valueOf("image/png");
// Test null mimeType
assertThatThrownBy(() -> new Media(null, new URL("http://example.com/image.png")))
assertThatThrownBy(() -> new Media(null, URI.create("http://example.com/image.png")))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("MimeType must not be null");
// Test null URL
assertThatThrownBy(() -> new Media(mimeType, (URL) null)).isInstanceOf(IllegalArgumentException.class)
.hasMessage("URL must not be null");
assertThatThrownBy(() -> new Media(mimeType, (URI) null)).isInstanceOf(IllegalArgumentException.class)
.hasMessage("URI must not be null");
// Compare with builder validation
assertThatThrownBy(() -> Media.builder().mimeType(null).data(new URL("http://example.com/image.png")).build())
assertThatThrownBy(
() -> Media.builder().mimeType(null).data(URI.create("http://example.com/image.png")).build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("MimeType must not be null");
assertThatThrownBy(() -> Media.builder().mimeType(mimeType).data((URL) null).build())
assertThatThrownBy(() -> Media.builder().mimeType(mimeType).data((URI) null).build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("URL must not be null");
.hasMessage("URI must not be null");
}
@Test