akanjs 3.0.0-alpha.39 → 3.0.0-alpha.40
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/client/capacitor.ts +34 -0
- package/dictionary/base.dictionary.ts +2 -0
- package/index.ts +1 -1
- package/local/apps/serverLifecycle/serverLifecycle-local.db-shm +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local_solid.db-shm +0 -0
- package/package.json +1 -1
- package/types/client/capacitor.d.ts +39 -0
- package/types/dictionary/base.dictionary.d.ts +1 -1
- package/types/dictionary/dictionary.d.ts +8 -8
- package/types/index.d.ts +1 -1
- package/types/ui/Agent/Chat.d.ts +8 -1
- package/types/ui/Agent/Mic.d.ts +8 -0
- package/types/ui/Agent/voice.d.ts +66 -0
- package/types/ui/index.d.ts +1 -0
- package/ui/Agent/Chat.tsx +103 -1
- package/ui/Agent/Mic.tsx +27 -0
- package/ui/Agent/voice.ts +168 -0
- package/ui/index.ts +1 -0
package/client/capacitor.ts
CHANGED
|
@@ -46,6 +46,28 @@ export type CapacitorContactsModule = {
|
|
|
46
46
|
};
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
+
export type CapacitorSpeechRecognitionModule = {
|
|
50
|
+
SpeechRecognition: {
|
|
51
|
+
available: () => Promise<{ available: boolean }>;
|
|
52
|
+
checkPermissions: () => Promise<{ speechRecognition: CapacitorPermissionState }>;
|
|
53
|
+
requestPermissions: () => Promise<{ speechRecognition: CapacitorPermissionState }>;
|
|
54
|
+
start: (options: Record<string, unknown>) => Promise<{ matches?: string[] }>;
|
|
55
|
+
stop: () => Promise<void>;
|
|
56
|
+
removeAllListeners: () => Promise<void> | void;
|
|
57
|
+
addListener: (
|
|
58
|
+
eventName: string,
|
|
59
|
+
listenerFunc: (data: { matches?: string[] }) => void,
|
|
60
|
+
) => Promise<{ remove: () => Promise<void> | void }> | { remove: () => Promise<void> | void };
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type CapacitorTextToSpeechModule = {
|
|
65
|
+
TextToSpeech: {
|
|
66
|
+
speak: (options: { text: string; lang?: string; rate?: number }) => Promise<void>;
|
|
67
|
+
stop: () => Promise<void>;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
|
|
49
71
|
export type CapacitorCoreModule = {
|
|
50
72
|
CapacitorCookies: {
|
|
51
73
|
setCookie: (options: { key: string; value: string; path?: string }) => Promise<void> | void;
|
|
@@ -155,6 +177,8 @@ type CapacitorModuleMap = {
|
|
|
155
177
|
preferences: CapacitorPreferencesModule;
|
|
156
178
|
pushNotifications: CapacitorPushNotificationsModule;
|
|
157
179
|
safeArea: CapacitorSafeAreaModule;
|
|
180
|
+
speechRecognition: CapacitorSpeechRecognitionModule;
|
|
181
|
+
textToSpeech: CapacitorTextToSpeechModule;
|
|
158
182
|
updater: CapacitorUpdaterModule;
|
|
159
183
|
};
|
|
160
184
|
|
|
@@ -257,6 +281,16 @@ export const loadCapacitorSafeArea = () =>
|
|
|
257
281
|
SafeArea: getCapacitorPlugin<CapacitorSafeAreaModule["SafeArea"]>("SafeArea"),
|
|
258
282
|
}));
|
|
259
283
|
|
|
284
|
+
export const loadCapacitorSpeechRecognition = () =>
|
|
285
|
+
loadCapacitorModule("speechRecognition", async () => ({
|
|
286
|
+
SpeechRecognition: getCapacitorPlugin<CapacitorSpeechRecognitionModule["SpeechRecognition"]>("SpeechRecognition"),
|
|
287
|
+
}));
|
|
288
|
+
|
|
289
|
+
export const loadCapacitorTextToSpeech = () =>
|
|
290
|
+
loadCapacitorModule("textToSpeech", async () => ({
|
|
291
|
+
TextToSpeech: getCapacitorPlugin<CapacitorTextToSpeechModule["TextToSpeech"]>("TextToSpeech"),
|
|
292
|
+
}));
|
|
293
|
+
|
|
260
294
|
export const loadCapacitorUpdater = () =>
|
|
261
295
|
loadCapacitorModule("updater", async () => ({
|
|
262
296
|
CapacitorUpdater: getCapacitorPlugin<CapacitorUpdaterModule["CapacitorUpdater"]>("CapacitorUpdater"),
|
|
@@ -54,6 +54,8 @@ export const baseDictionary = serviceDictionary(["en", "ko"])
|
|
|
54
54
|
agentClear: ["Clear conversation", "대화 비우기"],
|
|
55
55
|
agentQuestion: ["The agent needs your decision", "에이전트가 결정을 요청합니다"],
|
|
56
56
|
agentAnswer: ["Type your answer...", "답변을 입력하세요..."],
|
|
57
|
+
agentListen: ["Speak to the agent", "에이전트에게 말하기"],
|
|
58
|
+
agentVoiceFailed: ["The microphone could not be used.", "마이크를 사용할 수 없습니다."],
|
|
57
59
|
agentAttach: ["Attach a file", "파일 첨부"],
|
|
58
60
|
agentAttachRemove: ["Remove attachment", "첨부 제거"],
|
|
59
61
|
agentAttachTooLarge: ["{name} is too large to attach.", "{name}은(는) 용량이 너무 커서 첨부할 수 없습니다."],
|
package/index.ts
CHANGED
|
@@ -26,7 +26,7 @@ export interface AkanRouteConfig {
|
|
|
26
26
|
|
|
27
27
|
export type DatabaseMode = "single" | "multiple" | "cluster";
|
|
28
28
|
export type MobileEnv = "local" | "debug" | "develop" | "main";
|
|
29
|
-
export type MobilePermission = "camera" | "contacts" | "location" | "push";
|
|
29
|
+
export type MobilePermission = "camera" | "contacts" | "location" | "push" | "speech";
|
|
30
30
|
|
|
31
31
|
export interface AkanMobileTargetAssets {
|
|
32
32
|
icon?: string;
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -73,6 +73,41 @@ export type CapacitorContactsModule = {
|
|
|
73
73
|
}>;
|
|
74
74
|
};
|
|
75
75
|
};
|
|
76
|
+
export type CapacitorSpeechRecognitionModule = {
|
|
77
|
+
SpeechRecognition: {
|
|
78
|
+
available: () => Promise<{
|
|
79
|
+
available: boolean;
|
|
80
|
+
}>;
|
|
81
|
+
checkPermissions: () => Promise<{
|
|
82
|
+
speechRecognition: CapacitorPermissionState;
|
|
83
|
+
}>;
|
|
84
|
+
requestPermissions: () => Promise<{
|
|
85
|
+
speechRecognition: CapacitorPermissionState;
|
|
86
|
+
}>;
|
|
87
|
+
start: (options: Record<string, unknown>) => Promise<{
|
|
88
|
+
matches?: string[];
|
|
89
|
+
}>;
|
|
90
|
+
stop: () => Promise<void>;
|
|
91
|
+
removeAllListeners: () => Promise<void> | void;
|
|
92
|
+
addListener: (eventName: string, listenerFunc: (data: {
|
|
93
|
+
matches?: string[];
|
|
94
|
+
}) => void) => Promise<{
|
|
95
|
+
remove: () => Promise<void> | void;
|
|
96
|
+
}> | {
|
|
97
|
+
remove: () => Promise<void> | void;
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
export type CapacitorTextToSpeechModule = {
|
|
102
|
+
TextToSpeech: {
|
|
103
|
+
speak: (options: {
|
|
104
|
+
text: string;
|
|
105
|
+
lang?: string;
|
|
106
|
+
rate?: number;
|
|
107
|
+
}) => Promise<void>;
|
|
108
|
+
stop: () => Promise<void>;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
76
111
|
export type CapacitorCoreModule = {
|
|
77
112
|
CapacitorCookies: {
|
|
78
113
|
setCookie: (options: {
|
|
@@ -226,6 +261,8 @@ type CapacitorModuleMap = {
|
|
|
226
261
|
preferences: CapacitorPreferencesModule;
|
|
227
262
|
pushNotifications: CapacitorPushNotificationsModule;
|
|
228
263
|
safeArea: CapacitorSafeAreaModule;
|
|
264
|
+
speechRecognition: CapacitorSpeechRecognitionModule;
|
|
265
|
+
textToSpeech: CapacitorTextToSpeechModule;
|
|
229
266
|
updater: CapacitorUpdaterModule;
|
|
230
267
|
};
|
|
231
268
|
type CapacitorImportCache = Partial<{
|
|
@@ -247,5 +284,7 @@ export declare const loadCapacitorKeyboard: () => Promise<CapacitorKeyboardModul
|
|
|
247
284
|
export declare const loadCapacitorPreferences: () => Promise<CapacitorPreferencesModule>;
|
|
248
285
|
export declare const loadCapacitorPushNotifications: () => Promise<CapacitorPushNotificationsModule>;
|
|
249
286
|
export declare const loadCapacitorSafeArea: () => Promise<CapacitorSafeAreaModule>;
|
|
287
|
+
export declare const loadCapacitorSpeechRecognition: () => Promise<CapacitorSpeechRecognitionModule>;
|
|
288
|
+
export declare const loadCapacitorTextToSpeech: () => Promise<CapacitorTextToSpeechModule>;
|
|
250
289
|
export declare const loadCapacitorUpdater: () => Promise<CapacitorUpdaterModule>;
|
|
251
290
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const baseDictionary: import("./dictInfo.d.ts").ServiceDictInfo<[string, string], "ping" | "pingBody" | "pingParam" | "pingQuery" | "wsPing" | "pubsubPing", never, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view">;
|
|
1
|
+
export declare const baseDictionary: import("./dictInfo.d.ts").ServiceDictInfo<[string, string], "ping" | "pingBody" | "pingParam" | "pingQuery" | "wsPing" | "pubsubPing", never, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentListen" | "agentVoiceFailed" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view">;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { AgentEndpoint, AgentTurn, BaseEndpoint } from "akanjs/signal";
|
|
2
2
|
export declare const dictionary: {
|
|
3
|
-
base: import("./locale.d.ts").DictModule<import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view">, never>;
|
|
3
|
+
base: import("./locale.d.ts").DictModule<import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentListen" | "agentVoiceFailed" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view">, never>;
|
|
4
4
|
agentTurn: import("./locale.d.ts").DictModule<import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc`, never>;
|
|
5
5
|
agent: import("./locale.d.ts").DictModule<import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, "agent.error.llmUnavailable" | "agent.error.deepseekRequestFailed">;
|
|
6
6
|
};
|
|
7
|
-
export declare const Err: import("./trans.d.ts").ErrConstructor<"agent.error.llmUnavailable" | "agent.error.deepseekRequestFailed">, translate: (lang: "en" | "ko" | "zhChs" | "zhCht" | "ja", key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, data?: import("./trans.d.ts").TranslationData) => string, msg: {
|
|
8
|
-
info: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
|
|
9
|
-
success: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
|
|
10
|
-
error: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
|
|
11
|
-
warning: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
|
|
12
|
-
loading: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
|
|
13
|
-
}, getDictionary: (lang: "en" | "ko" | "zhChs" | "zhCht" | "ja") => object, getAllDictionary: () => import("./trans.d.ts").RootDictionary, __Dict_Key__: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, __Error_Key__: "agent.error.llmUnavailable" | "agent.error.deepseekRequestFailed";
|
|
7
|
+
export declare const Err: import("./trans.d.ts").ErrConstructor<"agent.error.llmUnavailable" | "agent.error.deepseekRequestFailed">, translate: (lang: "en" | "ko" | "zhChs" | "zhCht" | "ja", key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentListen" | "agentVoiceFailed" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, data?: import("./trans.d.ts").TranslationData) => string, msg: {
|
|
8
|
+
info: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentListen" | "agentVoiceFailed" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
|
|
9
|
+
success: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentListen" | "agentVoiceFailed" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
|
|
10
|
+
error: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentListen" | "agentVoiceFailed" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
|
|
11
|
+
warning: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentListen" | "agentVoiceFailed" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
|
|
12
|
+
loading: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentListen" | "agentVoiceFailed" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
|
|
13
|
+
}, getDictionary: (lang: "en" | "ko" | "zhChs" | "zhCht" | "ja") => object, getAllDictionary: () => import("./trans.d.ts").RootDictionary, __Dict_Key__: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentListen" | "agentVoiceFailed" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, __Error_Key__: "agent.error.llmUnavailable" | "agent.error.deepseekRequestFailed";
|
package/types/index.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export interface AkanRouteConfig {
|
|
|
27
27
|
}
|
|
28
28
|
export type DatabaseMode = "single" | "multiple" | "cluster";
|
|
29
29
|
export type MobileEnv = "local" | "debug" | "develop" | "main";
|
|
30
|
-
export type MobilePermission = "camera" | "contacts" | "location" | "push";
|
|
30
|
+
export type MobilePermission = "camera" | "contacts" | "location" | "push" | "speech";
|
|
31
31
|
export interface AkanMobileTargetAssets {
|
|
32
32
|
icon?: string;
|
|
33
33
|
splash?: string;
|
package/types/ui/Agent/Chat.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type ReactNode } from "react";
|
|
|
2
2
|
import { type AgentRunner } from "../../vendor/use-agentic.d.ts";
|
|
3
3
|
import { type AttachReader } from "./attachment.d.ts";
|
|
4
4
|
import { type PersistOption } from "./sessionHistory.d.ts";
|
|
5
|
+
import { type VoiceEngine } from "./voice.d.ts";
|
|
5
6
|
export interface ChatProps {
|
|
6
7
|
className?: string;
|
|
7
8
|
title?: string;
|
|
@@ -22,6 +23,12 @@ export interface ChatProps {
|
|
|
22
23
|
* the built-in, so it can also replace how an image is prepared.
|
|
23
24
|
*/
|
|
24
25
|
attach?: AttachReader;
|
|
26
|
+
/**
|
|
27
|
+
* Speech in and out. The engine listens and speaks; this component decides when — a press-to-talk microphone
|
|
28
|
+
* whose transcript lands in the composer for the user to correct, and a reply read aloud **only when the ask
|
|
29
|
+
* itself came in by voice**, so a typed question never turns the speakers on.
|
|
30
|
+
*/
|
|
31
|
+
voice?: VoiceEngine;
|
|
25
32
|
}
|
|
26
33
|
/**
|
|
27
34
|
* The user-facing half of the in-page agent: one floating chat wired to the same surface the dock inspects.
|
|
@@ -30,6 +37,6 @@ export interface ChatProps {
|
|
|
30
37
|
* no history). An enclosing AgentProvider's session wins, which is how an app isolates a surface or swaps the
|
|
31
38
|
* loop while keeping this UI.
|
|
32
39
|
*/
|
|
33
|
-
export declare const DefaultChat: ({ className, title, instructions, runner, maxTurns, defaultOpen, persist, inline, attach, }: ChatProps) => ReactNode;
|
|
40
|
+
export declare const DefaultChat: ({ className, title, instructions, runner, maxTurns, defaultOpen, persist, inline, attach, voice, }: ChatProps) => ReactNode;
|
|
34
41
|
declare const _default: import("react").ComponentType<ChatProps>;
|
|
35
42
|
export default _default;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** A recognition run in progress. `stop` ends it; a final result may still arrive after. */
|
|
2
|
+
export interface VoiceListener {
|
|
3
|
+
stop: () => void;
|
|
4
|
+
}
|
|
5
|
+
/** One utterance being spoken. `cancel` is barge-in; `done` resolves when it finished or was cancelled. */
|
|
6
|
+
export interface VoiceSpeech {
|
|
7
|
+
cancel: () => void;
|
|
8
|
+
done: Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export interface VoiceHandlers {
|
|
11
|
+
/** Partial text while the user is still speaking. An engine without interim results simply never calls it. */
|
|
12
|
+
onInterim?: (text: string) => void;
|
|
13
|
+
onFinal: (text: string) => void;
|
|
14
|
+
onError: (message: string) => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The seam a voice engine fills — one contract, whatever is behind it: the browser's own recognition, a cloud
|
|
18
|
+
* STT/TTS pair, or a native plugin. The engine speaks and listens; **the framework owns the policy** — when to
|
|
19
|
+
* listen, what to speak, sentence chunking, barge-in, and whether a reply is spoken at all.
|
|
20
|
+
*
|
|
21
|
+
* Shaped as a subscription rather than `listen(): Promise<string>` on purpose. A promise fits push-to-talk and
|
|
22
|
+
* nothing else, and continuous listening could then only arrive as a breaking change; this shape carries both, so
|
|
23
|
+
* hands-free is a later policy decision instead of a later contract.
|
|
24
|
+
*
|
|
25
|
+
* `listen` must be reachable from the click that starts it: microphone permission and, on iOS, the first
|
|
26
|
+
* `speechSynthesis` utterance are both gated on a user gesture.
|
|
27
|
+
*/
|
|
28
|
+
export interface VoiceEngine {
|
|
29
|
+
listen: (handlers: VoiceHandlers) => VoiceListener;
|
|
30
|
+
/** One sentence at a time, already stripped of markdown. The framework queues; the engine speaks. */
|
|
31
|
+
speak: (text: string) => VoiceSpeech;
|
|
32
|
+
/** Answering false hides the controls — a screen that cannot listen should not offer a microphone. */
|
|
33
|
+
available?: () => boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* What of a model's answer should be read aloud.
|
|
37
|
+
*
|
|
38
|
+
* Code blocks, tables, and rules are dropped rather than spoken: a fence read character by character is noise,
|
|
39
|
+
* and a table read cell by cell is worse than silence — in push-to-talk the user is looking at the screen that
|
|
40
|
+
* already renders both. Inline code keeps its content, because that is usually a name worth hearing.
|
|
41
|
+
*
|
|
42
|
+
* Monotonic over a growing source, which is what lets the reader below track one offset: new text never changes
|
|
43
|
+
* how earlier text was rendered, since a fence marker always arrives before the lines it swallows.
|
|
44
|
+
*/
|
|
45
|
+
export declare const speechText: (markdown: string) => string;
|
|
46
|
+
/**
|
|
47
|
+
* Reads an assistant answer aloud as it streams in, one sentence at a time.
|
|
48
|
+
*
|
|
49
|
+
* Sentence-at-a-time rather than delta-at-a-time because a delta cuts words in half, and whole-message-at-a-time
|
|
50
|
+
* because that waits for the turn to end. The offset is over `speechText` output, not the raw source, so a code
|
|
51
|
+
* block that arrives mid-answer costs nothing.
|
|
52
|
+
*/
|
|
53
|
+
export declare class VoiceReader {
|
|
54
|
+
#private;
|
|
55
|
+
constructor(engine: () => VoiceEngine | undefined);
|
|
56
|
+
get speaking(): boolean;
|
|
57
|
+
/** Call with the whole answer so far, as often as it changes; already-spoken text is never repeated. */
|
|
58
|
+
feed(source: string): void;
|
|
59
|
+
/**
|
|
60
|
+
* The turn ended: the last sentence has no whitespace behind it and would otherwise never be spoken. It still
|
|
61
|
+
* splits what is left, because a whole answer that arrived in one render must not be read as one long utterance.
|
|
62
|
+
*/
|
|
63
|
+
flush(source: string): void;
|
|
64
|
+
cancel(): void;
|
|
65
|
+
reset(): void;
|
|
66
|
+
}
|
package/types/ui/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { Agent } from "./Agent.d.ts";
|
|
2
2
|
export { type AttachReader, maxAttachmentBytes } from "./Agent/attachment.d.ts";
|
|
3
|
+
export type { VoiceEngine, VoiceHandlers, VoiceListener, VoiceSpeech } from "./Agent/voice.d.ts";
|
|
3
4
|
export { agentAttrs } from "./agentAttrs.d.ts";
|
|
4
5
|
export { animated } from "./animated.d.ts";
|
|
5
6
|
export { Badge } from "./Badge.d.ts";
|
package/ui/Agent/Chat.tsx
CHANGED
|
@@ -16,8 +16,10 @@ import Bubble from "./Bubble";
|
|
|
16
16
|
import { type ChatCommand, ChatCommands } from "./ChatCommands";
|
|
17
17
|
import { fetchRunner } from "./fetchRunner";
|
|
18
18
|
import Menu from "./Menu";
|
|
19
|
+
import { Mic } from "./Mic";
|
|
19
20
|
import Question from "./Question";
|
|
20
21
|
import { type PersistOption, sessionHistoryOf } from "./sessionHistory";
|
|
22
|
+
import { type VoiceEngine, type VoiceListener, VoiceReader } from "./voice";
|
|
21
23
|
|
|
22
24
|
export interface ChatProps {
|
|
23
25
|
className?: string;
|
|
@@ -39,6 +41,12 @@ export interface ChatProps {
|
|
|
39
41
|
* the built-in, so it can also replace how an image is prepared.
|
|
40
42
|
*/
|
|
41
43
|
attach?: AttachReader;
|
|
44
|
+
/**
|
|
45
|
+
* Speech in and out. The engine listens and speaks; this component decides when — a press-to-talk microphone
|
|
46
|
+
* whose transcript lands in the composer for the user to correct, and a reply read aloud **only when the ask
|
|
47
|
+
* itself came in by voice**, so a typed question never turns the speakers on.
|
|
48
|
+
*/
|
|
49
|
+
voice?: VoiceEngine;
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
const isApplePlatform = () => /Mac|iPhone|iPad|iPod/i.test(navigator.platform);
|
|
@@ -62,6 +70,7 @@ export const DefaultChat = ({
|
|
|
62
70
|
persist,
|
|
63
71
|
inline = false,
|
|
64
72
|
attach,
|
|
73
|
+
voice,
|
|
65
74
|
}: ChatProps) => {
|
|
66
75
|
const { l } = usePage();
|
|
67
76
|
const provided = useContext(SessionContext);
|
|
@@ -87,6 +96,18 @@ export const DefaultChat = ({
|
|
|
87
96
|
const [open, setOpen] = useState(defaultOpen);
|
|
88
97
|
const [draft, setDraft] = useState("");
|
|
89
98
|
const [attached, setAttached] = useState<MessageAttachment[]>([]);
|
|
99
|
+
const [listening, setListening] = useState(false);
|
|
100
|
+
|
|
101
|
+
const engine = useRef<VoiceEngine | undefined>(voice);
|
|
102
|
+
engine.current = voice;
|
|
103
|
+
const listener = useRef<VoiceListener | null>(null);
|
|
104
|
+
const reader = useRef<VoiceReader | null>(null);
|
|
105
|
+
reader.current ??= new VoiceReader(() => engine.current);
|
|
106
|
+
/** Set while the draft came from the microphone, consumed by the send that carries it. */
|
|
107
|
+
const byVoice = useRef(false);
|
|
108
|
+
/** The assistant message being read aloud. Null means this turn is not one to read. */
|
|
109
|
+
const spoken = useRef<{ at: number } | null>(null);
|
|
110
|
+
const seen = useRef(0);
|
|
90
111
|
const sent = useRef<string[]>([]);
|
|
91
112
|
const stashed = useRef("");
|
|
92
113
|
const [recall, setRecall] = useState(0);
|
|
@@ -119,6 +140,39 @@ export const DefaultChat = ({
|
|
|
119
140
|
if (!open) return;
|
|
120
141
|
inputRef.current?.focus();
|
|
121
142
|
}, [open, session.pendingQuestion?.callId]);
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
const speaker = reader.current;
|
|
145
|
+
|
|
146
|
+
if (session.messages.length < seen.current) {
|
|
147
|
+
speaker?.reset();
|
|
148
|
+
spoken.current = null;
|
|
149
|
+
}
|
|
150
|
+
seen.current = session.messages.length;
|
|
151
|
+
const reading = spoken.current;
|
|
152
|
+
if (!reading || !speaker) return;
|
|
153
|
+
const at = session.messages.findLastIndex(
|
|
154
|
+
(message) => message.role === "assistant" && !message.local && !!message.text,
|
|
155
|
+
);
|
|
156
|
+
if (at < 0) return;
|
|
157
|
+
|
|
158
|
+
if (at !== reading.at) {
|
|
159
|
+
speaker.reset();
|
|
160
|
+
reading.at = at;
|
|
161
|
+
}
|
|
162
|
+
const text = session.messages[at].text ?? "";
|
|
163
|
+
if (session.isRunning) speaker.feed(text);
|
|
164
|
+
else {
|
|
165
|
+
speaker.flush(text);
|
|
166
|
+
spoken.current = null;
|
|
167
|
+
}
|
|
168
|
+
}, [version]);
|
|
169
|
+
useEffect(
|
|
170
|
+
() => () => {
|
|
171
|
+
listener.current?.stop();
|
|
172
|
+
reader.current?.cancel();
|
|
173
|
+
},
|
|
174
|
+
[],
|
|
175
|
+
);
|
|
122
176
|
const runPrompt = async (prompt: AgentPrompt, args: string[]) => {
|
|
123
177
|
const usage = `/${prompt.name} ${prompt.args.map((arg) => `<${arg.name}>`).join(" ")}`.trim();
|
|
124
178
|
if (args.length < prompt.args.filter((arg) => arg.required).length) {
|
|
@@ -154,6 +208,39 @@ export const DefaultChat = ({
|
|
|
154
208
|
}
|
|
155
209
|
}
|
|
156
210
|
};
|
|
211
|
+
/** One press is one utterance, so the engine is told to stop even when it reported a final result itself. */
|
|
212
|
+
const endListening = () => {
|
|
213
|
+
const held = listener.current;
|
|
214
|
+
listener.current = null;
|
|
215
|
+
held?.stop();
|
|
216
|
+
setListening(false);
|
|
217
|
+
};
|
|
218
|
+
const toggleMic = () => {
|
|
219
|
+
if (listener.current) {
|
|
220
|
+
endListening();
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const voiceEngine = engine.current;
|
|
224
|
+
if (!voiceEngine) return;
|
|
225
|
+
|
|
226
|
+
reader.current?.cancel();
|
|
227
|
+
spoken.current = null;
|
|
228
|
+
listener.current = voiceEngine.listen({
|
|
229
|
+
onInterim: setDraft,
|
|
230
|
+
onFinal: (text) => {
|
|
231
|
+
setDraft(text);
|
|
232
|
+
byVoice.current = true;
|
|
233
|
+
endListening();
|
|
234
|
+
},
|
|
235
|
+
onError: (message) => {
|
|
236
|
+
|
|
237
|
+
console.warn(`[akan] voice input failed: ${message}`);
|
|
238
|
+
session.note(l("base.agentVoiceFailed"));
|
|
239
|
+
endListening();
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
setListening(true);
|
|
243
|
+
};
|
|
157
244
|
const remember = (text: string) => {
|
|
158
245
|
if (sent.current[sent.current.length - 1] !== text) sent.current = [...sent.current, text].slice(-30);
|
|
159
246
|
setRecall(0);
|
|
@@ -188,6 +275,7 @@ export const DefaultChat = ({
|
|
|
188
275
|
const question = session.pendingQuestion;
|
|
189
276
|
if (question && text) {
|
|
190
277
|
setDraft("");
|
|
278
|
+
byVoice.current = false;
|
|
191
279
|
question.answer(question.multiple ? [text] : text);
|
|
192
280
|
return;
|
|
193
281
|
}
|
|
@@ -202,6 +290,9 @@ export const DefaultChat = ({
|
|
|
202
290
|
const prompt = command ? prompts.current?.find(command.name) : null;
|
|
203
291
|
setDraft("");
|
|
204
292
|
if (text) remember(text);
|
|
293
|
+
|
|
294
|
+
spoken.current = byVoice.current ? { at: -1 } : null;
|
|
295
|
+
byVoice.current = false;
|
|
205
296
|
if (command && prompt) {
|
|
206
297
|
void runPrompt(prompt, command.args);
|
|
207
298
|
return;
|
|
@@ -213,6 +304,8 @@ export const DefaultChat = ({
|
|
|
213
304
|
setAttached([]);
|
|
214
305
|
void session.send([{ role: "user", ...(text ? { text } : {}), attachments: attached }]);
|
|
215
306
|
};
|
|
307
|
+
|
|
308
|
+
const canListen = !!voice && (voice.available?.() ?? true);
|
|
216
309
|
const query = /^\/[A-Za-z0-9_-]*$/.test(draft) ? draft : "";
|
|
217
310
|
const commandMenu = query ? ChatCommands.list(l).filter((command) => `/${command.name}`.startsWith(query)) : [];
|
|
218
311
|
const promptMenu = query
|
|
@@ -323,6 +416,7 @@ export const DefaultChat = ({
|
|
|
323
416
|
/>
|
|
324
417
|
) : null}
|
|
325
418
|
<div className="flex items-center gap-2">
|
|
419
|
+
{canListen ? <Mic label={l("base.agentListen")} listening={listening} onToggle={toggleMic} /> : null}
|
|
326
420
|
<Attach label={l("base.agentAttach")} onPick={(files) => void attachFiles(files)} />
|
|
327
421
|
<input
|
|
328
422
|
className={inputRecipe({ size: "sm" }, "flex-1")}
|
|
@@ -349,7 +443,15 @@ export const DefaultChat = ({
|
|
|
349
443
|
value={draft}
|
|
350
444
|
/>
|
|
351
445
|
{session.isRunning && !session.pendingQuestion ? (
|
|
352
|
-
<Button
|
|
446
|
+
<Button
|
|
447
|
+
onClick={() => {
|
|
448
|
+
reader.current?.cancel();
|
|
449
|
+
spoken.current = null;
|
|
450
|
+
session.abort();
|
|
451
|
+
}}
|
|
452
|
+
size="sm"
|
|
453
|
+
variant="outline"
|
|
454
|
+
>
|
|
353
455
|
{l("base.stop")}
|
|
354
456
|
</Button>
|
|
355
457
|
) : (
|
package/ui/Agent/Mic.tsx
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "akanjs/client";
|
|
3
|
+
import { AiOutlineAudio, AiOutlineAudioMuted } from "react-icons/ai";
|
|
4
|
+
|
|
5
|
+
interface MicProps {
|
|
6
|
+
className?: string;
|
|
7
|
+
listening: boolean;
|
|
8
|
+
label: string;
|
|
9
|
+
onToggle: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const Mic = ({ className, listening, label, onToggle }: MicProps) => (
|
|
13
|
+
<button
|
|
14
|
+
aria-label={label}
|
|
15
|
+
aria-pressed={listening}
|
|
16
|
+
className={cn(
|
|
17
|
+
"shrink-0 hover:text-foreground",
|
|
18
|
+
listening ? "animate-pulse text-primary" : "text-foreground/50",
|
|
19
|
+
className,
|
|
20
|
+
)}
|
|
21
|
+
onClick={onToggle}
|
|
22
|
+
title={label}
|
|
23
|
+
type="button"
|
|
24
|
+
>
|
|
25
|
+
{listening ? <AiOutlineAudioMuted /> : <AiOutlineAudio />}
|
|
26
|
+
</button>
|
|
27
|
+
);
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/** A recognition run in progress. `stop` ends it; a final result may still arrive after. */
|
|
2
|
+
export interface VoiceListener {
|
|
3
|
+
stop: () => void;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/** One utterance being spoken. `cancel` is barge-in; `done` resolves when it finished or was cancelled. */
|
|
7
|
+
export interface VoiceSpeech {
|
|
8
|
+
cancel: () => void;
|
|
9
|
+
done: Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface VoiceHandlers {
|
|
13
|
+
/** Partial text while the user is still speaking. An engine without interim results simply never calls it. */
|
|
14
|
+
onInterim?: (text: string) => void;
|
|
15
|
+
onFinal: (text: string) => void;
|
|
16
|
+
onError: (message: string) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The seam a voice engine fills — one contract, whatever is behind it: the browser's own recognition, a cloud
|
|
21
|
+
* STT/TTS pair, or a native plugin. The engine speaks and listens; **the framework owns the policy** — when to
|
|
22
|
+
* listen, what to speak, sentence chunking, barge-in, and whether a reply is spoken at all.
|
|
23
|
+
*
|
|
24
|
+
* Shaped as a subscription rather than `listen(): Promise<string>` on purpose. A promise fits push-to-talk and
|
|
25
|
+
* nothing else, and continuous listening could then only arrive as a breaking change; this shape carries both, so
|
|
26
|
+
* hands-free is a later policy decision instead of a later contract.
|
|
27
|
+
*
|
|
28
|
+
* `listen` must be reachable from the click that starts it: microphone permission and, on iOS, the first
|
|
29
|
+
* `speechSynthesis` utterance are both gated on a user gesture.
|
|
30
|
+
*/
|
|
31
|
+
export interface VoiceEngine {
|
|
32
|
+
listen: (handlers: VoiceHandlers) => VoiceListener;
|
|
33
|
+
/** One sentence at a time, already stripped of markdown. The framework queues; the engine speaks. */
|
|
34
|
+
speak: (text: string) => VoiceSpeech;
|
|
35
|
+
/** Answering false hides the controls — a screen that cannot listen should not offer a microphone. */
|
|
36
|
+
available?: () => boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const fenced = /^ {0,3}(?:```|~~~)/;
|
|
40
|
+
const tableRow = /^\s*\|/;
|
|
41
|
+
const horizontalRule = /^ {0,3}(?:-{3,}|\*{3,}|_{3,})\s*$/;
|
|
42
|
+
const lineMarkers = /^\s*(?:#{1,6}\s+|>\s?|[-*+]\s+|\d{1,9}[.)]\s+)/;
|
|
43
|
+
const image = /!\[([^\]]*)\]\([^)]*\)/g;
|
|
44
|
+
const link = /\[([^\]]*)\]\([^)]*\)/g;
|
|
45
|
+
const emphasis = /(\*\*\*|\*\*|\*|___|__|_|~~)/g;
|
|
46
|
+
const inlineCode = /`([^`]*)`/g;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* What of a model's answer should be read aloud.
|
|
50
|
+
*
|
|
51
|
+
* Code blocks, tables, and rules are dropped rather than spoken: a fence read character by character is noise,
|
|
52
|
+
* and a table read cell by cell is worse than silence — in push-to-talk the user is looking at the screen that
|
|
53
|
+
* already renders both. Inline code keeps its content, because that is usually a name worth hearing.
|
|
54
|
+
*
|
|
55
|
+
* Monotonic over a growing source, which is what lets the reader below track one offset: new text never changes
|
|
56
|
+
* how earlier text was rendered, since a fence marker always arrives before the lines it swallows.
|
|
57
|
+
*/
|
|
58
|
+
export const speechText = (markdown: string): string => {
|
|
59
|
+
const lines = markdown.replace(/\r\n?/g, "\n").split("\n");
|
|
60
|
+
const kept: string[] = [];
|
|
61
|
+
let inFence = false;
|
|
62
|
+
for (const line of lines) {
|
|
63
|
+
if (fenced.test(line)) {
|
|
64
|
+
inFence = !inFence;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (inFence || tableRow.test(line) || horizontalRule.test(line)) continue;
|
|
68
|
+
const plain = line
|
|
69
|
+
.replace(lineMarkers, "")
|
|
70
|
+
.replace(image, "$1")
|
|
71
|
+
.replace(link, "$1")
|
|
72
|
+
.replace(inlineCode, "$1")
|
|
73
|
+
.replace(emphasis, "");
|
|
74
|
+
kept.push(plain);
|
|
75
|
+
}
|
|
76
|
+
return kept.join("\n");
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** A terminator counts only with whitespace behind it, so `1.5` is not a sentence and a stream is never cut early. */
|
|
80
|
+
const sentenceEnd = /[.!?。!?…]+\s|\n/g;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Reads an assistant answer aloud as it streams in, one sentence at a time.
|
|
84
|
+
*
|
|
85
|
+
* Sentence-at-a-time rather than delta-at-a-time because a delta cuts words in half, and whole-message-at-a-time
|
|
86
|
+
* because that waits for the turn to end. The offset is over `speechText` output, not the raw source, so a code
|
|
87
|
+
* block that arrives mid-answer costs nothing.
|
|
88
|
+
*/
|
|
89
|
+
export class VoiceReader {
|
|
90
|
+
readonly #engine: () => VoiceEngine | undefined;
|
|
91
|
+
#queue: string[] = [];
|
|
92
|
+
#current: VoiceSpeech | null = null;
|
|
93
|
+
#offset = 0;
|
|
94
|
+
|
|
95
|
+
constructor(engine: () => VoiceEngine | undefined) {
|
|
96
|
+
this.#engine = engine;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
get speaking() {
|
|
100
|
+
return !!this.#current;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Call with the whole answer so far, as often as it changes; already-spoken text is never repeated. */
|
|
104
|
+
feed(source: string) {
|
|
105
|
+
this.#chunk(source, false);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The turn ended: the last sentence has no whitespace behind it and would otherwise never be spoken. It still
|
|
110
|
+
* splits what is left, because a whole answer that arrived in one render must not be read as one long utterance.
|
|
111
|
+
*/
|
|
112
|
+
flush(source: string) {
|
|
113
|
+
this.#chunk(source, true);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
cancel() {
|
|
117
|
+
this.#queue = [];
|
|
118
|
+
this.#current?.cancel();
|
|
119
|
+
this.#current = null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
reset() {
|
|
123
|
+
this.cancel();
|
|
124
|
+
this.#offset = 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
#chunk(source: string, final: boolean) {
|
|
128
|
+
const text = speechText(source);
|
|
129
|
+
|
|
130
|
+
if (text.length < this.#offset) this.reset();
|
|
131
|
+
const pending = text.slice(this.#offset);
|
|
132
|
+
sentenceEnd.lastIndex = 0;
|
|
133
|
+
let cut = 0;
|
|
134
|
+
for (let match = sentenceEnd.exec(pending); match; match = sentenceEnd.exec(pending)) {
|
|
135
|
+
const end = match.index + match[0].length;
|
|
136
|
+
this.#enqueue(pending.slice(cut, end));
|
|
137
|
+
cut = end;
|
|
138
|
+
}
|
|
139
|
+
if (final) {
|
|
140
|
+
this.#enqueue(pending.slice(cut));
|
|
141
|
+
cut = pending.length;
|
|
142
|
+
}
|
|
143
|
+
this.#offset += cut;
|
|
144
|
+
this.#pump();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
#enqueue(chunk: string) {
|
|
148
|
+
const text = chunk.trim();
|
|
149
|
+
if (text) this.#queue.push(text);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#pump() {
|
|
153
|
+
if (this.#current || !this.#queue.length) return;
|
|
154
|
+
const engine = this.#engine();
|
|
155
|
+
if (!engine) {
|
|
156
|
+
this.#queue = [];
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const next = this.#queue.shift() as string;
|
|
160
|
+
const speech = engine.speak(next);
|
|
161
|
+
this.#current = speech;
|
|
162
|
+
void speech.done.then(() => {
|
|
163
|
+
if (this.#current !== speech) return;
|
|
164
|
+
this.#current = null;
|
|
165
|
+
this.#pump();
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
package/ui/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { Agent } from "./Agent";
|
|
2
2
|
export { type AttachReader, maxAttachmentBytes } from "./Agent/attachment";
|
|
3
|
+
export type { VoiceEngine, VoiceHandlers, VoiceListener, VoiceSpeech } from "./Agent/voice";
|
|
3
4
|
export { agentAttrs } from "./agentAttrs";
|
|
4
5
|
export { animated } from "./animated";
|
|
5
6
|
export { Badge } from "./Badge";
|