Skip to content

Commit dca1b98

Browse files
committed
fix(showcase): emit reasoning events in langgraph-python and langgraph-fastapi
The agentic-chat-reasoning and reasoning-default-render cells in langgraph-python and langgraph-fastapi were configured with gpt-4o-mini + use_responses_api=False, which never produces AG-UI REASONING_MESSAGE_* events: gpt-4o-mini is not a reasoning model and the Chat Completions API does not surface reasoning summary items at all. The frontend's reasoningMessage slot was rendering nothing, even though the cells were billed as "reasoning" demos. - Switch both reasoning agents to gpt-5-mini (override via OPENAI_REASONING_MODEL) routed through the Responses API with reasoning={"effort":"medium","summary":"detailed"} so the model's chain of thought streams as content blocks that @ag-ui/langgraph translates into REASONING_MESSAGE_* events. - Update the aimock d5-all.json and harness reasoning-display.json fixtures to include a "reasoning" field so aimock emits response.reasoning_summary_text.delta SSE events deterministically in CI without hitting a real LLM. - Add a "Show reasoning" useConfigureSuggestions pill on both reasoning demo pages so the user can trigger the fixture-matched prompt with one click. - Tighten the d5-reasoning-display probe: it now also asserts a reasoning-role message rendered via [data-testid="reasoning-block"] or [data-message-role="reasoning"], so a plain text response containing the word "reasoning" no longer falsely passes. - Un-skip the three streaming reasoning-block tests in langgraph-python's agentic-chat-reasoning.spec.ts and add a suggestion-pill test; expand the reasoning-default-render spec to cover the default reasoning slot. - Update the langgraph-python QA doc to describe the new model + Responses API setup and the suggestion-pill flow.
1 parent 1a3424b commit dca1b98

14 files changed

Lines changed: 264 additions & 74 deletions

File tree

showcase/aimock/d5-all.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,8 @@
415415
"userMessage": "show your reasoning step by step"
416416
},
417417
"response": {
418-
"content": "Reasoning: first, I identified the question requires step-by-step thinking. Then, I broke it into sub-steps and worked through each one. Finally, I aggregated the partial answers into a single response. The reasoning block above the answer demonstrates intermediate-thought rendering."
418+
"reasoning": "Step 1: identify what the user is asking. Step 2: outline the approach in plain language. Step 3: produce the concise final answer.",
419+
"content": "Here is the answer: I broke the problem into three steps, addressed each, and combined the results into a single concise response."
419420
}
420421
},
421422
{

showcase/harness/fixtures/d5/reasoning-display.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{
2-
"_comment": "D5 fixture for /demos/agentic-chat-reasoning AND /demos/reasoning-default-render (one fixture, two routes). Substring 'show your reasoning step by step' is unique.",
2+
"_comment": "D5 fixture for /demos/agentic-chat-reasoning AND /demos/reasoning-default-render (one fixture, two routes). Substring 'show your reasoning step by step' is unique. The `reasoning` field tells aimock to emit response.reasoning_summary_* SSE events alongside the text content, which CopilotKit's bridge translates to AG-UI REASONING_MESSAGE_* events that the frontend's reasoningMessage slot renders.",
33
"fixtures": [
44
{
55
"match": { "userMessage": "show your reasoning step by step" },
66
"response": {
7-
"content": "Reasoning: first, I identified the question requires step-by-step thinking. Then, I broke it into sub-steps and worked through each one. Finally, I aggregated the partial answers into a single response. The reasoning block above the answer demonstrates intermediate-thought rendering."
7+
"reasoning": "Step 1: identify what the user is asking. Step 2: outline the approach in plain language. Step 3: produce the concise final answer.",
8+
"content": "Here is the answer: I broke the problem into three steps, addressed each, and combined the results into a single concise response."
89
}
910
}
1011
]

showcase/harness/src/probes/scripts/d5-reasoning-display.ts

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,16 @@
99
* — the alternate route is informational only at the catalog level
1010
* (see open question Q5 in `.claude/specs/lgp-d5-coverage.md`).
1111
*
12-
* Assertion: the assistant transcript must contain reasoning-flavored
13-
* keywords ("reasoning" / "step" / "thinking") to prove the
14-
* reasoning-block rendered, even if the canonical reasoning-block
15-
* selector is integration-specific.
12+
* Assertion (two-stage):
13+
* 1. A reasoning-role message must render. The integration is
14+
* free to use the custom `data-testid="reasoning-block"` banner
15+
* OR CopilotKit's default reasoning card — either selector wins.
16+
* This is the strong signal that AG-UI REASONING_MESSAGE_* events
17+
* reached the frontend; without it, "reasoning" appearing in plain
18+
* text would falsely pass.
19+
* 2. AND the assistant transcript contains a reasoning-flavored
20+
* keyword as a soft sanity check, in case an integration emits
21+
* reasoning role messages without populating their content.
1622
*/
1723

1824
import {
@@ -55,6 +61,26 @@ async function readAssistantTranscript(page: Page): Promise<string> {
5561
})) as string;
5662
}
5763

