diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionWebSearch.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionWebSearch.kt index 63cdffa4..76df58d5 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionWebSearch.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionWebSearch.kt @@ -70,6 +70,15 @@ private constructor( */ fun action(): Action = action.getRequired("action") + /** + * Returns the action if present. + * + * Unlike [action], this method returns an empty optional when the server omits the field, + * allowing callers to gracefully handle scenarios where the model did not provide tool + * parameters (for example, when `maxToolCalls` limits are reached). + */ + fun actionOptional(): Optional = action.getOptional("action") + /** * The status of the web search tool call. * @@ -287,7 +296,7 @@ private constructor( } id() - action().validate() + action.getOptional("action").ifPresent { it.validate() } status().validate() _type().let { if (it != JsonValue.from("web_search_call")) { diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionWebSearchTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionWebSearchTest.kt index 5d5b43b6..50c6e98b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionWebSearchTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionWebSearchTest.kt @@ -5,6 +5,7 @@ package com.openai.models.responses import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatCode import org.junit.jupiter.api.Test internal class ResponseFunctionWebSearchTest { @@ -72,4 +73,20 @@ internal class ResponseFunctionWebSearchTest { assertThat(roundtrippedResponseFunctionWebSearch).isEqualTo(responseFunctionWebSearch) } + + @Test + fun deserializeWithoutAction() { + val jsonMapper = jsonMapper() + val payload = """ + {"id":"ws_missing","status":"completed","type":"web_search_call"} + """.trimIndent() + + val responseFunctionWebSearch = + jsonMapper.readValue(payload, jacksonTypeRef()) + + assertThat(responseFunctionWebSearch.actionOptional()).isEmpty + assertThat(responseFunctionWebSearch.status()) + .isEqualTo(ResponseFunctionWebSearch.Status.COMPLETED) + assertThatCode { responseFunctionWebSearch.validate() }.doesNotThrowAnyException() + } }