@h-ai/ai 0.1.0-alpha9 → 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +647 -0
- package/README.ja.md +647 -0
- package/README.md +477 -1693
- package/dist/ai-audio-types-4R5yUAVl.d.ts +614 -0
- package/dist/ai-audio-ws-protocol-nfhO0tOo.d.ts +7605 -0
- package/dist/browser.d.ts +8 -5
- package/dist/browser.js +3 -2
- package/dist/chunk-GMQFIWND.js +254 -0
- package/dist/chunk-GMQFIWND.js.map +1 -0
- package/dist/chunk-HEVXR252.js +965 -0
- package/dist/chunk-HEVXR252.js.map +1 -0
- package/dist/chunk-HYMP57AC.js +118 -0
- package/dist/chunk-HYMP57AC.js.map +1 -0
- package/dist/client/index.d.ts +63 -231
- package/dist/client/index.js +2 -1
- package/dist/index.d.ts +48 -15
- package/dist/index.js +6715 -1701
- package/dist/index.js.map +1 -1
- package/package.json +43 -21
- package/dist/ai-reasoning-types-CGUgkLF7.d.ts +0 -2823
- package/dist/ai-types-CRCjluXI.d.ts +0 -1095
- package/dist/api/index.d.ts +0 -628
- package/dist/api/index.js +0 -243
- package/dist/api/index.js.map +0 -1
- package/dist/chunk-6PPXWQ7I.js +0 -462
- package/dist/chunk-6PPXWQ7I.js.map +0 -1
- package/dist/chunk-ABP7RKIP.js +0 -311
- package/dist/chunk-ABP7RKIP.js.map +0 -1
package/dist/browser.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
export { A as
|
|
2
|
-
export { A as
|
|
3
|
-
export {
|
|
4
|
-
|
|
1
|
+
export { A as A2AAgentCardConfig, a as A2AApiKeySecurity, b as A2AAuthenticator, c as A2ACallOptions, d as A2ACallResult, e as A2ACallerIdentity, f as A2AConfig, g as A2AConfigSchema, h as A2AContextInfo, i as A2AHandleResult, j as A2AMessageRecord, k as A2AOperations, l as A2ASecurityConfig, m as A2ASkillConfigSchema, n as A2ATaskFilter, o as AIConfig, p as AIConfigInput, q as AIConfigSchema, r as AIFunctions, s as AIInitOptions, t as AIRelStore, u as AIRelStoreCompareAndSwap, v as AIRelStoreOptions, w as AIStoreProvider, x as AIVectorBackend, y as AIVectorStore, z as AIVersionedRecord, B as AUDIO_WS_PATH, C as ApiType, D as ApiTypeSchema, E as AskOptions, F as AssistantMessage, G as AudioConfig, H as AudioConfigSchema, I as AudioFormatSchema, J as AudioModelEntry, K as AudioModelEntrySchema, L as AudioOperation, M as AudioOperationSchema, N as AudioOperationsSchema, O as AudioProviderName, P as AudioProviderSchema, Q as AudioWsClientMessage, R as AudioWsClientMessageSchema, S as AudioWsDoneMessage, T as AudioWsDoneMessageSchema, U as AudioWsEndMessage, V as AudioWsErrorMessage, W as AudioWsSegmentDoneMessage, X as AudioWsSegmentStartedMessage, Y as AudioWsServerMessage, Z as AudioWsSpeechMessage, _ as AudioWsStartMessage, $ as AudioWsStartMessageSchema, a0 as AudioWsTextMessage, a1 as AudioWsTextMessageSchema, a2 as AudioWsTranscriptMessage, a3 as ChatCompletionChoice, a4 as ChatCompletionChunk, a5 as ChatCompletionDelta, a6 as ChatCompletionRequest, a7 as ChatCompletionResponse, a8 as ChatHistoryOptions, a9 as ChatMessage, aa as ChatRecord, ab as Citation, ac as CommitTurnInput, ad as CompressConfig, ae as CompressConfigSchema, af as CompressOperations, ag as CompressOptions, ah as CompressResult, ai as CompressionStrategy, aj as CompressionStrategySchema, ak as ConsolidateOptions, al as ConsolidateResult, am as ContextChatOptions, an as ContextChatResult, ao as ContextManager, ap as ContextManagerOptions, aq as ContextOperations, ar as ContextResetOptions, as as ContextStreamEvent, at as ConversationTurn, au as ConversationTurnStatus, av as DefineToolOptions, aw as DeveloperMessage, ax as EmbeddingConfig, ay as EmbeddingConfigSchema, az as EmbeddingItem, aA as EmbeddingOperations, aB as EmbeddingRequest, aC as EmbeddingResponse, aD as EntityDocumentRelation, aE as EntityDocumentResult, aF as EntityListOptions, aG as EntityQueryOptions, aH as EntityType, aI as EntityTypeSchema, aJ as FileConfig, aK as FileConfigSchema, aL as FileOperations, aM as FileParseMethod, aN as FileParseOptions, aO as FileParseRequest, aP as FileParseResult, aQ as GenerateImageRequest, aR as GenerateImageResult, aS as GenerateObjectRequest, aT as GeneratedImage, aU as HaiAIError, aV as ImageConfig, aW as ImageConfigSchema, aX as ImageContent, aY as ImageModelEntry, aZ as ImageModelEntrySchema, a_ as ImageOperations, a$ as ImageProviderName, b0 as ImageProviderSchema, b1 as ImageSize, b2 as InteractionScope, b3 as KnowledgeAskOptions, b4 as KnowledgeAskResult, b5 as KnowledgeConfig, b6 as KnowledgeConfigSchema, b7 as KnowledgeDocumentInfo, b8 as KnowledgeDocumentListOptions, b9 as KnowledgeDocumentMetadata, ba as KnowledgeDocumentRemoveOptions, bb as KnowledgeDocumentRevision, bc as KnowledgeEntity, bd as KnowledgeIngestBatchProgress, be as KnowledgeIngestBatchResult, bf as KnowledgeIngestFileInput, bg as KnowledgeIngestInput, bh as KnowledgeIngestResult, bi as KnowledgeOperations, bj as KnowledgeRepairOptions, bk as KnowledgeRepairProgress, bl as KnowledgeRepairResult, bm as KnowledgeRetrieveItem, bn as KnowledgeRetrieveOptions, bo as KnowledgeRetrieveResult, bp as KnowledgeSetupOptions, bq as KnowledgeStore, br as LLMConfig, bs as LLMConfigSchema, bt as LLMOperations, bu as MCPConfig, bv as MCPConfigSchema, bw as MCPContext, bx as MCPOperations, by as MCPPrompt, bz as MCPPromptArgument, bA as MCPPromptContent, bB as MCPPromptMessage, bC as MCPResource, bD as MCPResourceContent, bE as MCPServerCapabilities, bF as MCPServerCapabilitiesSchema, bG as MCPServerConfig, bH as MCPServerConfigSchema, bI as MCPToolDefinition, bJ as MCPToolHandler, bK as McpServerOptions, bL as MemoryAccessScope, bM as MemoryAdminOperations, bN as MemoryClearAllOptions, bO as MemoryClearOptions, bP as MemoryConfig, bQ as MemoryConfigSchema, bR as MemoryCoreOperations, bS as MemoryEntry, bT as MemoryEntryInput, bU as MemoryExtractOptions, bV as MemoryInjectionOptions, bW as MemoryListOptions, bX as MemoryListPageOptions, bY as MemoryOperations, bZ as MemoryRecallOptions, b_ as MemoryType, b$ as MemoryTypeSchema, c0 as MemoryUpdateInput, c1 as MessageContent, c2 as MessageRole, c3 as ModelEntry, c4 as ModelEntrySchema, c5 as ModelScenario, c6 as ModelScenarioSchema, c7 as ObjectRef, c8 as OptionalSecretSchema, c9 as OutputFormat, ca as PersonaOperations, cb as PersonaProfile, cc as PersonaProfileInput, cd as PersonaProfileUpdate, ce as PersonaScopeOptions, cf as RagContextItem, cg as RagOperations, ch as RagOptions, ci as RagResult, cj as RagStreamEvent, ck as ReasoningOperations, cl as ReasoningOptions, cm as ReasoningResult, cn as ReasoningStep, co as ReasoningStepType, cp as ReasoningStrategy, cq as ReasoningStreamEvent, cr as ReferenceImage, cs as RerankDocument, ct as RerankItem, cu as RerankOperations, cv as RerankRequest, cw as RerankResponse, cx as ResolveRequiredModelEntryOptions, cy as ResolvedAudioModel, cz as ResolvedImageModel, cA as ResolvedModelConfig, cB as RetrievalConfig, cC as RetrievalConfigSchema, cD as RetrievalOperations, cE as RetrievalRequest, cF as RetrievalResult, cG as RetrievalResultItem, cH as RetrievalSource, cI as RetrievalSourceConfig, cJ as RetrievalSourceSchema, cK as SSEDecoder, cL as SSEEvent, cM as ScopedMemoryBinding, cN as ScopedMemoryOperations, cO as SessionInfo, cP as StoreFilter, cQ as StorePage, cR as StoreScope, cS as StreamOperations, cT as StreamProcessor, cU as StreamResult, cV as SummaryConfig, cW as SummaryConfigSchema, cX as SummaryOperations, cY as SummaryOptions, cZ as SummaryResult, c_ as SystemMessage, c$ as TempModelConfig, d0 as TextContent, d1 as TokenConfig, d2 as TokenConfigSchema, d3 as TokenOperations, d4 as TokenUsage, d5 as Tool, d6 as ToolAuthorizationRequest, d7 as ToolAuthorizer, d8 as ToolCall, d9 as ToolDefinition, da as ToolErrorType, db as ToolExecutionContext, dc as ToolExecutionOptions, dd as ToolMessage, de as ToolRegistryOperations, df as ToolRegistryOptions, dg as ToolsOperations, dh as UserMessage, di as WhereClause, dj as WhereOperator, dk as WhereValue, dl as ensureAudioCredential, dm as resolveAudioModel, dn as resolveImageModel, dp as resolveModelApi, dq as resolveModelEntry, dr as serializePlayableAudio } from './ai-audio-ws-protocol-nfhO0tOo.js';
|
|
2
|
+
export { A as AudioCapabilitiesRequest, a as AudioContent, b as AudioFormat, c as AudioInputStream, d as AudioModelCapabilities, e as AudioOperations, f as AudioReference, P as PlayableAudio, S as SynthesisEvent, g as SynthesisOptions, h as SynthesisRequest, i as SynthesisResult, j as SynthesisStreamRequest, k as SynthesisTextSegment, T as TranscriptionEvent, l as TranscriptionOptions, m as TranscriptionRequest, n as TranscriptionResult, o as TranscriptionSegment, p as TranscriptionStreamRequest, q as TranscriptionTimestampGranularity, r as TranscriptionWord } from './ai-audio-types-4R5yUAVl.js';
|
|
3
|
+
export { AudioClientConfig, AudioClientOperations, AudioTicketRequest, createAudioClient } from './client/index.js';
|
|
4
|
+
export { AgentCapabilities, AgentCard, AgentProvider, AgentSkill, Artifact, DataPart, FilePart, FileWithBytes, FileWithUri, Message, MessageSendParams, Part, PushNotificationConfig, Task, TaskArtifactUpdateEvent, TaskIdParams, TaskQueryParams, TaskState, TaskStatus, TaskStatusUpdateEvent, TextPart } from '@a2a-js/sdk';
|
|
5
|
+
export { AgentExecutor, ExecutionEventBus } from '@a2a-js/sdk/server';
|
|
5
6
|
import '@h-ai/core';
|
|
6
|
-
import '@h-ai/datapipe';
|
|
7
7
|
import 'zod';
|
|
8
8
|
import 'openai';
|
|
9
9
|
import 'node:buffer';
|
|
10
|
+
import '@h-ai/datapipe';
|
|
11
|
+
import '@modelcontextprotocol/sdk/types.js';
|
|
12
|
+
import 'zod/v4/core';
|
package/dist/browser.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { A2AConfigSchema, A2ASkillConfigSchema, AIConfigSchema, CompressConfigSchema, CompressionStrategySchema, EmbeddingConfigSchema, EntityTypeSchema, FileConfigSchema,
|
|
2
|
-
export {
|
|
1
|
+
export { A2AConfigSchema, A2ASkillConfigSchema, AIConfigSchema, AUDIO_WS_PATH, ApiTypeSchema, AudioConfigSchema, AudioFormatSchema, AudioModelEntrySchema, AudioOperationSchema, AudioOperationsSchema, AudioProviderSchema, AudioWsClientMessageSchema, AudioWsDoneMessageSchema, AudioWsStartMessageSchema, AudioWsTextMessageSchema, CompressConfigSchema, CompressionStrategySchema, EmbeddingConfigSchema, EntityTypeSchema, FileConfigSchema, ImageConfigSchema, ImageModelEntrySchema, ImageProviderSchema, KnowledgeConfigSchema, LLMConfigSchema, MCPConfigSchema, MCPServerCapabilitiesSchema, MCPServerConfigSchema, MemoryConfigSchema, MemoryTypeSchema, ModelEntrySchema, ModelScenarioSchema, OptionalSecretSchema, RetrievalConfigSchema, RetrievalSourceSchema, SummaryConfigSchema, TokenConfigSchema, ensureAudioCredential, resolveAudioModel, resolveImageModel, resolveModelApi, resolveModelEntry, serializePlayableAudio } from './chunk-HEVXR252.js';
|
|
2
|
+
export { createAudioClient } from './chunk-GMQFIWND.js';
|
|
3
|
+
export { HaiAIError } from './chunk-HYMP57AC.js';
|
|
3
4
|
//# sourceMappingURL=browser.js.map
|
|
4
5
|
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { HaiAIError } from './chunk-HYMP57AC.js';
|
|
2
|
+
|
|
3
|
+
// src/client/ai-audio-client.ts
|
|
4
|
+
function audioClientError(code, message) {
|
|
5
|
+
const error = new Error(message);
|
|
6
|
+
error.code = code;
|
|
7
|
+
return error;
|
|
8
|
+
}
|
|
9
|
+
function openBrowserWs(url, signal) {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const ws = new WebSocket(url);
|
|
12
|
+
ws.binaryType = "arraybuffer";
|
|
13
|
+
const queue = [];
|
|
14
|
+
let waiting = null;
|
|
15
|
+
let closed = false;
|
|
16
|
+
let opened = false;
|
|
17
|
+
let aborted = false;
|
|
18
|
+
let failure = null;
|
|
19
|
+
function settleClosed() {
|
|
20
|
+
if (closed)
|
|
21
|
+
return;
|
|
22
|
+
closed = true;
|
|
23
|
+
signal?.removeEventListener("abort", onAbort);
|
|
24
|
+
if (waiting) {
|
|
25
|
+
const w = waiting;
|
|
26
|
+
waiting = null;
|
|
27
|
+
w({ value: void 0, done: true });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function onAbort() {
|
|
31
|
+
aborted = true;
|
|
32
|
+
if (!opened) {
|
|
33
|
+
settleClosed();
|
|
34
|
+
reject(audioClientError(HaiAIError.AUDIO_CANCELLED.code, "Audio operation cancelled"));
|
|
35
|
+
}
|
|
36
|
+
ws.close();
|
|
37
|
+
}
|
|
38
|
+
function terminalError() {
|
|
39
|
+
if (aborted)
|
|
40
|
+
return audioClientError(HaiAIError.AUDIO_CANCELLED.code, "Audio operation cancelled");
|
|
41
|
+
if (failure)
|
|
42
|
+
return failure;
|
|
43
|
+
return audioClientError(HaiAIError.AUDIO_CONNECTION_FAILED.code, "Audio WebSocket closed before completion");
|
|
44
|
+
}
|
|
45
|
+
function push(message) {
|
|
46
|
+
if (waiting) {
|
|
47
|
+
const w = waiting;
|
|
48
|
+
waiting = null;
|
|
49
|
+
w({ value: message, done: false });
|
|
50
|
+
} else {
|
|
51
|
+
queue.push(message);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
ws.onmessage = (event) => {
|
|
55
|
+
if (typeof event.data === "string")
|
|
56
|
+
push({ isBinary: false, text: event.data });
|
|
57
|
+
else
|
|
58
|
+
push({ isBinary: true, binary: new Uint8Array(event.data) });
|
|
59
|
+
};
|
|
60
|
+
ws.onerror = () => {
|
|
61
|
+
failure = audioClientError(HaiAIError.AUDIO_CONNECTION_FAILED.code, "Audio WebSocket connection failed");
|
|
62
|
+
if (!opened) {
|
|
63
|
+
settleClosed();
|
|
64
|
+
reject(failure);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
ws.onclose = () => settleClosed();
|
|
68
|
+
if (signal) {
|
|
69
|
+
if (signal.aborted)
|
|
70
|
+
onAbort();
|
|
71
|
+
else
|
|
72
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
73
|
+
}
|
|
74
|
+
ws.onopen = () => {
|
|
75
|
+
opened = true;
|
|
76
|
+
resolve({
|
|
77
|
+
// DOM WebSocket.send 的 BufferSource 泛型限定为 ArrayBuffer,Uint8Array 需显式收窄
|
|
78
|
+
send: (data) => ws.send(data),
|
|
79
|
+
async *messages() {
|
|
80
|
+
while (true) {
|
|
81
|
+
if (queue.length > 0) {
|
|
82
|
+
yield queue.shift();
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (closed)
|
|
86
|
+
throw terminalError();
|
|
87
|
+
const next = await new Promise((res) => {
|
|
88
|
+
waiting = res;
|
|
89
|
+
});
|
|
90
|
+
if (next.done)
|
|
91
|
+
throw terminalError();
|
|
92
|
+
yield next.value;
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
close: () => ws.close()
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function createAudioClient(config) {
|
|
101
|
+
async function buildUrl(request) {
|
|
102
|
+
const ticket = await config.getTicket(request);
|
|
103
|
+
if (!ticket)
|
|
104
|
+
throw new Error("Audio WebSocket ticket is required");
|
|
105
|
+
const separator = config.url.includes("?") ? "&" : "?";
|
|
106
|
+
return `${config.url}${separator}ticket=${encodeURIComponent(ticket)}`;
|
|
107
|
+
}
|
|
108
|
+
function startMessage(operation, extra) {
|
|
109
|
+
return JSON.stringify({ type: "start", operation, ...extra });
|
|
110
|
+
}
|
|
111
|
+
async function* transcribeStream(request) {
|
|
112
|
+
const conn = await openBrowserWs(await buildUrl({ operation: "transcribe", model: request.model }), request.signal);
|
|
113
|
+
try {
|
|
114
|
+
const format = request.audio.format;
|
|
115
|
+
const sampleRate = request.audio.sampleRate;
|
|
116
|
+
conn.send(startMessage("transcribe", { stream: true, model: request.model, language: request.language, contextHints: request.contextHints, format, sampleRate, channels: request.audio.channels }));
|
|
117
|
+
const sendAudio = (async () => {
|
|
118
|
+
if ("chunks" in request.audio) {
|
|
119
|
+
for await (const part of request.audio.chunks)
|
|
120
|
+
conn.send(part);
|
|
121
|
+
} else {
|
|
122
|
+
conn.send(request.audio.data);
|
|
123
|
+
}
|
|
124
|
+
conn.send(JSON.stringify({ type: "done" }));
|
|
125
|
+
})();
|
|
126
|
+
let sendError;
|
|
127
|
+
sendAudio.catch((e) => {
|
|
128
|
+
sendError = e;
|
|
129
|
+
});
|
|
130
|
+
for await (const message of conn.messages()) {
|
|
131
|
+
if (message.isBinary || !message.text)
|
|
132
|
+
continue;
|
|
133
|
+
const event = JSON.parse(message.text);
|
|
134
|
+
if (event.type === "transcript")
|
|
135
|
+
yield { type: "transcript", text: event.text, final: event.final };
|
|
136
|
+
else if (event.type === "speech_started")
|
|
137
|
+
yield { type: "speech_started" };
|
|
138
|
+
else if (event.type === "speech_stopped")
|
|
139
|
+
yield { type: "speech_stopped" };
|
|
140
|
+
else if (event.type === "error")
|
|
141
|
+
throw audioClientError(event.code, event.message);
|
|
142
|
+
else if (event.type === "end")
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
if (sendError)
|
|
146
|
+
throw sendError;
|
|
147
|
+
} finally {
|
|
148
|
+
conn.close();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function transcribe(request) {
|
|
152
|
+
const conn = await openBrowserWs(await buildUrl({ operation: "transcribe", model: request.model }), request.signal);
|
|
153
|
+
try {
|
|
154
|
+
conn.send(startMessage("transcribe", { stream: false, model: request.model, language: request.language, contextHints: request.contextHints, format: request.audio.format, sampleRate: request.audio.sampleRate, channels: request.audio.channels }));
|
|
155
|
+
conn.send(request.audio.data);
|
|
156
|
+
conn.send(JSON.stringify({ type: "done" }));
|
|
157
|
+
let text = "";
|
|
158
|
+
for await (const message of conn.messages()) {
|
|
159
|
+
if (message.isBinary || !message.text)
|
|
160
|
+
continue;
|
|
161
|
+
const event = JSON.parse(message.text);
|
|
162
|
+
if (event.type === "transcript")
|
|
163
|
+
text = event.text;
|
|
164
|
+
else if (event.type === "error")
|
|
165
|
+
throw audioClientError(event.code, event.message);
|
|
166
|
+
else if (event.type === "end")
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
return { text };
|
|
170
|
+
} finally {
|
|
171
|
+
conn.close();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
async function* synthesizeStream(request) {
|
|
175
|
+
const conn = await openBrowserWs(await buildUrl({ operation: "synthesize", model: request.model }), request.signal);
|
|
176
|
+
try {
|
|
177
|
+
conn.send(startMessage("synthesize", { model: request.model, voice: request.voice, instruction: request.instruction, format: request.format, sampleRate: request.sampleRate }));
|
|
178
|
+
const sendText = (async () => {
|
|
179
|
+
const segments = isSynthesisTextSegment(request.text) ? singleSegment(request.text) : request.text;
|
|
180
|
+
for await (const segment of segments)
|
|
181
|
+
conn.send(JSON.stringify({ type: "text", segmentId: segment.id, text: segment.text }));
|
|
182
|
+
conn.send(JSON.stringify({ type: "done" }));
|
|
183
|
+
})();
|
|
184
|
+
let sendError;
|
|
185
|
+
sendText.catch((e) => {
|
|
186
|
+
sendError = e;
|
|
187
|
+
});
|
|
188
|
+
let activeSegmentId;
|
|
189
|
+
for await (const message of conn.messages()) {
|
|
190
|
+
if (message.isBinary && message.binary) {
|
|
191
|
+
if (!activeSegmentId)
|
|
192
|
+
throw new Error("Audio frame received outside a synthesis segment");
|
|
193
|
+
yield { type: "audio", segmentId: activeSegmentId, data: message.binary };
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (!message.text)
|
|
197
|
+
continue;
|
|
198
|
+
const event = JSON.parse(message.text);
|
|
199
|
+
if (event.type === "segment_started") {
|
|
200
|
+
if (activeSegmentId)
|
|
201
|
+
throw new Error("Synthesis segment started before the previous segment completed");
|
|
202
|
+
activeSegmentId = event.segmentId;
|
|
203
|
+
yield event;
|
|
204
|
+
} else if (event.type === "segment_done") {
|
|
205
|
+
if (activeSegmentId !== event.segmentId)
|
|
206
|
+
throw new Error("Synthesis segment completion does not match the active segment");
|
|
207
|
+
yield event;
|
|
208
|
+
activeSegmentId = void 0;
|
|
209
|
+
} else if (event.type === "error") {
|
|
210
|
+
throw audioClientError(event.code, event.message);
|
|
211
|
+
} else if (event.type === "end") {
|
|
212
|
+
if (activeSegmentId)
|
|
213
|
+
throw audioClientError(HaiAIError.AUDIO_PROTOCOL_ERROR.code, "Synthesis ended before the active segment completed");
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (sendError)
|
|
218
|
+
throw sendError;
|
|
219
|
+
} finally {
|
|
220
|
+
conn.close();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function synthesize(request) {
|
|
224
|
+
const chunks = [];
|
|
225
|
+
let meta;
|
|
226
|
+
for await (const event of synthesizeStream({ ...request, text: { id: "synthesis", text: request.text } })) {
|
|
227
|
+
if (event.type === "segment_started")
|
|
228
|
+
meta = { format: event.format, sampleRate: event.sampleRate, channels: event.channels };
|
|
229
|
+
else if (event.type === "audio")
|
|
230
|
+
chunks.push(event.data);
|
|
231
|
+
}
|
|
232
|
+
if (!meta)
|
|
233
|
+
throw audioClientError(HaiAIError.AUDIO_PROTOCOL_ERROR.code, "Synthesis completed without segment metadata");
|
|
234
|
+
const total = chunks.reduce((sum, c) => sum + c.length, 0);
|
|
235
|
+
const data = new Uint8Array(total);
|
|
236
|
+
let offset = 0;
|
|
237
|
+
for (const chunk of chunks) {
|
|
238
|
+
data.set(chunk, offset);
|
|
239
|
+
offset += chunk.length;
|
|
240
|
+
}
|
|
241
|
+
return { data, format: meta.format, sampleRate: meta.sampleRate, channels: meta.channels ?? 1 };
|
|
242
|
+
}
|
|
243
|
+
return { transcribe, transcribeStream, synthesize, synthesizeStream };
|
|
244
|
+
}
|
|
245
|
+
function isSynthesisTextSegment(value) {
|
|
246
|
+
return typeof value === "object" && value !== null && "id" in value && "text" in value;
|
|
247
|
+
}
|
|
248
|
+
async function* singleSegment(segment) {
|
|
249
|
+
yield segment;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export { createAudioClient };
|
|
253
|
+
//# sourceMappingURL=chunk-GMQFIWND.js.map
|
|
254
|
+
//# sourceMappingURL=chunk-GMQFIWND.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client/ai-audio-client.ts"],"names":[],"mappings":"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","file":"chunk-GMQFIWND.js","sourcesContent":["/**\n * @h-ai/ai/client — 浏览器语音客户端\n *\n * 通过统一 WebSocket 连接 `@h-ai/serv` 的语音入口,向浏览器暴露与 Node 端一致的\n * `ai.audio.*` 业务 API。WebSocket 建立、消息编解码等传输细节在此内部完成,\n * 调用方不接触任何厂商原生事件。\n * @module client/ai-audio-client\n */\n\nimport type {\n SynthesisEvent,\n SynthesisRequest,\n SynthesisResult,\n SynthesisStreamRequest,\n SynthesisTextSegment,\n TranscriptionEvent,\n TranscriptionRequest,\n TranscriptionResult,\n TranscriptionStreamRequest,\n} from '../audio/ai-audio-types.js'\nimport type { AudioWsServerMessage, AudioWsStartMessage } from '../audio/ai-audio-ws-protocol.js'\nimport { HaiAIError } from '../ai-types.js'\n\n/**\n * 构造带领域错误码的浏览器客户端错误\n *\n * 浏览器 Client 约定出错抛异常;此处在 `Error.code` 上保留领域错误码\n * (取消 / 连接失败 / 服务端领域错误),供调用方区分正常结束、取消与异常断连。\n */\nfunction audioClientError(code: string | number, message: string): Error {\n const error = new Error(message)\n ;(error as { code?: string | number }).code = code\n return error\n}\n\n// ─── 客户端配置 ───\n\n/**\n * getTicket 收到的当前 Audio 请求摘要\n *\n * 供应用签发与本次操作严格绑定的能力票据(如 `iam.ticket.issue({ grant })`),\n * 使票据只能用于本次操作 / 模型 / 会话,降低票据被重放的风险。\n *\n * @example\n * ```ts\n * import type { AudioTicketRequest } from '@h-ai/ai/client'\n * function grant(request: AudioTicketRequest) {\n * return { operation: request.operation, model: request.model } // 经已登录 HTTP 接口签发并在服务端原子消费。\n * }\n * ```\n */\nexport interface AudioTicketRequest {\n /** 本次操作类型 */\n operation: 'transcribe' | 'synthesize'\n /** 本次使用的模型 ID(如有指定) */\n model?: string\n}\n\n/**\n * 浏览器语音客户端配置\n *\n * @example\n * ```ts\n * import { createAudioClient } from '@h-ai/ai/client'\n * import type { AudioTicketRequest } from '@h-ai/ai/client'\n * function connect(issueTicket: (request: AudioTicketRequest) => Promise<string>) {\n * return createAudioClient({ url: 'wss://example.com/api/v1/ai/audio', getTicket: issueTicket })\n * }\n * ```\n */\nexport interface AudioClientConfig {\n /** 语音 WebSocket 完整 URL(如 `wss://host/api/v1/ai/audio`) */\n url: string\n /**\n * 获取短期、一次性的 Audio WebSocket ticket\n *\n * ticket 应由已登录 HTTP 请求签发,并在服务端首次校验时原子消费。\n * 收到当前请求摘要(operation / model),可签发与本次操作严格绑定的票据。\n */\n getTicket: (request: AudioTicketRequest) => string | Promise<string>\n}\n\n/**\n * 浏览器语音操作接口\n *\n * 与 Node 端 `AudioOperations` 业务形态一致;出错时抛出异常(浏览器 Client 约定)。\n *\n * @example\n * ```ts\n * import type { AudioClientOperations } from '@h-ai/ai/client'\n * async function speak(audio: AudioClientOperations) {\n * // 浏览器 AudioClient 普通方法也显式抛错,由应用交互边界处理。\n * const result = await audio.synthesize({ text: '你好', format: 'mp3' })\n * return result.data\n * }\n * ```\n */\nexport interface AudioClientOperations {\n /** 将完整音频识别为完整文本 */\n transcribe: (request: TranscriptionRequest) => Promise<TranscriptionResult>\n /** 持续输入音频或增量返回识别文本(含语音起止事件) */\n transcribeStream: (request: TranscriptionStreamRequest) => AsyncIterable<TranscriptionEvent>\n /** 将完整文本合成为完整音频 */\n synthesize: (request: SynthesisRequest) => Promise<SynthesisResult>\n /** 持续输入文本段并按段输出结构化音频事件 */\n synthesizeStream: (request: SynthesisStreamRequest) => AsyncIterable<SynthesisEvent>\n}\n\n// ─── 浏览器 WebSocket 辅助 ───\n\n/**\n * WebSocket 收到的消息\n *\n * @example\n * ```ts\n * // 模块内部上下文:示例复用当前声明所在模块的实际绑定。\n * const frame: BrowserWsMessage = { isBinary: true, binary: new Uint8Array([0, 0]) }\n * const bytes = frame.isBinary ? frame.binary : undefined\n * ```\n */\ninterface BrowserWsMessage {\n text?: string\n binary?: Uint8Array\n isBinary: boolean\n}\n\n/**\n * 浏览器 WebSocket 连接封装\n *\n * @example\n * ```ts\n * // 模块内部上下文:示例复用当前声明所在模块的实际绑定。\n * async function consume(connection: BrowserWsConnection) {\n * try {\n * connection.send(JSON.stringify({ type: 'done' }))\n * for await (const frame of connection.messages()) {\n * if (!frame.isBinary && frame.text) return JSON.parse(frame.text)\n * }\n * return undefined\n * }\n * finally { connection.close() }\n * }\n * ```\n */\ninterface BrowserWsConnection {\n send: (data: string | Uint8Array) => void\n messages: () => AsyncIterableIterator<BrowserWsMessage>\n close: () => void\n}\n\n/** 建立浏览器 WebSocket 连接(内部消息队列 → 异步迭代) */\nfunction openBrowserWs(url: string, signal?: AbortSignal): Promise<BrowserWsConnection> {\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(url)\n ws.binaryType = 'arraybuffer'\n\n const queue: BrowserWsMessage[] = []\n let waiting: ((result: IteratorResult<BrowserWsMessage>) => void) | null = null\n let closed = false\n let opened = false\n let aborted = false\n let failure: Error | null = null\n\n function settleClosed(): void {\n if (closed)\n return\n closed = true\n signal?.removeEventListener('abort', onAbort)\n if (waiting) {\n const w = waiting\n waiting = null\n w({ value: undefined, done: true })\n }\n }\n\n function onAbort(): void {\n aborted = true\n if (!opened) {\n // 连接建立前取消:直接以取消错误拒绝建连\n settleClosed()\n reject(audioClientError(HaiAIError.AUDIO_CANCELLED.code, 'Audio operation cancelled'))\n }\n // 连接建立后取消:关闭连接,由消息迭代器在终止时抛出 AUDIO_CANCELLED\n ws.close()\n }\n\n /**\n * socket 关闭且未按协议正常收到 `end` 时的终止错误。\n *\n * 消费方在收到服务端 `end` 时会主动 break,因而正常结束不会走到此分支;\n * 走到此分支即意味着连接在完成前关闭,须区分取消 / 网络异常抛出对应领域错误。\n */\n function terminalError(): Error {\n if (aborted)\n return audioClientError(HaiAIError.AUDIO_CANCELLED.code, 'Audio operation cancelled')\n if (failure)\n return failure\n return audioClientError(HaiAIError.AUDIO_CONNECTION_FAILED.code, 'Audio WebSocket closed before completion')\n }\n\n function push(message: BrowserWsMessage): void {\n if (waiting) {\n const w = waiting\n waiting = null\n w({ value: message, done: false })\n }\n else {\n queue.push(message)\n }\n }\n\n ws.onmessage = (event: MessageEvent): void => {\n if (typeof event.data === 'string')\n push({ isBinary: false, text: event.data })\n else\n push({ isBinary: true, binary: new Uint8Array(event.data as ArrayBuffer) })\n }\n // 连接建立前后的错误都记录:建立前直接拒绝,建立后由迭代器在关闭时抛出 AUDIO_CONNECTION_FAILED\n ws.onerror = (): void => {\n failure = audioClientError(HaiAIError.AUDIO_CONNECTION_FAILED.code, 'Audio WebSocket connection failed')\n if (!opened) {\n settleClosed()\n reject(failure)\n }\n }\n ws.onclose = (): void => settleClosed()\n\n if (signal) {\n if (signal.aborted)\n onAbort()\n else\n signal.addEventListener('abort', onAbort, { once: true })\n }\n\n ws.onopen = (): void => {\n opened = true\n resolve({\n // DOM WebSocket.send 的 BufferSource 泛型限定为 ArrayBuffer,Uint8Array 需显式收窄\n send: data => ws.send(data as string | Uint8Array<ArrayBuffer>),\n async* messages() {\n while (true) {\n if (queue.length > 0) {\n yield queue.shift() as BrowserWsMessage\n continue\n }\n if (closed)\n throw terminalError()\n const next = await new Promise<IteratorResult<BrowserWsMessage>>((res) => {\n waiting = res\n })\n if (next.done)\n throw terminalError()\n yield next.value\n }\n },\n close: () => ws.close(),\n })\n }\n })\n}\n\n// ─── 客户端实现 ───\n\n/**\n * 创建浏览器语音客户端\n *\n * @param config - 语音客户端配置\n * @returns 浏览器语音操作接口\n *\n * @example\n * ```ts\n * import { createAudioClient } from '@h-ai/ai/client'\n * import type { AudioTicketRequest } from '@h-ai/ai/client'\n * function connect(issueTicket: (request: AudioTicketRequest) => Promise<string>) {\n * return createAudioClient({ url: 'wss://example.com/api/v1/ai/audio', getTicket: issueTicket })\n * }\n * ```\n */\nexport function createAudioClient(config: AudioClientConfig): AudioClientOperations {\n async function buildUrl(request: AudioTicketRequest): Promise<string> {\n const ticket = await config.getTicket(request)\n if (!ticket)\n throw new Error('Audio WebSocket ticket is required')\n const separator = config.url.includes('?') ? '&' : '?'\n return `${config.url}${separator}ticket=${encodeURIComponent(ticket)}`\n }\n\n function startMessage(operation: 'transcribe' | 'synthesize', extra: Partial<AudioWsStartMessage>): string {\n return JSON.stringify({ type: 'start', operation, ...extra } satisfies AudioWsStartMessage)\n }\n\n async function* transcribeStream(request: TranscriptionStreamRequest): AsyncIterable<TranscriptionEvent> {\n const conn = await openBrowserWs(await buildUrl({ operation: 'transcribe', model: request.model }), request.signal)\n try {\n const format = request.audio.format\n const sampleRate = request.audio.sampleRate\n conn.send(startMessage('transcribe', { stream: true, model: request.model, language: request.language, contextHints: request.contextHints, format, sampleRate, channels: request.audio.channels }))\n\n const sendAudio = (async () => {\n if ('chunks' in request.audio) {\n for await (const part of request.audio.chunks)\n conn.send(part)\n }\n else {\n conn.send(request.audio.data)\n }\n conn.send(JSON.stringify({ type: 'done' }))\n })()\n let sendError: unknown\n sendAudio.catch((e: unknown) => {\n sendError = e\n })\n\n for await (const message of conn.messages()) {\n if (message.isBinary || !message.text)\n continue\n const event = JSON.parse(message.text) as AudioWsServerMessage\n if (event.type === 'transcript')\n yield { type: 'transcript', text: event.text, final: event.final }\n else if (event.type === 'speech_started')\n yield { type: 'speech_started' }\n else if (event.type === 'speech_stopped')\n yield { type: 'speech_stopped' }\n else if (event.type === 'error')\n throw audioClientError(event.code, event.message)\n else if (event.type === 'end')\n break\n }\n if (sendError)\n throw sendError\n }\n finally {\n conn.close()\n }\n }\n\n async function transcribe(request: TranscriptionRequest): Promise<TranscriptionResult> {\n // 完整识别:服务端缓冲全部音频后返回单条最终结果\n const conn = await openBrowserWs(await buildUrl({ operation: 'transcribe', model: request.model }), request.signal)\n try {\n conn.send(startMessage('transcribe', { stream: false, model: request.model, language: request.language, contextHints: request.contextHints, format: request.audio.format, sampleRate: request.audio.sampleRate, channels: request.audio.channels }))\n conn.send(request.audio.data)\n conn.send(JSON.stringify({ type: 'done' }))\n\n let text = ''\n for await (const message of conn.messages()) {\n if (message.isBinary || !message.text)\n continue\n const event = JSON.parse(message.text) as AudioWsServerMessage\n if (event.type === 'transcript')\n text = event.text\n else if (event.type === 'error')\n throw audioClientError(event.code, event.message)\n else if (event.type === 'end')\n break\n }\n return { text }\n }\n finally {\n conn.close()\n }\n }\n\n async function* synthesizeStream(request: SynthesisStreamRequest): AsyncIterable<SynthesisEvent> {\n const conn = await openBrowserWs(await buildUrl({ operation: 'synthesize', model: request.model }), request.signal)\n try {\n conn.send(startMessage('synthesize', { model: request.model, voice: request.voice, instruction: request.instruction, format: request.format, sampleRate: request.sampleRate }))\n\n const sendText = (async () => {\n const segments = isSynthesisTextSegment(request.text) ? singleSegment(request.text) : request.text\n for await (const segment of segments)\n conn.send(JSON.stringify({ type: 'text', segmentId: segment.id, text: segment.text }))\n conn.send(JSON.stringify({ type: 'done' }))\n })()\n let sendError: unknown\n sendText.catch((e: unknown) => {\n sendError = e\n })\n\n let activeSegmentId: string | undefined\n for await (const message of conn.messages()) {\n if (message.isBinary && message.binary) {\n if (!activeSegmentId)\n throw new Error('Audio frame received outside a synthesis segment')\n yield { type: 'audio', segmentId: activeSegmentId, data: message.binary }\n continue\n }\n if (!message.text)\n continue\n const event = JSON.parse(message.text) as AudioWsServerMessage\n if (event.type === 'segment_started') {\n if (activeSegmentId)\n throw new Error('Synthesis segment started before the previous segment completed')\n activeSegmentId = event.segmentId\n yield event\n }\n else if (event.type === 'segment_done') {\n if (activeSegmentId !== event.segmentId)\n throw new Error('Synthesis segment completion does not match the active segment')\n yield event\n activeSegmentId = undefined\n }\n else if (event.type === 'error') {\n throw audioClientError(event.code, event.message)\n }\n else if (event.type === 'end') {\n // 收到 end 但仍有未完成的合成段:视为本次合成失败,不得把部分音频当作成功结果\n if (activeSegmentId)\n throw audioClientError(HaiAIError.AUDIO_PROTOCOL_ERROR.code, 'Synthesis ended before the active segment completed')\n break\n }\n }\n if (sendError)\n throw sendError\n }\n finally {\n conn.close()\n }\n }\n\n async function synthesize(request: SynthesisRequest): Promise<SynthesisResult> {\n const chunks: Uint8Array[] = []\n let meta: { format: SynthesisResult['format'], sampleRate?: number, channels?: 1 | 2 } | undefined\n for await (const event of synthesizeStream({ ...request, text: { id: 'synthesis', text: request.text } })) {\n if (event.type === 'segment_started')\n meta = { format: event.format, sampleRate: event.sampleRate, channels: event.channels }\n else if (event.type === 'audio')\n chunks.push(event.data)\n }\n // 音频格式来自服务端解析后的 Provider 真实输出(随 segment_started 下发),不由请求参数猜测\n if (!meta)\n throw audioClientError(HaiAIError.AUDIO_PROTOCOL_ERROR.code, 'Synthesis completed without segment metadata')\n const total = chunks.reduce((sum, c) => sum + c.length, 0)\n const data = new Uint8Array(total)\n let offset = 0\n for (const chunk of chunks) {\n data.set(chunk, offset)\n offset += chunk.length\n }\n return { data, format: meta.format, sampleRate: meta.sampleRate, channels: meta.channels ?? 1 }\n }\n\n return { transcribe, transcribeStream, synthesize, synthesizeStream }\n}\n\nfunction isSynthesisTextSegment(value: SynthesisStreamRequest['text']): value is SynthesisTextSegment {\n return typeof value === 'object' && value !== null && 'id' in value && 'text' in value\n}\n\nasync function* singleSegment(segment: SynthesisTextSegment): AsyncIterable<SynthesisTextSegment> {\n yield segment\n}\n"]}
|