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 @@ -376,14 +376,14 @@ Kotlin::
[source,kotlin]
----
data class MathReasoning(
@get:JsonProperty(required = true, value = "steps") val steps: Steps,
@get:JsonProperty(required = true, value = "final_answer") val finalAnswer: String) {
val steps: Steps,
@get:JsonProperty(value = "final_answer") val finalAnswer: String) {

data class Steps(@get:JsonProperty(required = true, value = "items") val items: Array<Items>) {
data class Steps(val items: Array<Items>) {

data class Items(
@get:JsonProperty(required = true, value = "explanation") val explanation: String,
@get:JsonProperty(required = true, value = "output") val output: String)
val explanation: String,
val output: String)
}
}

Expand All @@ -405,9 +405,7 @@ val mathReasoning = outputConverter.convert(content)
======
--

NOTE: Ensure you use the `@JsonProperty(required = true,...)` annotation (`@get:JsonProperty(required = true,...)` with Kotlin in order to generate the annotation on the related getters, see link:https://kotlinlang.org/docs/annotations.html#annotation-use-site-targets[related documentation]).
This is crucial for generating a schema that accurately marks fields as `required`.
Although this is optional for JSON Schema, OpenAI link:https://platform.openai.com/docs/guides/structured-outputs/all-fields-must-be-required[mandates] it for the structured response to function correctly.
NOTE: Although this is optional for JSON Schema, OpenAI link:https://platform.openai.com/docs/guides/structured-outputs/all-fields-must-be-required#all-fields-must-be-required[mandates] required fields for the structured response to function correctly. Kotlin reflection is used to infer which property are required or not based on the nullability of types and default values of parameters, so for most use case `@get:JsonProperty(required = true)` is not needed. `@get:JsonProperty(value = "custom_name")` can be useful to customize the property name. Make sure to generate the annotation on the related getters with this `@get:` syntax, see link:https://kotlinlang.org/docs/annotations.html#annotation-use-site-targets[related documentation].

==== Configuring via Application Properties

Expand Down
6 changes: 6 additions & 0 deletions spring-ai-spring-boot-autoconfigure/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,12 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class FunctionCallbackResolverKotlinIT : BaseOllamaIT() {

companion object {

private val MODEL_NAME = OllamaModel.LLAMA3_2.getName();
private val MODEL_NAME = "qwen2.5:3b";

@JvmStatic
@BeforeAll
Expand Down Expand Up @@ -72,7 +72,7 @@ class FunctionCallbackResolverKotlinIT : BaseOllamaIT() {
val response = chatModel
.call(Prompt(listOf(userMessage), OllamaOptions.builder().function("weatherInfo").build()))

logger.info("Response: " + response)
logger.info("Response: $response")

assertThat(response.getResult().output.text).contains("30", "10", "15")
}
Expand All @@ -93,10 +93,11 @@ class FunctionCallbackResolverKotlinIT : BaseOllamaIT() {
.build()

val response = chatModel.call(Prompt(listOf(userMessage), functionOptions));
val output = response.getResult().output.text

logger.info("Response: " + response.getResult().getOutput().getText());
logger.info("Response: $output");

assertThat(response.getResult().output.text).contains("30", "10", "15");
assertThat(output).contains("30", "10", "15");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import org.springframework.ai.chat.prompt.Prompt
import org.springframework.ai.model.function.FunctionCallback
import org.springframework.ai.model.function.FunctionCallingOptions
import org.springframework.ai.ollama.OllamaChatModel
import org.springframework.ai.ollama.api.OllamaModel
import org.springframework.ai.ollama.api.OllamaOptions
import org.springframework.boot.autoconfigure.AutoConfigurations
import org.springframework.boot.test.context.runner.ApplicationContextRunner
Expand All @@ -39,7 +38,7 @@ class FunctionCallbackKotlinIT : BaseOllamaIT() {

companion object {

private val MODEL_NAME = OllamaModel.LLAMA3_2.getName();
private val MODEL_NAME = "qwen2.5:3b";

@JvmStatic
@BeforeAll
Expand Down Expand Up @@ -72,7 +71,7 @@ class FunctionCallbackKotlinIT : BaseOllamaIT() {
val response = chatModel
.call(Prompt(listOf(userMessage), OllamaOptions.builder().function("WeatherInfo").build()))

logger.info("Response: " + response)
logger.info("Response: $response")

assertThat(response.getResult().output.text).contains("30", "10", "15")
}
Expand All @@ -93,10 +92,10 @@ class FunctionCallbackKotlinIT : BaseOllamaIT() {
.build()

val response = chatModel.call(Prompt(listOf(userMessage), functionOptions));
val output = response.getResult().output.text
logger.info("Response: $output");

logger.info("Response: " + response.getResult().getOutput().getText());

assertThat(response.getResult().output.text).contains("30", "10", "15");
assertThat(output).contains("30", "10", "15");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,16 @@ enum class Unit(val unitName: String) {
@JsonInclude(Include.NON_NULL)
@JsonClassDescription("Weather API request")
data class KotlinRequest(
@get:JsonProperty(required = true, value = "location")

@get:JsonPropertyDescription("The city and state e.g. San Francisco, CA")
val location: String = "",
val location: String,

@get:JsonProperty(required = true, value = "lat")
@get:JsonPropertyDescription("The city latitude")
val lat: Double = 0.0,
val lat: Double,

@get:JsonProperty(required = true, value = "lon")
@get:JsonPropertyDescription("The city longitude")
val lon: Double = 0.0,
val lon: Double,

@get:JsonProperty(required = true, value = "unit")
@get:JsonPropertyDescription("Temperature unit")
val unit: Unit = Unit.C
)
Expand Down
Loading