From da909cc191258b98486c9ff9dbacb0ef51b0c138 Mon Sep 17 00:00:00 2001 From: Todd Hoffman Date: Thu, 9 Jul 2026 16:04:17 -0700 Subject: [PATCH] fix: always include content field on outgoing messages Assistant messages that carry only tool_calls are serialized by agent_framework without a "content" key. OpenAI's official API tolerates the omission, but stricter OpenAI-compatible backends (vLLM-style gateways) reject the request with: 400 {'message': 'Field required', 'param': 'messages.N.content', 'code': 'missing'} Since this scaffold targets local OpenAI-compatible servers by default (openai_base_url: http://localhost:8080/v1), agents generated from it are disproportionately likely to hit this on their first tool call. Subclass OpenAIChatCompletionClient and override _prepare_messages_for_openai (the documented customization hook) to default "content" to "" on any message that lacks it. The key is only added when absent, so text and multimodal messages are untouched, and empty-string content alongside tool_calls is accepted by the official API as well. This is a workaround for upstream agent_framework serialization and can be dropped if fixed there. Found while debugging an agent generated by this skill (kyuz0/deep-research-agent); this upstreams the same fix. Co-Authored-By: Claude Fable 5 --- .../basic-tui-agent/src/engine/orchestrator.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/skills/local-agent-builder/examples/basic-tui-agent/src/engine/orchestrator.py b/skills/local-agent-builder/examples/basic-tui-agent/src/engine/orchestrator.py index 8f6e577..d7a138e 100644 --- a/skills/local-agent-builder/examples/basic-tui-agent/src/engine/orchestrator.py +++ b/skills/local-agent-builder/examples/basic-tui-agent/src/engine/orchestrator.py @@ -63,8 +63,19 @@ def _get_default_options(): } return options +class _CompatChatCompletionClient(OpenAIChatCompletionClient): + """Some OpenAI-compatible gateways require a "content" field on every + message, but the SDK omits it on assistant messages that only carry + tool_calls. Ensure it is always present ("" is accepted everywhere). + """ + def _prepare_messages_for_openai(self, chat_messages, role_key="role", content_key="content"): + prepared = super()._prepare_messages_for_openai(chat_messages, role_key, content_key) + for msg in prepared: + msg.setdefault("content", "") + return prepared + def _build_client(): - return OpenAIChatCompletionClient( + return _CompatChatCompletionClient( base_url=config.cfg["api"]["openai_base_url"], api_key=os.getenv("OPENAI_API_KEY", "dummy"), model=config.cfg["api"]["openai_model"]