Repository navigation
Add AI Gateway tool calling - #363
Conversation
29987b9 to
4f114b9
Compare
…ponsibilities - Extract runAssistantLoop as a pure async generator (assistant-loop.ts) that yields events instead of calling session callbacks - Extract tool execution logic into tool-execution.ts - Introduce ChatRequestContext class to encapsulate per-request state (transcript staging + streaming turn buffer) - Reduce useAIChat hook to thin React orchestrator with single applyEvent dispatch point for all state transitions - Remove the 12-method AIChatSessionRequest god-interface
e38d1d2 to
e03b3e1
Compare
Code Metrics Report
Details | | main (1cae270) | #363 (95b9c63) | +/- |
|---------------------|----------------|----------------|-------|
+ | Coverage | 89.5% | 89.6% | +0.0% |
| Files | 265 | 269 | +4 |
| Lines | 7590 | 7724 | +134 |
+ | Covered | 6799 | 6926 | +127 |
- | Test Execution Time | 2m40s | 3m15s | +35s |Code coverage of files in pull request scope (88.8% → 91.0%, patch 92.5%)
Reported by octocov |
commit: |
…l-calling # Conflicts: # pnpm-lock.yaml Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
This comment was marked as outdated.
This comment was marked as outdated.
…l-calling # Conflicts: # decisions/documentation-management-overhaul.md Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
This comment was marked as outdated.
This comment was marked as outdated.
There was a problem hiding this comment.
Generated by Code Review for #363 · copilot · gpt54 · 102.3 AIC · ⌖ 5.3 AIC · ⊞ 22.3K
Comment /review to run again
| type AIGatewayChatMessage, | ||
| type AIGatewayChatRequest, | ||
| type AIGatewayTool, | ||
| type AIGatewayToolCall, | ||
| type AIGatewayFunctionTool, | ||
| type AIGatewayProviderTool, | ||
| type AIChatCompletionEvent, | ||
| type AIChatSource, |
There was a problem hiding this comment.
[1/1 — High] Widening and re-exporting the low-level transport unions here makes the feature addition a breaking change for existing consumers.
AIGatewayChatMessage now grows a new role: "tool" variant and AIChatCompletionEvent now grows type: "tool-call" (plus done.sources). Because both types are still exported from the package root, downstream code that does exhaustive switch/if handling over role or type will stop compiling after taking this release, even if those apps do not opt into tool calling. That needs either a semver-major path or an API split that keeps the new protocol shapes off the stable root surface.
There was a problem hiding this comment.
The compatibility concern is valid: adding these union members can break exhaustive low-level handlers, even when tools are not enabled. Adding optional done.sources alone is not the exhaustiveness issue.
After reviewing known consumer usage, we are not aware of affected low-level handlers. Given the feature’s limited adoption, the decision is to retain the unified client API and the planned minor release rather than introduce a parallel transport API. This is an accepted compatibility trade-off, not a claim that the union expansion is strictly backward-compatible.
Added an explicit migration note in 3a5897a9: exhaustive handlers need the new role: "tool" / type: "tool-call" cases. The high-level useAIChat().messages role union remains "user" | "assistant".
There was a problem hiding this comment.
🟡 Changes recommended
Cancellation can still start tool execution, and history retention, round limits, and streamed tool-name handling need correction.
4 open issues
1 file not reviewed
pnpm-lock.yaml: Generated file
What changed in this PR
Extends AppShell’s AI chat with tool calling while keeping protocol messages separate from public conversation history.
Changes:
- Adds schema-validated local tools, provider tools, and multi-turn orchestration.
- Adds transient tool labels and localized waiting indicators.
- Updates exports, tests, documentation, and release notes.
| File | Description |
|---|---|
| pnpm-lock.yaml | Locks Zod test dependency. |
| packages/core/src/index.ts | Exports tool APIs and types. |
| packages/core/src/i18n-labels.ts | Adds localized tool labels. |
| packages/core/src/components/ai-chat/tool-call.tsx | Adds compact tool-call rows. |
| packages/core/src/components/ai-chat/tool-call.test.tsx | Tests labels and row layout. |
| packages/core/src/components/ai-chat/index.ts | Exports tool-call props. |
| packages/core/src/components/ai-chat/conversation.tsx | Shows pending-response indicator. |
| packages/core/src/components/ai-chat/conversation.test.tsx | Tests indicator visibility. |
| packages/core/src/components/ai-chat/ai-chat.tsx | Attaches AIChat.ToolCall. |
| packages/core/src/components/ai-chat/ai-chat.test.tsx | Adds submitted-state snapshot test. |
| packages/core/src/components/ai-chat/ai-chat.test-d.ts | Checks tool-call prop contract. |
| packages/core/src/ai/use-ai-chat.ts | Integrates orchestration and progress state. |
| packages/core/src/ai/use-ai-chat.test.tsx | Covers tool flows and cancellation. |
| packages/core/src/ai/tools.ts | Defines local and provider tools. |
| packages/core/src/ai/tools.test.ts | Tests tool helpers. |
| packages/core/src/ai/tool-execution.ts | Validates and executes local tools. |
| packages/core/src/ai/client.ts | Transports tools, calls, and sources. |
| packages/core/src/ai/client.test.ts | Tests tool transport and sources. |
| packages/core/src/ai/assistant-loop.ts | Coordinates model and tool rounds. |
| packages/core/package.json | Adds Zod for testing. |
| packages/core/__snapshots__/src__components__ai-chat__tool-call.test.tsx.snap | Records tool-label rendering. |
| packages/core/__snapshots__/src__components__ai-chat__ai-chat.test.tsx.snap | Records waiting-state rendering. |
| docs/components/ai-chat.md | Updates generated component guide. |
| docs/api/use-ai-chat.md | Updates generated hook guide. |
| docs/api/create-ai-gateway-client.md | Updates generated transport guide. |
| docs-src/hooks/use-ai-chat.docs.outline.md | Documents tools and progress state. |
| docs-src/hooks/create-ai-gateway-client.docs.outline.md | Documents transport contracts. |
| docs-src/components/ai-chat.docs.outline.md | Documents tool labels and waiting UI. |
| docs-manifest.json | Refreshes documentation metadata. |
| .changeset/chat-progress-tool-names.md | Announces progress UI additions. |
| .changeset/ai-gateway-tools.md | Announces tool-calling support. |
Review effort: Balanced
|
@copilot resolve the merge conflicts in this pull request |
…l-calling # Conflicts: # docs-manifest.json Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
Merged |
|
@copilot resolve the merge conflicts in this pull request |
…l-calling # Conflicts: # docs-manifest.json # packages/core/__snapshots__/src__components__ai-chat__ai-chat.test.tsx.snap # packages/core/src/components/ai-chat/conversation.tsx Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
Merged the latest |
|
@copilot resolve the merge conflicts in this pull request |
…l-calling # Conflicts: # docs-manifest.json # packages/core/package.json # pnpm-lock.yaml Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
Merged the latest |


