@octanejs/tanstack-ai 0.0.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/LICENSE +21 -0
- package/README.md +123 -0
- package/package.json +54 -0
- package/src/index.ts +62 -0
- package/src/realtime-types.ts +146 -0
- package/src/types.ts +213 -0
- package/src/use-audio-recorder.tsrx +112 -0
- package/src/use-audio-recorder.tsrx.d.ts +44 -0
- package/src/use-chat.tsrx +363 -0
- package/src/use-chat.tsrx.d.ts +5 -0
- package/src/use-generate-audio.tsrx +125 -0
- package/src/use-generate-audio.tsrx.d.ts +88 -0
- package/src/use-generate-image.tsrx +127 -0
- package/src/use-generate-image.tsrx.d.ts +90 -0
- package/src/use-generate-speech.tsrx +121 -0
- package/src/use-generate-speech.tsrx.d.ts +84 -0
- package/src/use-generate-video.tsrx +245 -0
- package/src/use-generate-video.tsrx.d.ts +95 -0
- package/src/use-generation.tsrx +216 -0
- package/src/use-generation.tsrx.d.ts +81 -0
- package/src/use-mcp-app-bridge.tsrx +60 -0
- package/src/use-mcp-app-bridge.tsrx.d.ts +26 -0
- package/src/use-realtime-chat.tsrx +307 -0
- package/src/use-realtime-chat.tsrx.d.ts +43 -0
- package/src/use-summarize.tsrx +124 -0
- package/src/use-summarize.tsrx.d.ts +87 -0
- package/src/use-transcription.tsrx +131 -0
- package/src/use-transcription.tsrx.d.ts +93 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dominic Gannaway
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# @octanejs/tanstack-ai
|
|
2
|
+
|
|
3
|
+
[TanStack AI](https://tanstack.com/ai) bindings for the
|
|
4
|
+
[Octane](https://github.com/octanejs/octane) UI framework.
|
|
5
|
+
|
|
6
|
+
This package ports `@tanstack/ai-react@0.17.0` onto Octane while reusing
|
|
7
|
+
`@tanstack/ai` and `@tanstack/ai-client` unchanged. The runtime export surface
|
|
8
|
+
matches the React adapter, so migration starts by changing the package import:
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
// before
|
|
12
|
+
import { useChat } from '@tanstack/ai-react'
|
|
13
|
+
|
|
14
|
+
// after
|
|
15
|
+
import { useChat } from '@octanejs/tanstack-ai'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The renderer-bearing hook modules are authored as `.tsrx` and compiled by
|
|
19
|
+
Octane. Matching `.tsrx.d.ts` companions are checked declaration emits of
|
|
20
|
+
those implementations, preserving the complete generic surface for
|
|
21
|
+
TypeScript consumers.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add @octanejs/tanstack-ai @tanstack/ai @tanstack/ai-client octane
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { useState } from 'octane'
|
|
33
|
+
import { useChat } from '@octanejs/tanstack-ai'
|
|
34
|
+
|
|
35
|
+
export function Chat() @{
|
|
36
|
+
const [input, setInput] = useState('')
|
|
37
|
+
const chat = useChat({
|
|
38
|
+
fetcher: myFetcher,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
<div>
|
|
42
|
+
@for (const message of chat.messages; key message.id) {
|
|
43
|
+
<p>
|
|
44
|
+
{(message.role +
|
|
45
|
+
': ' +
|
|
46
|
+
message.parts
|
|
47
|
+
.map((part) => (part.type === 'text' ? part.content : ''))
|
|
48
|
+
.join('')) as string}
|
|
49
|
+
</p>
|
|
50
|
+
}
|
|
51
|
+
<input
|
|
52
|
+
value={input}
|
|
53
|
+
onInput={(event) => setInput(event.currentTarget.value)}
|
|
54
|
+
/>
|
|
55
|
+
<button
|
|
56
|
+
onClick={() => {
|
|
57
|
+
void chat.sendMessage(input)
|
|
58
|
+
setInput('')
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
Send
|
|
62
|
+
</button>
|
|
63
|
+
</div>
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`useChat` has no input state of its own — hold the text box value in a local
|
|
68
|
+
`useState` and pass it to `sendMessage`. Note the `onInput` handler: Octane
|
|
69
|
+
drives text controls per keystroke through the native `input` event, not a
|
|
70
|
+
synthetic `onChange`.
|
|
71
|
+
|
|
72
|
+
## API
|
|
73
|
+
|
|
74
|
+
The adapter includes `useChat`, `useRealtimeChat`, `useMcpAppBridge`,
|
|
75
|
+
`useGeneration`, `useGenerateImage`, `useGenerateAudio`, `useGenerateSpeech`,
|
|
76
|
+
`useGenerateVideo`, `useTranscription`, `useSummarize`, and
|
|
77
|
+
`useAudioRecorder`. It also re-exports all 30 `@tanstack/ai-client`
|
|
78
|
+
convenience helpers and types (`fetchServerSentEvents`, `fetchHttpStream`,
|
|
79
|
+
`xhrServerSentEvents`, `xhrHttpStream`, `stream`, `rpcStream`,
|
|
80
|
+
`createChatClientOptions`, `createMcpAppBridge`, and their associated types)
|
|
81
|
+
unchanged, mirroring the upstream `@tanstack/ai-react` index.
|
|
82
|
+
|
|
83
|
+
Server rendering through `octane/server` is supported. `useChat` renders its
|
|
84
|
+
initial message snapshot without browser-only setup.
|
|
85
|
+
|
|
86
|
+
## Divergences from `@tanstack/ai-react`
|
|
87
|
+
|
|
88
|
+
- The `./mcp-apps` subpath and its `MCPAppResource` component are not ported:
|
|
89
|
+
they render `AppRenderer` from the React-only `@mcp-ui/client`, which has no
|
|
90
|
+
Octane equivalent. The framework-agnostic `useMcpAppBridge` hook is ported
|
|
91
|
+
and available on the main entry.
|
|
92
|
+
- Octane uses native events: text/file/recorder inputs drive updates via
|
|
93
|
+
`onInput`; there is no synthetic `onChange` layer.
|
|
94
|
+
- Octane has no StrictMode double-invoke and always provides `useId`, so no
|
|
95
|
+
random-id fallback is needed.
|
|
96
|
+
- Realtime reconnects and token refreshes use the latest `getToken` and adapter
|
|
97
|
+
supplied to the hook; upstream captures the first render's callbacks.
|
|
98
|
+
- The declared realtime `onStatusChange` callback is invoked alongside the
|
|
99
|
+
hook's state update; upstream 0.17.0 currently drops the external callback.
|
|
100
|
+
- One upstream `useChat` test case ("auto-resume on mount / when the browser
|
|
101
|
+
comes back online") is omitted: it targets
|
|
102
|
+
`ChatClient.prototype.maybeAutoResume`, an API absent from the pinned (and
|
|
103
|
+
latest published) `@tanstack/ai-client@0.21.0` and never invoked by
|
|
104
|
+
`useChat`. It is untestable in this binding until that dependency ships the
|
|
105
|
+
method.
|
|
106
|
+
|
|
107
|
+
## Verification
|
|
108
|
+
|
|
109
|
+
The port runs TanStack AI's React adapter tests against Octane across all
|
|
110
|
+
eleven hooks, with no skipped, todo, or expected-failure cases (except the
|
|
111
|
+
untestable auto-resume case above). A differential test compiles a shared
|
|
112
|
+
chat fixture for Octane and React and compares streamed output after each
|
|
113
|
+
step; output is byte-equal against real `@tanstack/ai-react@0.17.0`. An SSR
|
|
114
|
+
fixture and the upstream compile-time type tests are also included.
|
|
115
|
+
|
|
116
|
+
Current scope and verification status are tracked in the generated
|
|
117
|
+
[bindings status table](../../docs/bindings-status.md), sourced from this
|
|
118
|
+
package's [`status.json`](./status.json).
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT — contains source derived from
|
|
123
|
+
[TanStack AI](https://github.com/TanStack/ai) (MIT), adapted for Octane.
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@octanejs/tanstack-ai",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=22"
|
|
8
|
+
},
|
|
9
|
+
"description": "TanStack AI bindings for Octane — reuses @tanstack/ai and @tanstack/ai-client and ports the React adapter's hooks (chat, realtime, generation, transcription, media).",
|
|
10
|
+
"author": {
|
|
11
|
+
"name": "Dominic Gannaway",
|
|
12
|
+
"email": "dg@domgan.com"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/octanejs/octane.git",
|
|
20
|
+
"directory": "packages/tanstack-ai"
|
|
21
|
+
},
|
|
22
|
+
"main": "src/index.ts",
|
|
23
|
+
"module": "src/index.ts",
|
|
24
|
+
"types": "src/index.ts",
|
|
25
|
+
"files": [
|
|
26
|
+
"src",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"exports": {
|
|
30
|
+
".": "./src/index.ts"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@tanstack/ai": "0.41.0",
|
|
34
|
+
"@tanstack/ai-client": "0.21.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"octane": "0.1.7"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@tanstack/ai-react": "0.17.0",
|
|
41
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
42
|
+
"@testing-library/user-event": "^14.6.1",
|
|
43
|
+
"@tsrx/react": "^0.2.37",
|
|
44
|
+
"esbuild": "^0.28.1",
|
|
45
|
+
"react": "^19.2.0",
|
|
46
|
+
"react-dom": "^19.2.0",
|
|
47
|
+
"vitest": "^4.1.9",
|
|
48
|
+
"@octanejs/testing-library": "0.1.4",
|
|
49
|
+
"octane": "0.1.7"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"test": "vitest run"
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export { useChat } from './use-chat.tsrx';
|
|
2
|
+
export { useRealtimeChat } from './use-realtime-chat.tsrx';
|
|
3
|
+
export { useMcpAppBridge } from './use-mcp-app-bridge.tsrx';
|
|
4
|
+
export type { UseMcpAppBridgeOptions } from './use-mcp-app-bridge.tsrx';
|
|
5
|
+
export type {
|
|
6
|
+
DeepPartial,
|
|
7
|
+
UseChatOptions,
|
|
8
|
+
UseChatReturn,
|
|
9
|
+
UIMessage,
|
|
10
|
+
ChatRequestBody,
|
|
11
|
+
} from './types';
|
|
12
|
+
export type { UseRealtimeChatOptions, UseRealtimeChatReturn } from './realtime-types';
|
|
13
|
+
|
|
14
|
+
export { useGeneration } from './use-generation.tsrx';
|
|
15
|
+
export type { UseGenerationOptions, UseGenerationReturn } from './use-generation.tsrx';
|
|
16
|
+
export { useGenerateImage } from './use-generate-image.tsrx';
|
|
17
|
+
export type { UseGenerateImageOptions, UseGenerateImageReturn } from './use-generate-image.tsrx';
|
|
18
|
+
export { useGenerateAudio } from './use-generate-audio.tsrx';
|
|
19
|
+
export type { UseGenerateAudioOptions, UseGenerateAudioReturn } from './use-generate-audio.tsrx';
|
|
20
|
+
export { useGenerateSpeech } from './use-generate-speech.tsrx';
|
|
21
|
+
export type { UseGenerateSpeechOptions, UseGenerateSpeechReturn } from './use-generate-speech.tsrx';
|
|
22
|
+
export { useTranscription } from './use-transcription.tsrx';
|
|
23
|
+
export type { UseTranscriptionOptions, UseTranscriptionReturn } from './use-transcription.tsrx';
|
|
24
|
+
export { useSummarize } from './use-summarize.tsrx';
|
|
25
|
+
export type { UseSummarizeOptions, UseSummarizeReturn } from './use-summarize.tsrx';
|
|
26
|
+
export { useGenerateVideo } from './use-generate-video.tsrx';
|
|
27
|
+
export type { UseGenerateVideoOptions, UseGenerateVideoReturn } from './use-generate-video.tsrx';
|
|
28
|
+
export { useAudioRecorder } from './use-audio-recorder.tsrx';
|
|
29
|
+
export type { UseAudioRecorderOptions, UseAudioRecorderReturn } from './use-audio-recorder.tsrx';
|
|
30
|
+
|
|
31
|
+
// Re-export from ai-client for convenience (mirror upstream index.ts)
|
|
32
|
+
export {
|
|
33
|
+
fetchServerSentEvents,
|
|
34
|
+
fetchHttpStream,
|
|
35
|
+
xhrServerSentEvents,
|
|
36
|
+
xhrHttpStream,
|
|
37
|
+
stream,
|
|
38
|
+
rpcStream,
|
|
39
|
+
createChatClientOptions,
|
|
40
|
+
createMcpAppBridge,
|
|
41
|
+
type McpAppBridge,
|
|
42
|
+
type CreateMcpAppBridgeOptions,
|
|
43
|
+
type ChatFetcher,
|
|
44
|
+
type ChatFetcherInput,
|
|
45
|
+
type ChatFetcherOptions,
|
|
46
|
+
type ConnectionAdapter,
|
|
47
|
+
type ConnectConnectionAdapter,
|
|
48
|
+
type SubscribeConnectionAdapter,
|
|
49
|
+
type RunAgentInputContext,
|
|
50
|
+
type FetchConnectionOptions,
|
|
51
|
+
type XhrConnectionOptions,
|
|
52
|
+
type InferChatMessages,
|
|
53
|
+
type GenerationClientState,
|
|
54
|
+
type ImageGenerateInput,
|
|
55
|
+
type AudioGenerateInput,
|
|
56
|
+
type SpeechGenerateInput,
|
|
57
|
+
type TranscriptionGenerateInput,
|
|
58
|
+
type SummarizeGenerateInput,
|
|
59
|
+
type VideoGenerateInput,
|
|
60
|
+
type VideoGenerateResult,
|
|
61
|
+
type VideoStatusInfo,
|
|
62
|
+
} from '@tanstack/ai-client';
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnyClientTool,
|
|
3
|
+
RealtimeMessage,
|
|
4
|
+
RealtimeMode,
|
|
5
|
+
RealtimeSessionConfig,
|
|
6
|
+
RealtimeStatus,
|
|
7
|
+
RealtimeToken,
|
|
8
|
+
UsageInfo,
|
|
9
|
+
} from '@tanstack/ai';
|
|
10
|
+
import type { RealtimeAdapter } from '@tanstack/ai-client';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Options for the useRealtimeChat hook.
|
|
14
|
+
*/
|
|
15
|
+
export interface UseRealtimeChatOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Function to fetch a realtime token from the server.
|
|
18
|
+
* Called on connect and when token needs refresh.
|
|
19
|
+
*/
|
|
20
|
+
getToken: () => Promise<RealtimeToken>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The realtime adapter to use (e.g., openaiRealtime())
|
|
24
|
+
*/
|
|
25
|
+
adapter: RealtimeAdapter;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Client-side tools with execution logic
|
|
29
|
+
*/
|
|
30
|
+
tools?: ReadonlyArray<AnyClientTool>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Auto-play assistant audio (default: true)
|
|
34
|
+
*/
|
|
35
|
+
autoPlayback?: boolean;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Request microphone access on connect (default: true)
|
|
39
|
+
*/
|
|
40
|
+
autoCapture?: boolean;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* System instructions for the assistant
|
|
44
|
+
*/
|
|
45
|
+
instructions?: string;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Voice to use for audio output
|
|
49
|
+
*/
|
|
50
|
+
voice?: string;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Voice activity detection mode (default: 'server')
|
|
54
|
+
*/
|
|
55
|
+
vadMode?: 'server' | 'semantic' | 'manual';
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Output modalities for responses (e.g., ['audio', 'text'])
|
|
59
|
+
*/
|
|
60
|
+
outputModalities?: Array<'audio' | 'text'>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Temperature for generation (provider-specific range)
|
|
64
|
+
*/
|
|
65
|
+
temperature?: number;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Maximum number of tokens in a response
|
|
69
|
+
*/
|
|
70
|
+
maxOutputTokens?: number | 'inf';
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Eagerness level for semantic VAD ('low', 'medium', 'high')
|
|
74
|
+
*/
|
|
75
|
+
semanticEagerness?: 'low' | 'medium' | 'high';
|
|
76
|
+
|
|
77
|
+
// Callbacks
|
|
78
|
+
onConnect?: () => void;
|
|
79
|
+
onDisconnect?: () => void;
|
|
80
|
+
onError?: (error: Error) => void;
|
|
81
|
+
onMessage?: (message: RealtimeMessage) => void;
|
|
82
|
+
onModeChange?: (mode: RealtimeMode) => void;
|
|
83
|
+
onInterrupted?: () => void;
|
|
84
|
+
onUsage?: (usage: UsageInfo) => void;
|
|
85
|
+
onGoAway?: (timeLeft?: string) => void;
|
|
86
|
+
onStatusChange?: (status: RealtimeStatus) => void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Return type for the useRealtimeChat hook.
|
|
91
|
+
*/
|
|
92
|
+
export interface UseRealtimeChatReturn {
|
|
93
|
+
// Connection state
|
|
94
|
+
/** Current connection status */
|
|
95
|
+
status: RealtimeStatus;
|
|
96
|
+
/** Current error, if any */
|
|
97
|
+
error: Error | null;
|
|
98
|
+
/** Connect to the realtime session */
|
|
99
|
+
connect: () => Promise<void>;
|
|
100
|
+
/** Disconnect from the realtime session */
|
|
101
|
+
disconnect: () => Promise<void>;
|
|
102
|
+
|
|
103
|
+
// Conversation state
|
|
104
|
+
/** Current mode (idle, listening, thinking, speaking) */
|
|
105
|
+
mode: RealtimeMode;
|
|
106
|
+
/** Conversation messages */
|
|
107
|
+
messages: Array<RealtimeMessage>;
|
|
108
|
+
/** User transcript while speaking (before finalized) */
|
|
109
|
+
pendingUserTranscript: string | null;
|
|
110
|
+
/** Assistant transcript while speaking (before finalized) */
|
|
111
|
+
pendingAssistantTranscript: string | null;
|
|
112
|
+
|
|
113
|
+
// Voice control
|
|
114
|
+
/** Start listening for voice input (manual VAD mode) */
|
|
115
|
+
startListening: () => void;
|
|
116
|
+
/** Stop listening for voice input (manual VAD mode) */
|
|
117
|
+
stopListening: () => void;
|
|
118
|
+
/** Interrupt the current assistant response */
|
|
119
|
+
interrupt: () => void;
|
|
120
|
+
|
|
121
|
+
// Text input
|
|
122
|
+
/** Send a text message instead of voice */
|
|
123
|
+
sendText: (text: string) => void;
|
|
124
|
+
|
|
125
|
+
// Image input
|
|
126
|
+
/** Send an image to the conversation */
|
|
127
|
+
sendImage: (imageData: string, mimeType: string) => void;
|
|
128
|
+
|
|
129
|
+
// Audio visualization (0-1 normalized)
|
|
130
|
+
/** Current input (microphone) volume level */
|
|
131
|
+
inputLevel: number;
|
|
132
|
+
/** Current output (speaker) volume level */
|
|
133
|
+
outputLevel: number;
|
|
134
|
+
/** Get frequency data for input audio visualization */
|
|
135
|
+
getInputFrequencyData: () => Uint8Array;
|
|
136
|
+
/** Get frequency data for output audio visualization */
|
|
137
|
+
getOutputFrequencyData: () => Uint8Array;
|
|
138
|
+
/** Get time domain data for input waveform */
|
|
139
|
+
getInputTimeDomainData: () => Uint8Array;
|
|
140
|
+
/** Get time domain data for output waveform */
|
|
141
|
+
getOutputTimeDomainData: () => Uint8Array;
|
|
142
|
+
|
|
143
|
+
// Session control
|
|
144
|
+
/** Update the active session and persist the configuration for reconnects. */
|
|
145
|
+
updateSession: (config: RealtimeSessionConfig) => void;
|
|
146
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnyClientTool,
|
|
3
|
+
InferSchemaType,
|
|
4
|
+
ModelMessage,
|
|
5
|
+
SchemaInput,
|
|
6
|
+
} from '@tanstack/ai/client';
|
|
7
|
+
import type {
|
|
8
|
+
AIDevtoolsDisplayOptions,
|
|
9
|
+
ChatClientOptions,
|
|
10
|
+
ChatClientState,
|
|
11
|
+
ChatRequestBody,
|
|
12
|
+
ClientContextOptionFromTools,
|
|
13
|
+
ConnectionStatus,
|
|
14
|
+
DistributedOmit,
|
|
15
|
+
InferredClientContext,
|
|
16
|
+
MultimodalContent,
|
|
17
|
+
UIMessage,
|
|
18
|
+
} from '@tanstack/ai-client';
|
|
19
|
+
|
|
20
|
+
// Re-export types from ai-client
|
|
21
|
+
export type { ChatRequestBody, MultimodalContent, UIMessage };
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Recursive partial — every property and every nested array element is optional.
|
|
25
|
+
* Used to type the in-flight `partial` value the hook exposes while a structured
|
|
26
|
+
* output stream is still arriving (the JSON has shape but is incomplete).
|
|
27
|
+
*/
|
|
28
|
+
export type DeepPartial<T> =
|
|
29
|
+
T extends ReadonlyArray<infer U>
|
|
30
|
+
? Array<DeepPartial<U>>
|
|
31
|
+
: T extends object
|
|
32
|
+
? { [K in keyof T]?: DeepPartial<T[K]> }
|
|
33
|
+
: T;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Options for the useChat hook.
|
|
37
|
+
*
|
|
38
|
+
* Pass either `connection` or `fetcher` — the XOR is enforced at the type
|
|
39
|
+
* level via `ChatTransport`.
|
|
40
|
+
*
|
|
41
|
+
* This extends ChatClientOptions but omits the state change callbacks that are
|
|
42
|
+
* managed internally by hook state:
|
|
43
|
+
* - `onMessagesChange` - Managed by hook state (exposed as `messages`)
|
|
44
|
+
* - `onLoadingChange` - Managed by hook state (exposed as `isLoading`)
|
|
45
|
+
* - `onErrorChange` - Managed by hook state (exposed as `error`)
|
|
46
|
+
* - `onStatusChange` - Managed by hook state (exposed as `status`)
|
|
47
|
+
*
|
|
48
|
+
* All other callbacks (onResponse, onChunk, onFinish, onError) are
|
|
49
|
+
* passed through to the underlying ChatClient and can be used for side effects.
|
|
50
|
+
*
|
|
51
|
+
* When `outputSchema` is supplied, the hook returns a typed `partial` (live
|
|
52
|
+
* progressive object, updated from `TEXT_MESSAGE_CONTENT` deltas via
|
|
53
|
+
* `parsePartialJSON`) and `final` (validated terminal payload from the
|
|
54
|
+
* `structured-output.complete` event). The schema is used purely for type
|
|
55
|
+
* inference on the client — server-side validation still runs against the
|
|
56
|
+
* schema you pass to `chat({ outputSchema })` on the server route.
|
|
57
|
+
*
|
|
58
|
+
* Changing `connection` or `fetcher` updates the active ChatClient in place,
|
|
59
|
+
* preserving its state. Changing `id` creates a fresh client.
|
|
60
|
+
*/
|
|
61
|
+
export type UseChatOptions<
|
|
62
|
+
TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
63
|
+
TSchema extends SchemaInput | undefined = undefined,
|
|
64
|
+
TContext = InferredClientContext<TTools>,
|
|
65
|
+
> = DistributedOmit<
|
|
66
|
+
ChatClientOptions<TTools, TContext>,
|
|
67
|
+
| 'onMessagesChange'
|
|
68
|
+
| 'onLoadingChange'
|
|
69
|
+
| 'onErrorChange'
|
|
70
|
+
| 'onStatusChange'
|
|
71
|
+
| 'onSubscriptionChange'
|
|
72
|
+
| 'onConnectionStatusChange'
|
|
73
|
+
| 'onSessionGeneratingChange'
|
|
74
|
+
| 'context'
|
|
75
|
+
| 'devtools'
|
|
76
|
+
> & {
|
|
77
|
+
/** Display options for TanStack AI Devtools. */
|
|
78
|
+
devtools?: AIDevtoolsDisplayOptions;
|
|
79
|
+
/**
|
|
80
|
+
* Opt into mount-time live subscription behavior.
|
|
81
|
+
* When enabled, the hook subscribes on mount and unsubscribes on unmount.
|
|
82
|
+
*/
|
|
83
|
+
live?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Standard-schema-compatible schema (Zod, Valibot, ArkType, or a plain JSON
|
|
86
|
+
* Schema). Used to infer the shape of `partial` and `final` in the return.
|
|
87
|
+
* The schema is **not** sent to the server — server-side validation runs
|
|
88
|
+
* against the schema passed to `chat({ outputSchema })` on the server route.
|
|
89
|
+
*/
|
|
90
|
+
outputSchema?: TSchema;
|
|
91
|
+
} & ClientContextOptionFromTools<TTools, TContext>;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Discriminated return shape: when `outputSchema` is supplied, the hook adds
|
|
95
|
+
* typed `partial` / `final` fields; when it is omitted (default), the return
|
|
96
|
+
* is unchanged.
|
|
97
|
+
*/
|
|
98
|
+
export type UseChatReturn<
|
|
99
|
+
TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
100
|
+
TSchema extends SchemaInput | undefined = undefined,
|
|
101
|
+
> = BaseUseChatReturn<TTools, TSchema extends SchemaInput ? InferSchemaType<TSchema> : unknown> &
|
|
102
|
+
(TSchema extends SchemaInput
|
|
103
|
+
? {
|
|
104
|
+
/**
|
|
105
|
+
* Live, progressively-parsed structured output. Updated from
|
|
106
|
+
* `TEXT_MESSAGE_CONTENT` deltas via `parsePartialJSON` while the stream
|
|
107
|
+
* is still arriving, and snapped to the validated payload when
|
|
108
|
+
* `structured-output.complete` fires. Resets on every new run
|
|
109
|
+
* (`sendMessage` / `reload`).
|
|
110
|
+
*/
|
|
111
|
+
partial: DeepPartial<InferSchemaType<TSchema>>;
|
|
112
|
+
/**
|
|
113
|
+
* Final, schema-validated structured output. `null` until the terminal
|
|
114
|
+
* `structured-output.complete` event arrives. Resets on every new run.
|
|
115
|
+
*/
|
|
116
|
+
final: InferSchemaType<TSchema> | null;
|
|
117
|
+
}
|
|
118
|
+
: Record<never, never>);
|
|
119
|
+
|
|
120
|
+
interface BaseUseChatReturn<TTools extends ReadonlyArray<AnyClientTool> = any, TData = unknown> {
|
|
121
|
+
/**
|
|
122
|
+
* Current messages in the conversation. When `outputSchema` is supplied,
|
|
123
|
+
* `messages[i].parts.find(p => p.type === 'structured-output')` is typed
|
|
124
|
+
* with the schema's inferred shape — `data: T`, `partial: DeepPartial<T>`.
|
|
125
|
+
*/
|
|
126
|
+
messages: Array<UIMessage<TTools, TData>>;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Send a message and get a response.
|
|
130
|
+
* Can be a simple string or multimodal content with images, audio, etc.
|
|
131
|
+
*/
|
|
132
|
+
sendMessage: (content: string | MultimodalContent) => Promise<void>;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Append a message to the conversation
|
|
136
|
+
*/
|
|
137
|
+
append: (message: ModelMessage | UIMessage<TTools, TData>) => Promise<void>;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Add the result of a client-side tool execution
|
|
141
|
+
*/
|
|
142
|
+
addToolResult: (result: {
|
|
143
|
+
toolCallId: string;
|
|
144
|
+
tool: string;
|
|
145
|
+
output: any;
|
|
146
|
+
state?: 'output-available' | 'output-error';
|
|
147
|
+
errorText?: string;
|
|
148
|
+
}) => Promise<void>;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Respond to a tool approval request
|
|
152
|
+
*/
|
|
153
|
+
addToolApprovalResponse: (response: {
|
|
154
|
+
id: string; // approval.id, not toolCallId
|
|
155
|
+
approved: boolean;
|
|
156
|
+
}) => Promise<void>;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Reload the last assistant message
|
|
160
|
+
*/
|
|
161
|
+
reload: () => Promise<void>;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Stop the current response generation
|
|
165
|
+
*/
|
|
166
|
+
stop: () => void;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Whether a response is currently being generated
|
|
170
|
+
*/
|
|
171
|
+
isLoading: boolean;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Current error, if any
|
|
175
|
+
*/
|
|
176
|
+
error: Error | undefined;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Current status of the chat client
|
|
180
|
+
*/
|
|
181
|
+
status: ChatClientState;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Whether the subscription loop is currently active
|
|
185
|
+
*/
|
|
186
|
+
isSubscribed: boolean;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Current connection lifecycle status
|
|
190
|
+
*/
|
|
191
|
+
connectionStatus: ConnectionStatus;
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Whether the shared session is actively generating.
|
|
195
|
+
* Derived from stream run events (RUN_STARTED / RUN_FINISHED / RUN_ERROR).
|
|
196
|
+
* Unlike `isLoading` (request-local), this reflects shared generation
|
|
197
|
+
* activity visible to all subscribers (e.g. across tabs/devices).
|
|
198
|
+
*/
|
|
199
|
+
sessionGenerating: boolean;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Set messages manually
|
|
203
|
+
*/
|
|
204
|
+
setMessages: (messages: Array<UIMessage<TTools, TData>>) => void;
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Clear all messages
|
|
208
|
+
*/
|
|
209
|
+
clear: () => void;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Note: createChatClientOptions and InferChatMessages are now in @tanstack/ai-client
|
|
213
|
+
// and re-exported from there for convenience
|