Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,83 @@ void shouldNotHaveKeyValuesWhenEmptyValues() {
HighCardinalityKeyNames.REQUEST_IMAGE_STYLE.asString());
}

@Test
void shouldHandleNullModel() {
ImageModelObservationContext observationContext = ImageModelObservationContext.builder()
.imagePrompt(new ImagePrompt("test prompt"))
.provider("test-provider")
.build();

assertThat(this.observationConvention.getContextualName(observationContext)).isEqualTo("image");
assertThat(this.observationConvention.getLowCardinalityKeyValues(observationContext))
.contains(KeyValue.of(AiObservationAttributes.REQUEST_MODEL.value(), KeyValue.NONE_VALUE));
}

@Test
void shouldHandleEmptyModel() {
ImageModelObservationContext observationContext = ImageModelObservationContext.builder()
.imagePrompt(generateImagePrompt(ImageOptionsBuilder.builder().model("").build()))
.provider("test-provider")
.build();

assertThat(this.observationConvention.getContextualName(observationContext)).isEqualTo("image");
}

@Test
void shouldHandleBlankModel() {
ImageModelObservationContext observationContext = ImageModelObservationContext.builder()
.imagePrompt(generateImagePrompt(ImageOptionsBuilder.builder().model(" ").build()))
.provider("test-provider")
.build();

assertThat(this.observationConvention.getContextualName(observationContext)).isEqualTo("image");
}

@Test
void shouldHandleEmptyStyle() {
var imageOptions = ImageOptionsBuilder.builder().model("test-model").style("").build();

ImageModelObservationContext observationContext = ImageModelObservationContext.builder()
.imagePrompt(generateImagePrompt(imageOptions))
.provider("test-provider")
.build();

// Empty style should not be included
assertThat(this.observationConvention.getHighCardinalityKeyValues(observationContext)
.stream()
.map(KeyValue::getKey)
.toList()).doesNotContain(HighCardinalityKeyNames.REQUEST_IMAGE_STYLE.asString());
}

@Test
void shouldHandleEmptyResponseFormat() {
var imageOptions = ImageOptionsBuilder.builder().model("test-model").responseFormat("").build();

ImageModelObservationContext observationContext = ImageModelObservationContext.builder()
.imagePrompt(generateImagePrompt(imageOptions))
.provider("test-provider")
.build();

// Empty response format should not be included
assertThat(this.observationConvention.getHighCardinalityKeyValues(observationContext)
.stream()
.map(KeyValue::getKey)
.toList()).doesNotContain(HighCardinalityKeyNames.REQUEST_IMAGE_RESPONSE_FORMAT.asString());
}

@Test
void shouldHandleImagePromptWithoutOptions() {
ImageModelObservationContext observationContext = ImageModelObservationContext.builder()
.imagePrompt(new ImagePrompt("simple prompt"))
.provider("simple-provider")
.build();

assertThat(this.observationConvention.getContextualName(observationContext)).isEqualTo("image");
assertThat(this.observationConvention.getLowCardinalityKeyValues(observationContext))
.contains(KeyValue.of(AiObservationAttributes.REQUEST_MODEL.value(), KeyValue.NONE_VALUE));
assertThat(this.observationConvention.getHighCardinalityKeyValues(observationContext)).isEmpty();
}

private ImagePrompt generateImagePrompt(ImageOptions imageOptions) {
return new ImagePrompt("here comes the sun", imageOptions);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.springframework.ai.image.ImagePrompt;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

/**
* Unit tests for {@link ImageModelObservationContext}.
Expand All @@ -41,6 +42,75 @@ void whenMandatoryRequestOptionsThenReturn() {
assertThat(observationContext).isNotNull();
}

@Test
void shouldBuildContextWithImageOptions() {
var imageOptions = ImageOptionsBuilder.builder().model("test-model").build();
var imagePrompt = new ImagePrompt("test prompt", imageOptions);

var observationContext = ImageModelObservationContext.builder()
.imagePrompt(imagePrompt)
.provider("test-provider")
.build();

assertThat(observationContext).isNotNull();
}

@Test
void shouldThrowExceptionWhenImagePromptIsNull() {
assertThatThrownBy(
() -> ImageModelObservationContext.builder().imagePrompt(null).provider("test-provider").build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("request cannot be null");
}

@Test
void shouldThrowExceptionWhenProviderIsNull() {
var imagePrompt = new ImagePrompt("test prompt");

assertThatThrownBy(() -> ImageModelObservationContext.builder().imagePrompt(imagePrompt).provider(null).build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("provider cannot be null or empty");
}

@Test
void shouldThrowExceptionWhenProviderIsEmpty() {
var imagePrompt = new ImagePrompt("test prompt");

assertThatThrownBy(() -> ImageModelObservationContext.builder().imagePrompt(imagePrompt).provider("").build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("provider cannot be null or empty");
}

@Test
void shouldThrowExceptionWhenProviderIsBlank() {
var imagePrompt = new ImagePrompt("test prompt");

assertThatThrownBy(
() -> ImageModelObservationContext.builder().imagePrompt(imagePrompt).provider(" ").build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("provider cannot be null or empty");
}

@Test
void shouldBuildMultipleContextsIndependently() {
var imagePrompt1 = new ImagePrompt("first prompt");
var imagePrompt2 = new ImagePrompt("second prompt");

var context1 = ImageModelObservationContext.builder()
.imagePrompt(imagePrompt1)
.provider("provider-alpha")
.build();

var context2 = ImageModelObservationContext.builder()
.imagePrompt(imagePrompt2)
.provider("provider-beta")
.build();

assertThat(context1).isNotNull();
assertThat(context2).isNotNull();
assertThat(context1).isNotEqualTo(context2);
}

private ImagePrompt generateImagePrompt(ImageOptions imageOptions) {
return new ImagePrompt("here comes the sun");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,18 @@ void testThrowToolExecutionException() {
.isInstanceOf(ToolExecutionException.class);
}

@Test
void testEmptyStringInput() {
TestFunctionTool tool = new TestFunctionTool();
FunctionToolCallback<String, Void> callback = FunctionToolCallback.builder("testTool", tool.stringConsumer())
.description("test empty string")
.inputType(String.class)
.build();

callback.call("\"\"");
assertEquals("", tool.calledValue.get());
}

static class TestFunctionTool {

AtomicReference<Object> calledValue = new AtomicReference<>();
Expand Down