-
-
Notifications
You must be signed in to change notification settings - Fork 116
feat: status property (ready, submitted, streaming error) for useChat #247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughAdds a ChatClientState lifecycle type ('ready' | 'submitted' | 'streaming' | 'error'), surfaces a new Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client
participant ChatClient as ChatClient
participant UseChat as UseChat Hook
participant UI as UI
Client->>ChatClient: sendMessage(...)
ChatClient->>ChatClient: setStatus("submitted")
ChatClient->>UseChat: onStatusChange("submitted")
ChatClient->>ChatClient: onStreamStart -> setStatus("streaming")
ChatClient->>UseChat: onStatusChange("streaming")
ChatClient->>ChatClient: onStreamEnd -> setStatus("ready")
ChatClient->>UseChat: onStatusChange("ready")
ChatClient->>UseChat: onError -> setStatus("error")
UseChat->>UI: expose status ('ready'|'submitted'|'streaming'|'error')
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
packages/typescript/ai-preact/src/types.ts (1)
27-31: Inconsistency with React:onStreamStartshould be omitted fromUseChatOptions.The React adapter omits
'onStreamStart'fromUseChatOptions(since the hook manages it internally to track status transitions), but the Preact adapter does not. This inconsistency allows Preact users to passonStreamStartwhile React users cannot.Suggested fix
export type UseChatOptions<TTools extends ReadonlyArray<AnyClientTool> = any> = Omit< ChatClientOptions<TTools>, - 'onMessagesChange' | 'onLoadingChange' | 'onErrorChange' + 'onMessagesChange' | 'onLoadingChange' | 'onErrorChange' | 'onStreamStart' >packages/typescript/ai-svelte/src/types.ts (1)
27-32: Inconsistency with React:onStreamStartshould be omitted fromCreateChatOptions.Same issue as in Preact — the React adapter omits
'onStreamStart'from options, but Svelte does not. For consistency, all framework adapters should handle internal callbacks the same way.Suggested fix
export type CreateChatOptions< TTools extends ReadonlyArray<AnyClientTool> = any, > = Omit< ChatClientOptions<TTools>, - 'onMessagesChange' | 'onLoadingChange' | 'onErrorChange' + 'onMessagesChange' | 'onLoadingChange' | 'onErrorChange' | 'onStreamStart' >packages/typescript/ai-client/src/chat-client.ts (1)
31-81: KeeponStreamStartin sync inupdateOptions.
updateOptionsupdatesonResponse/onChunk/onFinish/onErrorbut omitsonStreamStart, so refreshed callbacks won’t be picked up. Please add it for parity with the other callbacks.🛠️ Suggested fix
updateOptions(options: { connection?: ConnectionAdapter body?: Record<string, any> tools?: ReadonlyArray<AnyClientTool> onResponse?: (response?: Response) => void | Promise<void> onChunk?: (chunk: StreamChunk) => void onFinish?: (message: UIMessage) => void + onStreamStart?: () => void onError?: (error: Error) => void }): void { @@ if (options.onFinish !== undefined) { this.callbacksRef.current.onFinish = options.onFinish } + if (options.onStreamStart !== undefined) { + this.callbacksRef.current.onStreamStart = options.onStreamStart + } if (options.onError !== undefined) { this.callbacksRef.current.onError = options.onError } }packages/typescript/ai-preact/src/use-chat.ts (1)
1-10: Fix ESLint import order and type-only import usage.The linter reports two errors:
@tanstack/aitype import must occur afterpreact/hooks, andChatClientStateis only used as a type and should be imported withtype.🔧 Proposed import fix
-import type { AnyClientTool, ModelMessage } from '@tanstack/ai' -import { ChatClient, ChatClientState } from '@tanstack/ai-client' -import { - useCallback, - useEffect, - useId, - useMemo, - useRef, - useState, -} from 'preact/hooks' +import { + useCallback, + useEffect, + useId, + useMemo, + useRef, + useState, +} from 'preact/hooks' +import { ChatClient, type ChatClientState } from '@tanstack/ai-client' +import type { AnyClientTool, ModelMessage } from '@tanstack/ai'
🤖 Fix all issues with AI agents
In `@packages/typescript/ai-client/src/types.ts`:
- Around line 203-206: The JSDoc for the onStreamStart callback is missing a
space before the closing "*/"; update the comment above the onStreamStart?: ()
=> void declaration to match the project's JSDoc style (ensure a space before
the closing */, e.g. " * Callback when stream starts */" formatting) so it is
consistent with other comments in the file.
In `@packages/typescript/ai-svelte/src/create-chat.svelte.ts`:
- Around line 78-80: The onStreamStart handler currently only sets status =
'streaming' but doesn't call the user-provided callback; update the handler to
mirror the onFinish/onError pattern by invoking the passed-in callback (e.g.
call opts.onStreamStart?.() or onStreamStart?.() depending on how callbacks are
named) after setting status so user code is notified; reference the
onStreamStart handler and the existing onFinish/onError invocation pattern to
implement the call.
- Around line 1-2: Reorder the imports so that symbols from
'@tanstack/ai-client' come before '@tanstack/ai' and import ChatClientState as a
type-only import; specifically, update the top-level import statement to import
ChatClient and (type) ChatClientState from '@tanstack/ai-client' and then import
AnyClientTool and ModelMessage from '@tanstack/ai', keeping the rest of the file
(e.g. the $state<ChatClientState> usage) unchanged.
In `@packages/typescript/ai-vue/src/use-chat.ts`:
- Around line 48-50: The onStreamStart handler currently only sets status.value
= 'streaming' and ignores any user callback; update the onStreamStart
implementation to set status.value = 'streaming' then invoke the user-provided
callback (e.g., props.onStreamStart or options.onStreamStart /
callbacks.onStreamStart if available) in a safe way (check for existence before
calling) so consumer callbacks are forwarded while preserving the existing
status update.
🧹 Nitpick comments (5)
packages/typescript/ai-svelte/tests/use-chat.test.ts (1)
149-160: Consider adding status transition coverage for Svelte.This test only verifies the initial
'ready'state. To catch regressions and align with other framework tests, consider driving a mock stream/error to assert'submitted' → 'streaming' → 'ready'/'error'transitions.packages/typescript/ai-vue/tests/use-chat.test.ts (1)
1-10: Fix import order to satisfy ESLint rules.Type imports should occur after regular imports per the project's import order configuration.
♻️ Suggested fix
-import type { ModelMessage } from '@tanstack/ai' import { flushPromises } from '@vue/test-utils' import { describe, expect, it, vi } from 'vitest' -import type { UIMessage } from '../src/types' import { createMockConnectionAdapter, createTextChunks, createToolCallChunks, renderUseChat, } from './test-utils' +import type { ModelMessage } from '@tanstack/ai' +import type { UIMessage } from '../src/types'packages/typescript/ai-vue/tests/test-utils.ts (1)
1-5: Fix import order to satisfy ESLint rules.Type imports should occur after regular imports per the project's import order configuration.
♻️ Suggested fix
-import type { UIMessage } from '@tanstack/ai-client' import { mount } from '@vue/test-utils' import { defineComponent } from 'vue' -import type { UseChatOptions } from '../src/types' import { useChat } from '../src/use-chat' +import type { UIMessage } from '@tanstack/ai-client' +import type { UseChatOptions } from '../src/types'packages/typescript/ai-react/src/use-chat.ts (1)
2-2: Use type-only import forChatClientState.
ChatClientStateis only used as a type, so it should be imported using thetypekeyword to enable better tree-shaking and satisfy the ESLint rule.♻️ Suggested fix
-import { ChatClient, ChatClientState } from '@tanstack/ai-client' +import { ChatClient } from '@tanstack/ai-client' +import type { ChatClientState } from '@tanstack/ai-client'packages/typescript/ai-solid/src/use-chat.ts (1)
7-9: Fix import order and use type-only import forChatClientState.Two issues flagged by ESLint:
@tanstack/ai-clientimport should occur before the type import of@tanstack/aiChatClientStateis only used as a type♻️ Suggested fix
import { createEffect, createMemo, createSignal, createUniqueId, } from 'solid-js' - -import type { AnyClientTool, ModelMessage } from '@tanstack/ai' -import { ChatClient, ChatClientState } from '@tanstack/ai-client' +import { ChatClient } from '@tanstack/ai-client' +import type { AnyClientTool, ModelMessage } from '@tanstack/ai' +import type { ChatClientState } from '@tanstack/ai-client' import type { UIMessage, UseChatOptions, UseChatReturn } from './types'
| import type { AnyClientTool, ModelMessage } from '@tanstack/ai' | ||
| import { ChatClient, ChatClientState } from '@tanstack/ai-client' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Find the file and read its contents
cat -n packages/typescript/ai-svelte/src/create-chat.svelte.ts | head -50Repository: TanStack/ai
Length of output: 2040
🏁 Script executed:
# Check if ChatClientState is exported from the tanstack packages
rg "export.*ChatClientState" --type ts --type tsxRepository: TanStack/ai
Length of output: 82
🏁 Script executed:
# Search for how ChatClientState is used in the file
rg "ChatClientState" packages/typescript/ai-svelte/src/create-chat.svelte.ts -B 2 -A 2Repository: TanStack/ai
Length of output: 430
🏁 Script executed:
# Find where ChatClientState is exported from
rg "export.*ChatClientState" --type tsRepository: TanStack/ai
Length of output: 127
🏁 Script executed:
# Check import order patterns in similar files
rg -B2 -A2 "import.*@tanstack/ai-client" packages/typescript/ai-svelte/src/ --type ts | head -30Repository: TanStack/ai
Length of output: 487
🏁 Script executed:
# Check if there's an eslint config specifying import order
find . -name ".eslintrc*" -o -name "eslint.config.*" | head -5Repository: TanStack/ai
Length of output: 284
🏁 Script executed:
# Check the eslint config for ai-svelte
cat packages/typescript/ai-svelte/eslint.config.jsRepository: TanStack/ai
Length of output: 214
🏁 Script executed:
# Also check the root eslint config
cat ./eslint.config.jsRepository: TanStack/ai
Length of output: 603
Use a type-only import for ChatClientState and reorder imports.
ChatClientState is used only as a type annotation (line 47: $state<ChatClientState>), so it should be imported as type-only. Additionally, imports from @tanstack/ai-client should come before @tanstack/ai per ESLint conventions.
Suggested fix
-import type { AnyClientTool, ModelMessage } from '@tanstack/ai'
-import { ChatClient, ChatClientState } from '@tanstack/ai-client'
+import { ChatClient } from '@tanstack/ai-client'
+import type { ChatClientState } from '@tanstack/ai-client'
+import type { AnyClientTool, ModelMessage } from '@tanstack/ai'📝 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.
| import type { AnyClientTool, ModelMessage } from '@tanstack/ai' | |
| import { ChatClient, ChatClientState } from '@tanstack/ai-client' | |
| import { ChatClient } from '@tanstack/ai-client' | |
| import type { ChatClientState } from '@tanstack/ai-client' | |
| import type { AnyClientTool, ModelMessage } from '@tanstack/ai' |
🧰 Tools
🪛 ESLint
[error] 2-2: Imports "ChatClientState" are only used as type.
(@typescript-eslint/consistent-type-imports)
[error] 2-2: @tanstack/ai-client import should occur before type import of @tanstack/ai
(import/order)
🤖 Prompt for AI Agents
In `@packages/typescript/ai-svelte/src/create-chat.svelte.ts` around lines 1 - 2,
Reorder the imports so that symbols from '@tanstack/ai-client' come before
'@tanstack/ai' and import ChatClientState as a type-only import; specifically,
update the top-level import statement to import ChatClient and (type)
ChatClientState from '@tanstack/ai-client' and then import AnyClientTool and
ModelMessage from '@tanstack/ai', keeping the rest of the file (e.g. the
$state<ChatClientState> usage) unchanged.
| onStreamStart: () => { | ||
| status = 'streaming' | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forward onStreamStart to user callbacks.
Right now the status updates but user-provided onStreamStart is never invoked. Mirror the onFinish/onError pattern.
🛠️ Suggested fix
onStreamStart: () => {
status = 'streaming'
+ options.onStreamStart?.()
},📝 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.
| onStreamStart: () => { | |
| status = 'streaming' | |
| }, | |
| onStreamStart: () => { | |
| status = 'streaming' | |
| options.onStreamStart?.() | |
| }, |
🤖 Prompt for AI Agents
In `@packages/typescript/ai-svelte/src/create-chat.svelte.ts` around lines 78 -
80, The onStreamStart handler currently only sets status = 'streaming' but
doesn't call the user-provided callback; update the handler to mirror the
onFinish/onError pattern by invoking the passed-in callback (e.g. call
opts.onStreamStart?.() or onStreamStart?.() depending on how callbacks are
named) after setting status so user code is notified; reference the
onStreamStart handler and the existing onFinish/onError invocation pattern to
implement the call.
| onStreamStart: () => { | ||
| status.value = 'streaming' | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forward onStreamStart to user callbacks.
onStreamStart currently sets status but ignores any user-provided callback. Please forward it to preserve expected behavior.
🛠️ Suggested fix
onStreamStart: () => {
status.value = 'streaming'
+ options.onStreamStart?.()
},📝 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.
| onStreamStart: () => { | |
| status.value = 'streaming' | |
| }, | |
| onStreamStart: () => { | |
| status.value = 'streaming' | |
| options.onStreamStart?.() | |
| }, |
🤖 Prompt for AI Agents
In `@packages/typescript/ai-vue/src/use-chat.ts` around lines 48 - 50, The
onStreamStart handler currently only sets status.value = 'streaming' and ignores
any user callback; update the onStreamStart implementation to set status.value =
'streaming' then invoke the user-provided callback (e.g., props.onStreamStart or
options.onStreamStart / callbacks.onStreamStart if available) in a safe way
(check for existence before calling) so consumer callbacks are forwarded while
preserving the existing status update.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/typescript/ai-preact/src/use-chat.ts (1)
1-10: Resolve import order + type-only split (lint).This triggers
import/orderandimport/consistent-type-specifier-style.🛠️ Suggested fix
-import type { AnyClientTool, ModelMessage } from '@tanstack/ai' -import { ChatClient, type ChatClientState } from '@tanstack/ai-client' -import { - useCallback, - useEffect, - useId, - useMemo, - useRef, - useState, -} from 'preact/hooks' +import { + useCallback, + useEffect, + useId, + useMemo, + useRef, + useState, +} from 'preact/hooks' +import { ChatClient } from '@tanstack/ai-client' +import type { ChatClientState } from '@tanstack/ai-client' +import type { AnyClientTool, ModelMessage } from '@tanstack/ai'packages/typescript/ai-solid/src/use-chat.ts (1)
1-10: Fix import order + type-only split (lint).This violates
import/orderandimport/consistent-type-specifier-style.🛠️ Suggested fix
import { createEffect, createMemo, createSignal, createUniqueId, } from 'solid-js' -import type { AnyClientTool, ModelMessage } from '@tanstack/ai' -import { ChatClient, type ChatClientState } from '@tanstack/ai-client' +import { ChatClient } from '@tanstack/ai-client' +import type { ChatClientState } from '@tanstack/ai-client' +import type { AnyClientTool, ModelMessage } from '@tanstack/ai' import type { UIMessage, UseChatOptions, UseChatReturn } from './types'
🤖 Fix all issues with AI agents
In `@packages/typescript/ai-preact/src/use-chat.ts`:
- Around line 76-78: The onStreamStart handler currently only calls
setStatus('streaming') but never invokes the user-provided callback; update the
handler inside useChat (the onStreamStart block) to call the passed-in user
callback after updating status (e.g., options.onStreamStart or onStreamStart
prop) using safe optional invocation (options?.onStreamStart?.(...)) and forward
any relevant args/events to it so the user's callback runs when streaming
starts.
In `@packages/typescript/ai-react/src/use-chat.ts`:
- Around line 1-3: The import currently mixes a type with a value import for
ChatClientState; change it to a top-level type-only import by moving
ChatClientState into its own import line using the `import type {
ChatClientState } from '@tanstack/ai-client'` form and leave `import {
ChatClient } from '@tanstack/ai-client'` (or vice versa) so ChatClientState is
imported purely as a type (refer to the ChatClient and ChatClientState symbols
in use-chat.ts).
- Around line 54-56: The onStreamStart handler currently only calls
setStatus("streaming") and never invokes the consumer's callback; update the
onStreamStart block in use-chat.ts (the onStreamStart handler) to call the
user-provided callback after updating status — e.g., keep setStatus("streaming")
then check for and invoke the external onStreamStart callback (the
prop/option/callback passed into the useChat/useChat hook) so consumers receive
the event.
In `@packages/typescript/ai-solid/src/use-chat.ts`:
- Around line 58-60: The onStreamStart handler currently only calls
setStatus('streaming') but doesn't invoke the consumer's callback; update the
onStreamStart implementation to call the user-provided onStreamStart callback
after setting status (e.g., if (props.onStreamStart)
props.onStreamStart(...args) or options.onStreamStart?.(args)), preserving any
arguments the stream start provides and guarding for its existence so both
setStatus('streaming') and the user's callback run.
In `@packages/typescript/ai-vue/src/use-chat.ts`:
- Around line 1-4: The file imports ChatClientState with a mixed value/type
import which violates the consistent-type-specifier-style rule; change the
imports to use a top-level type-only import for ChatClientState and keep
ChatClient as a value import — i.e., split the current "import { ChatClient,
type ChatClientState } from '@tanstack/ai-client'" into a value import for
ChatClient and a separate "import type { ChatClientState }" so ChatClientState
is only imported as a type; update references to ChatClientState and ChatClient
in use-chat.ts accordingly.
♻️ Duplicate comments (2)
packages/typescript/ai-svelte/src/create-chat.svelte.ts (1)
1-3: Fix import ordering + type-only split to satisfy lint.ESLint still flags
import/orderandimport/consistent-type-specifier-stylehere.🛠️ Suggested fix
-import type { AnyClientTool, ModelMessage } from '@tanstack/ai' -import { ChatClient, type ChatClientState } from '@tanstack/ai-client' +import { ChatClient } from '@tanstack/ai-client' +import type { ChatClientState } from '@tanstack/ai-client' +import type { AnyClientTool, ModelMessage } from '@tanstack/ai'packages/typescript/ai-vue/src/use-chat.ts (1)
48-50: ForwardonStreamStartto consumer callback.Status changes but the user callback is skipped.
🛠️ Suggested fix
onStreamStart: () => { status.value = 'streaming' + options.onStreamStart?.() },
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@packages/typescript/ai-react/src/use-chat.ts`:
- Around line 1-4: Reorder the import statements to satisfy ESLint import-order
rules by placing React imports (useCallback, useEffect, useId, useMemo, useRef,
useState from 'react') before external package imports; move the line importing
ChatClient and ChatClientState from '@tanstack/ai-client' and the types
AnyClientTool and ModelMessage from '@tanstack/ai' after the React import so
that React comes first, then third‑party packages, keeping each import's
original named symbols (ChatClient, ChatClientState, AnyClientTool,
ModelMessage) unchanged.
In `@packages/typescript/ai-vue/src/use-chat.ts`:
- Around line 1-4: Reorder the imports so the concrete module imports from
'@tanstack/ai-client' (ChatClient, ChatClientState) appear before the type-only
import from '@tanstack/ai' (AnyClientTool, ModelMessage) to satisfy ESLint
ordering; keep the 'type' modifiers on type-only imports (e.g., type {
AnyClientTool, ModelMessage } and type { ChatClientState }) and preserve the
existing Vue imports (onScopeDispose, readonly, shallowRef, useId) after the
tanstack imports.
♻️ Duplicate comments (2)
packages/typescript/ai-react/src/use-chat.ts (1)
55-57: ForwardonStreamStartto the consumer callback.The internal handler updates status but doesn’t invoke the user’s
onStreamStart, so external listeners never fire.🛠️ Proposed fix
onStreamStart: () => { - setStatus("streaming") + setStatus('streaming') + optionsRef.current.onStreamStart?.() },packages/typescript/ai-vue/src/use-chat.ts (1)
49-50: ForwardonStreamStartto user callbacks.The hook updates status but doesn’t invoke
options.onStreamStart, so consumers miss the event.🛠️ Proposed fix
onStreamStart: () => { status.value = 'streaming' + options.onStreamStart?.() },
| import type { AnyClientTool, ModelMessage } from '@tanstack/ai' | ||
| import { ChatClient } from '@tanstack/ai-client' | ||
| import type { ChatClientState } from '@tanstack/ai-client' | ||
| import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix import ordering to satisfy lint rules.
ESLint flags the current ordering of the React and @tanstack/ai-client imports.
🛠️ Proposed fix
-import type { AnyClientTool, ModelMessage } from '@tanstack/ai'
-import { ChatClient } from '@tanstack/ai-client'
-import type { ChatClientState } from '@tanstack/ai-client'
-import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'
+import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'
+import { ChatClient } from '@tanstack/ai-client'
+import type { ChatClientState } from '@tanstack/ai-client'
+import type { AnyClientTool, ModelMessage } from '@tanstack/ai'📝 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.
| import type { AnyClientTool, ModelMessage } from '@tanstack/ai' | |
| import { ChatClient } from '@tanstack/ai-client' | |
| import type { ChatClientState } from '@tanstack/ai-client' | |
| import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react' | |
| import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react' | |
| import { ChatClient } from '@tanstack/ai-client' | |
| import type { ChatClientState } from '@tanstack/ai-client' | |
| import type { AnyClientTool, ModelMessage } from '@tanstack/ai' |
🧰 Tools
🪛 ESLint
[error] 2-2: @tanstack/ai-client import should occur before type import of @tanstack/ai
(import/order)
[error] 4-4: react import should occur before type import of @tanstack/ai
(import/order)
🤖 Prompt for AI Agents
In `@packages/typescript/ai-react/src/use-chat.ts` around lines 1 - 4, Reorder the
import statements to satisfy ESLint import-order rules by placing React imports
(useCallback, useEffect, useId, useMemo, useRef, useState from 'react') before
external package imports; move the line importing ChatClient and ChatClientState
from '@tanstack/ai-client' and the types AnyClientTool and ModelMessage from
'@tanstack/ai' after the React import so that React comes first, then
third‑party packages, keeping each import's original named symbols (ChatClient,
ChatClientState, AnyClientTool, ModelMessage) unchanged.
| if (newIsLoading) { | ||
| setStatus('submitted') | ||
| } else { | ||
| setStatus((prev) => (prev === 'error' ? 'error' : 'ready')) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mind collapsing this into setStatus(newIsLoading ? "submitted" : ...)
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx affected --targets=test:sherif,test:knip,tes... |
❌ Failed | 1m 50s | View ↗ |
nx run-many --targets=build --exclude=examples/** |
✅ Succeeded | 50s | View ↗ |
☁️ Nx Cloud last updated this comment at 2026-01-27 12:38:11 UTC
AlemTuzlak
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jherr Do you think we should collapse the status into the ChatClient internally instead of requiring state management per lib? I think that would be a sane way of approaching this and then the consumer libs just read it instead of having to deal with re-implementing the logic 5x times?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@packages/typescript/ai-vue/tests/test-utils.ts`:
- Around line 1-5: Reorder the imports so type-only imports come after the value
import from '../src/use-chat' to satisfy ESLint import/order: keep value imports
like import { useChat } from '../src/use-chat' and runtime imports (mount,
defineComponent) earlier, then move type imports (import type { UIMessage } and
import type { UseChatOptions }) immediately after those, ensuring you use
"import type" for the type-only symbols (UIMessage, UseChatOptions) and leave
useChat, mount, and defineComponent as value imports.
♻️ Duplicate comments (2)
packages/typescript/ai-react/src/use-chat.ts (2)
1-4: Resolve ESLint import/order violation.Line 1-4 should place React imports first and keep
@tanstack/ai-clientbefore the@tanstack/aitype import.🧹 Proposed import reorder
-import type { AnyClientTool, ModelMessage } from '@tanstack/ai' -import { ChatClient } from '@tanstack/ai-client' -import type { ChatClientState } from '@tanstack/ai-client' -import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react' +import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react' +import { ChatClient } from '@tanstack/ai-client' +import type { ChatClientState } from '@tanstack/ai-client' +import type { AnyClientTool, ModelMessage } from '@tanstack/ai'
55-65: Forward onStreamStart to the user callback.Line 55-57 updates status but drops the consumer callback, so external listeners never fire.
🛠️ Suggested fix
onStreamStart: () => { setStatus('streaming') + optionsRef.current.onStreamStart?.() },
| import type { UIMessage } from '@tanstack/ai-client' | ||
| import { mount } from '@vue/test-utils' | ||
| import { useChat } from '../src/use-chat' | ||
| import { defineComponent } from 'vue' | ||
| import type { UseChatOptions } from '../src/types' | ||
| import type { UIMessage } from '@tanstack/ai-client' | ||
| import { useChat } from '../src/use-chat' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix import order to satisfy ESLint.
Line 1-5 violate import/order (type imports should follow the value import from ../src/use-chat).
🧹 Proposed import reorder
-import type { UIMessage } from '@tanstack/ai-client'
-import { mount } from '@vue/test-utils'
-import { defineComponent } from 'vue'
-import type { UseChatOptions } from '../src/types'
-import { useChat } from '../src/use-chat'
+import { mount } from '@vue/test-utils'
+import { defineComponent } from 'vue'
+import { useChat } from '../src/use-chat'
+import type { UIMessage } from '@tanstack/ai-client'
+import type { UseChatOptions } from '../src/types'📝 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.
| import type { UIMessage } from '@tanstack/ai-client' | |
| import { mount } from '@vue/test-utils' | |
| import { useChat } from '../src/use-chat' | |
| import { defineComponent } from 'vue' | |
| import type { UseChatOptions } from '../src/types' | |
| import type { UIMessage } from '@tanstack/ai-client' | |
| import { useChat } from '../src/use-chat' | |
| import { mount } from '@vue/test-utils' | |
| import { defineComponent } from 'vue' | |
| import { useChat } from '../src/use-chat' | |
| import type { UIMessage } from '@tanstack/ai-client' | |
| import type { UseChatOptions } from '../src/types' |
🧰 Tools
🪛 ESLint
[error] 1-1: @tanstack/ai-client type import should occur after import of ../src/use-chat
(import/order)
[error] 4-4: ../src/types type import should occur after import of ../src/use-chat
(import/order)
🤖 Prompt for AI Agents
In `@packages/typescript/ai-vue/tests/test-utils.ts` around lines 1 - 5, Reorder
the imports so type-only imports come after the value import from
'../src/use-chat' to satisfy ESLint import/order: keep value imports like import
{ useChat } from '../src/use-chat' and runtime imports (mount, defineComponent)
earlier, then move type imports (import type { UIMessage } and import type {
UseChatOptions }) immediately after those, ensuring you use "import type" for
the type-only symbols (UIMessage, UseChatOptions) and leave useChat, mount, and
defineComponent as value imports.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@packages/typescript/ai-preact/tests/use-chat.test.ts`:
- Around line 1-10: Reorder the imports so type-only imports come after the
regular imports: move "import type { ModelMessage } from '@tanstack/ai'" and
"import type { UIMessage } from '../src/types'" to after the block that imports
createMockConnectionAdapter, createTextChunks, createToolCallChunks, and
renderUseChat from './test-utils' to satisfy the import/order ESLint rule; keep
the existing testing-library and vitest imports where they are and ensure only
type imports are relocated.
In `@packages/typescript/ai-vue/tests/use-chat.test.ts`:
- Around line 1-10: Move the type-only imports (ModelMessage, UIMessage) below
the regular imports from the test-utils module so they follow the
createMockConnectionAdapter, createTextChunks, createToolCallChunks,
renderUseChat imports; specifically, reorder the top of the file so the non-type
import block (createMockConnectionAdapter, createTextChunks,
createToolCallChunks, renderUseChat) appears before the type imports
(ModelMessage, UIMessage) to satisfy the import/order ESLint rule.
🧹 Nitpick comments (4)
packages/typescript/ai-client/src/chat-client.ts (2)
1-16: Import order flagged by linter.ESLint reports that regular imports should occur before type imports from the same module. This is a style issue.
♻️ Suggested fix
-import type { AnyClientTool, ModelMessage, StreamChunk } from '@tanstack/ai' import { StreamProcessor, generateMessageId, normalizeToUIMessage, } from '@tanstack/ai' +import type { AnyClientTool, ModelMessage, StreamChunk } from '@tanstack/ai' import type { ConnectionAdapter } from './connection-adapters' import type { ChatClientEventEmitter } from './events' import { DefaultChatClientEventEmitter } from './events'
81-91: DuplicatesetStatus('error')call when errors occur in stream processing.When an error occurs during stream processing,
setError(error)is called at line 89, which internally callssetStatus('error')(lines 205-207). Then line 90 explicitly callssetStatus('error')again. While this is harmless (idempotent), it triggersonStatusChangetwice with the same value.♻️ Suggested fix - remove redundant call
onError: (error: Error) => { this.setError(error) - this.setStatus('error') this.callbacksRef.current.onError(error) },packages/typescript/ai-preact/src/use-chat.ts (1)
1-2: Import order and type specifier style flagged by linter.ESLint reports import order issues and suggests using top-level type-only imports. This is a style consistency matter.
♻️ Suggested fix
+import { + useCallback, + useEffect, + useId, + useMemo, + useRef, + useState, +} from 'preact/hooks' + import type { AnyClientTool, ModelMessage } from '@tanstack/ai' -import { ChatClient, type ChatClientState } from '@tanstack/ai-client' -import { - useCallback, - useEffect, - useId, - useMemo, - useRef, - useState, -} from 'preact/hooks' +import type { ChatClientState } from '@tanstack/ai-client' +import { ChatClient } from '@tanstack/ai-client'packages/typescript/ai-solid/src/use-chat.ts (1)
7-9: Import order and type specifier style flagged by linter.ESLint reports import order issues and suggests using top-level type-only imports.
♻️ Suggested fix
import { createEffect, createMemo, createSignal, createUniqueId, } from 'solid-js' - import type { AnyClientTool, ModelMessage } from '@tanstack/ai' -import { ChatClient, type ChatClientState } from '@tanstack/ai-client' +import type { ChatClientState } from '@tanstack/ai-client' +import { ChatClient } from '@tanstack/ai-client' import type { UIMessage, UseChatOptions, UseChatReturn } from './types'
| import type { ModelMessage } from '@tanstack/ai' | ||
| import { act, waitFor } from '@testing-library/preact' | ||
| import { describe, expect, it, vi } from 'vitest' | ||
| import type { UIMessage } from '../src/types' | ||
| import { | ||
| createMockConnectionAdapter, | ||
| createTextChunks, | ||
| createToolCallChunks, | ||
| renderUseChat, | ||
| } from './test-utils' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix import order to satisfy ESLint rules.
Type imports should occur after the regular imports from ./test-utils per the configured import/order rule.
🔧 Proposed fix
-import type { ModelMessage } from '@tanstack/ai'
import { act, waitFor } from '@testing-library/preact'
import { describe, expect, it, vi } from 'vitest'
-import type { UIMessage } from '../src/types'
import {
createMockConnectionAdapter,
createTextChunks,
createToolCallChunks,
renderUseChat,
} from './test-utils'
+import type { ModelMessage } from '@tanstack/ai'
+import type { UIMessage } from '../src/types'📝 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.
| import type { ModelMessage } from '@tanstack/ai' | |
| import { act, waitFor } from '@testing-library/preact' | |
| import { describe, expect, it, vi } from 'vitest' | |
| import type { UIMessage } from '../src/types' | |
| import { | |
| createMockConnectionAdapter, | |
| createTextChunks, | |
| createToolCallChunks, | |
| renderUseChat, | |
| } from './test-utils' | |
| import { act, waitFor } from '@testing-library/preact' | |
| import { describe, expect, it, vi } from 'vitest' | |
| import { | |
| createMockConnectionAdapter, | |
| createTextChunks, | |
| createToolCallChunks, | |
| renderUseChat, | |
| } from './test-utils' | |
| import type { ModelMessage } from '@tanstack/ai' | |
| import type { UIMessage } from '../src/types' |
🧰 Tools
🪛 ESLint
[error] 1-1: @tanstack/ai type import should occur after import of ./test-utils
(import/order)
[error] 4-4: ../src/types type import should occur after import of ./test-utils
(import/order)
🤖 Prompt for AI Agents
In `@packages/typescript/ai-preact/tests/use-chat.test.ts` around lines 1 - 10,
Reorder the imports so type-only imports come after the regular imports: move
"import type { ModelMessage } from '@tanstack/ai'" and "import type { UIMessage
} from '../src/types'" to after the block that imports
createMockConnectionAdapter, createTextChunks, createToolCallChunks, and
renderUseChat from './test-utils' to satisfy the import/order ESLint rule; keep
the existing testing-library and vitest imports where they are and ensure only
type imports are relocated.
| import type { ModelMessage } from '@tanstack/ai' | ||
| import { flushPromises } from '@vue/test-utils' | ||
| import { describe, expect, it, vi } from 'vitest' | ||
| import type { UIMessage } from '../src/types' | ||
| import { | ||
| createMockConnectionAdapter, | ||
| createTextChunks, | ||
| createToolCallChunks, | ||
| renderUseChat, | ||
| } from './test-utils' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix import order to satisfy ESLint rules.
Type imports should occur after the regular imports from ./test-utils per the configured import/order rule.
🔧 Proposed fix
-import type { ModelMessage } from '@tanstack/ai'
import { flushPromises } from '@vue/test-utils'
import { describe, expect, it, vi } from 'vitest'
-import type { UIMessage } from '../src/types'
import {
createMockConnectionAdapter,
createTextChunks,
createToolCallChunks,
renderUseChat,
} from './test-utils'
+import type { ModelMessage } from '@tanstack/ai'
+import type { UIMessage } from '../src/types'📝 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.
| import type { ModelMessage } from '@tanstack/ai' | |
| import { flushPromises } from '@vue/test-utils' | |
| import { describe, expect, it, vi } from 'vitest' | |
| import type { UIMessage } from '../src/types' | |
| import { | |
| createMockConnectionAdapter, | |
| createTextChunks, | |
| createToolCallChunks, | |
| renderUseChat, | |
| } from './test-utils' | |
| import { flushPromises } from '@vue/test-utils' | |
| import { describe, expect, it, vi } from 'vitest' | |
| import { | |
| createMockConnectionAdapter, | |
| createTextChunks, | |
| createToolCallChunks, | |
| renderUseChat, | |
| } from './test-utils' | |
| import type { ModelMessage } from '@tanstack/ai' | |
| import type { UIMessage } from '../src/types' |
🧰 Tools
🪛 ESLint
[error] 1-1: @tanstack/ai type import should occur after import of ./test-utils
(import/order)
[error] 4-4: ../src/types type import should occur after import of ./test-utils
(import/order)
🤖 Prompt for AI Agents
In `@packages/typescript/ai-vue/tests/use-chat.test.ts` around lines 1 - 10, Move
the type-only imports (ModelMessage, UIMessage) below the regular imports from
the test-utils module so they follow the createMockConnectionAdapter,
createTextChunks, createToolCallChunks, renderUseChat imports; specifically,
reorder the top of the file so the non-type import block
(createMockConnectionAdapter, createTextChunks, createToolCallChunks,
renderUseChat) appears before the type imports (ModelMessage, UIMessage) to
satisfy the import/order ESLint rule.
| if (error) { | ||
| this.setStatus('error') | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is called both here and on line 90, remove this check
AlemTuzlak
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks really neat now! Thank you @nikas-belogolov , I'll let @jherr review it as well
Just please address my comment on the error status
@tanstack/ai
@tanstack/ai-anthropic
@tanstack/ai-client
@tanstack/ai-devtools-core
@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: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@packages/typescript/ai-client/src/chat-client.ts`:
- Around line 88-91: The onError handler is redundantly calling
setStatus('error') after setError(error) (which already sets status), causing
duplicate onStatusChange notifications; remove the extra setStatus('error')
calls in the onError blocks so only setError(error) is invoked before calling
callbacksRef.current.onError(error); apply the same removal for the second
occurrence around the code that mirrors this logic (the one referenced at lines
205-207).
…ored test cases for status
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@packages/typescript/ai-preact/tests/use-chat.test.ts`:
- Around line 566-650: Three status tests are commented out (initial status,
error transition, stop transition) in the use-chat test suite; locate the tests
using identifiers renderUseChat, createMockConnectionAdapter,
result.current.sendMessage, result.current.stop and either (A) re-enable and fix
them so they pass (restore the original it(...) blocks and address any
framework-specific failure causing assertions on result.current.status), or (B)
explicitly mark them as skipped with a reason using it.skip('reason', ...) or
add a TODO/FIXME comment above each block explaining why they are disabled and
linking to an issue; pick one approach and apply it consistently for the three
blocks so test intent and visibility are preserved.
🧹 Nitpick comments (3)
packages/typescript/ai-client/src/chat-client.ts (1)
1-16: Fix import order per ESLint rules.The static analysis indicates import order violations. Type imports from
@tanstack/aishould occur after regular imports, and other type imports should be grouped appropriately.🔧 Suggested fix
-import type { AnyClientTool, ModelMessage, StreamChunk } from '@tanstack/ai' import { StreamProcessor, generateMessageId, normalizeToUIMessage, } from '@tanstack/ai' -import type { ConnectionAdapter } from './connection-adapters' -import type { ChatClientEventEmitter } from './events' import { DefaultChatClientEventEmitter } from './events' +import type { AnyClientTool, ModelMessage, StreamChunk } from '@tanstack/ai' +import type { ConnectionAdapter } from './connection-adapters' +import type { ChatClientEventEmitter } from './events' import type { ChatClientOptions, ChatClientState, MessagePart, ToolCallPart, UIMessage, } from './types'packages/typescript/ai-client/tests/chat-client.test.ts (1)
395-423: Consider adding explicit tests for initial status and error transitions.The status test suite covers the happy path well but could be more comprehensive:
- Missing explicit test for initial status being
'ready'(constructor default)- Missing dedicated test for
statustransitioning to'error'(currently only tested implicitly in the error handling section)This would align with the test coverage in other framework test files (e.g., Svelte tests have explicit error transition tests in the status section).
💡 Suggested additional tests
it('should have initial status of ready', () => { const adapter = createMockConnectionAdapter() const client = new ChatClient({ connection: adapter }) expect(client.getStatus()).toBe('ready') }) it('should transition to error on connection failure', async () => { const adapter = createMockConnectionAdapter({ shouldError: true, error: new Error('Test error'), }) const client = new ChatClient({ connection: adapter }) await client.sendMessage('Test') expect(client.getStatus()).toBe('error') })packages/typescript/ai-vue/tests/use-chat.test.ts (1)
466-489: Consider adding a test for the'error'status transition.The test covers the happy path (
'ready'→'submitted'/'streaming'→'ready'), but the'error'status is not explicitly tested. Since the status property includes'error'as a valid state, consider adding a test to verify the status transitions to'error'when an error occurs.♻️ Suggested test addition
it('should transition through states during generation', async () => { // ... existing test }) + + it('should transition to error status on failure', async () => { + const error = new Error('Test error') + const adapter = createMockConnectionAdapter({ + shouldError: true, + error, + }) + const { result } = renderUseChat({ connection: adapter }) + + expect(result.current.status).toBe('ready') + + await result.current.sendMessage('Test') + await flushPromises() + + expect(result.current.status).toBe('error') + expect(result.current.error).toBeDefined() + }) })

🎯 Changes
Added a status property to useChat to track the generation lifecycle (ready, submitted, streaming, error).
✅ Checklist
pnpm run test:pr.🚀 Release Impact
Summary by CodeRabbit
New Features
Tests
Chores
✏️ Tip: You can customize this high-level summary in your review settings.