Agent loop stops after first tool call with non-Claude models (GLM-5): generateSandboxedUi never invoked
Mantenedores costumam responder em até 1 dia
Ninguém assumiu esta issue ainda.
Avaliação
- Dificuldade
- 4/5
- Tempo estimado
- 3-5 dias
- Facilidade para iniciantes
- 48/100
- Tipo de issue
- Bug
- Clareza
- Razoavelmente clara
- Status de atividade
- Pouca atividade
- Stack de tecnologia
- python, typescript
- Domínio
- ai-infra-agents, backend-api-design
Direção de pesquisa
Reproduce the issue using apps/agent/src/model.py and its build_model() configuration for GLM-5. Trace the tool-call continuation after plan_visualization through deepagents, CopilotKitMiddleware, and ag-ui-langgraph, using the reported _generate/_agenerate and HTTP-request observations as checkpoints. Done means a second LLM request invokes generateSandboxedUi and the UI renders an iframe.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
Summary
When using a non-Claude OpenAI-compatible model (tested with GLM-5 via Zhipu coding endpoint https://open.bigmodel.cn/api/coding/paas/v4), the agent correctly calls plan_visualization on the first LLM turn but never proceeds to a second LLM turn to call generateSandboxedUi. The result is that the user sees the assistant's text description of the widget that "would" be built, but no sandboxed iframe is ever rendered (0 iframes in the DOM).
The README warns that "weaker models will produce broken layouts", but this is a different failure mode: GLM-5 successfully generates a complete, valid widget when prompted directly (9.6 KB of JS, all 6 generateSandboxedUi parameters emitted in the correct order). The failure is not model capability — it's that the agent loop doesn't give the model a second turn after the plan_visualization tool returns.
Environment
- Repo commit:
457e60c(currentmainas of 2026-07-18) - Model:
glm-5(Zhipu coding endpoint), wired in vialangchain_openai.ChatOpenAIwithLLM_MODEL=glm-5,LLM_PROVIDER=openai - Python 3.13, Node 24, pnpm 10, uv
- macOS Darwin 25.5.0
- Same behavior on fresh thread (cleared localStorage + reopened browser)
Repro
- Replace
apps/agent/src/model.pybuild_model()so thatLLM_MODEL=glm-5+LLM_PROVIDER=openairoutes toChatOpenAIwith the Zhipubase_url. make setup+make dev.- Open http://localhost:3000, type "Draw binary search visualization".
- GLM-5 replies with text + calls
plan_visualization. UI shows the "✓ Plan: visualization" pill. - Nothing else happens. No iframe, no
generateSandboxedUiactivity event.
Evidence
What the agent loop sees (first LLM turn) — captured by monkey-patching bind_tools on the model:
[MODEL_DEBUG] bind_tools (19): ['write_todos', 'ls', 'read_file', 'write_file', 'edit_file', 'glob', 'grep', 'task', 'query_data', 'plan_visualization', 'manage_todos', 'get_todos', 'generate_form', 'generateSandboxedUi', 'toggleTheme', 'pieChart', 'barChart', 'scheduleTime', 'render_a2ui']
generateSandboxedUi IS in the offered tools. GLM-5 returns:
finish_reason: tool_calls
content: "I'll create an interactive binary search visualization that shows how the algorithm narrows down the search space step by step. Let me plan and build this for you."
tool_calls: [plan_visualization(approach=..., technology=..., key_elements=[...])]
After plan_visualization returns its string, no further LLM call is made — verified by hooking both _generate / _agenerate and the underlying httpx client; zero additional HTTP requests to /chat/completions after the first turn.
Direct replay against GLM-5 API with the same system prompt + same tool list + the plan_visualization tool result already in messages:
messages = [
{"role":"system","content":"You MUST call generateSandboxedUi now that plan is done."},
{"role":"user","content":"Draw binary search visualization"},
{"role":"assistant","content":"I'll build this.","tool_calls":[{"id":"call_1",...,"function":{"name":"plan_visualization","arguments":"..."}}]},
{"role":"tool","tool_call_id":"call_1","content":"Plan: step-by-step\nTech: SVG\n- array\n- pointers\n- controls"},
]
GLM-5 returns:
finish_reason: tool_calls
content: "" (empty)
tool_calls: [generateSandboxedUi(
initialHeight=580,
placeholderMessages=[3 items],
css="<3201 chars>",
html="<2086 chars>",
jsFunctions="<9673 chars>", # full binary search algorithm + UI
jsExpressions=["<347 chars>"]
)]
usage: completion_tokens=4839 (incl. 87 reasoning_tokens)
So the model is fully capable of producing the widget in a single shot once the plan_visualization tool result is fed back — but the agent loop never feeds it back.
Hypothesis
When finish_reason=tool_calls and content is non-empty simultaneously (GLM-5's default style — it emits a short sentence alongside the tool call), some layer in deepagents + CopilotKitMiddleware + ag-ui-langgraph appears to treat the message as a final response and does not run the tool-→-LLM-continue cycle. Claude models, per the README's recommended config, apparently emit plan + widget in a single turn or in a way that sidesteps this path; GLM emits them across two turns and gets stuck between.
I tried to nail down the exact layer but the LangChain 1.x callback surface (on_chat_model_start etc.) doesn't fire for the calls that GLM-5 actually answers, so I couldn't trace whether deepagents ran the second turn and it was dropped at the CopilotKit middleware, or whether deepagents itself never re-entered the model.
Suggested fixes / things to try
- Force a second LLM turn after
plan_visualizationeven when the first turn's message has bothcontentandtool_calls. The current behavior looks like a short-circuit on "has content → treat as final". - Or allow
plan_visualizationto be skipped so a model that prefers to callgenerateSandboxedUidirectly can do so in a single turn. - Or document explicitly that non-Claude models must emit
plan_visualizationandgenerateSandboxedUiin a single assistant turn, with a system-prompt nudge that makes GLM/etc. do that. - Investigate whether
ag-ui-langgraphorCopilotKitMiddlewareis the layer that drops the continuation — happy to dig further if a maintainer can point me at where to instrument.
Reproducibility script
I have a self-contained Python script that hits GLM-5 directly with the same tool schemas and prompts, reproducing the "2-turn happy path" reliably — can paste it in if useful.
Why I'm reporting this
The project is marketed as "open-source showcase for AI-generated UI" and the README explicitly supports gpt-* fallback via LLM_MODEL. Any user trying it with a non-Claude OpenAI-compatible endpoint (GLM / DeepSeek / Qwen / Kimi / MiniMax / etc.) will hit this silent half-failure and conclude the model is too weak, when in fact it's an agent-loop bug. Worth either fixing or documenting.
- Linguagem predominante
- TypeScript
- Estrelas
- 3.9k
- Forks
- 414
- Merge médio
- 4h
- PRs com merge (30d)
- 6
Preparar o ambiente
- Sem Dockerfile nem arquivo Docker Compose
- Sem modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de CopilotKit/OpenIntelligentUI
-
Script dedup key truncation may cause collisionsTalvez já em andamento @saschabuehrle assumiu há 215 dias. Abertabug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100
CopilotKit/OpenIntelligentUI#37 ·
Mantenedores costumam responder em até 1 dia
-
enhancement
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100
CopilotKit/OpenIntelligentUI#29 ·
Mantenedores costumam responder em até 1 dia
-
Self-host with Docker Compose and optional server-held keysTalvez já em andamento @kannan983 assumiu há 16 dias. Aberta
Dificuldade 4/5 3-5 dias Facilidade para iniciantes 18/100
CopilotKit/OpenIntelligentUI#98 ·
Mantenedores costumam responder em até 1 dia
-
Support OpenAI-compatible gateways (OpenRouter) via vendor/model namesTalvez já em andamento @kannan983 assumiu há 16 dias. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 25/100
CopilotKit/OpenIntelligentUI#97 ·
Mantenedores costumam responder em até 1 dia
-
UIAberta
Dificuldade 5/5 Mais de uma semana Facilidade para iniciantes 8/100
CopilotKit/OpenIntelligentUI#96 ·
Mantenedores costumam responder em até 1 dia
Todas as issues de CopilotKit/OpenIntelligentUI
Issues semelhantes
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100
grafana/profiles-drilldown#1212 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
grafana/traces-drilldown#980 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
openremote/openremote#3487 · 1 comentário ·
-
triage:needs-human
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 67/100
garrytan/gbrain#7635 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 82/100
coollabsio/jean#867 ·
Mantenedores costumam responder em até 1 dia