@bytexbyte/nxtlinq-ai-agent-web-development 0.1.8 → 0.2.0
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/legacy/core/lib/useVoiceMode.d.ts +4 -2
- package/dist/legacy/core/lib/useVoiceMode.d.ts.map +1 -1
- package/dist/legacy/core/lib/useVoiceMode.js +8 -2
- package/dist/voice/useVoiceTranscriptMessages.d.ts.map +1 -1
- package/dist/voice/useVoiceTranscriptMessages.js +1 -15
- package/package.json +2 -2
- package/src/legacy/core/lib/useVoiceMode.ts +13 -1
- package/src/voice/useVoiceTranscriptMessages.ts +3 -14
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import type { AITApi, Message, VoiceTransport } from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
3
|
-
import { type VoiceStatus } from '../../api/voice';
|
|
3
|
+
import { type VoiceStatus, type VoiceToolCallEvent, type VoiceToolResultEvent } from '../../api/voice';
|
|
4
4
|
export type UseVoiceModeOptions = {
|
|
5
5
|
apiKey: string;
|
|
6
6
|
apiSecret: string;
|
|
@@ -14,11 +14,13 @@ export type UseVoiceModeOptions = {
|
|
|
14
14
|
getMessages: () => Message[];
|
|
15
15
|
setMessages: React.Dispatch<React.SetStateAction<Message[]>>;
|
|
16
16
|
onError?: (error: Error) => void;
|
|
17
|
+
onToolCall?: (event: VoiceToolCallEvent) => void;
|
|
18
|
+
onToolResult?: (event: VoiceToolResultEvent) => void;
|
|
17
19
|
stopRecording: () => void;
|
|
18
20
|
stopTextToSpeech: () => void;
|
|
19
21
|
voiceTransport?: VoiceTransport;
|
|
20
22
|
};
|
|
21
|
-
export declare function useVoiceMode({ apiKey, apiSecret, pseudoId, externalId, aitId, walletAddress, aitToken, metadata, nxtlinqApi, getMessages, setMessages, onError, stopRecording, stopTextToSpeech, voiceTransport, }: UseVoiceModeOptions): {
|
|
23
|
+
export declare function useVoiceMode({ apiKey, apiSecret, pseudoId, externalId, aitId, walletAddress, aitToken, metadata, nxtlinqApi, getMessages, setMessages, onError, onToolCall, onToolResult, stopRecording, stopTextToSpeech, voiceTransport, }: UseVoiceModeOptions): {
|
|
22
24
|
isVoiceMode: boolean;
|
|
23
25
|
voiceStatus: VoiceStatus;
|
|
24
26
|
isVoiceConnecting: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useVoiceMode.d.ts","sourceRoot":"","sources":["../../../../src/legacy/core/lib/useVoiceMode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,8CAA8C,CAAC;AACpG,OAAO,EAKL,KAAK,WAAW,
|
|
1
|
+
{"version":3,"file":"useVoiceMode.d.ts","sourceRoot":"","sources":["../../../../src/legacy/core/lib/useVoiceMode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,8CAA8C,CAAC;AACpG,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EAE1B,MAAM,iBAAiB,CAAC;AAgBzB,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,OAAO,EAAE,CAAC;IAC7B,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACjD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACrD,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,SAAS,EACT,QAAQ,EACR,UAAU,EACV,KAAK,EACL,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,WAAW,EACX,WAAW,EACX,OAAO,EACP,UAAU,EACV,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,cAA8B,GAC/B,EAAE,mBAAmB;;;;;;;;;;EA0XrB"}
|
|
@@ -12,7 +12,7 @@ const USER_TRANSCRIPT_BLOCKED = new Set([
|
|
|
12
12
|
const ASSISTANT_MIC_HOLD_STATUSES = new Set([
|
|
13
13
|
'transcribing', 'thinking', 'generating', 'speaking',
|
|
14
14
|
]);
|
|
15
|
-
export function useVoiceMode({ apiKey, apiSecret, pseudoId, externalId, aitId, walletAddress, aitToken, metadata, nxtlinqApi, getMessages, setMessages, onError, stopRecording, stopTextToSpeech, voiceTransport = 'ws-realtime', }) {
|
|
15
|
+
export function useVoiceMode({ apiKey, apiSecret, pseudoId, externalId, aitId, walletAddress, aitToken, metadata, nxtlinqApi, getMessages, setMessages, onError, onToolCall, onToolResult, stopRecording, stopTextToSpeech, voiceTransport = 'ws-realtime', }) {
|
|
16
16
|
const [isVoiceMode, setIsVoiceMode] = React.useState(false);
|
|
17
17
|
const [voiceStatus, setVoiceStatus] = React.useState('idle');
|
|
18
18
|
const [isVoiceConnecting, setIsVoiceConnecting] = React.useState(false);
|
|
@@ -170,6 +170,8 @@ export function useVoiceMode({ apiKey, apiSecret, pseudoId, externalId, aitId, w
|
|
|
170
170
|
onStatus: (_status) => { },
|
|
171
171
|
onTranscript: (_event) => { },
|
|
172
172
|
onDone: (_event) => { },
|
|
173
|
+
onToolCall: (_event) => { },
|
|
174
|
+
onToolResult: (_event) => { },
|
|
173
175
|
});
|
|
174
176
|
const stopVoiceSession = React.useCallback(async (reason = 'unknown') => {
|
|
175
177
|
const session = sessionRef.current;
|
|
@@ -289,6 +291,8 @@ export function useVoiceMode({ apiKey, apiSecret, pseudoId, externalId, aitId, w
|
|
|
289
291
|
onStatus: (status) => voiceHandlersRef.current.onStatus(status),
|
|
290
292
|
onTranscript: (event) => voiceHandlersRef.current.onTranscript(event),
|
|
291
293
|
onDone: (event) => voiceHandlersRef.current.onDone(event),
|
|
294
|
+
onToolCall: (event) => voiceHandlersRef.current.onToolCall(event),
|
|
295
|
+
onToolResult: (event) => voiceHandlersRef.current.onToolResult(event),
|
|
292
296
|
onError: (err) => {
|
|
293
297
|
if (isConnectCancelled())
|
|
294
298
|
return;
|
|
@@ -358,8 +362,10 @@ export function useVoiceMode({ apiKey, apiSecret, pseudoId, externalId, aitId, w
|
|
|
358
362
|
handleTranscriptUi(event);
|
|
359
363
|
},
|
|
360
364
|
onDone: handleDone,
|
|
365
|
+
onToolCall: (event) => onToolCall?.(event),
|
|
366
|
+
onToolResult: (event) => onToolResult?.(event),
|
|
361
367
|
};
|
|
362
|
-
}, [handleStatus, handleTranscriptTurn, handleTranscriptUi, handleDone]);
|
|
368
|
+
}, [handleStatus, handleTranscriptTurn, handleTranscriptUi, handleDone, onToolCall, onToolResult]);
|
|
363
369
|
React.useEffect(() => () => { void stopVoiceSessionRef.current('unmount'); }, []);
|
|
364
370
|
// 追踪当前的 isVoiceMode 状态,用于 visibilitychange 事件
|
|
365
371
|
const isVoiceModeRef = React.useRef(isVoiceMode);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useVoiceTranscriptMessages.d.ts","sourceRoot":"","sources":["../../src/voice/useVoiceTranscriptMessages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,oBAAoB,EACrB,MAAM,8CAA8C,CAAC;AAMtD,KAAK,eAAe,GAAG,MAAM,GAAG,OAAO,CAAC;AAExC,MAAM,MAAM,uBAAuB,GAAG;IACpC,WAAW,EAAE,MAAM,OAAO,EAAE,CAAC;IAC7B,cAAc,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,IAAI,CAAC;IAClE,oBAAoB,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtE,CAAC;AAWF,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,uBAAuB,EAC5B,eAAe,EAAE,eAAe,EAChC,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,iBAAiB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,EACvC,kBAAkB,CAAC,EAAE,MAAM,MAAM;8BA0GvB,oBAAoB;
|
|
1
|
+
{"version":3,"file":"useVoiceTranscriptMessages.d.ts","sourceRoot":"","sources":["../../src/voice/useVoiceTranscriptMessages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,oBAAoB,EACrB,MAAM,8CAA8C,CAAC;AAMtD,KAAK,eAAe,GAAG,MAAM,GAAG,OAAO,CAAC;AAExC,MAAM,MAAM,uBAAuB,GAAG;IACpC,WAAW,EAAE,MAAM,OAAO,EAAE,CAAC;IAC7B,cAAc,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,IAAI,CAAC;IAClE,oBAAoB,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtE,CAAC;AAWF,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,uBAAuB,EAC5B,eAAe,EAAE,eAAe,EAChC,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,iBAAiB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,EACvC,kBAAkB,CAAC,EAAE,MAAM,MAAM;8BA0GvB,oBAAoB;wBAiBpB,cAAc,YAAY;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE;;EAkCjE"}
|
|
@@ -95,21 +95,7 @@ export function useVoiceTranscriptMessages(api, interactionMode, voiceSessionId,
|
|
|
95
95
|
return;
|
|
96
96
|
}
|
|
97
97
|
if (event.role === 'user' && !event.interim && text) {
|
|
98
|
-
api.updateMessages((prev) =>
|
|
99
|
-
const last = prev[prev.length - 1];
|
|
100
|
-
if (last?.role === 'user' && last.content === text)
|
|
101
|
-
return prev;
|
|
102
|
-
return [
|
|
103
|
-
...prev,
|
|
104
|
-
{
|
|
105
|
-
id: `voice-user-${Date.now()}`,
|
|
106
|
-
role: 'user',
|
|
107
|
-
content: text,
|
|
108
|
-
timestamp: new Date().toISOString(),
|
|
109
|
-
metadata: voiceMeta(resolveSessionId()),
|
|
110
|
-
},
|
|
111
|
-
];
|
|
112
|
-
});
|
|
98
|
+
api.updateMessages((prev) => ensureUserBubbleForVoiceTurn(prev, text, undefined, voiceMeta(resolveSessionId())));
|
|
113
99
|
}
|
|
114
100
|
}, [api, isVoiceUiActive, resolveSessionId, upsertStreaming]);
|
|
115
101
|
const handleDone = useCallback((event, options) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bytexbyte/nxtlinq-ai-agent-web-development",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "React Web headless SDK for nxtlinq AI Agent — hooks and browser ports",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"react-dom": ">=18.0.0"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@bytexbyte/nxtlinq-ai-agent-core-development": "0.4.
|
|
40
|
+
"@bytexbyte/nxtlinq-ai-agent-core-development": "0.4.4",
|
|
41
41
|
"ethers": "^6.16.0",
|
|
42
42
|
"fast-json-stable-stringify": "^2.1.0",
|
|
43
43
|
"metakeep": "^2.2.8",
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
type VoiceDoneEvent,
|
|
8
8
|
type VoiceSession,
|
|
9
9
|
type VoiceStatus,
|
|
10
|
+
type VoiceToolCallEvent,
|
|
11
|
+
type VoiceToolResultEvent,
|
|
10
12
|
type VoiceTranscriptEvent,
|
|
11
13
|
} from '../../api/voice';
|
|
12
14
|
import { appendServerHistoryIntoMessages } from './messageHistory';
|
|
@@ -37,6 +39,8 @@ export type UseVoiceModeOptions = {
|
|
|
37
39
|
getMessages: () => Message[];
|
|
38
40
|
setMessages: React.Dispatch<React.SetStateAction<Message[]>>;
|
|
39
41
|
onError?: (error: Error) => void;
|
|
42
|
+
onToolCall?: (event: VoiceToolCallEvent) => void;
|
|
43
|
+
onToolResult?: (event: VoiceToolResultEvent) => void;
|
|
40
44
|
stopRecording: () => void;
|
|
41
45
|
stopTextToSpeech: () => void;
|
|
42
46
|
voiceTransport?: VoiceTransport;
|
|
@@ -55,6 +59,8 @@ export function useVoiceMode({
|
|
|
55
59
|
getMessages,
|
|
56
60
|
setMessages,
|
|
57
61
|
onError,
|
|
62
|
+
onToolCall,
|
|
63
|
+
onToolResult,
|
|
58
64
|
stopRecording,
|
|
59
65
|
stopTextToSpeech,
|
|
60
66
|
voiceTransport = 'ws-realtime',
|
|
@@ -222,6 +228,8 @@ export function useVoiceMode({
|
|
|
222
228
|
onStatus: (_status: VoiceStatus) => {},
|
|
223
229
|
onTranscript: (_event: VoiceTranscriptEvent) => {},
|
|
224
230
|
onDone: (_event: VoiceDoneEvent) => {},
|
|
231
|
+
onToolCall: (_event: VoiceToolCallEvent) => {},
|
|
232
|
+
onToolResult: (_event: VoiceToolResultEvent) => {},
|
|
225
233
|
});
|
|
226
234
|
|
|
227
235
|
const stopVoiceSession = React.useCallback(async (reason = 'unknown') => {
|
|
@@ -333,6 +341,8 @@ export function useVoiceMode({
|
|
|
333
341
|
onStatus: (status) => voiceHandlersRef.current.onStatus(status),
|
|
334
342
|
onTranscript: (event) => voiceHandlersRef.current.onTranscript(event),
|
|
335
343
|
onDone: (event) => voiceHandlersRef.current.onDone(event),
|
|
344
|
+
onToolCall: (event) => voiceHandlersRef.current.onToolCall(event),
|
|
345
|
+
onToolResult: (event) => voiceHandlersRef.current.onToolResult(event),
|
|
336
346
|
onError: (err) => {
|
|
337
347
|
if (isConnectCancelled()) return;
|
|
338
348
|
onError?.(err);
|
|
@@ -396,8 +406,10 @@ export function useVoiceMode({
|
|
|
396
406
|
handleTranscriptUi(event);
|
|
397
407
|
},
|
|
398
408
|
onDone: handleDone,
|
|
409
|
+
onToolCall: (event) => onToolCall?.(event),
|
|
410
|
+
onToolResult: (event) => onToolResult?.(event),
|
|
399
411
|
};
|
|
400
|
-
}, [handleStatus, handleTranscriptTurn, handleTranscriptUi, handleDone]);
|
|
412
|
+
}, [handleStatus, handleTranscriptTurn, handleTranscriptUi, handleDone, onToolCall, onToolResult]);
|
|
401
413
|
|
|
402
414
|
React.useEffect(() => () => { void stopVoiceSessionRef.current('unmount'); }, []);
|
|
403
415
|
|
|
@@ -144,20 +144,9 @@ export function useVoiceTranscriptMessages(
|
|
|
144
144
|
return;
|
|
145
145
|
}
|
|
146
146
|
if (event.role === 'user' && !event.interim && text) {
|
|
147
|
-
api.updateMessages((prev) =>
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
return [
|
|
151
|
-
...prev,
|
|
152
|
-
{
|
|
153
|
-
id: `voice-user-${Date.now()}`,
|
|
154
|
-
role: 'user' as const,
|
|
155
|
-
content: text,
|
|
156
|
-
timestamp: new Date().toISOString(),
|
|
157
|
-
metadata: voiceMeta(resolveSessionId()),
|
|
158
|
-
},
|
|
159
|
-
];
|
|
160
|
-
});
|
|
147
|
+
api.updateMessages((prev) =>
|
|
148
|
+
ensureUserBubbleForVoiceTurn(prev, text, undefined, voiceMeta(resolveSessionId())),
|
|
149
|
+
);
|
|
161
150
|
}
|
|
162
151
|
},
|
|
163
152
|
[api, isVoiceUiActive, resolveSessionId, upsertStreaming],
|