@tanstack/ai-solid 0.15.1 → 0.16.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/README.md +17 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/types.d.ts +29 -3
- package/dist/use-chat.js +81 -5
- package/dist/use-chat.js.map +1 -1
- package/dist/use-generate-audio.d.ts +9 -10
- package/dist/use-generate-audio.js +4 -11
- package/dist/use-generate-audio.js.map +1 -1
- package/dist/use-generate-image.d.ts +9 -10
- package/dist/use-generate-image.js +4 -11
- package/dist/use-generate-image.js.map +1 -1
- package/dist/use-generate-speech.d.ts +9 -10
- package/dist/use-generate-speech.js +4 -11
- package/dist/use-generate-speech.js.map +1 -1
- package/dist/use-generate-video.d.ts +50 -4
- package/dist/use-generate-video.js +54 -23
- package/dist/use-generate-video.js.map +1 -1
- package/dist/use-generation.d.ts +59 -6
- package/dist/use-generation.js +43 -19
- package/dist/use-generation.js.map +1 -1
- package/dist/use-summarize.d.ts +9 -10
- package/dist/use-summarize.js +4 -11
- package/dist/use-summarize.js.map +1 -1
- package/dist/use-transcription.d.ts +9 -10
- package/dist/use-transcription.js +4 -11
- package/dist/use-transcription.js.map +1 -1
- package/package.json +6 -6
- package/src/index.ts +10 -0
- package/src/types.ts +44 -0
- package/src/use-chat.ts +123 -6
- package/src/use-generate-audio.ts +33 -23
- package/src/use-generate-image.ts +33 -23
- package/src/use-generate-speech.ts +32 -23
- package/src/use-generate-video.ts +120 -25
- package/src/use-generation.ts +129 -25
- package/src/use-summarize.ts +34 -23
- package/src/use-transcription.ts +33 -24
package/dist/use-summarize.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { UseGenerationOptions, UseGenerationReturn } from "./use-generation.js";
|
|
1
2
|
import { Accessor } from "solid-js";
|
|
2
|
-
import { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutputFromReturn, SummarizeGenerateInput } from "@tanstack/ai-client";
|
|
3
|
+
import { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, GenerationPersistenceOptions, InferGenerationOutputFromReturn, SummarizeGenerateInput } from "@tanstack/ai-client";
|
|
3
4
|
import { StreamChunk, SummarizationResult } from "@tanstack/ai";
|
|
4
5
|
|
|
5
6
|
//#region src/use-summarize.d.ts
|
|
@@ -9,12 +10,14 @@ import { StreamChunk, SummarizationResult } from "@tanstack/ai";
|
|
|
9
10
|
*
|
|
10
11
|
* @template TOutput - The transformed output type (defaults to SummarizationResult)
|
|
11
12
|
*/
|
|
12
|
-
interface UseSummarizeOptions<TOutput = SummarizationResult> {
|
|
13
|
+
interface UseSummarizeOptions<TOutput = SummarizationResult> extends Pick<UseGenerationOptions<SummarizeGenerateInput, SummarizationResult, TOutput>, 'persistence' | 'threadId' | 'hydrateGeneration' | 'joinRun'> {
|
|
13
14
|
/** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */
|
|
14
15
|
connection?: ConnectConnectionAdapter;
|
|
15
16
|
/** Direct async function for summarization */
|
|
16
17
|
fetcher?: GenerationFetcher<SummarizeGenerateInput, SummarizationResult>;
|
|
17
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
|
|
20
|
+
*/
|
|
18
21
|
id?: string;
|
|
19
22
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
20
23
|
body?: Record<string, any>;
|
|
@@ -40,7 +43,7 @@ interface UseSummarizeOptions<TOutput = SummarizationResult> {
|
|
|
40
43
|
*
|
|
41
44
|
* @template TOutput - The transformed output type (defaults to SummarizationResult)
|
|
42
45
|
*/
|
|
43
|
-
interface UseSummarizeReturn<TOutput = SummarizationResult> {
|
|
46
|
+
interface UseSummarizeReturn<TOutput = SummarizationResult> extends Omit<UseGenerationReturn<TOutput>, 'generate'> {
|
|
44
47
|
/** Trigger summarization */
|
|
45
48
|
generate: (input: SummarizeGenerateInput) => Promise<void>;
|
|
46
49
|
/** The summarization result, or null */
|
|
@@ -51,10 +54,6 @@ interface UseSummarizeReturn<TOutput = SummarizationResult> {
|
|
|
51
54
|
error: Accessor<Error | undefined>;
|
|
52
55
|
/** Current state of the generation */
|
|
53
56
|
status: Accessor<GenerationClientState>;
|
|
54
|
-
/** Abort the current summarization */
|
|
55
|
-
stop: () => void;
|
|
56
|
-
/** Clear result, error, and return to idle */
|
|
57
|
-
reset: () => void;
|
|
58
57
|
}
|
|
59
58
|
/**
|
|
60
59
|
* Solid hook for summarizing text using AI models.
|
|
@@ -85,9 +84,9 @@ interface UseSummarizeReturn<TOutput = SummarizationResult> {
|
|
|
85
84
|
* }
|
|
86
85
|
* ```
|
|
87
86
|
*/
|
|
88
|
-
declare function useSummarize<TTransformed = void>(options: Omit<UseSummarizeOptions, 'onResult'> & {
|
|
87
|
+
declare function useSummarize<TTransformed = void>(options: Omit<UseSummarizeOptions, 'onResult' | 'persistence' | 'threadId' | 'id'> & {
|
|
89
88
|
onResult?: (result: SummarizationResult) => TTransformed;
|
|
90
|
-
}): UseSummarizeReturn<InferGenerationOutputFromReturn<SummarizationResult, TTransformed>>;
|
|
89
|
+
} & GenerationPersistenceOptions): UseSummarizeReturn<InferGenerationOutputFromReturn<SummarizationResult, TTransformed>>;
|
|
91
90
|
//#endregion
|
|
92
91
|
export { UseSummarizeOptions, UseSummarizeReturn, useSummarize };
|
|
93
92
|
//# sourceMappingURL=use-summarize.d.ts.map
|
package/dist/use-summarize.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useGeneration } from "./use-generation.js";
|
|
2
|
+
import { reconstructSummarizeResult } from "@tanstack/ai-client";
|
|
2
3
|
|
|
3
4
|
//#region src/use-summarize.ts
|
|
4
5
|
/**
|
|
@@ -37,19 +38,11 @@ function useSummarize(options) {
|
|
|
37
38
|
hookName: "useSummarize",
|
|
38
39
|
outputKind: "text"
|
|
39
40
|
};
|
|
40
|
-
|
|
41
|
+
return useGeneration({
|
|
41
42
|
...options,
|
|
42
|
-
devtools
|
|
43
|
+
devtools,
|
|
44
|
+
reconstructResult: reconstructSummarizeResult
|
|
43
45
|
});
|
|
44
|
-
return {
|
|
45
|
-
generate,
|
|
46
|
-
result,
|
|
47
|
-
isLoading,
|
|
48
|
-
error,
|
|
49
|
-
status,
|
|
50
|
-
stop,
|
|
51
|
-
reset
|
|
52
|
-
};
|
|
53
46
|
}
|
|
54
47
|
|
|
55
48
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-summarize.js","names":[],"sources":["../src/use-summarize.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport type { StreamChunk, SummarizationResult } from '@tanstack/ai'\nimport type {\n AIDevtoolsDisplayOptions,\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n InferGenerationOutputFromReturn,\n SummarizeGenerateInput,\n} from '@tanstack/ai-client'\nimport type { Accessor } from 'solid-js'\n\n/**\n * Options for the useSummarize hook.\n *\n * @template TOutput - The transformed output type (defaults to SummarizationResult)\n */\nexport interface UseSummarizeOptions
|
|
1
|
+
{"version":3,"file":"use-summarize.js","names":[],"sources":["../src/use-summarize.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport { reconstructSummarizeResult } from '@tanstack/ai-client'\nimport type {\n UseGenerationOptions,\n UseGenerationReturn,\n} from './use-generation'\nimport type { StreamChunk, SummarizationResult } from '@tanstack/ai'\nimport type {\n AIDevtoolsDisplayOptions,\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n GenerationPersistenceOptions,\n InferGenerationOutputFromReturn,\n SummarizeGenerateInput,\n} from '@tanstack/ai-client'\nimport type { Accessor } from 'solid-js'\n\n/**\n * Options for the useSummarize hook.\n *\n * @template TOutput - The transformed output type (defaults to SummarizationResult)\n */\nexport interface UseSummarizeOptions<\n TOutput = SummarizationResult,\n> extends Pick<\n UseGenerationOptions<SummarizeGenerateInput, SummarizationResult, TOutput>,\n 'persistence' | 'threadId' | 'hydrateGeneration' | 'joinRun'\n> {\n /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */\n connection?: ConnectConnectionAdapter\n /** Direct async function for summarization */\n fetcher?: GenerationFetcher<SummarizeGenerateInput, SummarizationResult>\n /**\n * @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).\n */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /** Display options for TanStack AI Devtools. */\n devtools?: AIDevtoolsDisplayOptions\n /**\n * Callback when summarization is complete. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: SummarizationResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useSummarize hook.\n *\n * @template TOutput - The transformed output type (defaults to SummarizationResult)\n */\nexport interface UseSummarizeReturn<TOutput = SummarizationResult> extends Omit<\n UseGenerationReturn<TOutput>,\n 'generate'\n> {\n /** Trigger summarization */\n generate: (input: SummarizeGenerateInput) => Promise<void>\n /** The summarization result, or null */\n result: Accessor<TOutput | null>\n /** Whether summarization is in progress */\n isLoading: Accessor<boolean>\n /** Current error, if any */\n error: Accessor<Error | undefined>\n /** Current state of the generation */\n status: Accessor<GenerationClientState>\n}\n\n/**\n * Solid hook for summarizing text using AI models.\n *\n * @example\n * ```tsx\n * import { useSummarize } from '@tanstack/ai-solid'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function Summarizer() {\n * const { generate, result, isLoading } = useSummarize({\n * connection: fetchServerSentEvents('/api/summarize'),\n * })\n *\n * return (\n * <div>\n * <button onClick={() => generate({\n * text: 'Long article text...',\n * style: 'bullet-points',\n * maxLength: 200,\n * })}>\n * Summarize\n * </button>\n * {isLoading() && <p>Summarizing...</p>}\n * {result() && <p>{result()!.summary}</p>}\n * </div>\n * )\n * }\n * ```\n */\nexport function useSummarize<TTransformed = void>(\n options: Omit<\n UseSummarizeOptions,\n 'onResult' | 'persistence' | 'threadId' | 'id'\n > & {\n onResult?: (result: SummarizationResult) => TTransformed\n } & GenerationPersistenceOptions,\n): UseSummarizeReturn<\n InferGenerationOutputFromReturn<SummarizationResult, TTransformed>\n> {\n const devtools = {\n ...options.devtools,\n framework: 'solid',\n hookName: 'useSummarize',\n outputKind: 'text' as const,\n }\n const generation = useGeneration<\n SummarizeGenerateInput,\n SummarizationResult,\n TTransformed\n >({\n ...options,\n devtools,\n reconstructResult: reconstructSummarizeResult,\n })\n\n return generation\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2GA,SAAgB,aACd,SAQA;CACA,MAAM,WAAW;EACf,GAAG,QAAQ;EACX,WAAW;EACX,UAAU;EACV,YAAY;EACb;AAWD,QAVmB,cAIjB;EACA,GAAG;EACH;EACA,mBAAmB;EACpB,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { UseGenerationOptions, UseGenerationReturn } from "./use-generation.js";
|
|
1
2
|
import { Accessor } from "solid-js";
|
|
2
|
-
import { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutputFromReturn, TranscriptionGenerateInput } from "@tanstack/ai-client";
|
|
3
|
+
import { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, GenerationPersistenceOptions, InferGenerationOutputFromReturn, TranscriptionGenerateInput } from "@tanstack/ai-client";
|
|
3
4
|
import { StreamChunk, TranscriptionResult } from "@tanstack/ai";
|
|
4
5
|
|
|
5
6
|
//#region src/use-transcription.d.ts
|
|
@@ -9,12 +10,14 @@ import { StreamChunk, TranscriptionResult } from "@tanstack/ai";
|
|
|
9
10
|
*
|
|
10
11
|
* @template TOutput - The transformed output type (defaults to TranscriptionResult)
|
|
11
12
|
*/
|
|
12
|
-
interface UseTranscriptionOptions<TOutput = TranscriptionResult> {
|
|
13
|
+
interface UseTranscriptionOptions<TOutput = TranscriptionResult> extends Pick<UseGenerationOptions<TranscriptionGenerateInput, TranscriptionResult, TOutput>, 'persistence' | 'threadId' | 'hydrateGeneration' | 'joinRun'> {
|
|
13
14
|
/** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */
|
|
14
15
|
connection?: ConnectConnectionAdapter;
|
|
15
16
|
/** Direct async function for transcription */
|
|
16
17
|
fetcher?: GenerationFetcher<TranscriptionGenerateInput, TranscriptionResult>;
|
|
17
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
|
|
20
|
+
*/
|
|
18
21
|
id?: string;
|
|
19
22
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
20
23
|
body?: Record<string, any>;
|
|
@@ -40,7 +43,7 @@ interface UseTranscriptionOptions<TOutput = TranscriptionResult> {
|
|
|
40
43
|
*
|
|
41
44
|
* @template TOutput - The transformed output type (defaults to TranscriptionResult)
|
|
42
45
|
*/
|
|
43
|
-
interface UseTranscriptionReturn<TOutput = TranscriptionResult> {
|
|
46
|
+
interface UseTranscriptionReturn<TOutput = TranscriptionResult> extends Omit<UseGenerationReturn<TOutput>, 'generate'> {
|
|
44
47
|
/** Trigger transcription */
|
|
45
48
|
generate: (input: TranscriptionGenerateInput) => Promise<void>;
|
|
46
49
|
/** The transcription result, or null */
|
|
@@ -51,10 +54,6 @@ interface UseTranscriptionReturn<TOutput = TranscriptionResult> {
|
|
|
51
54
|
error: Accessor<Error | undefined>;
|
|
52
55
|
/** Current state of the generation */
|
|
53
56
|
status: Accessor<GenerationClientState>;
|
|
54
|
-
/** Abort the current transcription */
|
|
55
|
-
stop: () => void;
|
|
56
|
-
/** Clear result, error, and return to idle */
|
|
57
|
-
reset: () => void;
|
|
58
57
|
}
|
|
59
58
|
/**
|
|
60
59
|
* Solid hook for transcribing audio to text using AI models.
|
|
@@ -91,9 +90,9 @@ interface UseTranscriptionReturn<TOutput = TranscriptionResult> {
|
|
|
91
90
|
* }
|
|
92
91
|
* ```
|
|
93
92
|
*/
|
|
94
|
-
declare function useTranscription<TTransformed = void>(options: Omit<UseTranscriptionOptions, 'onResult'> & {
|
|
93
|
+
declare function useTranscription<TTransformed = void>(options: Omit<UseTranscriptionOptions, 'onResult' | 'persistence' | 'threadId' | 'id'> & {
|
|
95
94
|
onResult?: (result: TranscriptionResult) => TTransformed;
|
|
96
|
-
}): UseTranscriptionReturn<InferGenerationOutputFromReturn<TranscriptionResult, TTransformed>>;
|
|
95
|
+
} & GenerationPersistenceOptions): UseTranscriptionReturn<InferGenerationOutputFromReturn<TranscriptionResult, TTransformed>>;
|
|
97
96
|
//#endregion
|
|
98
97
|
export { UseTranscriptionOptions, UseTranscriptionReturn, useTranscription };
|
|
99
98
|
//# sourceMappingURL=use-transcription.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useGeneration } from "./use-generation.js";
|
|
2
|
+
import { reconstructTranscriptionResult } from "@tanstack/ai-client";
|
|
2
3
|
|
|
3
4
|
//#region src/use-transcription.ts
|
|
4
5
|
/**
|
|
@@ -43,19 +44,11 @@ function useTranscription(options) {
|
|
|
43
44
|
hookName: "useTranscription",
|
|
44
45
|
outputKind: "text"
|
|
45
46
|
};
|
|
46
|
-
|
|
47
|
+
return useGeneration({
|
|
47
48
|
...options,
|
|
48
|
-
devtools
|
|
49
|
+
devtools,
|
|
50
|
+
reconstructResult: reconstructTranscriptionResult
|
|
49
51
|
});
|
|
50
|
-
return {
|
|
51
|
-
generate,
|
|
52
|
-
result,
|
|
53
|
-
isLoading,
|
|
54
|
-
error,
|
|
55
|
-
status,
|
|
56
|
-
stop,
|
|
57
|
-
reset
|
|
58
|
-
};
|
|
59
52
|
}
|
|
60
53
|
|
|
61
54
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-transcription.js","names":[],"sources":["../src/use-transcription.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport type { StreamChunk, TranscriptionResult } from '@tanstack/ai'\nimport type {\n AIDevtoolsDisplayOptions,\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n InferGenerationOutputFromReturn,\n TranscriptionGenerateInput,\n} from '@tanstack/ai-client'\nimport type { Accessor } from 'solid-js'\n\n/**\n * Options for the useTranscription hook.\n *\n * @template TOutput - The transformed output type (defaults to TranscriptionResult)\n */\nexport interface UseTranscriptionOptions
|
|
1
|
+
{"version":3,"file":"use-transcription.js","names":[],"sources":["../src/use-transcription.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport { reconstructTranscriptionResult } from '@tanstack/ai-client'\nimport type {\n UseGenerationOptions,\n UseGenerationReturn,\n} from './use-generation'\nimport type { StreamChunk, TranscriptionResult } from '@tanstack/ai'\nimport type {\n AIDevtoolsDisplayOptions,\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n GenerationPersistenceOptions,\n InferGenerationOutputFromReturn,\n TranscriptionGenerateInput,\n} from '@tanstack/ai-client'\nimport type { Accessor } from 'solid-js'\n\n/**\n * Options for the useTranscription hook.\n *\n * @template TOutput - The transformed output type (defaults to TranscriptionResult)\n */\nexport interface UseTranscriptionOptions<\n TOutput = TranscriptionResult,\n> extends Pick<\n UseGenerationOptions<\n TranscriptionGenerateInput,\n TranscriptionResult,\n TOutput\n >,\n 'persistence' | 'threadId' | 'hydrateGeneration' | 'joinRun'\n> {\n /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */\n connection?: ConnectConnectionAdapter\n /** Direct async function for transcription */\n fetcher?: GenerationFetcher<TranscriptionGenerateInput, TranscriptionResult>\n /**\n * @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).\n */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /** Display options for TanStack AI Devtools. */\n devtools?: AIDevtoolsDisplayOptions\n /**\n * Callback when transcription is complete. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: TranscriptionResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useTranscription hook.\n *\n * @template TOutput - The transformed output type (defaults to TranscriptionResult)\n */\nexport interface UseTranscriptionReturn<\n TOutput = TranscriptionResult,\n> extends Omit<UseGenerationReturn<TOutput>, 'generate'> {\n /** Trigger transcription */\n generate: (input: TranscriptionGenerateInput) => Promise<void>\n /** The transcription result, or null */\n result: Accessor<TOutput | null>\n /** Whether transcription is in progress */\n isLoading: Accessor<boolean>\n /** Current error, if any */\n error: Accessor<Error | undefined>\n /** Current state of the generation */\n status: Accessor<GenerationClientState>\n}\n\n/**\n * Solid hook for transcribing audio to text using AI models.\n *\n * @example\n * ```tsx\n * import { useTranscription } from '@tanstack/ai-solid'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function Transcriber() {\n * const { generate, result, isLoading } = useTranscription({\n * connection: fetchServerSentEvents('/api/transcribe'),\n * })\n *\n * const handleFile = (e: Event) => {\n * const input = e.target as HTMLInputElement\n * const file = input.files?.[0]\n * if (file) {\n * const reader = new FileReader()\n * reader.onload = () => {\n * generate({ audio: reader.result as string, language: 'en' })\n * }\n * reader.readAsDataURL(file)\n * }\n * }\n *\n * return (\n * <div>\n * <input type=\"file\" accept=\"audio/*\" onChange={handleFile} />\n * {isLoading() && <p>Transcribing...</p>}\n * {result() && <p>{result()!.text}</p>}\n * </div>\n * )\n * }\n * ```\n */\nexport function useTranscription<TTransformed = void>(\n options: Omit<\n UseTranscriptionOptions,\n 'onResult' | 'persistence' | 'threadId' | 'id'\n > & {\n onResult?: (result: TranscriptionResult) => TTransformed\n } & GenerationPersistenceOptions,\n): UseTranscriptionReturn<\n InferGenerationOutputFromReturn<TranscriptionResult, TTransformed>\n> {\n const devtools = {\n ...options.devtools,\n framework: 'solid',\n hookName: 'useTranscription',\n outputKind: 'text' as const,\n }\n const generation = useGeneration<\n TranscriptionGenerateInput,\n TranscriptionResult,\n TTransformed\n >({ ...options, devtools, reconstructResult: reconstructTranscriptionResult })\n\n return generation\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoHA,SAAgB,iBACd,SAQA;CACA,MAAM,WAAW;EACf,GAAG,QAAQ;EACX,WAAW;EACX,UAAU;EACV,YAAY;EACb;AAOD,QANmB,cAIjB;EAAE,GAAG;EAAS;EAAU,mBAAmB;EAAgC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-solid",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"description": "Solid hooks for TanStack AI streaming chat, structured outputs, and media generation.",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
"media-generation"
|
|
46
46
|
],
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@tanstack/ai-client": "0.
|
|
48
|
+
"@tanstack/ai-client": "0.23.1"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"solid-js": ">=1.9.10",
|
|
52
|
-
"@tanstack/ai": "^0.
|
|
52
|
+
"@tanstack/ai": "^0.43.1"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@solidjs/testing-library": "^0.8.10",
|
|
@@ -60,12 +60,12 @@
|
|
|
60
60
|
"solid-js": "^1.9.10",
|
|
61
61
|
"tsdown": "^0.17.0-beta.6",
|
|
62
62
|
"typescript": "5.9.3",
|
|
63
|
-
"vitest": "^4.
|
|
64
|
-
"@tanstack/ai": "0.
|
|
63
|
+
"vitest": "^4.1.10",
|
|
64
|
+
"@tanstack/ai": "0.43.1"
|
|
65
65
|
},
|
|
66
66
|
"scripts": {
|
|
67
67
|
"clean": "premove ./build ./dist",
|
|
68
|
-
"test:
|
|
68
|
+
"test:oxlint": "oxlint src --type-aware",
|
|
69
69
|
"test:lib": "vitest run",
|
|
70
70
|
"test:lib:dev": "pnpm test:lib --watch",
|
|
71
71
|
"test:types": "tsc",
|
package/src/index.ts
CHANGED
|
@@ -62,6 +62,16 @@ export type {
|
|
|
62
62
|
// Re-export from ai-client for convenience
|
|
63
63
|
export {
|
|
64
64
|
fetchServerSentEvents,
|
|
65
|
+
localStoragePersistence,
|
|
66
|
+
sessionStoragePersistence,
|
|
67
|
+
indexedDBPersistence,
|
|
68
|
+
StorageUnavailableError,
|
|
69
|
+
type ChatClientPersistence,
|
|
70
|
+
type ChatPersistedState,
|
|
71
|
+
type ChatPersistenceOption,
|
|
72
|
+
type ChatStorageAdapter,
|
|
73
|
+
type WebStoragePersistenceOptions,
|
|
74
|
+
type IndexedDBPersistenceOptions,
|
|
65
75
|
fetchHttpStream,
|
|
66
76
|
xhrServerSentEvents,
|
|
67
77
|
xhrHttpStream,
|
package/src/types.ts
CHANGED
|
@@ -2,13 +2,18 @@ import type {
|
|
|
2
2
|
AnyClientTool,
|
|
3
3
|
InferSchemaType,
|
|
4
4
|
ModelMessage,
|
|
5
|
+
RunAgentResumeItem,
|
|
5
6
|
SchemaInput,
|
|
6
7
|
} from '@tanstack/ai'
|
|
7
8
|
import type {
|
|
8
9
|
AIDevtoolsDisplayOptions,
|
|
10
|
+
BoundInterrupts,
|
|
9
11
|
ChatClientOptions,
|
|
10
12
|
ChatClientState,
|
|
13
|
+
ChatInterrupt,
|
|
14
|
+
ChatInterruptState,
|
|
11
15
|
ChatRequestBody,
|
|
16
|
+
ChatResumeState,
|
|
12
17
|
ClientContextOptionFromTools,
|
|
13
18
|
ConnectionStatus,
|
|
14
19
|
DistributedOmit,
|
|
@@ -84,8 +89,14 @@ export type UseChatOptions<
|
|
|
84
89
|
| 'onConnectionStatusChange'
|
|
85
90
|
| 'onSessionGeneratingChange'
|
|
86
91
|
| 'onQueueChange'
|
|
92
|
+
| 'onResumeStateChange'
|
|
93
|
+
| 'onRunIdChange'
|
|
87
94
|
| 'context'
|
|
88
95
|
| 'devtools'
|
|
96
|
+
// `id` is not a hook option: the hook's identity is its `threadId`, which is
|
|
97
|
+
// also the persistence key. Persist across reloads by passing a stable
|
|
98
|
+
// `threadId`; there is no separate id to set.
|
|
99
|
+
| 'id'
|
|
89
100
|
> & {
|
|
90
101
|
/** Display options for TanStack AI Devtools. */
|
|
91
102
|
devtools?: AIDevtoolsDisplayOptions
|
|
@@ -181,6 +192,39 @@ interface BaseUseChatReturn<
|
|
|
181
192
|
approved: boolean
|
|
182
193
|
}) => Promise<void>
|
|
183
194
|
|
|
195
|
+
/**
|
|
196
|
+
* The id of the run this client has in flight — one it started or rejoined —
|
|
197
|
+
* or `null` when there is none (including while a run sits paused on an
|
|
198
|
+
* interrupt, waiting on approval).
|
|
199
|
+
*
|
|
200
|
+
* A run is one turn of the conversation, so this changes from turn to turn. A
|
|
201
|
+
* whole tool loop stays inside one run, while resuming after an interrupt
|
|
202
|
+
* continues the turn under a new id — so one user message can produce several
|
|
203
|
+
* run ids. Use it to talk to your own server about that run (cancel it, poll
|
|
204
|
+
* it, correlate a log line).
|
|
205
|
+
*/
|
|
206
|
+
runId: Accessor<string | null>
|
|
207
|
+
interrupts: Accessor<BoundInterrupts<TTools>>
|
|
208
|
+
/** @deprecated Use `interrupts`. */
|
|
209
|
+
pendingInterrupts: Accessor<BoundInterrupts<TTools>>
|
|
210
|
+
interruptErrors: Accessor<ChatInterruptState<TTools>['interruptErrors']>
|
|
211
|
+
resuming: Accessor<boolean>
|
|
212
|
+
resolveInterrupts: {
|
|
213
|
+
(approved: boolean): void
|
|
214
|
+
(resolver: (interrupt: ChatInterrupt<TTools>) => undefined): void
|
|
215
|
+
}
|
|
216
|
+
cancelInterrupts: () => void
|
|
217
|
+
retryInterrupts: () => void
|
|
218
|
+
resumeInterruptsUnsafe: (
|
|
219
|
+
resume: Array<RunAgentResumeItem>,
|
|
220
|
+
state?: ChatResumeState,
|
|
221
|
+
) => Promise<boolean>
|
|
222
|
+
/** @deprecated Use bound interrupt methods or `resumeInterruptsUnsafe`. */
|
|
223
|
+
resumeInterrupts: (
|
|
224
|
+
resume: Array<RunAgentResumeItem>,
|
|
225
|
+
state?: ChatResumeState,
|
|
226
|
+
) => Promise<boolean>
|
|
227
|
+
|
|
184
228
|
/**
|
|
185
229
|
* Reload the last assistant message
|
|
186
230
|
*/
|
package/src/use-chat.ts
CHANGED
|
@@ -11,6 +11,9 @@ import { ChatClient } from '@tanstack/ai-client'
|
|
|
11
11
|
import { createChatDevtoolsBridge } from '@tanstack/ai-client/devtools'
|
|
12
12
|
import type {
|
|
13
13
|
ChatClientState,
|
|
14
|
+
ChatInterrupt,
|
|
15
|
+
ChatInterruptState,
|
|
16
|
+
ChatResumeState,
|
|
14
17
|
ConnectionStatus,
|
|
15
18
|
InferredClientContext,
|
|
16
19
|
QueuedMessage,
|
|
@@ -21,6 +24,7 @@ import type {
|
|
|
21
24
|
AnyClientTool,
|
|
22
25
|
InferSchemaType,
|
|
23
26
|
ModelMessage,
|
|
27
|
+
RunAgentResumeItem,
|
|
24
28
|
SchemaInput,
|
|
25
29
|
StreamChunk,
|
|
26
30
|
} from '@tanstack/ai'
|
|
@@ -32,6 +36,9 @@ import type {
|
|
|
32
36
|
UseChatReturn,
|
|
33
37
|
} from './types'
|
|
34
38
|
|
|
39
|
+
const EMPTY_INTERRUPTS = Object.freeze([])
|
|
40
|
+
const EMPTY_INTERRUPT_ERRORS = Object.freeze([])
|
|
41
|
+
|
|
35
42
|
export function useChat<
|
|
36
43
|
const TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
37
44
|
TSchema extends SchemaInput | undefined = undefined,
|
|
@@ -43,8 +50,12 @@ export function useChat<
|
|
|
43
50
|
TContext
|
|
44
51
|
>,
|
|
45
52
|
): UseChatReturn<TTools, TSchema> {
|
|
53
|
+
// The hook's identity is its `threadId` — also the persistence key, so a
|
|
54
|
+
// reload with the same `threadId` restores the same conversation. `hookId` is
|
|
55
|
+
// only a stable fallback for client-recreation keying when no `threadId` is
|
|
56
|
+
// given (an ephemeral chat), never a persistence key.
|
|
46
57
|
const hookId = createUniqueId()
|
|
47
|
-
const clientId = options.
|
|
58
|
+
const clientId = options.threadId ?? hookId
|
|
48
59
|
|
|
49
60
|
const [messages, setMessages] = createSignal<Array<UIMessage<TTools>>>(
|
|
50
61
|
options.initialMessages || [],
|
|
@@ -57,6 +68,20 @@ export function useChat<
|
|
|
57
68
|
createSignal<ConnectionStatus>('disconnected')
|
|
58
69
|
const [sessionGenerating, setSessionGenerating] = createSignal(false)
|
|
59
70
|
const [queue, setQueue] = createSignal<Array<QueuedMessage>>([])
|
|
71
|
+
const [runId, setRunId] = createSignal<string | null>(null)
|
|
72
|
+
const [interruptState, setInterruptState] = createSignal<
|
|
73
|
+
ChatInterruptState<TTools>
|
|
74
|
+
>({
|
|
75
|
+
interrupts: EMPTY_INTERRUPTS,
|
|
76
|
+
pendingInterrupts: EMPTY_INTERRUPTS,
|
|
77
|
+
interruptErrors: EMPTY_INTERRUPT_ERRORS,
|
|
78
|
+
resuming: false,
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
const syncResumeState = () => {
|
|
82
|
+
setRunId(client().getCurrentRunId())
|
|
83
|
+
setInterruptState(client().getInterruptState())
|
|
84
|
+
}
|
|
60
85
|
|
|
61
86
|
// Structured-output `partial` / `final` are derived from `messages` —
|
|
62
87
|
// specifically from the structured-output part on the latest assistant
|
|
@@ -82,13 +107,15 @@ export function useChat<
|
|
|
82
107
|
return new ChatClient<TTools, TContext>({
|
|
83
108
|
devtoolsBridgeFactory: createChatDevtoolsBridge,
|
|
84
109
|
...transport,
|
|
85
|
-
id: clientId,
|
|
86
110
|
...(options.initialMessages !== undefined && {
|
|
87
111
|
initialMessages: options.initialMessages,
|
|
88
112
|
}),
|
|
89
113
|
...(options.persistence !== undefined && {
|
|
90
114
|
persistence: options.persistence,
|
|
91
115
|
}),
|
|
116
|
+
...(options.initialResumeSnapshot !== undefined && {
|
|
117
|
+
initialResumeSnapshot: options.initialResumeSnapshot,
|
|
118
|
+
}),
|
|
92
119
|
body: options.body,
|
|
93
120
|
...(options.threadId !== undefined && { threadId: options.threadId }),
|
|
94
121
|
...(options.forwardedProps !== undefined && {
|
|
@@ -122,6 +149,7 @@ export function useChat<
|
|
|
122
149
|
},
|
|
123
150
|
onLoadingChange: (newIsLoading: boolean) => {
|
|
124
151
|
setIsLoading(newIsLoading)
|
|
152
|
+
syncResumeState()
|
|
125
153
|
},
|
|
126
154
|
onStatusChange: (newStatus: ChatClientState) => {
|
|
127
155
|
setStatus(newStatus)
|
|
@@ -142,12 +170,27 @@ export function useChat<
|
|
|
142
170
|
onQueueChange: (nextQueue: Array<QueuedMessage>) => {
|
|
143
171
|
setQueue(nextQueue)
|
|
144
172
|
},
|
|
173
|
+
onRunIdChange: (nextRunId) => {
|
|
174
|
+
setRunId(nextRunId)
|
|
175
|
+
},
|
|
176
|
+
onResumeStateChange: (_nextResumeState, nextPendingInterrupts) => {
|
|
177
|
+
setInterruptState((current) => ({
|
|
178
|
+
...current,
|
|
179
|
+
interrupts: nextPendingInterrupts,
|
|
180
|
+
pendingInterrupts: nextPendingInterrupts,
|
|
181
|
+
}))
|
|
182
|
+
},
|
|
183
|
+
onInterruptStateChange: (nextInterruptState) => {
|
|
184
|
+
setInterruptState(nextInterruptState)
|
|
185
|
+
options.onInterruptStateChange?.(nextInterruptState)
|
|
186
|
+
},
|
|
145
187
|
})
|
|
146
188
|
// Only recreate when clientId changes
|
|
147
189
|
// Connection and other options are captured at creation time
|
|
148
190
|
}, [clientId])
|
|
149
191
|
|
|
150
192
|
setMessages(client().getMessages())
|
|
193
|
+
syncResumeState()
|
|
151
194
|
|
|
152
195
|
// Sync body / forwardedProps changes to the client.
|
|
153
196
|
// Both populate the same wire payload; `forwardedProps` is preferred
|
|
@@ -181,11 +224,24 @@ export function useChat<
|
|
|
181
224
|
})
|
|
182
225
|
|
|
183
226
|
onMount(() => {
|
|
227
|
+
// START TAILING HERE, not in the constructor. A client is idle until a view
|
|
228
|
+
// attaches it, so a client that gets built and thrown away never opens a
|
|
229
|
+
// connection — an unreachable stream would hold one of the browser's ~6
|
|
230
|
+
// connections per origin until the page reloaded.
|
|
231
|
+
client().attach()
|
|
184
232
|
client().mountDevtools()
|
|
233
|
+
// Delivery-durability resume is transparent: the resumable SSE connection
|
|
234
|
+
// adapter reattaches via the browser's native Last-Event-ID on reconnect.
|
|
235
|
+
// We only seed interrupt (state) resume from the client here.
|
|
236
|
+
syncResumeState()
|
|
185
237
|
})
|
|
186
238
|
|
|
187
239
|
// Cleanup on unmount: stop any in-flight requests.
|
|
188
240
|
onCleanup(() => {
|
|
241
|
+
// Release the connection first: `detach` is the counterpart of the `attach`
|
|
242
|
+
// above, and it keeps the transcript and resume pointer so a later mount can
|
|
243
|
+
// pick the run back up from the durable log.
|
|
244
|
+
client().detach()
|
|
189
245
|
if (options.live) {
|
|
190
246
|
client().unsubscribe()
|
|
191
247
|
} else {
|
|
@@ -201,15 +257,27 @@ export function useChat<
|
|
|
201
257
|
content: string | MultimodalContent,
|
|
202
258
|
sendOptions?: SendMessageOptions,
|
|
203
259
|
) => {
|
|
204
|
-
|
|
260
|
+
try {
|
|
261
|
+
await client().sendMessage(content, undefined, sendOptions)
|
|
262
|
+
} finally {
|
|
263
|
+
syncResumeState()
|
|
264
|
+
}
|
|
205
265
|
}
|
|
206
266
|
|
|
207
267
|
const append = async (message: ModelMessage | UIMessage<TTools>) => {
|
|
208
|
-
|
|
268
|
+
try {
|
|
269
|
+
await client().append(message)
|
|
270
|
+
} finally {
|
|
271
|
+
syncResumeState()
|
|
272
|
+
}
|
|
209
273
|
}
|
|
210
274
|
|
|
211
275
|
const reload = async () => {
|
|
212
|
-
|
|
276
|
+
try {
|
|
277
|
+
await client().reload()
|
|
278
|
+
} finally {
|
|
279
|
+
syncResumeState()
|
|
280
|
+
}
|
|
213
281
|
}
|
|
214
282
|
|
|
215
283
|
const stop = () => {
|
|
@@ -218,6 +286,7 @@ export function useChat<
|
|
|
218
286
|
|
|
219
287
|
const clear = () => {
|
|
220
288
|
client().clear()
|
|
289
|
+
syncResumeState()
|
|
221
290
|
}
|
|
222
291
|
|
|
223
292
|
const setMessagesManually = (newMessages: Array<UIMessage<TTools>>) => {
|
|
@@ -239,10 +308,48 @@ export function useChat<
|
|
|
239
308
|
approved: boolean
|
|
240
309
|
}) => {
|
|
241
310
|
await client().addToolApprovalResponse(response)
|
|
311
|
+
syncResumeState()
|
|
242
312
|
}
|
|
243
313
|
|
|
244
314
|
const cancelQueued = (id: string) => client().cancelQueued(id)
|
|
245
315
|
|
|
316
|
+
const resumeInterrupts = async (
|
|
317
|
+
resumeItems: Array<RunAgentResumeItem>,
|
|
318
|
+
state?: ChatResumeState,
|
|
319
|
+
) => {
|
|
320
|
+
const result = await client().resumeInterrupts(resumeItems, state)
|
|
321
|
+
syncResumeState()
|
|
322
|
+
return result
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const resolveInterrupts = (
|
|
326
|
+
resolution: boolean | ((interrupt: ChatInterrupt<TTools>) => undefined),
|
|
327
|
+
) => {
|
|
328
|
+
if (typeof resolution === 'boolean') {
|
|
329
|
+
client().resolveInterrupts(resolution)
|
|
330
|
+
} else {
|
|
331
|
+
client().resolveInterrupts(resolution)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const cancelInterrupts = () => {
|
|
336
|
+
client().cancelInterrupts()
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const retryInterrupts = () => {
|
|
340
|
+
client().retryInterrupts()
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const resumeInterruptsUnsafe = (
|
|
344
|
+
resumeItems: Array<RunAgentResumeItem>,
|
|
345
|
+
state?: ChatResumeState,
|
|
346
|
+
) => client().resumeInterruptsUnsafe(resumeItems, state)
|
|
347
|
+
|
|
348
|
+
const interrupts = () => interruptState().interrupts
|
|
349
|
+
const pendingInterrupts = () => interruptState().pendingInterrupts
|
|
350
|
+
const interruptErrors = () => interruptState().interruptErrors
|
|
351
|
+
const resuming = () => interruptState().resuming
|
|
352
|
+
|
|
246
353
|
// The "active" structured-output part is on the assistant message after
|
|
247
354
|
// the latest user message. When no user message exists yet, return null
|
|
248
355
|
// rather than scanning history — otherwise a stale `final` from
|
|
@@ -283,7 +390,7 @@ export function useChat<
|
|
|
283
390
|
|
|
284
391
|
// partial / final are runtime-tracked unconditionally; the conditional
|
|
285
392
|
// return type hides them when no `outputSchema` is supplied.
|
|
286
|
-
//
|
|
393
|
+
// oxlint-disable-next-line eslint-js/no-restricted-syntax -- primitive return shape diverges from generic UseChatReturn<TTools, TSchema>; TS can't structurally narrow the conditional partial/final fields
|
|
287
394
|
return {
|
|
288
395
|
messages,
|
|
289
396
|
queue,
|
|
@@ -302,6 +409,16 @@ export function useChat<
|
|
|
302
409
|
addToolResult,
|
|
303
410
|
addToolApprovalResponse,
|
|
304
411
|
cancelQueued,
|
|
412
|
+
runId,
|
|
413
|
+
interrupts,
|
|
414
|
+
pendingInterrupts,
|
|
415
|
+
interruptErrors,
|
|
416
|
+
resuming,
|
|
417
|
+
resolveInterrupts,
|
|
418
|
+
cancelInterrupts,
|
|
419
|
+
retryInterrupts,
|
|
420
|
+
resumeInterruptsUnsafe,
|
|
421
|
+
resumeInterrupts,
|
|
305
422
|
partial,
|
|
306
423
|
final,
|
|
307
424
|
} as unknown as UseChatReturn<TTools, TSchema>
|