Conversation
…ack/ai into feat/splitting-adapters-up
* fix: refactoring ai for more activities
* smoke tests passing
* woot, all the test stuff is working
* dev panel updates for images, summarization, one shot and structured
* enhancing smoke tests
* fixing tests
* adding grok
* last minute tests
* Refactor imports in documentation and examples to use named imports for `ai`
- Updated all instances of `import ai from "@tanstack/ai"` to `import { ai } from "@tanstack/ai"` across various documentation files, guides, and examples.
- Ensured consistency in import statements for better clarity and adherence to best practices.
* ci: apply automated fixes
* fix typesafety on ai
* ci: apply automated fixes
* cleanup types
* ci: apply automated fixes
* remove grok
* ci: apply automated fixes
* fix provenence?
* update deps
* fix tests
---------
Co-authored-by: Alem Tuzlak <t.zlak@hotmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* video generation * text to speech and speech to text * adding some cool audio UI to the dev panel * small fixups * ci: apply automated fixes * client fixes on tool calls * few more client fixups * one last test fix --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
…into splitting-adapters-up-fixes
- Add createOptions() function for type-safe adapter option creation - Refactor OpenAI summarize adapter to use text adapter for streaming - Deprecate textOptions() in favor of createOptions() - Update examples to use createOptions pattern - Add runtime adapter switching documentation guide
…into splitting-adapters-up-fixes
📝 WalkthroughWalkthroughIntroduces experimental Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant text as text() Activity
participant StreamProcessor as Streaming<br/>Processor
participant Adapter
participant DevTools
Client->>text: text({adapter, messages, tools, stream: true})
text->>text: create request ID
text->>DevTools: emit text:request
text->>StreamProcessor: runStreamingText()
StreamProcessor->>Adapter: adapter.chatStream(options)
loop for each StreamChunk
Adapter-->>StreamProcessor: StreamChunk
StreamProcessor->>DevTools: emit text:chunk
StreamProcessor-->>Client: yield StreamChunk
end
StreamProcessor->>DevTools: emit text:finished / text:completed
StreamProcessor-->>Client: stream complete
sequenceDiagram
participant Client
participant agentLoop as agentLoop()
participant Engine as AgentLoopEngine
participant textFn as text() Function
participant Adapter
participant ToolExecutor as Tool Executor
Client->>agentLoop: agentLoop({textCreator, tools, maxIterations})
loop while not finished
agentLoop->>Engine: run cycle
Engine->>textFn: call text() with messages
textFn->>Adapter: adapter.chatStream()
Adapter-->>textFn: StreamChunk (with tool calls)
textFn-->>Engine: return message with tools
Engine->>Engine: extract tool calls
alt Tools require approval
Engine->>Client: emit approval:request
Client-->>Engine: approval response
end
Engine->>ToolExecutor: execute approved tools
ToolExecutor-->>Engine: tool results
Engine->>Engine: append tool results to messages
Engine->>Client: emit tool:complete
end
Engine-->>Client: final message / structured result
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
View your CI Pipeline Execution ↗ for commit af72d51
☁️ Nx Cloud last updated this comment at |
@tanstack/ai
@tanstack/ai-anthropic
@tanstack/ai-client
@tanstack/ai-devtools-core
@tanstack/ai-fal
@tanstack/ai-gemini
@tanstack/ai-grok
@tanstack/ai-ollama
@tanstack/ai-openai
@tanstack/ai-openrouter
@tanstack/ai-preact
@tanstack/ai-react
@tanstack/ai-react-ui
@tanstack/ai-solid
@tanstack/ai-solid-ui
@tanstack/ai-svelte
@tanstack/ai-vue
@tanstack/ai-vue-ui
@tanstack/preact-ai-devtools
@tanstack/react-ai-devtools
@tanstack/solid-ai-devtools
commit: |
…ntLoop # Conflicts: # .claude/settings.local.json
Co-Authored-By: Warp <agent@warp.dev>
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/api/ai.md (1)
396-415:⚠️ Potential issue | 🟠 MajorUpdate
StreamChunkdocumentation to reflect AG-UI protocol implementation.The documented
StreamChunktype definition is stale. The actual implementation definesStreamChunk = AGUIEvent, which is a union of AG-UI protocol event types (RunStartedEvent, RunFinishedEvent, RunErrorEvent, TextMessageStartEvent, TextMessageContentEvent, TextMessageEndEvent, ToolCallStartEvent, ToolCallArgsEvent, ToolCallEndEvent, StepStartedEvent, etc.), not theContentStreamChunk,ThinkingStreamChunk,ToolCallStreamChunk,ToolResultStreamChunk,DoneStreamChunk, andErrorStreamChunkshown in the current doc snippet. The documentation needs to be rewritten to match the current AG-UI event-based architecture.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/api/ai.md` around lines 396 - 415, The docs currently list StreamChunk as a union of legacy types (ContentStreamChunk, ThinkingStreamChunk, ToolCallStreamChunk, ToolResultStreamChunk, DoneStreamChunk, ErrorStreamChunk); update the documentation to reflect the current implementation where StreamChunk = AGUIEvent and describe the AG-UI protocol event types instead — mention concrete event names like RunStartedEvent, RunFinishedEvent, RunErrorEvent, TextMessageStartEvent, TextMessageContentEvent, TextMessageEndEvent, ToolCallStartEvent, ToolCallArgsEvent, ToolCallEndEvent, StepStartedEvent (etc.), replacing the old union and adding a short one-line description for each AGUIEvent type so readers understand the event-based stream architecture.
🧹 Nitpick comments (5)
packages/typescript/ai/tests/generate-types.test-d.ts (1)
433-436: Consider top-levelimport { z } from 'zod'instead ofrequire.Using
require('zod') as typeof import('zod')in a type-test file is an unusual pattern. Since this is a.test-d.tsfile that only checks types (never runs at runtime), a standardimportwould be cleaner and more idiomatic.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai/tests/generate-types.test-d.ts` around lines 433 - 436, Replace the runtime-style require usage for Zod with a top-level TypeScript import: remove the inline "const { z } = require('zod') as typeof import('zod')" and instead add a top-level "import { z } from 'zod'" so the test-d.ts file uses an idiomatic compile-time import; update any references to the local z binding accordingly (e.g., usages within the describe('chat() with outputSchema') block).packages/typescript/ai/src/agent/index.ts (2)
256-267: Multipleas Array<any>casts bypass type safety on messages.The constructor casts
config.options.messagestoArray<any>twice — once forextractClientStateFromOriginalMessages(line 259) and once forconvertMessagesToModelMessages(line 266). Consider introducing a union type (e.g.,Array<ModelMessage | UIMessage>) that both functions accept, or using separate typed parameters to preserve type checking.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai/src/agent/index.ts` around lines 256 - 267, The code repeatedly casts config.options.messages to Array<any>, bypassing type safety; instead, create a properly typed local variable (e.g., const typedMessages: Array<ModelMessage | UIMessage>) and use it when calling extractClientStateFromOriginalMessages and convertMessagesToModelMessages, and update those function signatures if needed to accept Array<ModelMessage | UIMessage>; assign the results to this.initialApprovals, this.initialClientToolResults and this.messages using the typedMessages to preserve compile-time checks (refer to extractClientStateFromOriginalMessages, convertMessagesToModelMessages, initialApprovals, initialClientToolResults, and messages).
1237-1263: Structured output path makes an additional LLM call after the loop completes.
runStructuredAgentLoopfirst consumes the entire streaming loop (executing all tool calls), then makes a separatetextFn()call withoutputSchemato extract structured output. This means one extra LLM call per structured agent loop invocation. The design is sound (the final call has full context from tool execution), but this cost implication may be worth documenting for consumers, particularly for expensive models.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai/src/agent/index.ts` around lines 1237 - 1263, runStructuredAgentLoop currently consumes the streaming AgentLoopEngine and then makes a separate textFn call with outputSchema, causing an extra LLM call; to fix, push structured extraction into the engine so the final structured output is produced as part of the main loop: modify AgentLoopEngine (and its constructor/run signature) to accept an optional outputSchema and to perform the final textFn/schema extraction before completing, then remove the standalone textFn call in runStructuredAgentLoop (use engine.getStructuredResult() or have engine.getMessages() return the structured result). Update references to textFn/outputSchema/engine.run/engine.getMessages so the structured extraction happens inside AgentLoopEngine.packages/typescript/ai/src/activities/chat/index.ts (1)
280-295:outputSchema as anyerases type safety.The cast on line 294 bypasses the type system entirely. If
agentLoopexpects a narrower schema type, misuse won't be caught at compile time. Consider using a more precise cast (e.g.,as SchemaInput) or adjusting theagentLoopsignature to accept the same schema type.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai/src/activities/chat/index.ts` around lines 280 - 295, The call site casts outputSchema to any which erases type safety; instead update the agentLoop invocation to preserve the schema type by casting outputSchema to the expected input type (e.g., use the concrete schema type parameter expected by agentLoop such as SchemaInput or the generic matching TSchema/TStream) or update the agentLoop signature to accept the same generic schema type so you can pass outputSchema without using any; locate the call in agentLoop({... outputSchema: outputSchema as any }) and either replace the any cast with a precise cast (e.g., outputSchema as SchemaInput) or change agentLoop's parameter types so outputSchema: TSchema (or the appropriate generic) is accepted directly, and ensure the returned type remains TextActivityResult<TSchema, TStream>.packages/typescript/ai/src/activities/text/index.ts (1)
352-358: Content accumulation treatschunk.contentas a full replacement but falsy check misses empty-string resets.When
chunk.contentis an empty string"", the falsy check falls through toaccumulatedContent += chunk.delta, which appends rather than resetting. If the adapter ever sendscontent: ""to signal a reset to empty, it won't take effect. This is likely fine in practice (adapters probably don't send empty-string content), but worth noting.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai/src/activities/text/index.ts` around lines 352 - 358, The switch branch for 'TEXT_MESSAGE_CONTENT' treats chunk.content with a falsy check so an empty string isn't recognized as an explicit reset; change the condition to check for presence explicitly (e.g., chunk.content !== undefined / chunk.content != null) so that chunk.content === "" will replace accumulatedContent (in the code handling chunk and accumulatedContent within the 'TEXT_MESSAGE_CONTENT' case) instead of falling back to appending chunk.delta.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/api/ai.md`:
- Around line 156-161: The agentLoop() docs use Zod-specific types for
outputSchema; update them to match the framework-agnostic terminology used by
text(): change the table entry for `outputSchema` from `z.ZodType` to
`StandardSchema` and update the Returns section from `Promise<z.infer<TSchema>>`
to `Promise<InferSchemaType<TSchema>>` (matching `text()`), ensuring references
to `outputSchema`, `agentLoop()`, and the generic `TSchema` use the same
StandardSchema/InferSchemaType types used elsewhere.
- Around line 130-137: The docs example passes tools incorrectly as tools:
[myTools], which can create a nested array if myTools is already an array;
update the agentLoop call to pass a flat array: if you have multiple tools in an
array named myTools, use tools: myTools; if you have a single tool variable
named myTool, use tools: [myTool]; adjust the example around the agentLoop
invocation and the variable name (myTools vs myTool) accordingly to avoid
[[...tools]].
In `@packages/typescript/ai/src/agent/index.ts`:
- Around line 10-17: Update the relative import statements to consistently use
.js extensions: change imports that reference
'../activities/chat/tools/tool-calls',
'../activities/chat/agent-loop-strategies', '../activities/text/index', and
'../activities/chat/messages' so they end with .js; ensure the lines importing
ToolCallManager and executeToolCalls, maxIterations as maxIterationsStrategy,
text, and convertMessagesToModelMessages are updated to the .js-suffixed paths
to match aiEventClient's '../event-client.js' style.
In `@packages/typescript/ai/tests/generate-types.test-d.ts`:
- Around line 224-230: Several negative type tests in generate-types.test-d.ts
(e.g., the chat({...}) call using model: 'invalid-model') are missing the
required // `@ts-expect-error` annotation; add a single-line // `@ts-expect-error`
comment immediately above every call that is intended to produce a TypeScript
error (search for comments like "invalid model should error", "should not
allow", "should reject", "should NOT accept", "should error on") so Vitest
asserts the line actually errors — apply this to all affected test calls
(including the chat(...) calls and other invalid-usage calls referenced in the
review ranges).
In `@packages/typescript/smoke-tests/adapters/src/tests/emb-embedding.ts`:
- Around line 18-25: The AdapterContext interface in harness.ts is missing
embeddingAdapter and embeddingModel which causes TS errors when emb-embedding.ts
accesses them; update the AdapterContext interface (the same block that defines
summarizeAdapter/summarizeModel and imageAdapter/imageModel) to add
embeddingAdapter?: any and embeddingModel?: string so emb-embedding.ts can
safely read adapterContext.embeddingAdapter and adapterContext.embeddingModel.
In `@testing/panel/tests/helpers.ts`:
- Around line 377-394: The current try/catch around JSON.parse swallows
server-sent errors because throw new Error(parsed.error) is caught locally;
change the flow in the function that handles the SSE chunk (the block using
JSON.parse, parsed, throw new Error, catch) so JSON.parse is wrapped in its own
try/catch and any parse errors are ignored, but the parsed result is then
inspected outside that parse-only catch — if parsed.type === 'error' rethrow or
propagate that error (do not allow it to be swallowed), otherwise continue with
the existing TEXT_MESSAGE_CONTENT handling (chunkCount, summary, provider,
model). Ensure you only catch parse exceptions, not the intentional error
throws.
---
Outside diff comments:
In `@docs/api/ai.md`:
- Around line 396-415: The docs currently list StreamChunk as a union of legacy
types (ContentStreamChunk, ThinkingStreamChunk, ToolCallStreamChunk,
ToolResultStreamChunk, DoneStreamChunk, ErrorStreamChunk); update the
documentation to reflect the current implementation where StreamChunk =
AGUIEvent and describe the AG-UI protocol event types instead — mention concrete
event names like RunStartedEvent, RunFinishedEvent, RunErrorEvent,
TextMessageStartEvent, TextMessageContentEvent, TextMessageEndEvent,
ToolCallStartEvent, ToolCallArgsEvent, ToolCallEndEvent, StepStartedEvent
(etc.), replacing the old union and adding a short one-line description for each
AGUIEvent type so readers understand the event-based stream architecture.
---
Nitpick comments:
In `@packages/typescript/ai/src/activities/chat/index.ts`:
- Around line 280-295: The call site casts outputSchema to any which erases type
safety; instead update the agentLoop invocation to preserve the schema type by
casting outputSchema to the expected input type (e.g., use the concrete schema
type parameter expected by agentLoop such as SchemaInput or the generic matching
TSchema/TStream) or update the agentLoop signature to accept the same generic
schema type so you can pass outputSchema without using any; locate the call in
agentLoop({... outputSchema: outputSchema as any }) and either replace the any
cast with a precise cast (e.g., outputSchema as SchemaInput) or change
agentLoop's parameter types so outputSchema: TSchema (or the appropriate
generic) is accepted directly, and ensure the returned type remains
TextActivityResult<TSchema, TStream>.
In `@packages/typescript/ai/src/activities/text/index.ts`:
- Around line 352-358: The switch branch for 'TEXT_MESSAGE_CONTENT' treats
chunk.content with a falsy check so an empty string isn't recognized as an
explicit reset; change the condition to check for presence explicitly (e.g.,
chunk.content !== undefined / chunk.content != null) so that chunk.content ===
"" will replace accumulatedContent (in the code handling chunk and
accumulatedContent within the 'TEXT_MESSAGE_CONTENT' case) instead of falling
back to appending chunk.delta.
In `@packages/typescript/ai/src/agent/index.ts`:
- Around line 256-267: The code repeatedly casts config.options.messages to
Array<any>, bypassing type safety; instead, create a properly typed local
variable (e.g., const typedMessages: Array<ModelMessage | UIMessage>) and use it
when calling extractClientStateFromOriginalMessages and
convertMessagesToModelMessages, and update those function signatures if needed
to accept Array<ModelMessage | UIMessage>; assign the results to
this.initialApprovals, this.initialClientToolResults and this.messages using the
typedMessages to preserve compile-time checks (refer to
extractClientStateFromOriginalMessages, convertMessagesToModelMessages,
initialApprovals, initialClientToolResults, and messages).
- Around line 1237-1263: runStructuredAgentLoop currently consumes the streaming
AgentLoopEngine and then makes a separate textFn call with outputSchema, causing
an extra LLM call; to fix, push structured extraction into the engine so the
final structured output is produced as part of the main loop: modify
AgentLoopEngine (and its constructor/run signature) to accept an optional
outputSchema and to perform the final textFn/schema extraction before
completing, then remove the standalone textFn call in runStructuredAgentLoop
(use engine.getStructuredResult() or have engine.getMessages() return the
structured result). Update references to
textFn/outputSchema/engine.run/engine.getMessages so the structured extraction
happens inside AgentLoopEngine.
In `@packages/typescript/ai/tests/generate-types.test-d.ts`:
- Around line 433-436: Replace the runtime-style require usage for Zod with a
top-level TypeScript import: remove the inline "const { z } = require('zod') as
typeof import('zod')" and instead add a top-level "import { z } from 'zod'" so
the test-d.ts file uses an idiomatic compile-time import; update any references
to the local z binding accordingly (e.g., usages within the describe('chat()
with outputSchema') block).
| for await (const chunk of agentLoop({ | ||
| adapter: openaiText("gpt-4o"), | ||
| messages: [{ role: "user", content: "Complete this task" }], | ||
| tools: [myTools], | ||
| agentLoopStrategy: maxIterations(10), | ||
| })) { | ||
| // ... | ||
| } |
There was a problem hiding this comment.
tools: [myTools] likely wraps an array in an array.
If myTools is an array of tools, this creates [[...tools]]. If it's a single tool, the plural name is misleading. Should be either tools: [myTool] or tools: myTools.
📝 Suggested fix
- tools: [myTools],
+ tools: [myTool],📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for await (const chunk of agentLoop({ | |
| adapter: openaiText("gpt-4o"), | |
| messages: [{ role: "user", content: "Complete this task" }], | |
| tools: [myTools], | |
| agentLoopStrategy: maxIterations(10), | |
| })) { | |
| // ... | |
| } | |
| for await (const chunk of agentLoop({ | |
| adapter: openaiText("gpt-4o"), | |
| messages: [{ role: "user", content: "Complete this task" }], | |
| tools: [myTool], | |
| agentLoopStrategy: maxIterations(10), | |
| })) { | |
| // ... | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/api/ai.md` around lines 130 - 137, The docs example passes tools
incorrectly as tools: [myTools], which can create a nested array if myTools is
already an array; update the agentLoop call to pass a flat array: if you have
multiple tools in an array named myTools, use tools: myTools; if you have a
single tool variable named myTool, use tools: [myTool]; adjust the example
around the agentLoop invocation and the variable name (myTools vs myTool)
accordingly to avoid [[...tools]].
| | `outputSchema` | `z.ZodType` | For structured output after tool execution | | ||
|
|
||
| ### Returns | ||
|
|
||
| - Default: `AsyncIterable<StreamChunk>` (streaming) | ||
| - With `outputSchema`: `Promise<z.infer<TSchema>>` |
There was a problem hiding this comment.
Inconsistent schema documentation between text() and agentLoop().
The text() options table (line 85) describes outputSchema as StandardSchema and returns Promise<InferSchemaType<TSchema>>, which is framework-agnostic. The agentLoop() section here uses Zod-specific types (z.ZodType, Promise<z.infer<TSchema>>). Since both APIs support Standard Schema (Zod v4+, ArkType, Valibot per the code), the agentLoop docs should use the same framework-agnostic terminology.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/api/ai.md` around lines 156 - 161, The agentLoop() docs use Zod-specific
types for outputSchema; update them to match the framework-agnostic terminology
used by text(): change the table entry for `outputSchema` from `z.ZodType` to
`StandardSchema` and update the Returns section from `Promise<z.infer<TSchema>>`
to `Promise<InferSchemaType<TSchema>>` (matching `text()`), ensuring references
to `outputSchema`, `agentLoop()`, and the generic `TSchema` use the same
StandardSchema/InferSchemaType types used elsewhere.
| import { aiEventClient } from '../event-client.js' | ||
| import { | ||
| ToolCallManager, | ||
| executeToolCalls, | ||
| } from '../activities/chat/tools/tool-calls' | ||
| import { maxIterations as maxIterationsStrategy } from '../activities/chat/agent-loop-strategies' | ||
| import { text } from '../activities/text/index' | ||
| import { convertMessagesToModelMessages } from '../activities/chat/messages' |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, let's see the actual imports in the agent/index.ts file
echo "=== Actual imports in agent/index.ts (lines 10-17) ==="
head -20 packages/typescript/ai/src/agent/index.ts | tail -10
echo ""
echo "=== All imports in agent/index.ts ==="
sed -n '1,30p' packages/typescript/ai/src/agent/index.ts | grep "^import"
echo ""
echo "=== tsconfig configuration ==="
find . -name "tsconfig*.json" -type f | head -3 | xargs -I{} sh -c 'echo "--- {} ---" && cat {}'Repository: TanStack/ai
Length of output: 2841
🏁 Script executed:
#!/bin/bash
# Find the tsconfig for packages/typescript/ai
echo "=== Looking for tsconfig in packages/typescript/ai ==="
find packages/typescript/ai -name "tsconfig*.json" -type f | xargs -I{} sh -c 'echo "--- {} ---" && cat {}'
echo ""
echo "=== Root tsconfig.json if exists ==="
cat tsconfig.json 2>/dev/null || echo "Not found in root"
echo ""
echo "=== Check packages/typescript/tsconfig ==="
cat packages/typescript/tsconfig.json 2>/dev/null || echo "Not found"
echo ""
echo "=== package.json type field in packages/typescript/ai ==="
grep -A5 '"type"' packages/typescript/ai/package.json 2>/dev/null || echo "Not found"
</parameter>Repository: TanStack/ai
Length of output: 1997
🏁 Script executed:
#!/bin/bash
# Check import patterns in other files in the same directory
echo "=== Import patterns in packages/typescript/ai/src ==="
find packages/typescript/ai/src -name "*.ts" -o -name "*.tsx" | head -5 | xargs -I{} sh -c 'echo "--- {} ---" && head -20 {} | grep "^import.*from"'
echo ""
echo "=== Check overall import extension consistency in ai package ==="
rg "^import.*from ['\"]\.\." packages/typescript/ai/src --max-count=20 | head -30Repository: TanStack/ai
Length of output: 3649
Use consistent .js extensions in relative imports.
Line 10 uses '../event-client.js' with the .js extension, while lines 12–17 omit it for '../activities/chat/tools/tool-calls', '../activities/chat/agent-loop-strategies', '../activities/text/index', and '../activities/chat/messages'. For consistency and clarity, extend all relative imports to use .js extensions.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/typescript/ai/src/agent/index.ts` around lines 10 - 17, Update the
relative import statements to consistently use .js extensions: change imports
that reference '../activities/chat/tools/tool-calls',
'../activities/chat/agent-loop-strategies', '../activities/text/index', and
'../activities/chat/messages' so they end with .js; ensure the lines importing
ToolCallManager and executeToolCalls, maxIterations as maxIterationsStrategy,
text, and convertMessagesToModelMessages are updated to the .js-suffixed paths
to match aiEventClient's '../event-client.js' style.
| // invalid model should error | ||
| chat({ | ||
| adapter: textAdapter, | ||
| model: 'invalid-model', | ||
| messages: [{ role: 'user', content: 'Hello' }], | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Negative type tests are missing @ts-expect-error annotations.
In .test-d.ts files, calls expected to produce type errors will either (a) fail the type-check entirely if they actually error, preventing all tests from running, or (b) pass silently if the types don't reject them, providing a false sense of safety. Each call that "should error" needs a // @ts-expect-error`` annotation so Vitest verifies the line does produce a type error.
This pattern applies throughout the file — every test case with comments like "invalid model should error", "should not allow", "should reject", "should NOT accept", or "should error on" needs this annotation. A non-exhaustive list of affected lines: 225–229, 243–247, 260–265, 282–291, 308–315, 331–339, 345–351, 356–361, 367–372, 378–383, 401–408, 422–429, 500–513, 515–528, 634–643, 793–801, 803–811, 814–830, 832–848, 850–866, 868–888, 905–913, 926–935, 962–980, 982–1000, 1002–1020, 1052–1070, 1072–1090, 1122–1140, 1202–1211, 1213–1231, 1248–1255, 1268–1276, 1279–1302, 1304–1320, 1322–1345, 1347–1376, 1384–1414, 1416–1446, 1448–1471, 1473–1502, 1509–1540, 1542–1558, 1560–1583, 1585–1623, 1657–1661, 1674–1679, 1693–1697, 1714–1723, 1740–1748, 1761–1769, 1800–1806, 1809–1832.
Example fix for one case:
🔧 Example annotation
// invalid model should error
+ // `@ts-expect-error` - 'invalid-model' is not a valid model for TestTextAdapter
chat({
adapter: textAdapter,
model: 'invalid-model',
messages: [{ role: 'user', content: 'Hello' }],
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // invalid model should error | |
| chat({ | |
| adapter: textAdapter, | |
| model: 'invalid-model', | |
| messages: [{ role: 'user', content: 'Hello' }], | |
| }) | |
| }) | |
| // invalid model should error | |
| // `@ts-expect-error` - 'invalid-model' is not a valid model for TestTextAdapter | |
| chat({ | |
| adapter: textAdapter, | |
| model: 'invalid-model', | |
| messages: [{ role: 'user', content: 'Hello' }], | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/typescript/ai/tests/generate-types.test-d.ts` around lines 224 -
230, Several negative type tests in generate-types.test-d.ts (e.g., the
chat({...}) call using model: 'invalid-model') are missing the required //
`@ts-expect-error` annotation; add a single-line // `@ts-expect-error` comment
immediately above every call that is intended to produce a TypeScript error
(search for comments like "invalid model should error", "should not allow",
"should reject", "should NOT accept", "should error on") so Vitest asserts the
line actually errors — apply this to all affected test calls (including the
chat(...) calls and other invalid-usage calls referenced in the review ranges).
| if (!adapterContext.embeddingAdapter) { | ||
| console.log( | ||
| `[${adapterName}] — ${testName}: Ignored (no embedding adapter)`, | ||
| ) | ||
| return { passed: true, ignored: true } | ||
| } | ||
|
|
||
| const model = adapterContext.embeddingModel || adapterContext.model |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
rg -n 'embeddingAdapter|embeddingModel' --type=ts -C3 -g '!node_modules'Repository: TanStack/ai
Length of output: 2140
🏁 Script executed:
rg -n 'interface AdapterContext' --type=ts -A 30Repository: TanStack/ai
Length of output: 2687
AdapterContext interface is missing embeddingAdapter and embeddingModel properties.
The AdapterContext interface in harness.ts (lines 48–70) does not include embeddingAdapter or embeddingModel, but the test file at lines 18, 25, and 41 references these properties. This will cause TypeScript compilation errors.
Add the missing properties to the AdapterContext interface following the existing pattern:
embeddingAdapter?: any(alongsidesummarizeAdapter,imageAdapter, etc.)embeddingModel?: string(alongsidesummarizeModel,imageModel, etc.)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/typescript/smoke-tests/adapters/src/tests/emb-embedding.ts` around
lines 18 - 25, The AdapterContext interface in harness.ts is missing
embeddingAdapter and embeddingModel which causes TS errors when emb-embedding.ts
accesses them; update the AdapterContext interface (the same block that defines
summarizeAdapter/summarizeModel and imageAdapter/imageModel) to add
embeddingAdapter?: any and embeddingModel?: string so emb-embedding.ts can
safely read adapterContext.embeddingAdapter and adapterContext.embeddingModel.
| try { | ||
| const parsed = JSON.parse(data) | ||
| if (parsed.type === 'error') { | ||
| throw new Error(parsed.error) | ||
| } | ||
| if (parsed.type === 'TEXT_MESSAGE_CONTENT') { | ||
| chunkCount++ | ||
| if (parsed.delta) { | ||
| summary += parsed.delta | ||
| } else if (parsed.content) { | ||
| summary = parsed.content | ||
| } | ||
| provider = parsed.provider || provider | ||
| model = parsed.model || model | ||
| } | ||
| provider = parsed.provider || provider | ||
| model = parsed.model || model | ||
| } catch { | ||
| // Ignore parse errors | ||
| } |
There was a problem hiding this comment.
Server-sent error events are silently swallowed.
The throw new Error(parsed.error) on line 380 is caught by the immediately surrounding catch on line 392 and discarded. A server error response will be treated as a successful (empty) result instead of propagating the error.
🐛 Proposed fix
try {
const parsed = JSON.parse(data)
if (parsed.type === 'error') {
throw new Error(parsed.error)
}
if (parsed.type === 'TEXT_MESSAGE_CONTENT') {
chunkCount++
if (parsed.delta) {
summary += parsed.delta
} else if (parsed.content) {
summary = parsed.content
}
provider = parsed.provider || provider
model = parsed.model || model
}
- } catch {
- // Ignore parse errors
+ } catch (e) {
+ // Re-throw application-level errors, ignore JSON parse errors
+ if (e instanceof Error && e.message && !e.message.includes('JSON')) {
+ throw e
+ }
}Alternatively, separate the JSON parsing from the error-type check:
try {
- const parsed = JSON.parse(data)
- if (parsed.type === 'error') {
- throw new Error(parsed.error)
- }
- if (parsed.type === 'TEXT_MESSAGE_CONTENT') {
- chunkCount++
- if (parsed.delta) {
- summary += parsed.delta
- } else if (parsed.content) {
- summary = parsed.content
- }
- provider = parsed.provider || provider
- model = parsed.model || model
- }
- } catch {
- // Ignore parse errors
+ const parsed = JSON.parse(data)
+ if (parsed.type === 'error') {
+ throw new Error(parsed.error)
+ }
+ if (parsed.type === 'TEXT_MESSAGE_CONTENT') {
+ chunkCount++
+ if (parsed.delta) {
+ summary += parsed.delta
+ } else if (parsed.content) {
+ summary = parsed.content
+ }
+ provider = parsed.provider || provider
+ model = parsed.model || model
+ }
+ } catch (e) {
+ if (e instanceof SyntaxError) continue
+ throw e
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| try { | |
| const parsed = JSON.parse(data) | |
| if (parsed.type === 'error') { | |
| throw new Error(parsed.error) | |
| } | |
| if (parsed.type === 'TEXT_MESSAGE_CONTENT') { | |
| chunkCount++ | |
| if (parsed.delta) { | |
| summary += parsed.delta | |
| } else if (parsed.content) { | |
| summary = parsed.content | |
| } | |
| provider = parsed.provider || provider | |
| model = parsed.model || model | |
| } | |
| provider = parsed.provider || provider | |
| model = parsed.model || model | |
| } catch { | |
| // Ignore parse errors | |
| } | |
| try { | |
| const parsed = JSON.parse(data) | |
| if (parsed.type === 'error') { | |
| throw new Error(parsed.error) | |
| } | |
| if (parsed.type === 'TEXT_MESSAGE_CONTENT') { | |
| chunkCount++ | |
| if (parsed.delta) { | |
| summary += parsed.delta | |
| } else if (parsed.content) { | |
| summary = parsed.content | |
| } | |
| provider = parsed.provider || provider | |
| model = parsed.model || model | |
| } | |
| } catch (e) { | |
| // Re-throw application-level errors, ignore JSON parse errors | |
| if (e instanceof Error && e.message && !e.message.includes('JSON')) { | |
| throw e | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@testing/panel/tests/helpers.ts` around lines 377 - 394, The current try/catch
around JSON.parse swallows server-sent errors because throw new
Error(parsed.error) is caught locally; change the flow in the function that
handles the SSE chunk (the block using JSON.parse, parsed, throw new Error,
catch) so JSON.parse is wrapped in its own try/catch and any parse errors are
ignored, but the parsed result is then inspected outside that parse-only catch —
if parsed.type === 'error' rethrow or propagate that error (do not allow it to
be swallowed), otherwise continue with the existing TEXT_MESSAGE_CONTENT
handling (chunkCount, summary, provider, model). Ensure you only catch parse
exceptions, not the intentional error throws.
🎯 Changes
Creates a
textactivity and anagentLoopfunction to replace the agentic loop functionality and text generation inchat.✅ Checklist
pnpm run test:pr.🚀 Release Impact
Summary by CodeRabbit
Release Notes
New Features
text()function for straightforward text generation with streaming and structured output support.agentLoop()function enabling agentic workflows with automatic tool execution, approval flows, and configurable loop strategies.Bug Fixes & Improvements
Documentation
chat()to newtext()andagentLoop()APIs.Breaking Changes
ThinkingStreamChunkfrom public API type union.