Skip to content

chat reasoning + tool-call templates (chat 0.0.19, langgraph 0.0.11, ag-ui 0.0.3)#192

Merged
blove merged 24 commits into
mainfrom
claude/chat-reasoning-and-tool-call-templates
May 4, 2026
Merged

chat reasoning + tool-call templates (chat 0.0.19, langgraph 0.0.11, ag-ui 0.0.3)#192
blove merged 24 commits into
mainfrom
claude/chat-reasoning-and-tool-call-templates

Conversation

@blove
Copy link
Copy Markdown
Contributor

@blove blove commented May 4, 2026

Summary

Stacks on top of #191 (chat 0.0.18 + langgraph 0.0.10).

Reasoning (new)

  • <chat-reasoning> primitive renders model reasoning content as a collapsible "Thinking…" / "Thought for Ns" pill above the assistant response. Auto-rendered by <chat> when Message.reasoning is populated.
  • Message.reasoning + Message.reasoningDurationMs optional fields on the shared agent contract. Both adapters populate them from provider-agnostic sources: LangGraph {type:'reasoning'|'thinking'} blocks, AG-UI REASONING_MESSAGE_* events.
  • Shared assertReasoningFixtureMessages conformance test ensures both adapters produce identical Message[] from the same abstract event sequence.

Tool-call extension surface

  • chatToolCallTemplate directive registers per-tool-name templates inside <chat-tool-calls>. A "*" wildcard catches unmapped names.
  • <chat-tool-calls> auto-groups sequential same-name calls into a labeled strip ("Searched 5 sites"). [grouping]="'none'" opts out.
  • <chat-tool-call-card> defaults to collapsed when complete; running and errored cards stay expanded. Status pill (spinner / check / error glyph) replaces inline plaintext.
  • <chat> re-projects chatToolCallTemplate directives into the inner <chat-tool-calls>.

FakeAgent

  • FakeAgent gains an optional reasoningTokens constructor option for offline demos and integration tests.

Docs

  • New chat-reasoning.mdx, chat-tool-call-template.mdx, chat-tool-calls.mdx.
  • Updated chat-tool-call-card.mdx, chat.mdx.
  • New 0.0.19 changelog entry.

Infra

  • Wired @analogjs/vite-plugin-angular + tsconfig.spec.json into the chat library so vitest can run Angular component specs (TestBed, signal inputs).

Bumps @ngaf/chat 0.0.18 → 0.0.19, @ngaf/langgraph 0.0.10 → 0.0.11, @ngaf/ag-ui 0.0.2 → 0.0.3.

Test plan

  • Smoke against ~/tmp/ngaf + LangGraph langgraph dev + gpt-5-mini at reasoning.effort='minimal': reasoning pill appears + collapses after streaming.
  • Smoke at reasoning.effort='high': reasoning streams visibly while pill says "Thinking…", collapses on text start.
  • Tool-call card renders collapsed after a tool completes (any graph that exercises a tool).
  • Register a chatToolCallTemplate="search_web" in the smoke app; verify it replaces the default card.
  • CI green.

🤖 Generated with Claude Code

blove and others added 23 commits May 3, 2026 20:37
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>
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>
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>
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>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mendments

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>
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>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
…ming

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>
…ationMs

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>
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>
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>
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>
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>
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>
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>
…mplate

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>
…hangelog

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>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cacheplane Ready Ready Preview, Comment May 4, 2026 4:55am

Request Review

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>
@blove blove merged commit 3efff37 into main May 4, 2026
14 checks passed
@blove blove deleted the claude/chat-reasoning-and-tool-call-templates branch May 7, 2026 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant