Commit c317da8
chat reasoning + tool-call templates (chat 0.0.19, langgraph 0.0.11, ag-ui 0.0.3) (#192)
* feat(chat): add Message.reasoning + Message.reasoningDurationMs
Optional fields on the shared Message contract. Adapters populate them
from provider-agnostic sources (LangGraph reasoning/thinking content
blocks, AG-UI REASONING_MESSAGE_* events). UI primitives consume the
fields without provider-specific knowledge.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(chat): add formatDuration utility
Renders millisecond durations as compact human-readable labels:
<1s, Ns, Nm Ms. Powers the chat-reasoning 'Thought for Ns' pill.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(chat): chat-reasoning styles
Pill-shaped header with chevron + animated pulse dot for the streaming
state, expanded body with thin left border (matches the blockquote
pattern). Muted text throughout so reasoning content recedes next to
the response.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(chat): chat-reasoning primitive
Renders model reasoning content as a compact pill. Three visual states
(streaming with pulse + auto-expand, idle with 'Thought for Ns', idle
with 'Show reasoning' fallback). User toggle wins over auto logic for
the lifetime of the instance. Body re-uses chat-streaming-md so
markdown in reasoning output renders consistently with the response.
Adds @analogjs/vite-plugin-angular to the chat library's vite config
(with pool: 'forks' to preserve existing test isolation) so that
Angular signal inputs resolve correctly in vitest HostComponent specs.
Also adds tsconfig.spec.json required by the Angular compiler plugin.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(chat): export ChatReasoningComponent + formatDuration
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(chat): chat-reasoning auto-resets on streaming re-engage + spec amendments
Phase 2 review found three issues. Behavioral fix: re-add the
constructor effect that resets the manual expanded-override to null
when isStreaming transitions false→true (spec §3.3 bullet 3 — same
component instance reused across follow-up turns auto-expands when
the next reasoning phase begins). The previous "preserves user choice"
test conflated two scenarios; replace with one test asserting bullet 2
(no force-collapse on true→false), one test asserting user collapse
persists, and one test asserting auto-reset on false→true.
Spec drift: amend §3.1 so the content input defaults to '' (matching
the shipped pragmatic API; pairs cleanly with data-has-content="false"
hide-when-empty styling). Drop the unused [chatReasoningLabel] slot
from §3.1 — the [label] string input covers the common case.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(chat): chatToolCallTemplate directive
Per-tool-name template registry consumed by <chat-tool-calls>. A '*'
wildcard registers a catch-all for any unmapped tool name.
Also extends DebugElement.prototype.queryAll in test-setup to traverse
DebugNode (comment) children so directive-on-ng-template specs can use
the injector-predicate pattern under Angular 21.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(chat): export ChatToolCallTemplateDirective + context type
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(chat): test chatToolCallTemplate via viewChildren signal
The original spec used DebugElement.queryAll to enumerate directive
instances, which doesn't traverse comment nodes (where ng-template
compiles). The previous workaround monkey-patched
DebugElement.prototype.queryAll across the whole chat library — too
broad. Use viewChildren(ChatToolCallTemplateDirective) on the host
component instead; it picks up directives on ng-template nodes
naturally and needs no test-infrastructure changes. Revert the
monkey-patch in test-setup.ts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(chat): chat-tool-calls grouping + per-tool template registry
Sequential same-name tool calls auto-group into a collapsible strip
with a sensible default summary ('Searched N sites'). Consumers can
register per-tool-name templates via chatToolCallTemplate to fully
replace the default card UX, plus a '*' wildcard for any unmapped
name. [grouping]='none' opts out of the auto-collapse behavior;
[groupSummary] lets callers override the default registry.
Also widens ToolCallInfo to carry an optional status — Phase 5 will
use this to drive the at-a-glance status pill on chat-tool-call-card.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(chat): chat-tool-call-card status pill + default-collapsed
Tool-call cards now render a consistent status pill (spinner /
checkmark / error glyph) regardless of state, and default to collapsed
when complete. Running and errored cards stay expanded so progress and
failures are visible without clicks. User toggle wins for the lifetime
of the card. Adds defaultExpanded input to chat-trace; drops the
unused 200ms auto-collapse-on-done timeout in favor of explicit
defaults driven by consumers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(langgraph): extractReasoning + accumulateReasoning helpers
Walks complex-content arrays for {type:'reasoning'}/{type:'thinking'}
blocks (provider-agnostic between OpenAI Responses API and
Anthropic). Same accumulator semantics as accumulateContent: superset
takes priority for final-id swap, prefix-match keeps the longer side,
otherwise pure-delta append. Returns string so downstream code never
sees the raw block array. Exports _internalsForTesting for conformance
tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(langgraph): bridge accumulates reasoning + tracks per-message timing
mergeMessages now reads incoming reasoning content (from
{type:'reasoning'|'thinking'} blocks or an explicit Message.reasoning
field) and accumulates it into the merged slot alongside response
text. A per-message reasoningTimingMap captures when reasoning chunks
first arrive and when response text first overlaps; the manager
exposes getReasoningDurationMs(id) so the agent.fn projection can
populate Message.reasoningDurationMs. Map is cleared on thread
switch and bridge teardown.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(langgraph): toMessage populates Message.reasoning + reasoningDurationMs
agent.fn's toMessage projection reads the bridge's accumulated
reasoning string and asks the manager for the per-message duration.
Both fields land as undefined when no reasoning was emitted, so
existing consumers see no shape change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ag-ui): handle REASONING_MESSAGE_* events
REASONING_MESSAGE_START creates (or finds) an assistant slot with an
empty reasoning string and starts a per-message timing entry.
REASONING_MESSAGE_CONTENT/CHUNK appends to it. REASONING_MESSAGE_END
records the end timestamp and writes Message.reasoningDurationMs onto
the slot. TEXT_MESSAGE_START is now idempotent so a follow-up text
stream on the same messageId reuses the existing slot rather than
splitting reasoning + response into separate messages.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ag-ui): FakeAgent reasoningTokens option
Optional reasoningTokens?: string[] constructor argument that, when
provided, emits a REASONING_MESSAGE_START → CONTENT × N → END
sequence before the existing text-message stream. provideFakeAgUiAgent
plumbs the new option through. Lets demo apps and integration tests
exercise the reasoning UI end-to-end without a real model.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(chat/testing): add provider-neutral reasoning fixture
Defines a canonical abstract event sequence (reasoning start → three
chunks → end → text start → three chunks → end) and an
assertReasoningFixtureMessages() helper that both adapter conformance
suites use to verify identical Message[] output. Keeps the
populating logic for Message.reasoning + reasoningDurationMs honest
across implementations.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(ag-ui): reasoning-fixture conformance
Translates the shared @ngaf/chat/testing fixture sequence into AG-UI
wire format and asserts the reducer produces the expected Message[]
shape (single assistant message with full reasoning, full content,
and a non-negative reasoningDurationMs).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(langgraph): reasoning-fixture conformance
Translates the shared @ngaf/chat/testing fixture sequence into
LangGraph AIMessageChunk shape (complex-content arrays with
{type:'reasoning'} and {type:'text'} blocks) and asserts the bridge's
mergeMessages + toMessage projection produces the same Message[]
shape AG-UI does. One fixture, two adapters — keeps both honest.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(langgraph): wrap toMessage in arrow fn to avoid map index collision
Array.prototype.map passes (value, index, array) to its callback.
Passing toMessage directly caused TypeScript to infer that the optional
second parameter (getReasoningDurationMs: (id: string) => number |
undefined) could receive the numeric index, producing TS2345. Wrapping
it in an arrow function makes the call explicit and type-safe.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(chat): <chat> renders <chat-reasoning> + forwards chatToolCallTemplate
When an assistant Message carries a non-empty reasoning string, the
chat composition automatically renders <chat-reasoning> above the
response markdown. The pill streams visibly while reasoning content
is arriving (tail message + agent loading + no response text yet),
then collapses to 'Thought for Ns' once response tokens begin.
Consumers projecting <ng-template chatToolCallTemplate='search_web'>
directly into <chat> have those templates forwarded into the inner
<chat-tool-calls> via the same outer-content re-projection pattern
used for [chatInputModelSelect] and [chatWelcomeSuggestions].
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(chat): chat-reasoning + tool-call template references + 0.0.19 changelog
Six new/updated MDX pages covering Phase B2 surface:
- New chat-reasoning.mdx — primitive reference (states, inputs, slot)
- New chat-tool-call-template.mdx — directive reference + dispatch order
- New chat-tool-calls.mdx — grouping inputs + default summaries + template extension
- Updated chat-tool-call-card.mdx — status pill table + default-collapsed
- Updated chat.mdx — reasoning subsection + tool-call template projection example
- New getting-started/changelog.mdx — 0.0.19 entry
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump chat 0.0.19, langgraph 0.0.11, ag-ui 0.0.3
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(chat): satisfy @nx/dependency-checks for vite-only @analogjs plugin
Add @analogjs/vite-plugin-angular to ignoredDependencies in the chat
project's eslint config — it is used only in vite.config.mts for test
setup and must not appear in the published package dependencies.
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 2a1c563 commit c317da8
43 files changed
Lines changed: 1920 additions & 213 deletions
File tree
- apps/website/content/docs/chat
- components
- getting-started
- docs/superpowers/specs
- libs
- ag-ui
- src/lib
- testing
- chat
- src
- lib
- agent
- compositions
- chat-tool-call-card
- chat
- primitives
- chat-reasoning
- chat-tool-calls
- chat-trace
- styles
- utils
- testing
- 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.
Lines changed: 57 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
Lines changed: 25 additions & 107 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
11 | | - | |
| 10 | + | |
12 | 11 | | |
13 | 12 | | |
14 | | - | |
| 13 | + | |
15 | 14 | | |
16 | | - | |
17 | | - | |
18 | | - | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
19 | 20 | | |
20 | | - | |
| 21 | + | |
21 | 22 | | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
30 | 28 | | |
31 | | - | |
| 29 | + | |
32 | 30 | | |
33 | | - | |
| 31 | + | |
34 | 32 | | |
35 | 33 | | |
36 | | - | |
37 | | - | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
38 | 37 | | |
39 | | - | |
| 38 | + | |
40 | 39 | | |
41 | 40 | | |
42 | 41 | | |
43 | 42 | | |
44 | 43 | | |
45 | 44 | | |
| 45 | + | |
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
| 50 | + | |
81 | 51 | | |
82 | 52 | | |
83 | | - | |
84 | | - | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
| 53 | + | |
89 | 54 | | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | | - | |
118 | | - | |
119 | | - | |
120 | | - | |
| 55 | + | |
| 56 | + | |
121 | 57 | | |
122 | | - | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
128 | | - | |
129 | | - | |
130 | | - | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
136 | | - | |
137 | | - | |
138 | | - | |
139 | | - | |
Lines changed: 73 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
Lines changed: 68 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
193 | 193 | | |
194 | 194 | | |
195 | 195 | | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
0 commit comments