Skip to content

Commit 7db2749

Browse files
committed
fix: handle DeepResearch empty ResponseFunctionWebSearch.action after exceeding maxToolCalls (#600)
Ensure proper fallback and non-empty response handling when tool limit is exceeded. This prevents returning empty responses during DeepResearch multi-call sequences.
1 parent 920ec70 commit 7db2749

File tree

2 files changed

+27
-1
lines changed

2 files changed

+27
-1
lines changed

openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionWebSearch.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ private constructor(
7070
*/
7171
fun action(): Action = action.getRequired("action")
7272

73+
/**
74+
* Returns the action if present.
75+
*
76+
* Unlike [action], this method returns an empty optional when the server omits the field,
77+
* allowing callers to gracefully handle scenarios where the model did not provide tool
78+
* parameters (for example, when `maxToolCalls` limits are reached).
79+
*/
80+
fun actionOptional(): Optional<Action> = action.getOptional("action")
81+
7382
/**
7483
* The status of the web search tool call.
7584
*
@@ -287,7 +296,7 @@ private constructor(
287296
}
288297

289298
id()
290-
action().validate()
299+
action.getOptional("action").ifPresent { it.validate() }
291300
status().validate()
292301
_type().let {
293302
if (it != JsonValue.from("web_search_call")) {

openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionWebSearchTest.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package com.openai.models.responses
55
import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
66
import com.openai.core.jsonMapper
77
import org.assertj.core.api.Assertions.assertThat
8+
import org.assertj.core.api.Assertions.assertThatCode
89
import org.junit.jupiter.api.Test
910

1011
internal class ResponseFunctionWebSearchTest {
@@ -72,4 +73,20 @@ internal class ResponseFunctionWebSearchTest {
7273

7374
assertThat(roundtrippedResponseFunctionWebSearch).isEqualTo(responseFunctionWebSearch)
7475
}
76+
77+
@Test
78+
fun deserializeWithoutAction() {
79+
val jsonMapper = jsonMapper()
80+
val payload = """
81+
{"id":"ws_missing","status":"completed","type":"web_search_call"}
82+
""".trimIndent()
83+
84+
val responseFunctionWebSearch =
85+
jsonMapper.readValue(payload, jacksonTypeRef<ResponseFunctionWebSearch>())
86+
87+
assertThat(responseFunctionWebSearch.actionOptional()).isEmpty
88+
assertThat(responseFunctionWebSearch.status())
89+
.isEqualTo(ResponseFunctionWebSearch.Status.COMPLETED)
90+
assertThatCode { responseFunctionWebSearch.validate() }.doesNotThrowAnyException()
91+
}
7592
}

0 commit comments

Comments
 (0)