Skip to content

Add AI Gateway tool calling - #363

Merged
IzumiSy merged 33 commits into
mainfrom
izumis/ai-gateway-tool-calling
Oct 30, 2026
Merged

IzumiSy merged 33 commits into
mainfrom
izumis/ai-gateway-tool-calling

Conversation

@IzumiSy

@IzumiSy IzumiSy commented Jul 3, 2026 •

Copy link
Copy Markdown
Contributor

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 through tools: Record<string, AILocalTool>. Tools use defineAIChatTool(...), 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 existing AIChat.Sources UI remains available for application-supplied data.

The hook owns local orchestration and cancellation. Tool-call and tool-result protocol messages remain internal; public messages stay 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]
Loading

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 toolCalls list 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:

<AIChat.ToolCall call={call} />

Each call occupies its own row, so multiple calls stack vertically. The existing expandable AIChat.Tool component is unchanged.

AIChat.Conversation reuses the existing submitted status to show a localized Spinner while 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

  • Add local function tools to useAIChat() and both streaming and JSON AI Gateway transports, including assembly of fragmented tool names and arguments.
  • Validate and execute local calls in a bounded loop with per-request cancellation and safe interrupted history; keep public messages text-first.
  • Limit tool configuration and transport to local function calls, without provider-native tools or citation metadata.
  • Add transient tool-call labels through AIChat.ToolCall and a waiting indicator across response and tool-round waits, with English/Japanese labels.
  • Add transport, orchestration, cancellation, UI, and public typing coverage; update authored/generated documentation and changesets, including the low-level union compatibility warning.

@IzumiSy
IzumiSy requested a review from a team as a code owner July 3, 2026 14:50
@IzumiSy
IzumiSy marked this pull request as draft July 6, 2026 05:42
Base automatically changed from izumis/ai-gateway-openai-sdk to main July 8, 2026 05:31
@IzumiSy
IzumiSy force-pushed the izumis/ai-gateway-tool-calling branch from 29987b9 to 4f114b9 Compare July 8, 2026 06:07
…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
@IzumiSy
IzumiSy force-pushed the izumis/ai-gateway-tool-calling branch from e38d1d2 to e03b3e1 Compare September 17, 2026 05:50
@github-actions

github-actions Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Code Metrics Report

main (1cae270) #363 (95b9c63) +/-
Coverage 89.5% 89.6% +0.0%
Test Execution Time 2m40s 3m15s +35s
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%)

Files Coverage +/- Patch Coverage Status
packages/core/package.json 100.0% 0.0% - modified
packages/core/src/ai/assistant-loop.ts 100.0% +100.0% 100.0% added
packages/core/src/ai/client.ts 86.9% +5.0% 90.6% modified
packages/core/src/ai/tool-execution.ts 81.3% +81.3% 81.3% added
packages/core/src/ai/tools.ts 100.0% +100.0% 100.0% added
packages/core/src/ai/use-ai-chat.ts 95.7% +5.1% 97.1% modified
packages/core/src/components/ai-chat/ai-chat.tsx 91.6% 0.0% - modified
packages/core/src/components/ai-chat/conversation.tsx 93.8% +0.2% 100.0% modified
packages/core/src/components/ai-chat/index.ts 0.0% 0.0% - modified
packages/core/src/components/ai-chat/tool-call.tsx 100.0% +100.0% 100.0% added
packages/core/src/i18n-labels.ts 60.0% -2.5% 50.0% modified
packages/core/src/index.ts 0.0% 0.0% - modified

Reported by octocov

@IzumiSy IzumiSy changed the title Add AI Gateway tool calling support Add AI Gateway tools and browser MCP support Sep 18, 2026
@IzumiSy IzumiSy self-assigned this Sep 18, 2026
@IzumiSy IzumiSy added the preview A trigger label to create preview build by pkg-pr-new label Sep 18, 2026
@pkg-pr-new

pkg-pr-new Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/@tailor-platform/app-shell@875b3d7
pnpm add https://pkg.pr.new/@tailor-platform/eslint-plugin-app-shell@875b3d7
pnpm add https://pkg.pr.new/@tailor-platform/sdk-plugin-app-shell@875b3d7
pnpm add https://pkg.pr.new/@tailor-platform/vite-plugin-app-shell@875b3d7

commit: 875b3d7

@IzumiSy IzumiSy changed the title Add AI Gateway tools and browser MCP support Add AI Gateway tool calling Sep 25, 2026
…l-calling

# Conflicts:
#	pnpm-lock.yaml

Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
@IzumiSy

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.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by Code Review for #363 · copilot · gpt54 · 102.3 AIC · ⌖ 5.3 AIC · ⊞ 22.3K
Comment /review to run again

Comment thread packages/core/src/index.ts Outdated
Comment on lines +72 to +79
type AIGatewayChatMessage,
type AIGatewayChatRequest,
type AIGatewayTool,
type AIGatewayToolCall,
type AIGatewayFunctionTool,
type AIGatewayProviderTool,
type AIChatCompletionEvent,
type AIChatSource,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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".

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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

Comment thread packages/core/src/ai/tool-execution.ts
Comment thread packages/core/src/ai/assistant-loop.ts Outdated
Comment thread packages/core/src/ai/client.ts Outdated
Comment thread packages/core/src/ai/use-ai-chat.ts
@IzumiSy IzumiSy removed the preview A trigger label to create preview build by pkg-pr-new label Oct 19, 2026
@IzumiSy

IzumiSy commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

@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>

Copilot AI commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Merged main and resolved the docs manifest conflict; regenerated docs and verified docs-kit checks pass. Merge commit: ec03632.

@IzumiSy

IzumiSy commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

@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>

Copilot AI commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Merged the latest main, resolved the docs manifest, snapshot, and conversation conflicts, and verified the focused AI-chat tests and docs check pass. Merge commit: 0ec4525.

@IzumiSy

IzumiSy commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

@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>

Copilot AI commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Merged the latest main, resolved the docs manifest, package.json, and lockfile conflicts, and verified the docs check, core type-check, and focused AI-chat tests. Merge commit: a80e9509.

@IzumiSy
IzumiSy merged commit 4a183fd into main Oct 30, 2026
9 checks passed
@IzumiSy
IzumiSy deleted the izumis/ai-gateway-tool-calling branch October 30, 2026 04:58
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.

4 participants