import { AnyClientTool, SchemaInput, InterruptDefinition, ModelMessage, RunAgentResumeItem, InferSchemaType, StreamChunk, ImageGenerationResult, AudioGenerationResult, TTSResult, TranscriptionResult, SummarizationResult } from '@tanstack/ai'; import { InferredClientContext, DistributedOmit, ChatClientOptions, AIDevtoolsDisplayOptions, ClientContextOptionFromTools, UIMessage, MultimodalContent, SendMessageOptions, QueuedMessage, BoundInterrupts, ChatInterruptState, ResolvableChatInterrupt, ChatResumeState, ChatClientState, ConnectionStatus, ConnectConnectionAdapter, GenerationFetcher, GenerationRestoredResult, GenerationClientState, GenerationPersistenceOptions, InferGenerationOutputFromReturn, ImageGenerateInput, AudioGenerateInput, SpeechGenerateInput, TranscriptionGenerateInput, AudioRecorderOptions, AudioRecording, InferAudioRecordingOutput, SummarizeGenerateInput, VideoGenerateResult, VideoGenerateInput, VideoStatusInfo } from '@tanstack/ai-client'; export { AudioGenerateInput, ChatClientPersistence, ChatPersistedState, ChatPersistenceOption, ChatRequestBody, ChatStorageAdapter, ConnectConnectionAdapter, ConnectionAdapter, FetchConnectionOptions, GenerationClientState, ImageGenerateInput, IndexedDBPersistenceOptions, InferChatMessages, MultimodalContent, QueueConfig, QueueOption, QueueStrategy, QueuedMessage, RunAgentInputContext, SendMessageOptions, SpeechGenerateInput, StorageUnavailableError, SubscribeConnectionAdapter, SummarizeGenerateInput, TranscriptionGenerateInput, UIMessage, VideoGenerateInput, VideoGenerateResult, VideoStatusInfo, WebSocketConnectionOptions, WebStoragePersistenceOptions, WhenBusy, XhrConnectionOptions, createChatClientOptions, fetchHttpStream, fetchServerSentEvents, indexedDBPersistence, localStoragePersistence, rpcStream, sessionStoragePersistence, stream, webSocket, xhrHttpStream, xhrServerSentEvents } from '@tanstack/ai-client'; import { Signal } from '@angular/core'; import { ByokClient, ByokSnapshot } from '@tanstack/ai-client/byok'; import { ProviderId } from '@tanstack/ai/byok'; /** * A value that may be supplied to `injectChat` either as a static value, an * Angular `Signal`, or a zero-arg getter. The getter form lets callers read * other signals so the option stays reactive. */ type ReactiveOption = T | Signal | (() => T); /** * Recursive partial — every property and every nested array element is optional. * Used to type the in-flight `partial` value while a structured-output stream * is still arriving. */ type DeepPartial = T extends ReadonlyArray ? Array> : T extends object ? { [K in keyof T]?: DeepPartial; } : T; /** * Options for {@link injectChat}. * * Mirrors the Vue `useChat` options, except: * - State-change callbacks are managed internally and exposed as signals. * - `body`, `forwardedProps`, and `live` accept a {@link ReactiveOption} so * they can be a static value, a `Signal`, or a getter and stay reactive. */ type InjectChatOptions = any, TSchema extends SchemaInput | undefined = undefined, TContext = InferredClientContext, TInterrupts extends ReadonlyArray> = readonly []> = DistributedOmit, 'onMessagesChange' | 'onLoadingChange' | 'onErrorChange' | 'onStatusChange' | 'onSubscriptionChange' | 'onConnectionStatusChange' | 'onSessionGeneratingChange' | 'onQueueChange' | 'onResumeStateChange' | 'onRunIdChange' | 'context' | 'devtools' | 'body' | 'forwardedProps'> & { /** Display options for TanStack AI Devtools. */ devtools?: AIDevtoolsDisplayOptions; /** Additional request body params. Reactive. */ body?: ReactiveOption>; /** Forwarded request props (preferred over `body`). Reactive. */ forwardedProps?: ReactiveOption>; /** Whether to keep a live subscription open. Reactive. */ live?: ReactiveOption; /** * Standard-schema-compatible schema (Zod, Valibot, ArkType, or JSON Schema). * Used to infer the shape of `partial` and `final`. */ outputSchema?: TSchema; } & ClientContextOptionFromTools>; /** * Return shape of {@link injectChat}. When `outputSchema` is supplied, adds * typed `partial` / `final` signals; otherwise the return is unchanged. */ type InjectChatResult = any, TSchema extends SchemaInput | undefined = undefined, TInterrupts extends ReadonlyArray> = readonly []> = BaseInjectChatResult : unknown, TInterrupts> & (TSchema extends SchemaInput ? { /** Live progressively-parsed structured output. */ partial: Signal>>; /** Final, schema-validated structured output. `null` until complete. */ final: Signal | null>; } : Record); interface BaseInjectChatResult = any, TData = unknown, TInterrupts extends ReadonlyArray> = readonly []> { /** Current messages in the conversation. */ messages: Signal>>; /** * Send a message (string or multimodal content). * Pass `{ whenBusy }` to override the queue policy for a single send, or * `{ body }` to merge per-call JSON into this request's `forwardedProps`. */ sendMessage: (content: string | MultimodalContent, options?: SendMessageOptions) => Promise; /** Pending messages queued while a stream is in flight. */ queue: Signal>; /** Cancel a queued message before it drains. */ cancelQueued: (id: string) => void; /** Append a message to the conversation. */ append: (message: ModelMessage | UIMessage) => Promise; /** Add the result of a client-side tool execution. */ addToolResult: (result: { toolCallId: string; tool: string; output: any; state?: 'output-available' | 'output-error'; errorText?: string; }) => Promise; /** Respond to a tool approval request. */ addToolApprovalResponse: (response: { id: string; approved: boolean; }) => Promise; /** * The id of the run this client has in flight — one it started or rejoined — * or `null` when there is none (including while a run sits paused on an * interrupt, waiting on approval). * * A run is one turn of the conversation, so this changes from turn to turn. A * whole tool loop stays inside one run, while resuming after an interrupt * continues the turn under a new id — so one user message can produce several * run ids. Use it to talk to your own server about that run (cancel it, poll * it, correlate a log line). */ runId: Signal; /** Immutable bound interrupts for the current interrupted run. */ interrupts: Signal>; /** @deprecated Use `interrupts`. */ pendingInterrupts: Signal>; /** Batch-level interrupt errors. */ interruptErrors: Signal['interruptErrors']>; /** Whether the client is submitting an interrupt batch. */ resuming: Signal; resolveInterrupts: { (approved: boolean): void; (resolver: (interrupt: ResolvableChatInterrupt) => undefined): void; }; cancelInterrupts: () => void; retryInterrupts: () => void; resumeInterruptsUnsafe: (resume: Array, state?: ChatResumeState) => Promise; /** Reload the last assistant message. */ reload: () => Promise; /** Stop the current response generation. */ stop: () => void; /** Whether a response is currently being generated. */ isLoading: Signal; /** Current error, if any. */ error: Signal; /** Set messages manually. */ setMessages: (messages: Array>) => void; /** Clear all messages. */ clear: () => void; /** Current generation status. */ status: Signal; /** Whether the subscription loop is active. */ isSubscribed: Signal; /** Current connection lifecycle status. */ connectionStatus: Signal; /** Whether the shared session is actively generating. */ sessionGenerating: Signal; } declare function injectChat = any, TSchema extends SchemaInput | undefined = undefined, TContext = InferredClientContext, const TInterrupts extends ReadonlyArray> = readonly []>(options?: InjectChatOptions): InjectChatResult; declare function injectByok(client: ByokClient): Signal; interface InjectGenerationOptions { /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */ connection?: ConnectConnectionAdapter; /** Direct async function for one-shot generation (no streaming protocol needed) */ fetcher?: GenerationFetcher; /** Additional request body params. Reactive. */ body?: ReactiveOption>; /** Optional BYOK keyring. Keys go in `x-byok-*` headers, never the body. */ byok?: ByokClient; /** Optional provider id. If it returns a slug, only that key is sent. If no slug resolves (`byokProvider`, then `body.provider`), generate throws. */ byokProvider?: () => ProviderId | undefined; /** Display options for TanStack AI Devtools. */ devtools?: AIDevtoolsDisplayOptions; /** * How this generation persists across reloads. * - Omit / `false`: ephemeral, in-memory only. * - `true`: server-driven — on mount the client hydrates the last generation * for its `threadId` from the server (needs a connection with a * `hydrateGeneration` handler) and repaints it; it never auto-starts a run. */ persistence?: boolean; /** * The **scope** this generation belongs to: a stable, app-chosen name for the * slot successive runs fill — not a link to a chat conversation. * * The hook starts empty and produces many runs over its life; each gets its * own `runId`, but all belong to one scope. Persistence keys on this, so * derive it from your own domain and keep it identical across reloads (e.g. * `` `video-${videoId}-start-frame` ``). It is also sent as the AG-UI thread * id on the wire, which the protocol requires. * * **Required whenever `persistence` is set** — an app that cannot name the * scope has nothing to restore to. Optional for ephemeral generations. If * omitted, the client mints a wire id after mount. */ threadId?: string; /** * Server-driven hydration handler for `persistence: true` when the * connection doesn't carry one (e.g. alongside `fetcher`, or a `stream()` / * `rpcStream()` adapter built without handlers) — typically a one-line * server-function call. The connection's own handler takes precedence. */ hydrateGeneration?: ConnectConnectionAdapter['hydrateGeneration']; /** * Re-attach handler that replays a run still generating to completion on * mount, when the connection doesn't carry one. Without it, a restored * `running` snapshot surfaces as an (interrupted) error. The connection's * own handler takes precedence. */ joinRun?: ConnectConnectionAdapter['joinRun']; /** * Callback when a result is received. Can optionally return a transformed value. * * - Return a non-null value to transform and store it as the result * - Return `null` to keep the previous result unchanged * - Return nothing (`void`) to store the raw result as-is */ onResult?: (result: TResult) => TOutput | null | void; /** Callback when an error occurs */ onError?: (error: Error) => void; /** Callback when progress is reported (0-100) */ onProgress?: (progress: number, message?: string) => void; /** Callback for each stream chunk (connect-based adapter mode only) */ onChunk?: (chunk: StreamChunk) => void; /** * @internal Rebuild a typed result from a restored snapshot, injected by each * specialized injectable (image / speech / audio / transcription / summarize). * Forwarded to the client so a server-hydrate restore repaints `result`. */ reconstructResult?: (restored: GenerationRestoredResult) => TResult | null; } /** * Return type for the injectGeneration function. * * @template TOutput - The output type (after optional transform) * @template TInput - The input type accepted by `generate` (defaults to any object) */ interface InjectGenerationResult = Record> { /** Trigger a generation request */ generate: (input: TInput) => Promise; /** The generation result, or null if not yet generated */ result: Signal; /** Whether a generation is currently in progress */ isLoading: Signal; /** Current error, if any */ error: Signal; /** Current state of the generation client */ status: Signal; /** Abort the current generation */ stop: () => void; /** Clear result, error, and return to idle */ reset: () => void; /** Identity of the in-flight run while one is streaming, or null after it ends */ /** * The id of the generation job currently running, or `null` when nothing is in * flight. Each call to `generate` is one job with its own id. Pass it to your * own endpoint to cancel or poll the provider job — `stop()` only aborts the * local stream, it does not stop work already running on the provider. */ runId: Signal; } declare function injectGeneration, TResult, TTransformed = void>(options: Omit, 'onResult' | 'persistence' | 'threadId'> & { onResult?: (result: TResult) => TTransformed; } & GenerationPersistenceOptions): InjectGenerationResult, TInput>; type InjectGenerateImageOptions = Omit, 'onResult' | 'reconstructResult'> & { onResult?: (result: ImageGenerationResult) => TOutput | null | void; }; interface InjectGenerateImageResult extends Omit, 'generate'> { generate: (input: ImageGenerateInput) => Promise; result: Signal; isLoading: Signal; error: Signal; status: Signal; } declare function injectGenerateImage(options: Omit & { onResult?: (result: ImageGenerationResult) => TTransformed; } & GenerationPersistenceOptions): InjectGenerateImageResult>; /** * Options for the injectGenerateAudio injectable. * * @template TOutput - The output type after optional transform (defaults to AudioGenerationResult) */ interface InjectGenerateAudioOptions extends Pick, 'persistence' | 'threadId' | 'hydrateGeneration' | 'joinRun' | 'byok' | 'byokProvider'> { /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */ connection?: ConnectConnectionAdapter; /** Direct async function for audio generation */ fetcher?: GenerationFetcher; /** Additional body parameters to send with connect-based adapter requests. Reactive. */ body?: ReactiveOption>; /** Display options for TanStack AI Devtools. */ devtools?: AIDevtoolsDisplayOptions; /** * Callback when audio is generated. Can optionally return a transformed value. * * - Return a non-null value to transform and store it as the result * - Return `null` to keep the previous result unchanged * - Return nothing (`void`) to store the raw result as-is */ onResult?: (result: AudioGenerationResult) => TOutput | null | void; /** Callback when an error occurs */ onError?: (error: Error) => void; /** Callback when progress is reported (0-100) */ onProgress?: (progress: number, message?: string) => void; /** Callback for each stream chunk (connect-based adapter mode only) */ onChunk?: (chunk: StreamChunk) => void; } /** * Return type for the injectGenerateAudio injectable. * * @template TOutput - The output type (after optional transform) */ interface InjectGenerateAudioResult extends Omit, 'generate'> { /** Trigger audio generation */ generate: (input: AudioGenerateInput) => Promise; /** The generation result containing audio, or null */ result: Signal; /** Whether generation is in progress */ isLoading: Signal; /** Current error, if any */ error: Signal; /** Current state of the generation */ status: Signal; } /** * Angular injectable for generating audio (music, sound effects) using AI models. * * @example * ```typescript * import { Component } from '@angular/core' * import { injectGenerateAudio } from '@tanstack/ai-angular' * import { fetchServerSentEvents } from '@tanstack/ai-client' * * @Component({ * selector: 'app-audio', * template: ` * * @if (result()) { * * } * `, * }) * export class AudioComponent { * private gen = injectGenerateAudio({ * connection: fetchServerSentEvents('/api/generate/audio'), * }) * * generate = this.gen.generate * result = this.gen.result * isLoading = this.gen.isLoading * } * ``` */ declare function injectGenerateAudio(options: Omit & { onResult?: (result: AudioGenerationResult) => TTransformed; } & GenerationPersistenceOptions): InjectGenerateAudioResult>; type InjectGenerateSpeechOptions = Omit, 'onResult' | 'reconstructResult'> & { onResult?: (result: TTSResult) => TOutput | null | void; }; interface InjectGenerateSpeechResult extends Omit, 'generate'> { generate: (input: SpeechGenerateInput) => Promise; result: Signal; isLoading: Signal; error: Signal; status: Signal; } declare function injectGenerateSpeech(options: Omit & { onResult?: (result: TTSResult) => TTransformed; } & GenerationPersistenceOptions): InjectGenerateSpeechResult>; type InjectTranscriptionOptions = Omit, 'onResult' | 'reconstructResult'> & { onResult?: (result: TranscriptionResult) => TOutput | null | void; }; interface InjectTranscriptionResult extends Omit, 'generate'> { generate: (input: TranscriptionGenerateInput) => Promise; result: Signal; isLoading: Signal; error: Signal; status: Signal; } declare function injectTranscription(options: Omit & { onResult?: (result: TranscriptionResult) => TTransformed; } & GenerationPersistenceOptions): InjectTranscriptionResult>; type InjectAudioRecorderOptions = AudioRecorderOptions & { /** * Optional transform applied to the recording when `stop()` resolves. Its * (awaited) return value becomes `recording` and the resolved value of * `stop()`. Return nothing to keep the raw `AudioRecording`. */ onComplete?: TOnComplete; }; interface InjectAudioRecorderResult { /** Reactive: latest recording (transformed if `onComplete` provided), or null. */ recording: Signal; /** Reactive: true while actively capturing audio. */ isRecording: Signal; /** Whether the browser supports recording. */ isSupported: boolean; start: () => Promise; /** Stop and resolve with the completed recording (transformed if `onComplete` provided). */ stop: () => Promise; /** Discard the in-progress recording and release the mic. */ cancel: () => void; } /** * Angular injectable for recording an audio message. The resolved recording * carries `.part` (for `injectChat`'s `sendMessage`) and `.base64` (for the * generation injectables). Must be called in an injection context. * * Errors are delivered via `onError`. `start()` and `stop()` also reject on * failure (and `stop()` rejects with `Recording cancelled` if `cancel()` runs * while a stop is in flight, e.g. on destroy) — handle one channel, not both. */ declare function injectAudioRecorder unknown>(options: InjectAudioRecorderOptions & { onComplete: TOnComplete; }): InjectAudioRecorderResult>; declare function injectAudioRecorder(options?: InjectAudioRecorderOptions): InjectAudioRecorderResult; type InjectSummarizeOptions = Omit, 'onResult' | 'reconstructResult'> & { onResult?: (result: SummarizationResult) => TOutput | null | void; }; interface InjectSummarizeResult extends Omit, 'generate'> { generate: (input: SummarizeGenerateInput) => Promise; result: Signal; isLoading: Signal; error: Signal; status: Signal; } declare function injectSummarize(options: Omit & { onResult?: (result: SummarizationResult) => TTransformed; } & GenerationPersistenceOptions): InjectSummarizeResult>; interface InjectGenerateVideoOptions { connection?: ConnectConnectionAdapter; fetcher?: GenerationFetcher; body?: ReactiveOption>; /** Optional BYOK keyring. Keys go in `x-byok-*` headers, never the body. */ byok?: ByokClient; /** Optional provider id. If it returns a slug, only that key is sent. If no slug resolves (`byokProvider`, then `body.provider`), generate throws. */ byokProvider?: () => ProviderId | undefined; devtools?: AIDevtoolsDisplayOptions; /** * How this generation persists across reloads. * - Omit / `false`: ephemeral, in-memory only. * - `true`: server-driven — on mount the client hydrates the last generation * for its `threadId` from the server (needs a connection with a * `hydrateGeneration` handler) and repaints it; it never auto-starts a run. */ persistence?: boolean; /** * The **scope** this generation belongs to: a stable, app-chosen name for the * slot successive runs fill — not a link to a chat conversation. * * The hook starts empty and produces many runs over its life; each gets its * own `runId`, but all belong to one scope. Persistence keys on this, so * derive it from your own domain and keep it identical across reloads (e.g. * `` `video-${videoId}-start-frame` ``). It is also sent as the AG-UI thread * id on the wire, which the protocol requires. * * **Required whenever `persistence` is set** — an app that cannot name the * scope has nothing to restore to. Optional for ephemeral generations. If * omitted, the client mints a wire id after mount. */ threadId?: string; /** * Server-driven hydration handler for `persistence: true` when the * connection doesn't carry one (e.g. alongside `fetcher`, or a `stream()` / * `rpcStream()` adapter built without handlers) — typically a one-line * server-function call. The connection's own handler takes precedence. */ hydrateGeneration?: ConnectConnectionAdapter['hydrateGeneration']; /** * Re-attach handler that replays a run still generating to completion on * mount, when the connection doesn't carry one. Without it, a restored * `running` snapshot surfaces as an (interrupted) error. The connection's * own handler takes precedence. */ joinRun?: ConnectConnectionAdapter['joinRun']; onResult?: (result: VideoGenerateResult) => TOutput | null | void; onError?: (error: Error) => void; onProgress?: (progress: number, message?: string) => void; onJobCreated?: (jobId: string) => void; onStatusUpdate?: (status: VideoStatusInfo) => void; onChunk?: (chunk: StreamChunk) => void; } interface InjectGenerateVideoResult { generate: (input: VideoGenerateInput) => Promise; result: Signal; jobId: Signal; videoStatus: Signal; isLoading: Signal; error: Signal; status: Signal; stop: () => void; reset: () => void; /** * The id of the generation job currently running, or `null` when nothing is in * flight. Each call to `generate` is one job with its own id. Pass it to your * own endpoint to cancel or poll the provider job — `stop()` only aborts the * local stream, it does not stop work already running on the provider. */ runId: Signal; } declare function injectGenerateVideo(options: Omit & { onResult?: (result: VideoGenerateResult) => TTransformed; } & GenerationPersistenceOptions): InjectGenerateVideoResult>; export { injectAudioRecorder, injectByok, injectChat, injectGenerateAudio, injectGenerateImage, injectGenerateSpeech, injectGenerateVideo, injectGeneration, injectSummarize, injectTranscription }; export type { DeepPartial, InjectAudioRecorderOptions, InjectAudioRecorderResult, InjectChatOptions, InjectChatResult, InjectGenerateAudioOptions, InjectGenerateAudioResult, InjectGenerateImageOptions, InjectGenerateImageResult, InjectGenerateSpeechOptions, InjectGenerateSpeechResult, InjectGenerateVideoOptions, InjectGenerateVideoResult, InjectGenerationOptions, InjectGenerationResult, InjectSummarizeOptions, InjectSummarizeResult, InjectTranscriptionOptions, InjectTranscriptionResult, ReactiveOption };