@tanstack/ai-solid 0.5.4 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/use-chat.js CHANGED
@@ -24,6 +24,7 @@ function useChat(options = {}) {
24
24
  options.onError?.(err);
25
25
  },
26
26
  tools: options.tools,
27
+ onCustomEvent: options.onCustomEvent,
27
28
  streamProcessor: options.streamProcessor,
28
29
  onMessagesChange: (newMessages) => {
29
30
  setMessages(newMessages);
@@ -1 +1 @@
1
- {"version":3,"file":"use-chat.js","names":[],"sources":["../src/use-chat.ts"],"sourcesContent":["import {\n createEffect,\n createMemo,\n createSignal,\n createUniqueId,\n} from 'solid-js'\n\nimport { ChatClient } from '@tanstack/ai-client'\nimport type { ChatClientState } from '@tanstack/ai-client'\nimport type { AnyClientTool, ModelMessage } from '@tanstack/ai'\nimport type {\n MultimodalContent,\n UIMessage,\n UseChatOptions,\n UseChatReturn,\n} from './types'\n\nexport function useChat<TTools extends ReadonlyArray<AnyClientTool> = any>(\n options: UseChatOptions<TTools> = {} as UseChatOptions<TTools>,\n): UseChatReturn<TTools> {\n const hookId = createUniqueId()\n const clientId = options.id || hookId\n\n const [messages, setMessages] = createSignal<Array<UIMessage<TTools>>>(\n options.initialMessages || [],\n )\n const [isLoading, setIsLoading] = createSignal(false)\n const [error, setError] = createSignal<Error | undefined>(undefined)\n const [status, setStatus] = createSignal<ChatClientState>('ready')\n\n // Create ChatClient instance with callbacks to sync state\n // Note: Options are captured at client creation time.\n // The connection adapter can use functions for dynamic values (url, headers, etc.)\n // which are evaluated lazily on each request.\n const client = createMemo(() => {\n return new ChatClient({\n connection: options.connection,\n id: clientId,\n initialMessages: options.initialMessages,\n body: options.body,\n onResponse: options.onResponse,\n onChunk: options.onChunk,\n onFinish: (message) => {\n options.onFinish?.(message)\n },\n onError: (err) => {\n options.onError?.(err)\n },\n tools: options.tools,\n streamProcessor: options.streamProcessor,\n onMessagesChange: (newMessages: Array<UIMessage<TTools>>) => {\n setMessages(newMessages)\n },\n onLoadingChange: (newIsLoading: boolean) => {\n setIsLoading(newIsLoading)\n },\n onStatusChange: (newStatus: ChatClientState) => {\n setStatus(newStatus)\n },\n onErrorChange: (newError: Error | undefined) => {\n setError(newError)\n },\n })\n // Only recreate when clientId changes\n // Connection and other options are captured at creation time\n }, [clientId])\n\n // Sync body changes to the client\n // This allows dynamic body values (like model selection) to be updated without recreating the client\n createEffect(() => {\n const currentBody = options.body\n client().updateOptions({ body: currentBody })\n })\n\n // Sync initial messages on mount only\n // Note: initialMessages are passed to ChatClient constructor, but we also\n // set them here to ensure React state is in sync\n createEffect(() => {\n if (options.initialMessages && options.initialMessages.length > 0) {\n // Only set if current messages are empty (initial state)\n if (messages().length === 0) {\n client().setMessagesManually(options.initialMessages)\n }\n }\n }) // Only run on mount - initialMessages are handled by ChatClient constructor\n\n // Cleanup on unmount: stop any in-flight requests\n // Note: We use createEffect with a cleanup return to handle component unmount.\n // The cleanup only runs on disposal (unmount), not on signal changes.\n createEffect(() => {\n return () => {\n // Stop any active generation when component unmounts\n client().stop()\n }\n })\n\n // Note: Callback options (onResponse, onChunk, onFinish, onError, onToolCall)\n // are captured at client creation time. Changes to these callbacks require\n // remounting the component or changing the connection to recreate the client.\n\n const sendMessage = async (content: string | MultimodalContent) => {\n await client().sendMessage(content)\n }\n\n const append = async (message: ModelMessage | UIMessage<TTools>) => {\n await client().append(message)\n }\n\n const reload = async () => {\n await client().reload()\n }\n\n const stop = () => {\n client().stop()\n }\n\n const clear = () => {\n client().clear()\n }\n\n const setMessagesManually = (newMessages: Array<UIMessage<TTools>>) => {\n client().setMessagesManually(newMessages)\n }\n\n const addToolResult = async (result: {\n toolCallId: string\n tool: string\n output: any\n state?: 'output-available' | 'output-error'\n errorText?: string\n }) => {\n await client().addToolResult(result)\n }\n\n const addToolApprovalResponse = async (response: {\n id: string\n approved: boolean\n }) => {\n await client().addToolApprovalResponse(response)\n }\n\n return {\n messages,\n sendMessage,\n append,\n reload,\n stop,\n isLoading,\n error,\n status,\n setMessages: setMessagesManually,\n clear,\n addToolResult,\n addToolApprovalResponse,\n }\n}\n"],"mappings":";;;;AAiBA,SAAgB,QACd,UAAkC,EAAE,EACb;CACvB,MAAM,SAAS,gBAAgB;CAC/B,MAAM,WAAW,QAAQ,MAAM;CAE/B,MAAM,CAAC,UAAU,eAAe,aAC9B,QAAQ,mBAAmB,EAAE,CAC9B;CACD,MAAM,CAAC,WAAW,gBAAgB,aAAa,MAAM;CACrD,MAAM,CAAC,OAAO,YAAY,aAAgC,OAAU;CACpE,MAAM,CAAC,QAAQ,aAAa,aAA8B,QAAQ;CAMlE,MAAM,SAAS,iBAAiB;AAC9B,SAAO,IAAI,WAAW;GACpB,YAAY,QAAQ;GACpB,IAAI;GACJ,iBAAiB,QAAQ;GACzB,MAAM,QAAQ;GACd,YAAY,QAAQ;GACpB,SAAS,QAAQ;GACjB,WAAW,YAAY;AACrB,YAAQ,WAAW,QAAQ;;GAE7B,UAAU,QAAQ;AAChB,YAAQ,UAAU,IAAI;;GAExB,OAAO,QAAQ;GACf,iBAAiB,QAAQ;GACzB,mBAAmB,gBAA0C;AAC3D,gBAAY,YAAY;;GAE1B,kBAAkB,iBAA0B;AAC1C,iBAAa,aAAa;;GAE5B,iBAAiB,cAA+B;AAC9C,cAAU,UAAU;;GAEtB,gBAAgB,aAAgC;AAC9C,aAAS,SAAS;;GAErB,CAAC;IAGD,CAAC,SAAS,CAAC;AAId,oBAAmB;EACjB,MAAM,cAAc,QAAQ;AAC5B,UAAQ,CAAC,cAAc,EAAE,MAAM,aAAa,CAAC;GAC7C;AAKF,oBAAmB;AACjB,MAAI,QAAQ,mBAAmB,QAAQ,gBAAgB,SAAS,GAE9D;OAAI,UAAU,CAAC,WAAW,EACxB,SAAQ,CAAC,oBAAoB,QAAQ,gBAAgB;;GAGzD;AAKF,oBAAmB;AACjB,eAAa;AAEX,WAAQ,CAAC,MAAM;;GAEjB;CAMF,MAAM,cAAc,OAAO,YAAwC;AACjE,QAAM,QAAQ,CAAC,YAAY,QAAQ;;CAGrC,MAAM,SAAS,OAAO,YAA8C;AAClE,QAAM,QAAQ,CAAC,OAAO,QAAQ;;CAGhC,MAAM,SAAS,YAAY;AACzB,QAAM,QAAQ,CAAC,QAAQ;;CAGzB,MAAM,aAAa;AACjB,UAAQ,CAAC,MAAM;;CAGjB,MAAM,cAAc;AAClB,UAAQ,CAAC,OAAO;;CAGlB,MAAM,uBAAuB,gBAA0C;AACrE,UAAQ,CAAC,oBAAoB,YAAY;;CAG3C,MAAM,gBAAgB,OAAO,WAMvB;AACJ,QAAM,QAAQ,CAAC,cAAc,OAAO;;CAGtC,MAAM,0BAA0B,OAAO,aAGjC;AACJ,QAAM,QAAQ,CAAC,wBAAwB,SAAS;;AAGlD,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,aAAa;EACb;EACA;EACA;EACD"}
1
+ {"version":3,"file":"use-chat.js","names":[],"sources":["../src/use-chat.ts"],"sourcesContent":["import {\n createEffect,\n createMemo,\n createSignal,\n createUniqueId,\n} from 'solid-js'\n\nimport { ChatClient } from '@tanstack/ai-client'\nimport type { ChatClientState } from '@tanstack/ai-client'\nimport type { AnyClientTool, ModelMessage } from '@tanstack/ai'\nimport type {\n MultimodalContent,\n UIMessage,\n UseChatOptions,\n UseChatReturn,\n} from './types'\n\nexport function useChat<TTools extends ReadonlyArray<AnyClientTool> = any>(\n options: UseChatOptions<TTools> = {} as UseChatOptions<TTools>,\n): UseChatReturn<TTools> {\n const hookId = createUniqueId()\n const clientId = options.id || hookId\n\n const [messages, setMessages] = createSignal<Array<UIMessage<TTools>>>(\n options.initialMessages || [],\n )\n const [isLoading, setIsLoading] = createSignal(false)\n const [error, setError] = createSignal<Error | undefined>(undefined)\n const [status, setStatus] = createSignal<ChatClientState>('ready')\n\n // Create ChatClient instance with callbacks to sync state\n // Note: Options are captured at client creation time.\n // The connection adapter can use functions for dynamic values (url, headers, etc.)\n // which are evaluated lazily on each request.\n const client = createMemo(() => {\n return new ChatClient({\n connection: options.connection,\n id: clientId,\n initialMessages: options.initialMessages,\n body: options.body,\n onResponse: options.onResponse,\n onChunk: options.onChunk,\n onFinish: (message) => {\n options.onFinish?.(message)\n },\n onError: (err) => {\n options.onError?.(err)\n },\n tools: options.tools,\n onCustomEvent: options.onCustomEvent,\n streamProcessor: options.streamProcessor,\n onMessagesChange: (newMessages: Array<UIMessage<TTools>>) => {\n setMessages(newMessages)\n },\n onLoadingChange: (newIsLoading: boolean) => {\n setIsLoading(newIsLoading)\n },\n onStatusChange: (newStatus: ChatClientState) => {\n setStatus(newStatus)\n },\n onErrorChange: (newError: Error | undefined) => {\n setError(newError)\n },\n })\n // Only recreate when clientId changes\n // Connection and other options are captured at creation time\n }, [clientId])\n\n // Sync body changes to the client\n // This allows dynamic body values (like model selection) to be updated without recreating the client\n createEffect(() => {\n const currentBody = options.body\n client().updateOptions({ body: currentBody })\n })\n\n // Sync initial messages on mount only\n // Note: initialMessages are passed to ChatClient constructor, but we also\n // set them here to ensure React state is in sync\n createEffect(() => {\n if (options.initialMessages && options.initialMessages.length > 0) {\n // Only set if current messages are empty (initial state)\n if (messages().length === 0) {\n client().setMessagesManually(options.initialMessages)\n }\n }\n }) // Only run on mount - initialMessages are handled by ChatClient constructor\n\n // Cleanup on unmount: stop any in-flight requests\n // Note: We use createEffect with a cleanup return to handle component unmount.\n // The cleanup only runs on disposal (unmount), not on signal changes.\n createEffect(() => {\n return () => {\n // Stop any active generation when component unmounts\n client().stop()\n }\n })\n\n // Note: Callback options (onResponse, onChunk, onFinish, onError, onToolCall)\n // are captured at client creation time. Changes to these callbacks require\n // remounting the component or changing the connection to recreate the client.\n\n const sendMessage = async (content: string | MultimodalContent) => {\n await client().sendMessage(content)\n }\n\n const append = async (message: ModelMessage | UIMessage<TTools>) => {\n await client().append(message)\n }\n\n const reload = async () => {\n await client().reload()\n }\n\n const stop = () => {\n client().stop()\n }\n\n const clear = () => {\n client().clear()\n }\n\n const setMessagesManually = (newMessages: Array<UIMessage<TTools>>) => {\n client().setMessagesManually(newMessages)\n }\n\n const addToolResult = async (result: {\n toolCallId: string\n tool: string\n output: any\n state?: 'output-available' | 'output-error'\n errorText?: string\n }) => {\n await client().addToolResult(result)\n }\n\n const addToolApprovalResponse = async (response: {\n id: string\n approved: boolean\n }) => {\n await client().addToolApprovalResponse(response)\n }\n\n return {\n messages,\n sendMessage,\n append,\n reload,\n stop,\n isLoading,\n error,\n status,\n setMessages: setMessagesManually,\n clear,\n addToolResult,\n addToolApprovalResponse,\n }\n}\n"],"mappings":";;;;AAiBA,SAAgB,QACd,UAAkC,EAAE,EACb;CACvB,MAAM,SAAS,gBAAgB;CAC/B,MAAM,WAAW,QAAQ,MAAM;CAE/B,MAAM,CAAC,UAAU,eAAe,aAC9B,QAAQ,mBAAmB,EAAE,CAC9B;CACD,MAAM,CAAC,WAAW,gBAAgB,aAAa,MAAM;CACrD,MAAM,CAAC,OAAO,YAAY,aAAgC,OAAU;CACpE,MAAM,CAAC,QAAQ,aAAa,aAA8B,QAAQ;CAMlE,MAAM,SAAS,iBAAiB;AAC9B,SAAO,IAAI,WAAW;GACpB,YAAY,QAAQ;GACpB,IAAI;GACJ,iBAAiB,QAAQ;GACzB,MAAM,QAAQ;GACd,YAAY,QAAQ;GACpB,SAAS,QAAQ;GACjB,WAAW,YAAY;AACrB,YAAQ,WAAW,QAAQ;;GAE7B,UAAU,QAAQ;AAChB,YAAQ,UAAU,IAAI;;GAExB,OAAO,QAAQ;GACf,eAAe,QAAQ;GACvB,iBAAiB,QAAQ;GACzB,mBAAmB,gBAA0C;AAC3D,gBAAY,YAAY;;GAE1B,kBAAkB,iBAA0B;AAC1C,iBAAa,aAAa;;GAE5B,iBAAiB,cAA+B;AAC9C,cAAU,UAAU;;GAEtB,gBAAgB,aAAgC;AAC9C,aAAS,SAAS;;GAErB,CAAC;IAGD,CAAC,SAAS,CAAC;AAId,oBAAmB;EACjB,MAAM,cAAc,QAAQ;AAC5B,UAAQ,CAAC,cAAc,EAAE,MAAM,aAAa,CAAC;GAC7C;AAKF,oBAAmB;AACjB,MAAI,QAAQ,mBAAmB,QAAQ,gBAAgB,SAAS,GAE9D;OAAI,UAAU,CAAC,WAAW,EACxB,SAAQ,CAAC,oBAAoB,QAAQ,gBAAgB;;GAGzD;AAKF,oBAAmB;AACjB,eAAa;AAEX,WAAQ,CAAC,MAAM;;GAEjB;CAMF,MAAM,cAAc,OAAO,YAAwC;AACjE,QAAM,QAAQ,CAAC,YAAY,QAAQ;;CAGrC,MAAM,SAAS,OAAO,YAA8C;AAClE,QAAM,QAAQ,CAAC,OAAO,QAAQ;;CAGhC,MAAM,SAAS,YAAY;AACzB,QAAM,QAAQ,CAAC,QAAQ;;CAGzB,MAAM,aAAa;AACjB,UAAQ,CAAC,MAAM;;CAGjB,MAAM,cAAc;AAClB,UAAQ,CAAC,OAAO;;CAGlB,MAAM,uBAAuB,gBAA0C;AACrE,UAAQ,CAAC,oBAAoB,YAAY;;CAG3C,MAAM,gBAAgB,OAAO,WAMvB;AACJ,QAAM,QAAQ,CAAC,cAAc,OAAO;;CAGtC,MAAM,0BAA0B,OAAO,aAGjC;AACJ,QAAM,QAAQ,CAAC,wBAAwB,SAAS;;AAGlD,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,aAAa;EACb;EACA;EACA;EACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-solid",
3
- "version": "0.5.4",
3
+ "version": "0.6.1",
4
4
  "description": "Solid hooks for TanStack AI",
5
5
  "author": "",
6
6
  "license": "MIT",
@@ -31,11 +31,11 @@
31
31
  "streaming"
32
32
  ],
33
33
  "dependencies": {
34
- "@tanstack/ai-client": "0.4.5"
34
+ "@tanstack/ai-client": "0.5.1"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "solid-js": ">=1.9.10",
38
- "@tanstack/ai": "^0.5.1"
38
+ "@tanstack/ai": "^0.6.1"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@solidjs/testing-library": "^0.8.10",
@@ -46,7 +46,7 @@
46
46
  "tsdown": "^0.17.0-beta.6",
47
47
  "typescript": "5.9.3",
48
48
  "vitest": "^4.0.14",
49
- "@tanstack/ai": "0.5.1"
49
+ "@tanstack/ai": "0.6.1"
50
50
  },
51
51
  "scripts": {
52
52
  "clean": "premove ./build ./dist",
package/src/use-chat.ts CHANGED
@@ -47,6 +47,7 @@ export function useChat<TTools extends ReadonlyArray<AnyClientTool> = any>(
47
47
  options.onError?.(err)
48
48
  },
49
49
  tools: options.tools,
50
+ onCustomEvent: options.onCustomEvent,
50
51
  streamProcessor: options.streamProcessor,
51
52
  onMessagesChange: (newMessages: Array<UIMessage<TTools>>) => {
52
53
  setMessages(newMessages)