fix(api-server): mark replayed tool calls completed in Responses output items
The non-streaming /v1/responses path built function_call and function_call_output output items with no status field (and no item id), while the SSE streaming path correctly emits status in_progress -> completed. Spec-strict OpenAI clients reading the non-streaming output array could interpret the status-less function_call items as pending calls the CLIENT must execute — but these tools were already executed server-side by the Hermes agent and are replayed for structured tool UI only. Reported by a community user whose GPT-5.6 client concluded 'a server should not tell an OpenAI client to execute a tool the server already executed itself'. - _extract_output_items now stamps status: completed and spec-shaped item ids (fc_/fco_) on replayed items, matching the streaming path - test updated to pin status + id shape - docs example updated + explicit note that output tool calls are replayed, never pending
This commit is contained in:
parent
5dc0fa3889
commit
a51a4cb096
|
|
@ -5981,14 +5981,23 @@ class APIServerAdapter(BasePlatformAdapter):
|
|||
for tc in msg["tool_calls"]:
|
||||
func = tc.get("function", {})
|
||||
items.append({
|
||||
"id": f"fc_{uuid.uuid4().hex[:24]}",
|
||||
"type": "function_call",
|
||||
# These calls were already executed server-side by the
|
||||
# Hermes agent; they are replayed for structured tool
|
||||
# UI only. Mark them completed (matching the SSE
|
||||
# streaming path) so OpenAI clients don't interpret
|
||||
# them as pending calls the client must execute.
|
||||
"status": "completed",
|
||||
"name": func.get("name", ""),
|
||||
"arguments": func.get("arguments", ""),
|
||||
"call_id": tc.get("id", ""),
|
||||
})
|
||||
elif role == "tool":
|
||||
items.append({
|
||||
"id": f"fco_{uuid.uuid4().hex[:24]}",
|
||||
"type": "function_call_output",
|
||||
"status": "completed",
|
||||
"call_id": msg.get("tool_call_id", ""),
|
||||
"output": msg.get("content", ""),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -2033,9 +2033,15 @@ class TestToolCallsInOutput:
|
|||
assert output[0]["name"] == "calculator"
|
||||
assert output[0]["arguments"] == '{"expression": "6*7"}'
|
||||
assert output[0]["call_id"] == "call_abc123"
|
||||
# Replayed server-executed calls must be marked completed so
|
||||
# OpenAI clients don't treat them as pending calls to execute.
|
||||
assert output[0]["status"] == "completed"
|
||||
assert output[0]["id"].startswith("fc_")
|
||||
assert output[1]["type"] == "function_call_output"
|
||||
assert output[1]["call_id"] == "call_abc123"
|
||||
assert output[1]["output"] == "42"
|
||||
assert output[1]["status"] == "completed"
|
||||
assert output[1]["id"].startswith("fco_")
|
||||
assert output[2]["type"] == "message"
|
||||
assert output[2]["content"][0]["text"] == "The result is 42."
|
||||
|
||||
|
|
|
|||
|
|
@ -134,14 +134,16 @@ OpenAI Responses API format. Supports server-side conversation state via `previo
|
|||
"status": "completed",
|
||||
"model": "hermes-agent",
|
||||
"output": [
|
||||
{"type": "function_call", "name": "terminal", "arguments": "{\"command\": \"ls\"}", "call_id": "call_1"},
|
||||
{"type": "function_call_output", "call_id": "call_1", "output": "README.md src/ tests/"},
|
||||
{"type": "function_call", "status": "completed", "name": "terminal", "arguments": "{\"command\": \"ls\"}", "call_id": "call_1"},
|
||||
{"type": "function_call_output", "status": "completed", "call_id": "call_1", "output": "README.md src/ tests/"},
|
||||
{"type": "message", "role": "assistant", "content": [{"type": "output_text", "text": "Your project has..."}]}
|
||||
],
|
||||
"usage": {"input_tokens": 50, "output_tokens": 200, "total_tokens": 250}
|
||||
}
|
||||
```
|
||||
|
||||
Tool calls in the `output` array were already executed server-side by the Hermes agent — they are replayed with `"status": "completed"` for structured tool UI, never as pending calls for the client to execute.
|
||||
|
||||
**Inline image input:** `input[].content` can contain `input_text` and `input_image` parts. Both remote URLs and `data:image/...` URLs are supported:
|
||||
|
||||
```json
|
||||
|
|
|
|||
Loading…
Reference in New Issue