Skip to content

feat(chat): add chat/ai subpath for AI SDK utilities#492

Merged
dancer merged 11 commits into
mainfrom
chat-ai
May 15, 2026
Merged

feat(chat): add chat/ai subpath for AI SDK utilities#492
dancer merged 11 commits into
mainfrom
chat-ai

Conversation

@bensabic
Copy link
Copy Markdown
Contributor

Summary

Introduces a dedicated chat/ai subpath as the home for every Vercel AI SDK helper that ships with Chat SDK. Importing from this subpath keeps the optional ai and zod peer dependencies out of bundles that don't use them.

What's new

  • createChatTools — exposes Chat SDK operations as ready-to-use AI SDK tools so an agent can read, post, react, edit, delete, and manage thread subscriptions across every adapter the supplied Chat instance has registered.
    • Write operations require user approval by default (requireApproval: true); toggle globally or per-tool.
    • Three presets — reader, messenger, moderator — scope the toolset.
    • Individual tools can also be cherry-picked (import { postMessage, addReaction } from "chat/ai").
  • toAiMessages (and the Ai* / ToAiMessagesOptions types) now live alongside the tools at chat/ai. The previous chat re-exports continue to work, but are flagged @deprecated with an editor hint pointing to the new home — migration is a one-line import change.
  • Docs — new /docs/ai section between Usage and Adapters in the sidebar:
    • /docs/ai — Overview
    • /docs/ai/ai-sdk-toolscreateChatTools guide
    • /docs/ai/to-ai-messagestoAiMessages reference
    • /docs/ai/types — Reference for every type exported from chat/ai
  • Example appexamples/nextjs-chat now demos the new surface via a "Run Agent Demo" button on the welcome card and a free-form /agent <prompt> slash command (streaming, with a placeholder so users get immediate feedback in channel contexts where Slack's typing-status API is a no-op).

Future plans

createChatTools currently exposes the cross-adapter Chat SDK surface only. A natural follow-up is to also support platform-specific tools — e.g. expose Slack-only pin/unpin, Discord-only thread archiving, GitHub-only issue commenting, etc., so users can further extend what their agent can do without dropping back to raw adapter calls. The shape would likely be additional opt-in factories under chat/ai (or per-adapter subpaths like @chat-adapter/slack/ai) that return tools layered on top of the platform-specific adapter clients, while keeping the cross-platform createChatTools API as the lowest common denominator.

Coverage

  • createChatTools orchestrator: 100% statements / 94.7% branches.
  • Every tool factory's execute() is exercised end-to-end (29 tests in index.test.ts).
  • toAiMessages keeps its existing 35-test suite covering role mapping, attachment handling, links, transforms, and unsupported-attachment fallbacks.
  • Tools folder overall: 99.0% statements / 86.1% branches / 97.4% functions / 98.9% lines.

bensabic added 3 commits May 12, 2026 21:25
Introduces a dedicated `chat/ai` subpath as the home for every Vercel AI
SDK helper that ships with Chat SDK. Importing from this subpath keeps
the optional `ai` and `zod` peer dependencies out of bundles that don't
use them.

What's new:

- `createChatTools` exposes Chat SDK operations as ready-to-use AI SDK
  tools so an agent can read, post, react, edit, delete, and manage
  thread subscriptions across every adapter the supplied `Chat`
  instance has registered. Write operations require user approval by
  default and can be toggled globally or per-tool via `requireApproval`.
  Three presets (`reader`, `messenger`, `moderator`) scope the toolset,
  and individual tools can also be cherry-picked.

- `toAiMessages` (and the supporting `Ai*` / `ToAiMessagesOptions`
  types) now ship from `chat/ai` alongside the tools. The previous
  `chat` re-exports continue to work, but are flagged `@deprecated`
  with an editor hint pointing to the new home — migrating is a
  one-line import change.

Coverage on the new module is comprehensive: the `createChatTools`
orchestrator hits 100% statements / 94.7% branches, every tool
factory's `execute()` is exercised end-to-end, and the existing
`toAiMessages` test suite continues to cover role mapping, attachment
handling, links, transforms, and unsupported-attachment fallbacks.
Restructures the AI-related documentation into a dedicated section
slotted between the Usage and Adapters groups in the sidebar:

- /docs/ai                 — Overview of the chat/ai subpath
- /docs/ai/ai-sdk-tools    — createChatTools guide (was /docs/ai-tools)
- /docs/ai/to-ai-messages  — toAiMessages reference (was /docs/api/to-ai-messages)
- /docs/ai/types           — Reference for every type exported from chat/ai

The section uses the same `"...folder"` flattening trick as API
Reference, so the four pages render as flat siblings under the AI
header instead of inside an accordion. The legacy `/docs/api/to-ai-messages`
entry is removed from the API meta and the API overview now points at
the new section. All cross-links in the streaming, conversation
history, posting messages, and message reference pages are updated to
the new URLs.
…command

Wires the example app up to the new chat/ai surface so the new tools
can be exercised end-to-end:

- "Run Agent Demo" button on the welcome card runs a fixed,
  deterministic prompt that reacts to the trigger message, fetches
  recent messages, and posts a short markdown summary using the
  `messenger` preset.
- `/agent <prompt>` slash command lets you invoke the same toolset
  with arbitrary natural-language instructions. The reply streams in
  via `ToolLoopAgent.stream(...).fullStream`, with an explicit
  "Agent thinking..." placeholder posted first so users get immediate
  feedback in channel contexts where Slack's typing-status API is a
  no-op (it requires a thread context).

Approval gating is disabled in both flows for ease of testing —
production bots should keep the default `requireApproval: true`.

Also adds a `chat/ai` path mapping so the workspace TypeScript
resolution lines up with the source-file mapping the example already
uses for `chat`, and migrates the existing `toAiMessages` import to
its new home.
@bensabic bensabic requested a review from a team as a code owner May 12, 2026 11:31
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 12, 2026

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

Project Deployment Actions Updated (UTC)
chat Ready Ready Preview, Comment, Open in v0 May 15, 2026 2:58am
chat-sdk-nextjs-chat Ready Ready Preview, Comment, Open in v0 May 15, 2026 2:58am

@bensabic bensabic requested a review from HugoRCD May 12, 2026 11:31
Comment thread packages/chat/src/ai/tools/threads.ts Outdated
`subscribeThread` and `unsubscribeThread` discarded the `ToolOptions`
they received from `createChatTools`, so `needsApproval` was never set
on the underlying AI SDK tool. Both write tools therefore ran without
the approval gate the rest of the toolset enforces.

Wire the option through and tighten the approval tests so every write
tool is asserted under the default, global-off, and per-tool-override
paths — covering both subscription tools.
Comment thread apps/docs/content/docs/ai/index.mdx Outdated
Comment thread apps/docs/content/docs/ai/ai-sdk-tools.mdx Outdated
Comment thread apps/docs/content/docs/ai/index.mdx Outdated
Comment thread apps/docs/content/docs/ai/ai-sdk-tools.mdx Outdated
Comment thread examples/nextjs-chat/src/lib/bot.tsx Outdated
Comment thread examples/nextjs-chat/src/lib/bot.tsx Outdated
Comment thread packages/chat/src/ai/index.ts Outdated
bensabic added 3 commits May 12, 2026 23:19
- Swap the bash `pnpm add ai zod` snippets for the project's
  `<PackageInstall>` component so the install instructions cover every
  package manager and match the rest of the docs.
- Tighten the `Overview` example: drop the manual async-iterator buffer
  and use the documented `thread.adapter.fetchMessages(thread.id, { limit })`
  one-liner — same behavior, far less code, mirrors the canonical
  `toAiMessages` example.
- Add an inline comment next to `requireApproval: false` in
  `ai-sdk-tools.mdx` explaining the trade-off, so newcomers don't
  silently disable the human-in-the-loop guard.
Standardize every model id in `bot.tsx` — the welcome-card responder
plus the two `chat/ai` demos added in this PR (agent button and
`/agent` slash command) — on `openai/gpt-5.4` so the example app
runs on a single, current AI Gateway model.
`createChatTools` previously instantiated all 18 AI SDK tools — each
with its own zod input schema and approval lookup — and only then
filtered the result down to the requested preset. For an agent wired
to `preset: "reader"` that meant building (and immediately discarding)
every write tool, plus computing `requireApproval` for each.

Convert the table to a record of factories and only invoke the ones
the active preset allows. Behavior is unchanged — same return shape,
same overrides semantics, same per-tool `needsApproval` defaults — but
the unused tools and their schemas are no longer constructed at all.
# Conflicts:
#	packages/adapter-web/package.json
#	pnpm-lock.yaml
@socket-security
Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​ai-sdk/​vue@​3.0.182751007398100
Addednpm/​@​ai-sdk/​svelte@​4.0.1821001007498100
Addednpm/​@​ai-sdk/​react@​3.0.1841001007498100
Addednpm/​ai@​6.0.1829210010099100

View full report

@dancer dancer merged commit ac8a207 into main May 15, 2026
17 checks passed
@dancer dancer deleted the chat-ai branch May 15, 2026 03:13
patrick-chinchill added a commit to Chinchill-AI/chat-sdk-python that referenced this pull request May 30, 2026
…th (#116)

* refactor(ai): convert chat_sdk.ai module to package for chat/ai subpath (vercel/chat#492)

Mirror upstream PR vercel/chat#492 which split ai.ts into ai/messages.ts to
make room for ai/tools.ts. Convert the chat_sdk.ai module into a package:

- src/chat_sdk/ai/messages.py: to_ai_messages + supporting types, moved
  verbatim from the former ai.py (docstring path updated only).
- src/chat_sdk/ai/__init__.py: re-export shim exposing every symbol the old
  module exported, so from chat_sdk.ai import to_ai_messages keeps working.
- tests/test_ai.py renamed to tests/test_ai_messages.py (logic unchanged).

No public import path changed. PR 1 of 3 (module move only); the tool
factory (create_chat_tools) lands in PR 2.

Design issue #109, tracking #98.

https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj

* fix(scripts): update AI test-fidelity mapping after module move (review)

* fix(ai): unbreak strict fidelity check + re-export TEXT_MIME_PREFIXES

Two CI / review follow-ups on PR #116:

1. Fidelity-mapping fix went too far in the previous commit. The script's
   parity pin is `chat@4.26.0` (see `lint.yml:63`), where the upstream
   AI test file is still the flat `packages/chat/src/ai.test.ts` — the
   move to `ai/messages.test.ts` happened in 4.29.0. Pointing the mapping
   at the 4.29-era path made `verify_test_fidelity.py --strict` fail with
   "upstream checkout missing — mapped TS file not found". Revert just
   the TS half of the mapping to `ai.test.ts`; keep the Python half at
   `tests/test_ai_messages.py` (the file this PR actually renamed). When
   the parity pin moves to 4.29.0 the TS path can advance too.

   Locally verified against a fresh `chat@4.26.0` clone:
       TOTAL: 564/564 matched (100%), 0 missing
       All TS tests have Python equivalents.

2. The package shim was missing `TEXT_MIME_PREFIXES` (flagged by Codex
   P2). The PR description promises to "re-export everything the former
   `chat_sdk.ai` module exposed" — add the constant to both the import
   block and `__all__` so the contract holds.

https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj

---------

Co-authored-by: Claude <noreply@anthropic.com>
patrick-chinchill added a commit to Chinchill-AI/chat-sdk-python that referenced this pull request May 30, 2026
* feat(ai): add create_chat_tools tool factory (PR 2 of 3) (vercel/chat#492)

Port of `packages/chat/src/ai/tools.ts` (plus the supporting
`tools/{channels,messages,reactions,threads,users}.ts` and `types.ts`)
introduced by vercel/chat#492. Builds on top of #116, which moved
`chat_sdk.ai` from a single module to a package so `tools.py` has a home.

`create_chat_tools(chat, preset=, require_approval=, overrides=)` returns
a mapping of tool-name -> `ChatTool` dataclass holding a description,
JSON-Schema-shaped `input_schema`, an async `execute` callable, and the
`needs_approval` flag. Three presets (`reader` / `messenger` /
`moderator`) and per-tool overrides match the upstream API surface
verbatim. Individual factories (`post_message`, `add_reaction`, ...) are
also exported so callers can cherry-pick.

PR 3 will wire these tools into the existing handler paths; this PR
adds the surface only.

Validation:
  uv run ruff check src/ tests/                  -> clean
  uv run ruff format --check src/ tests/         -> clean
  uv run python scripts/audit_test_quality.py    -> 0 hard failures
  uv run pytest tests/ -q                        -> 4081 passed, 3 skipped

Refs #98, #109.

* fix(ai/tools): wrap ChatNotImplementedError + tighten preset check (gemini review)

Four small follow-ups to PR 2 of the chat-ai port from Gemini's review:

- Import `ChatNotImplementedError` alongside `ChatError`.
- Wrap `fetch_channel_messages` invocation in `try/except
  ChatNotImplementedError` and re-raise as `ChatError` (preserves cause).
  The early `getattr(adapter, "fetch_channel_messages", None) is None`
  branch only catches missing attributes; `BaseAdapter` exposes the
  attribute as a stub that itself raises `ChatNotImplementedError`, so
  without this wrap the tool surfaces a different exception type than
  callers expect. Matches the `Chat.get_user` pattern.
- Same wrap for `list_threads`.
- `_resolve_preset_tools` now distinguishes a single `str` preset from any
  iterable of presets via `isinstance(preset, str)` rather than
  `isinstance(preset, list)`, so tuples/sets/Sequences work correctly.

Two new regression tests (`test_fetch_channel_messages_wraps_not_implemented`,
`test_list_threads_wraps_not_implemented`) drive an `AsyncMock` whose
`side_effect` is `ChatNotImplementedError`, assert the surfaced exception is
`ChatError` with the expected message, and pin the cause chain
(`__cause__ is ChatNotImplementedError`).

https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj

* fix(ai/tools): isolate per-tool input schemas to prevent mutation bleed (review)

* fix(ai/tools): pass list_threads options as keyword (codex P2)

`MockAdapter.list_threads` is declared as
`list_threads(self, channel_id, **kwargs)`, so the tool's positional
`list_method(channel_id, ListThreadsOptions(...))` raised `TypeError` for
any consumer wiring `create_chat_tools` into MockAdapter (the SDK's own
mock). Production adapters accept the kwarg form just as readily as the
positional form, so the change is universally safe.

The existing `test_list_threads_projects_summaries` used
`AsyncMock(return_value=...)` which masked the TypeError by replacing the
real `MockAdapter.list_threads` entirely. New
`test_list_threads_uses_keyword_options` exercises the real MockAdapter
and asserts the default empty result — fails on the old positional call
with `TypeError`, passes after.

https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj

* style(ai/tools): apply is-not-None idiom + hoist core import (audit nits)

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

3 participants