Commit 2a1c563
fix(chat+langgraph): streaming complex-content + GFM markdown + model picker (chat 0.0.18, langgraph 0.0.10) (#191)
* fix(langgraph): stream complex-content correctly + dedupe assistant bubbles
OpenAI's gpt-5/o-series Responses API streams `BaseMessage.content` as
arrays of typed blocks (`[{type:'text',text:'…',index:n}, …]`) rather
than accumulated strings, and emits two parallel views of the same
assistant message: per-event `AIMessageChunk`s on `messages-tuple` and
the canonical `ai` on `values`-sync. The bridge wasn't equipped for
either — chunks JSON-dumped into the bubble and the two paths each
spawned their own bubble that never collapsed.
Bridge fixes (`stream-manager.bridge.ts`):
- `extractText` walks complex-content arrays and pulls visible text
blocks (`text` / `output_text`), skipping reasoning / tool_use /
image blocks.
- `accumulateContent` merges incoming-chunk content into the prior
slot's accumulated text. Handles the three cases: incoming is a
strict superset (final-id swap), existing is a strict superset
(chunk arrives after canonical), or pure delta append. Always
returns string so downstream `findContentMatch` can prefix-compare
cleanly without `JSON.stringify`-ing the array.
- `normalizeMessageType` collapses `AIMessage` / `AIMessageChunk` /
`ai` / `assistant` to `ai` so `findContentMatch` and
`sameRoleAndContent` correctly match across the values-sync and
messages-tuple paths.
- `mergeMessages` gains an AIMessageChunk fallback: when an
AIMessageChunk arrives without an id-match or content-prefix match,
accumulate into the trailing AI message. The OpenAI Responses API
emits per-chunk *event* ids, not message ids, so consecutive chunks
would otherwise each create a fresh bubble.
- Empty-content AI placeholders are dropped from `state.messages`
before the values-sync merge — keeping them creates a phantom slot
that competes with the chunk-streamed AIMessageChunk.
- `collapseAdjacentAi` post-pass collapses adjacent AI messages where
one's text is a prefix/equal of the other, keeping the older slot's
id for stable track-by-id.
Also dropped an obsolete hand-rolled rawMessages throttle in
`agent.fn.ts` — `messages$` already emits at the bridge boundary and
extra signal-side throttling collapsed visible token streaming.
Bumps @ngaf/langgraph 0.0.9 → 0.0.10.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(chat): render markdown with full GFM coverage + visible CommonMark styles
Two related rendering bugs converged into a chat that showed raw JSON
arrays and then, after fixing that, plain unstyled paragraphs with no
heading sizes, bullets, or table borders.
`messageContent` shared util:
- Was JSON-stringifying complex-content arrays, dumping
`[{"type":"text",…}]` into the chat bubble for OpenAI gpt-5 / o-series
output. Now extracts visible `text` / `output_text` blocks the same
way the langgraph adapter does and joins them; everything else
(reasoning, tool_use, images) is skipped.
`chat-streaming-md` component:
- Switched to `ViewEncapsulation.None`. The component renders markdown
by assigning sanitized HTML to `innerHTML`, so the resulting `<ul>`,
`<p>`, `<table>`, etc. nodes never carry the `_ngcontent-…`
attribute that emulated encapsulation requires. Without this the
exported `CHAT_MARKDOWN_STYLES` were silently skipped for every
selector below `:host`.
- Wired `CHAT_MARKDOWN_STYLES` into the component (it was exported but
never applied anywhere).
`CHAT_MARKDOWN_STYLES`:
- Re-scoped from `:host` to `chat-streaming-md` element selectors so
the rules stay locally meaningful under `ViewEncapsulation.None`.
- Expanded coverage to the full CommonMark + GFM surface: heading
hierarchy (h1–h6 with size scale), `strong` / `b`, `em` / `i`,
`del` / `s`, `mark`, `sub` / `sup`, `a` (with hover), bullet /
ordered / nested lists with visible `disc` / `circle` / `decimal`
markers, GFM task-list checkboxes, inline `code` / fenced `pre`,
`blockquote`, `hr`, GFM `table` (bordered, header background), and
`img` (max-width).
`marked` options:
- Enabled `gfm: true, breaks: true` so single newlines render as
`<br>` (matching common chat UX) and tables / strikethrough / task
lists / autolinks are honored.
Bumps @ngaf/chat 0.0.17 → 0.0.18.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(chat): first-class model picker on <chat> + restyle welcome suggestions as pills
Two UX improvements driven by smoke-testing the chat-select primitive in
real apps.
`<chat>` composition:
- New `[modelOptions]`, `[(selectedModel)]`, `[modelPickerPlaceholder]`
inputs that, when populated, render a `<chat-select chatInputModelSelect>`
inside the chat-input pill on both the welcome screen and the
conversation footer. Consumers no longer have to project the slot
themselves for the common "model picker" use case — they just pass
options + a model signal.
- Slot projection still works in conversation mode for any consumer
that needs custom chat-input children (an inner `ngProjectAs`
bridges the outer `[chatInputModelSelect]` content through).
Welcome suggestions:
- Restyled from full-width stacked rows separated by dividers to
centered floating pills (`border-radius: 9999px`, surface
background, equal gap). Matches the chat-input pill aesthetic.
- Container becomes `flex-wrap` + `justify-content: center` + `gap: 8px`
so suggestions reflow naturally on narrow viewports.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump cockpit demos + superpowers docs from gpt-4o-mini to gpt-5-mini
Default model in every demo graph and prose example is now gpt-5-mini.
Reasoning models (gpt-5/o-series) stream visibly out of the box at
`reasoning.effort='minimal'`, which the langgraph adapter sets by
default — no developer-facing config needed. Older non-reasoning
gpt-4o-mini references in graphs and docs were stale.
No code path changed; pure model-name swap across cockpit examples
and docs/superpowers plan / spec files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(spec): chat reasoning + tool-call templates (B2 design)
Design spec for the next chat phase: surface model reasoning content
as a first-class collapsible "Thinking… / Thought for Ns" pill above
the assistant response, and turn tool-call rendering into a first-
class extension surface via a chatToolCallTemplate directive while
keeping a polished default that auto-collapses completed cards and
groups sequential same-name calls.
Lands one new primitive (<chat-reasoning>), one new directive
(chatToolCallTemplate, including a "*" wildcard catch-all), augments
two existing primitives (<chat-tool-calls>, <chat-tool-call-card>),
and adds two new optional fields (Message.reasoning,
Message.reasoningDurationMs) populated by both adapters from
provider-agnostic sources: LangGraph complex-content reasoning blocks
and AG-UI REASONING_MESSAGE_* events.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(chat): messageContent now extracts text from complex-content arrays
The prior assertion checked that JSON-stringified output contained the
literal string 'text' (the JSON key from {type:'text',...}). After the
0.0.18 fix to extract visible text instead of dumping JSON, the
assertion is wrong by construction. Replaced with two assertions:
single-block text extraction, and multi-block concatenation that skips
reasoning blocks.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(plan): chat reasoning + tool-call templates implementation plan
Eleven phases, ~70 tasks. TDD throughout for new primitives + the
reasoning conformance fixture. Each task is self-contained with full
context, exact paths, complete code, and explicit verification
commands. Subagent-friendly per superpowers:subagent-driven-development.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent ed2825d commit 2a1c563
42 files changed
Lines changed: 4484 additions & 163 deletions
File tree
- cockpit
- chat/generative-ui/python/src
- deep-agents
- filesystem/python
- docs
- src
- memory/python/src
- planning/python/src
- sandboxes/python
- docs
- src
- skills/python
- docs
- src
- subagents/python/src
- langgraph
- deployment-runtime/python/src
- durable-execution/python
- docs
- src
- interrupts/python/docs
- memory/python
- docs
- src
- persistence/python/docs
- streaming/python
- docs
- src
- subgraphs/python
- docs
- src
- time-travel/python
- docs
- src
- docs/superpowers
- plans
- specs
- libs
- chat
- src/lib
- compositions
- chat
- streaming
- styles
- langgraph
- src/lib
- internals
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
| 22 | + | |
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
139 | 139 | | |
140 | 140 | | |
141 | 141 | | |
142 | | - | |
| 142 | + | |
143 | 143 | | |
144 | 144 | | |
145 | 145 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
38 | | - | |
| 38 | + | |
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
27 | | - | |
| 27 | + | |
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
30 | | - | |
| 30 | + | |
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
153 | 153 | | |
154 | 154 | | |
155 | 155 | | |
156 | | - | |
| 156 | + | |
157 | 157 | | |
158 | 158 | | |
159 | 159 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
74 | | - | |
| 74 | + | |
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
150 | 150 | | |
151 | 151 | | |
152 | 152 | | |
153 | | - | |
| 153 | + | |
154 | 154 | | |
155 | 155 | | |
156 | 156 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
68 | | - | |
| 68 | + | |
69 | 69 | | |
70 | 70 | | |
71 | 71 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
26 | | - | |
| 26 | + | |
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
31 | | - | |
| 31 | + | |
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| |||
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
41 | | - | |
| 41 | + | |
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
| |||
48 | 48 | | |
49 | 49 | | |
50 | 50 | | |
51 | | - | |
| 51 | + | |
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
| |||
0 commit comments