Motivation
Part of https://github.com/tailor-inc/platform-planning/issues/1462. This PR implements the AppShell-side local function tool flow; gateway-side function tool support is tracked there and is not included here.
useAIChat()previously covered only text request/response flows. Applications need models to call local functions without exposing provider SDK types in ordinary application code, while reusing existing authenticated data access.Tool-enabled conversations also need visible progress: waiting for the first response, executing a tool, and waiting for the next model turn should not look like an idle chat. Requested tool names should be visible during that flow without becoming permanent conversation entries.
Design Decision
Support local function tools only
useAIChat()accepts local tools throughtools: Record<string, AILocalTool>. Tools usedefineAIChatTool(...), validate arguments with the application's schema, execute in the browser, and return a tool result to the next model turn.Provider-native tools, including hosted web search, are intentionally unsupported. The low-level client accepts only normalized
type: "function"definitions and rejects other tool types before sending a request. There is no provider citation metadata pipeline; the existingAIChat.SourcesUI remains available for application-supplied data.The hook owns local orchestration and cancellation. Tool-call and tool-result protocol messages remain internal; public
messagesstay user/assistant text only.flowchart LR Chat[useAIChat] <-->|model turns and tool calls| Gateway[AI Gateway / model] Chat -->|validate arguments| Local[Local tool] Local -->|result for next turn| Chat Chat --> Progress[Transient toolCalls] Chat --> History[Text-only messages]Reuse application schema libraries
Local tools require runtime validation and JSON Schema generation. AppShell accepts Standard Schema and Standard JSON Schema implementations instead of introducing a schema DSL; Zod 4 supports both directly. This avoids duplicating validation and schema-generation behavior while preserving typed executor arguments.
Bound execution and preserve usable history
The hook executes up to eight tool batches per user message, then allows a final model response using those results. A ninth requested batch fails before execution.
Stopping or failing a request retains visible assistant text and completed tool exchanges, but excludes unanswered tool calls from subsequent request history. Cancellation during validation prevents execution from starting. Stopping does not roll back side effects or guarantee safe retries of tools already running.
Separate active progress from conversation history
The hook exposes a separate
toolCallslist for the active request. It accumulates requested tool names across tool rounds and clears on completion, stop, or error. These names describe model requests, not successful execution; arguments and results remain internal.Consumers can pass each call directly to the new compact, localized component:
Each call occupies its own row, so multiple calls stack vertically. The existing expandable
AIChat.Toolcomponent is unchanged.AIChat.Conversationreuses the existingsubmittedstatus to show a localizedSpinnerwhile waiting for text, running tools, or awaiting the next model reply. No additional loading state is required, and the indicator disappears when streaming starts or the request ends.Summary
useAIChat()and both streaming and JSON AI Gateway transports, including assembly of fragmented tool names and arguments.AIChat.ToolCalland a waiting indicator across response and tool-round waits, with English/Japanese labels.