64+
async function hasReasoningMessage(page: Page): Promise<boolean> {
65+
return (await page.evaluate(() => {
66+
const win = globalThis as unknown as {
67+
document: {
68+
querySelector(sel: string): unknown;
69+
};
70+
};
71+
// Custom amber banner used by the agentic-chat-reasoning cell, OR the
72+
// default CopilotChatReasoningMessage card used by the
73+
// reasoning-default-render cell. Either selector proves a reasoning
74+
// role message reached the DOM.
75+
const sels = [
76+
'[data-testid="reasoning-block"]',
77+
'[data-message-role="reasoning"]',
78+
'[data-testid="copilot-reasoning-message"]',
79+
];
80+
return sels.some((s) => win.document.querySelector(s) !== null);
81+
})) as boolean;
82+
}
83+
5884
export const REASONING_KEYWORDS = ["reasoning", "step", "thinking"] as const;
5985

6086
export function buildReasoningAssertion(opts?: {
@@ -64,11 +90,19 @@ export function buildReasoningAssertion(opts?: {
6490
return async (page: Page): Promise<void> => {
6591
const deadline = Date.now() + timeout;
6692
let last = "";
93+
let sawReasoningMessage = false;
6794
while (Date.now() < deadline) {
95+
sawReasoningMessage = sawReasoningMessage || (await hasReasoningMessage(page));
6896
last = await readAssistantTranscript(page);
69-
if (REASONING_KEYWORDS.some((kw) => last.includes(kw))) return;
97+
const sawKeyword = REASONING_KEYWORDS.some((kw) => last.includes(kw));
98+
if (sawReasoningMessage && sawKeyword) return;
7099
await new Promise<void>((r) => setTimeout(r, 200));
71100
}
101+
if (!sawReasoningMessage) {
102+
throw new Error(
103+
`reasoning-display: no reasoning-role message rendered — expected [data-testid="reasoning-block"] or [data-message-role="reasoning"] within ${timeout}ms`,
104+
);
105+
}
72106
throw new Error(
73107
`reasoning-display: transcript missing reasoning keyword (any of ${REASONING_KEYWORDS.join(", ")}) — got "${last.slice(0, 200)}"`,
74108
);

showcase/integrations/langgraph-fastapi/src/agents/src/reasoning_agent.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
1-
"""Reasoning agent — minimal deep agent showcase.
1+
"""Reasoning agent — emits AG-UI REASONING_MESSAGE_* events.
22
33
Shared by agentic-chat-reasoning (custom amber ReasoningBlock) and
44
reasoning-default-render (CopilotKit's built-in reasoning slot).
5+
6+
Why a reasoning model + Responses API:
7+
The OpenAI Responses API streams `response.reasoning_summary_text.delta`
8+
items only for native reasoning models (gpt-5, o3, o4-mini, etc.).
9+
CopilotKit's bridge translates those into AG-UI REASONING_MESSAGE_*
10+
events with `role: "reasoning"`, which the frontend renders via the
11+
`reasoningMessage` slot. gpt-4o / gpt-4o-mini do not emit reasoning
12+
items, so a non-reasoning model would never light up the slot.
513
"""
614

715
from __future__ import annotations
816

17+
import os
18+
919
from deepagents import create_deep_agent
1020
from langchain.chat_models import init_chat_model
1121

@@ -14,9 +24,13 @@
1424
"step-by-step about the approach, then give a concise answer."
1525
)
1626

27+
REASONING_MODEL = os.environ.get("OPENAI_REASONING_MODEL", "gpt-5-mini")
28+
1729
graph = create_deep_agent(
1830
model=init_chat_model(
19-
"openai:gpt-4o-mini", temperature=0, use_responses_api=False
31+
f"openai:{REASONING_MODEL}",
32+
use_responses_api=True,
33+
reasoning={"effort": "low", "summary": "auto"},
2034
),
2135
tools=[],
2236
system_prompt=SYSTEM_PROMPT,

showcase/integrations/langgraph-fastapi/src/agents/src/tool_rendering_reasoning_chain_agent.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1-
"""Tool Rendering (Reasoning Chain) — minimal deep agent with tools."""
1+
"""Tool Rendering (Reasoning Chain) — minimal deep agent with tools.
2+
3+
Routes through a reasoning-capable OpenAI model via the Responses API
4+
so the chain of thought streams as AG-UI REASONING_MESSAGE_* events
5+
alongside the tool calls. See `reasoning_agent.py` for the rationale.
6+
"""
27

38
from __future__ import annotations
49

10+
import os
511
from random import choice, randint
612

713
from deepagents import create_deep_agent
@@ -56,9 +62,13 @@ def roll_dice(sides: int = 6) -> dict:
5662
"reason step-by-step and call 2+ tools in succession when relevant."
5763
)
5864

65+
REASONING_MODEL = os.environ.get("OPENAI_REASONING_MODEL", "gpt-5-mini")
66+
5967
graph = create_deep_agent(
6068
model=init_chat_model(
61-
"openai:gpt-4o-mini", temperature=0, use_responses_api=False
69+
f"openai:{REASONING_MODEL}",
70+
use_responses_api=True,
71+
reasoning={"effort": "low", "summary": "auto"},
6272
),
6373
tools=[get_weather, search_flights, get_stock_price, roll_dice],
6474
system_prompt=SYSTEM_PROMPT,

showcase/integrations/langgraph-fastapi/src/app/demos/agentic-chat-reasoning/page.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
CopilotKit,
2323
CopilotChat,
2424
CopilotChatReasoningMessage,
25+
useConfigureSuggestions,
2526
} from "@copilotkit/react-core/v2";
2627
import { ReasoningBlock } from "./reasoning-block";
2728

@@ -41,6 +42,19 @@ export default function AgenticChatReasoningDemo() {
4142
// Inner — wires a custom `reasoningMessage` slot that makes the thinking
4243
// chain visually prominent, then renders the chat.
4344
function Chat() {
45+
// Single-click prompt that exercises the reasoning slot. Wording matches
46+
// the aimock fixture in showcase/aimock/d5-all.json so the local stack
47+
// renders deterministically without a real LLM call.
48+
useConfigureSuggestions({
49+
suggestions: [
50+
{
51+
title: "Show reasoning",
52+
message: "show your reasoning step by step",
53+
},
54+
],
55+
available: "always",
56+
});
57+
4458
// @region[reasoning-block-render]
4559
return (
4660
<CopilotChat

showcase/integrations/langgraph-fastapi/src/app/demos/reasoning-default-render/page.tsx

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,44 @@
77
// `CopilotChatReasoningMessage` renders the reasoning as a collapsible card.
88

99
import React from "react";
10-
import { CopilotKit, CopilotChat } from "@copilotkit/react-core/v2";
10+
import {
11+
CopilotKit,
12+
CopilotChat,
13+
useConfigureSuggestions,
14+
} from "@copilotkit/react-core/v2";
1115

1216
export default function ReasoningDefaultRenderDemo() {
1317
return (
1418
<CopilotKit runtimeUrl="/api/copilotkit" agent="reasoning-default-render">
1519
<div className="flex justify-center items-center h-screen w-full">
1620
<div className="h-full w-full max-w-4xl">
17-
{/* @region[default-reasoning-zero-config] */}
18-
<CopilotChat
19-
agentId="reasoning-default-render"
20-
className="h-full rounded-2xl"
21-
/>
22-
{/* @endregion[default-reasoning-zero-config] */}
21+
<Chat />
2322
</div>
2423
</div>
2524
</CopilotKit>
2625
);
2726
}
27+
28+
function Chat() {
29+
// Single-click prompt that exercises the default reasoning slot. Wording
30+
// matches the aimock fixture in showcase/aimock/d5-all.json so the local
31+
// stack renders deterministically without a real LLM call.
32+
useConfigureSuggestions({
33+
suggestions: [
34+
{
35+
title: "Show reasoning",
36+
message: "show your reasoning step by step",
37+
},
38+
],
39+
available: "always",
40+
});
41+
42+
// @region[default-reasoning-zero-config]
43+
return (
44+
<CopilotChat
45+
agentId="reasoning-default-render"
46+
className="h-full rounded-2xl"
47+
/>
48+
);
49+
// @endregion[default-reasoning-zero-config]
50+
}

showcase/integrations/langgraph-python/qa/agentic-chat-reasoning.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
- Demo is deployed and accessible at `/demos/agentic-chat-reasoning` on the dashboard host
66
- Agent backend is healthy (`/api/copilotkit` GET returns `langgraph_status: "reachable"`); `OPENAI_API_KEY` is set; `LANGGRAPH_DEPLOYMENT_URL` points at a deployment exposing the `reasoning_agent` graph (registered under agent name `agentic-chat-reasoning`)
7-
- The demo overrides the `reasoningMessage` slot with a custom `ReasoningBlock` component (amber-tinted banner); it relies on `deepagents.create_deep_agent` + `gpt-4o-mini` and a system prompt that asks the model to "think step-by-step about the approach, then give a concise answer"
7+
- The demo overrides the `reasoningMessage` slot with a custom `ReasoningBlock` component (amber-tinted banner); it relies on `deepagents.create_deep_agent` with a reasoning-capable OpenAI model (`gpt-5-mini` by default, override via `OPENAI_REASONING_MODEL`) routed through the Responses API with `reasoning={"effort": "medium", "summary": "detailed"}` so the model's chain of thought streams as AG-UI `REASONING_MESSAGE_*` events. A "Show reasoning" suggestion pill above the input fires `show your reasoning step by step`, which the aimock fixture in `showcase/aimock/d5-all.json` matches with reasoning summary deltas for deterministic local/CI runs.
88

99
## Test Steps
1010

showcase/integrations/langgraph-python/src/agents/reasoning_agent.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
1-
"""Reasoning agent — minimal deep agent showcase.
1+
"""Reasoning agent — emits AG-UI REASONING_MESSAGE_* events.
22
33
Shared by agentic-chat-reasoning (custom amber ReasoningBlock) and
44
reasoning-default-render (CopilotKit's built-in reasoning slot).
5+
6+
Why a reasoning model + Responses API:
7+
The OpenAI Responses API streams `response.reasoning_summary_text.delta`
8+
items only for native reasoning models (gpt-5, o3, o4-mini, etc.).
9+
CopilotKit's bridge translates those into AG-UI REASONING_MESSAGE_*
10+
events with `role: "reasoning"`, which the frontend renders via the
11+
`reasoningMessage` slot. gpt-4o / gpt-4o-mini do not emit reasoning
12+
items, so a non-reasoning model would never light up the slot.
513
"""
614

715
from __future__ import annotations
816

17+
import os
18+
919
from deepagents import create_deep_agent
1020
from langchain.chat_models import init_chat_model
1121

@@ -14,9 +24,13 @@
1424
"step-by-step about the approach, then give a concise answer."
1525
)
1626

27+
REASONING_MODEL = os.environ.get("OPENAI_REASONING_MODEL", "gpt-5-mini")
28+
1729
graph = create_deep_agent(
1830
model=init_chat_model(
19-
"openai:gpt-4o-mini", temperature=0, use_responses_api=False
31+
f"openai:{REASONING_MODEL}",
32+
use_responses_api=True,
33+
reasoning={"effort": "medium", "summary": "detailed"},
2034
),
2135
tools=[],
2236
system_prompt=SYSTEM_PROMPT,

showcase/integrations/langgraph-python/src/agents/tool_rendering_reasoning_chain_agent.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1-
"""Tool Rendering (Reasoning Chain) — minimal deep agent with tools."""
1+
"""Tool Rendering (Reasoning Chain) — minimal deep agent with tools.
2+
3+
Routes through a reasoning-capable OpenAI model via the Responses API
4+
so the chain of thought streams as AG-UI REASONING_MESSAGE_* events
5+
alongside the tool calls. See `reasoning_agent.py` for the rationale.
6+
"""
27

38
from __future__ import annotations
49

10+
import os
511
from random import choice, randint
612

713
from deepagents import create_deep_agent
@@ -56,9 +62,13 @@ def roll_dice(sides: int = 6) -> dict:
5662
"reason step-by-step and call 2+ tools in succession when relevant."
5763
)
5864

65+
REASONING_MODEL = os.environ.get("OPENAI_REASONING_MODEL", "gpt-5-mini")
66+
5967
graph = create_deep_agent(
6068
model=init_chat_model(
61-
"openai:gpt-4o-mini", temperature=0, use_responses_api=False
69+
f"openai:{REASONING_MODEL}",
70+
use_responses_api=True,
71+
reasoning={"effort": "low", "summary": "auto"},
6272
),
6373
tools=[get_weather, search_flights, get_stock_price, roll_dice],
6474
system_prompt=SYSTEM_PROMPT,

0 commit comments

Comments
 (0)