@absolutejs/voice 0.0.22-beta.5 → 0.0.22-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { createVoiceAssistant, createVoiceExperiment, summarizeVoiceAssistantRun
3
3
  export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool } from './agent';
4
4
  export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap, createStoredVoiceIntegrationEvent, createStoredVoiceOpsTask, createVoiceFileExternalObjectMapStore, createVoiceFileAssistantMemoryStore, createVoiceFileIntegrationEventStore, createVoiceFileReviewStore, createVoiceFileRuntimeStorage, createVoiceFileSessionStore, createVoiceFileTaskStore, createVoiceFileTraceSinkDeliveryStore, createVoiceFileTraceEventStore } from './fileStore';
5
5
  export { createVoiceAssistantMemoryHandle, createVoiceAssistantMemoryRecord, createVoiceMemoryAssistantMemoryStore, resolveVoiceAssistantMemoryNamespace } from './assistantMemory';
6
- export { createJSONVoiceAssistantModel, createOpenAIVoiceAssistantModel } from './modelAdapters';
6
+ export { createAnthropicVoiceAssistantModel, createGeminiVoiceAssistantModel, createJSONVoiceAssistantModel, createOpenAIVoiceAssistantModel } from './modelAdapters';
7
7
  export { buildVoiceTraceReplay, createVoiceMemoryTraceSinkDeliveryStore, createVoiceTraceHTTPSink, createVoiceMemoryTraceEventStore, createVoiceTraceSinkDeliveryId, createVoiceTraceSinkDeliveryRecord, createVoiceTraceSinkStore, createVoiceTraceEvent, createVoiceTraceEventId, deliverVoiceTraceEventsToSinks, evaluateVoiceTrace, exportVoiceTrace, filterVoiceTraceEvents, pruneVoiceTraceEvents, redactVoiceTraceEvent, redactVoiceTraceEvents, redactVoiceTraceText, renderVoiceTraceHTML, renderVoiceTraceMarkdown, resolveVoiceTraceRedactionOptions, selectVoiceTraceEventsForPrune, summarizeVoiceTrace } from './trace';
8
8
  export { createVoiceSQLiteExternalObjectMapStore, createVoiceSQLiteIntegrationEventStore, createVoiceSQLiteReviewStore, createVoiceSQLiteRuntimeStorage, createVoiceSQLiteSessionStore, createVoiceSQLiteTaskStore, createVoiceSQLiteTraceSinkDeliveryStore, createVoiceSQLiteTraceEventStore } from './sqliteStore';
9
9
  export { createVoicePostgresExternalObjectMapStore, createVoicePostgresIntegrationEventStore, createVoicePostgresReviewStore, createVoicePostgresRuntimeStorage, createVoicePostgresSessionStore, createVoicePostgresTaskStore, createVoicePostgresTraceSinkDeliveryStore, createVoicePostgresTraceEventStore } from './postgresStore';
@@ -26,7 +26,7 @@ export { resolveTurnDetectionConfig, TURN_PROFILE_DEFAULTS } from './turnProfile
26
26
  export { createVoiceCallReviewFromLiveTelephonyReport, createVoiceCallReviewRecorder, renderVoiceCallReviewHTML, renderVoiceCallReviewMarkdown } from './testing/review';
27
27
  export type { VoiceAssistant, VoiceAssistantArtifactPlan, VoiceAssistantExperiment, VoiceAssistantExperimentOptions, VoiceAssistantGuardrailInput, VoiceAssistantGuardrails, VoiceAssistantMemoryLifecycle, VoiceAssistantMemoryLifecycleInput, VoiceAssistantOptions, VoiceAssistantOutputGuardrailInput, VoiceAssistantPreset, VoiceAssistantRunsSummary, VoiceAssistantRunSummary, VoiceAssistantVariant } from './assistant';
28
28
  export type { VoiceAssistantMemoryBinding, VoiceAssistantMemoryHandle, VoiceAssistantMemoryOptions, VoiceAssistantMemoryRecord, VoiceAssistantMemoryStore } from './assistantMemory';
29
- export type { OpenAIVoiceAssistantModelOptions, VoiceJSONAssistantModelHandler, VoiceJSONAssistantModelOptions } from './modelAdapters';
29
+ export type { AnthropicVoiceAssistantModelOptions, GeminiVoiceAssistantModelOptions, OpenAIVoiceAssistantModelOptions, VoiceJSONAssistantModelHandler, VoiceJSONAssistantModelOptions } from './modelAdapters';
30
30
  export type { VoiceAgent, VoiceAgentMessage, VoiceAgentMessageRole, VoiceAgentModel, VoiceAgentModelInput, VoiceAgentModelOutput, VoiceAgentOptions, VoiceAgentRunResult, VoiceAgentSquadOptions, VoiceAgentTool, VoiceAgentToolCall, VoiceAgentToolResult } from './agent';
31
31
  export type { VoiceOpsRuntime, VoiceOpsRuntimeConfig, VoiceOpsRuntimeSummary, VoiceOpsRuntimeSinkWorkerConfig, VoiceOpsRuntimeTaskWorkerConfig, VoiceOpsRuntimeTickResult, VoiceOpsRuntimeWebhookWorkerConfig } from './opsRuntime';
32
32
  export type { VoiceOpsPresetName, VoiceOpsPresetOverrides, VoiceResolvedOpsPreset } from './opsPresets';
package/dist/index.js CHANGED
@@ -5150,6 +5150,17 @@ var createVoiceAgent = (options) => {
5150
5150
  if (output.assistantText?.trim()) {
5151
5151
  messages.push({
5152
5152
  content: output.assistantText,
5153
+ metadata: output.toolCalls?.length ? {
5154
+ toolCalls: output.toolCalls
5155
+ } : undefined,
5156
+ role: "assistant"
5157
+ });
5158
+ } else if (output.toolCalls?.length) {
5159
+ messages.push({
5160
+ content: "",
5161
+ metadata: {
5162
+ toolCalls: output.toolCalls
5163
+ },
5153
5164
  role: "assistant"
5154
5165
  });
5155
5166
  }
@@ -7141,6 +7152,7 @@ var OUTPUT_SCHEMA = {
7141
7152
  },
7142
7153
  type: "object"
7143
7154
  };
7155
+ var ROUTE_RESULT_INSTRUCTION = "Return a JSON object with assistantText, complete, transfer, escalate, voicemail, noAnswer, and result when you are not calling tools.";
7144
7156
  var parseJSON = (value) => {
7145
7157
  try {
7146
7158
  const parsed = JSON.parse(value);
@@ -7151,6 +7163,18 @@ var parseJSON = (value) => {
7151
7163
  };
7152
7164
  }
7153
7165
  };
7166
+ var parseJSONValue = (value) => {
7167
+ try {
7168
+ return JSON.parse(value);
7169
+ } catch {
7170
+ return value;
7171
+ }
7172
+ };
7173
+ var getMessageToolCalls = (message) => {
7174
+ const toolCalls = message.metadata?.toolCalls;
7175
+ return Array.isArray(toolCalls) ? toolCalls.filter((toolCall) => toolCall && typeof toolCall === "object" && typeof toolCall.name === "string") : [];
7176
+ };
7177
+ var createHTTPError = (provider, response) => new Error(`${provider} voice assistant model failed: HTTP ${response.status}`);
7154
7178
  var normalizeRouteOutput = (output) => {
7155
7179
  const result = {};
7156
7180
  if (typeof output.assistantText === "string") {
@@ -7206,15 +7230,147 @@ var createJSONVoiceAssistantModel = (options) => ({
7206
7230
  });
7207
7231
  var messageToOpenAIInput = (message) => {
7208
7232
  if (message.role === "tool") {
7233
+ return [
7234
+ {
7235
+ call_id: message.toolCallId ?? message.name ?? crypto.randomUUID(),
7236
+ output: message.content,
7237
+ type: "function_call_output"
7238
+ }
7239
+ ];
7240
+ }
7241
+ const toolCalls = getMessageToolCalls(message);
7242
+ if (message.role === "assistant" && toolCalls.length) {
7243
+ return toolCalls.map((toolCall) => ({
7244
+ arguments: JSON.stringify(toolCall.args),
7245
+ call_id: toolCall.id ?? crypto.randomUUID(),
7246
+ name: toolCall.name,
7247
+ type: "function_call"
7248
+ }));
7249
+ }
7250
+ return [
7251
+ {
7252
+ content: message.content,
7253
+ role: message.role === "system" ? "developer" : message.role
7254
+ }
7255
+ ];
7256
+ };
7257
+ var messagesToOpenAIInput = (messages) => messages.flatMap(messageToOpenAIInput);
7258
+ var messageToAnthropicMessage = (message) => {
7259
+ if (message.role === "system") {
7260
+ return;
7261
+ }
7262
+ if (message.role === "tool") {
7263
+ if (!message.toolCallId) {
7264
+ return {
7265
+ content: `Tool result from ${message.name ?? "tool"}: ${message.content}`,
7266
+ role: "user"
7267
+ };
7268
+ }
7209
7269
  return {
7210
- call_id: message.toolCallId ?? message.name ?? crypto.randomUUID(),
7211
- output: message.content,
7212
- type: "function_call_output"
7270
+ content: [
7271
+ {
7272
+ content: message.content,
7273
+ tool_use_id: message.toolCallId,
7274
+ type: "tool_result"
7275
+ }
7276
+ ],
7277
+ role: "user"
7278
+ };
7279
+ }
7280
+ const toolCalls = getMessageToolCalls(message);
7281
+ if (message.role === "assistant" && toolCalls.length) {
7282
+ return {
7283
+ content: [
7284
+ ...message.content ? [
7285
+ {
7286
+ text: message.content,
7287
+ type: "text"
7288
+ }
7289
+ ] : [],
7290
+ ...toolCalls.map((toolCall) => ({
7291
+ id: toolCall.id ?? crypto.randomUUID(),
7292
+ input: toolCall.args,
7293
+ name: toolCall.name,
7294
+ type: "tool_use"
7295
+ }))
7296
+ ],
7297
+ role: "assistant"
7213
7298
  };
7214
7299
  }
7215
7300
  return {
7216
7301
  content: message.content,
7217
- role: message.role === "system" ? "developer" : message.role
7302
+ role: message.role
7303
+ };
7304
+ };
7305
+ var toGeminiSchema = (schema) => {
7306
+ const next = {};
7307
+ for (const [key, value] of Object.entries(schema)) {
7308
+ if (key === "additionalProperties") {
7309
+ continue;
7310
+ }
7311
+ if (key === "type" && typeof value === "string") {
7312
+ next[key] = value.toUpperCase();
7313
+ continue;
7314
+ }
7315
+ if (Array.isArray(value)) {
7316
+ next[key] = value.map((item) => item && typeof item === "object" ? toGeminiSchema(item) : item);
7317
+ continue;
7318
+ }
7319
+ if (value && typeof value === "object") {
7320
+ next[key] = toGeminiSchema(value);
7321
+ continue;
7322
+ }
7323
+ next[key] = value;
7324
+ }
7325
+ return next;
7326
+ };
7327
+ var messageToGeminiContent = (message) => {
7328
+ if (message.role === "system") {
7329
+ return;
7330
+ }
7331
+ if (message.role === "tool") {
7332
+ return {
7333
+ parts: [
7334
+ {
7335
+ functionResponse: {
7336
+ id: message.toolCallId,
7337
+ name: message.name ?? "tool",
7338
+ response: {
7339
+ result: parseJSONValue(message.content)
7340
+ }
7341
+ }
7342
+ }
7343
+ ],
7344
+ role: "user"
7345
+ };
7346
+ }
7347
+ const toolCalls = getMessageToolCalls(message);
7348
+ if (message.role === "assistant" && toolCalls.length) {
7349
+ return {
7350
+ parts: [
7351
+ ...message.content ? [
7352
+ {
7353
+ text: message.content
7354
+ }
7355
+ ] : [],
7356
+ ...toolCalls.map((toolCall) => ({
7357
+ functionCall: {
7358
+ args: toolCall.args,
7359
+ id: toolCall.id,
7360
+ name: toolCall.name
7361
+ }
7362
+ }))
7363
+ ],
7364
+ role: "model"
7365
+ };
7366
+ }
7367
+ return {
7368
+ parts: [
7369
+ {
7370
+ text: message.content
7371
+ }
7372
+ ],
7373
+ role: message.role === "assistant" ? "model" : "user"
7218
7374
  };
7219
7375
  };
7220
7376
  var extractText = (response) => {
@@ -7268,7 +7424,7 @@ var createOpenAIVoiceAssistantModel = (options) => {
7268
7424
  generate: async (input) => {
7269
7425
  const response = await fetchImpl(`${baseUrl.replace(/\/$/, "")}/responses`, {
7270
7426
  body: JSON.stringify({
7271
- input: input.messages.map(messageToOpenAIInput),
7427
+ input: messagesToOpenAIInput(input.messages),
7272
7428
  instructions: [
7273
7429
  input.system,
7274
7430
  "Return a JSON object with assistantText, complete, transfer, escalate, voicemail, noAnswer, and result when you are not calling tools."
@@ -7305,7 +7461,7 @@ var createOpenAIVoiceAssistantModel = (options) => {
7305
7461
  method: "POST"
7306
7462
  });
7307
7463
  if (!response.ok) {
7308
- throw new Error(`OpenAI voice assistant model failed: HTTP ${response.status}`);
7464
+ throw createHTTPError("OpenAI", response);
7309
7465
  }
7310
7466
  const body = await response.json();
7311
7467
  if (body.usage && typeof body.usage === "object") {
@@ -7321,6 +7477,178 @@ var createOpenAIVoiceAssistantModel = (options) => {
7321
7477
  }
7322
7478
  };
7323
7479
  };
7480
+ var extractAnthropicText = (response) => {
7481
+ const content = Array.isArray(response.content) ? response.content : [];
7482
+ return content.map((item) => item && typeof item === "object" && item.type === "text" && typeof item.text === "string" ? item.text : "").filter(Boolean).join(`
7483
+ `);
7484
+ };
7485
+ var extractAnthropicToolCalls = (response) => {
7486
+ const content = Array.isArray(response.content) ? response.content : [];
7487
+ const toolCalls = [];
7488
+ for (const item of content) {
7489
+ if (!item || typeof item !== "object") {
7490
+ continue;
7491
+ }
7492
+ const record = item;
7493
+ if (record.type !== "tool_use" || typeof record.name !== "string") {
7494
+ continue;
7495
+ }
7496
+ toolCalls.push({
7497
+ args: record.input && typeof record.input === "object" ? record.input : {},
7498
+ id: typeof record.id === "string" ? record.id : undefined,
7499
+ name: record.name
7500
+ });
7501
+ }
7502
+ return toolCalls;
7503
+ };
7504
+ var createAnthropicVoiceAssistantModel = (options) => {
7505
+ const fetchImpl = options.fetch ?? globalThis.fetch;
7506
+ const baseUrl = options.baseUrl ?? "https://api.anthropic.com/v1";
7507
+ const model = options.model ?? "claude-sonnet-4-5";
7508
+ return {
7509
+ generate: async (input) => {
7510
+ const response = await fetchImpl(`${baseUrl.replace(/\/$/, "")}/messages`, {
7511
+ body: JSON.stringify({
7512
+ max_tokens: options.maxOutputTokens ?? 1024,
7513
+ messages: input.messages.map(messageToAnthropicMessage).filter(Boolean),
7514
+ model,
7515
+ system: [input.system, ROUTE_RESULT_INSTRUCTION].filter(Boolean).join(`
7516
+
7517
+ `),
7518
+ temperature: options.temperature,
7519
+ tool_choice: input.tools.length ? { type: "auto" } : { type: "none" },
7520
+ tools: input.tools.map((tool) => ({
7521
+ description: tool.description,
7522
+ input_schema: tool.parameters ?? {
7523
+ additionalProperties: true,
7524
+ type: "object"
7525
+ },
7526
+ name: tool.name
7527
+ }))
7528
+ }),
7529
+ headers: {
7530
+ "anthropic-version": options.version ?? "2023-06-01",
7531
+ "content-type": "application/json",
7532
+ "x-api-key": options.apiKey
7533
+ },
7534
+ method: "POST"
7535
+ });
7536
+ if (!response.ok) {
7537
+ throw createHTTPError("Anthropic", response);
7538
+ }
7539
+ const body = await response.json();
7540
+ if (body.usage && typeof body.usage === "object") {
7541
+ await options.onUsage?.(body.usage);
7542
+ }
7543
+ const toolCalls = extractAnthropicToolCalls(body);
7544
+ if (toolCalls.length) {
7545
+ return {
7546
+ assistantText: extractAnthropicText(body) || undefined,
7547
+ toolCalls
7548
+ };
7549
+ }
7550
+ return normalizeRouteOutput(parseJSON(extractAnthropicText(body)));
7551
+ }
7552
+ };
7553
+ };
7554
+ var extractGeminiCandidateParts = (response) => {
7555
+ const candidates = Array.isArray(response.candidates) ? response.candidates : [];
7556
+ const first = candidates[0];
7557
+ if (!first || typeof first !== "object") {
7558
+ return [];
7559
+ }
7560
+ const content = first.content;
7561
+ if (!content || typeof content !== "object") {
7562
+ return [];
7563
+ }
7564
+ const parts = content.parts;
7565
+ return Array.isArray(parts) ? parts : [];
7566
+ };
7567
+ var extractGeminiText = (response) => extractGeminiCandidateParts(response).map((part) => part && typeof part === "object" && typeof part.text === "string" ? part.text : "").filter(Boolean).join(`
7568
+ `);
7569
+ var extractGeminiToolCalls = (response) => {
7570
+ const toolCalls = [];
7571
+ for (const part of extractGeminiCandidateParts(response)) {
7572
+ if (!part || typeof part !== "object") {
7573
+ continue;
7574
+ }
7575
+ const functionCall = part.functionCall;
7576
+ if (!functionCall || typeof functionCall !== "object") {
7577
+ continue;
7578
+ }
7579
+ const record = functionCall;
7580
+ if (typeof record.name !== "string") {
7581
+ continue;
7582
+ }
7583
+ toolCalls.push({
7584
+ args: record.args && typeof record.args === "object" ? record.args : {},
7585
+ id: typeof record.id === "string" ? record.id : undefined,
7586
+ name: record.name
7587
+ });
7588
+ }
7589
+ return toolCalls;
7590
+ };
7591
+ var createGeminiVoiceAssistantModel = (options) => {
7592
+ const fetchImpl = options.fetch ?? globalThis.fetch;
7593
+ const baseUrl = options.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta";
7594
+ const model = options.model ?? "gemini-2.5-flash";
7595
+ return {
7596
+ generate: async (input) => {
7597
+ const endpoint = `${baseUrl.replace(/\/$/, "")}/models/${encodeURIComponent(model)}:generateContent?key=${encodeURIComponent(options.apiKey)}`;
7598
+ const response = await fetchImpl(endpoint, {
7599
+ body: JSON.stringify({
7600
+ contents: input.messages.map(messageToGeminiContent).filter(Boolean),
7601
+ generationConfig: {
7602
+ maxOutputTokens: options.maxOutputTokens,
7603
+ responseMimeType: "application/json",
7604
+ responseSchema: toGeminiSchema(OUTPUT_SCHEMA),
7605
+ temperature: options.temperature
7606
+ },
7607
+ systemInstruction: {
7608
+ parts: [
7609
+ {
7610
+ text: [input.system, ROUTE_RESULT_INSTRUCTION].filter(Boolean).join(`
7611
+
7612
+ `)
7613
+ }
7614
+ ]
7615
+ },
7616
+ tools: input.tools.length ? [
7617
+ {
7618
+ functionDeclarations: input.tools.map((tool) => ({
7619
+ description: tool.description,
7620
+ name: tool.name,
7621
+ parameters: toGeminiSchema(tool.parameters ?? {
7622
+ additionalProperties: true,
7623
+ type: "object"
7624
+ })
7625
+ }))
7626
+ }
7627
+ ] : undefined
7628
+ }),
7629
+ headers: {
7630
+ "content-type": "application/json"
7631
+ },
7632
+ method: "POST"
7633
+ });
7634
+ if (!response.ok) {
7635
+ throw createHTTPError("Gemini", response);
7636
+ }
7637
+ const body = await response.json();
7638
+ if (body.usageMetadata && typeof body.usageMetadata === "object") {
7639
+ await options.onUsage?.(body.usageMetadata);
7640
+ }
7641
+ const toolCalls = extractGeminiToolCalls(body);
7642
+ if (toolCalls.length) {
7643
+ return {
7644
+ assistantText: extractGeminiText(body) || undefined,
7645
+ toolCalls
7646
+ };
7647
+ }
7648
+ return normalizeRouteOutput(parseJSON(extractGeminiText(body)));
7649
+ }
7650
+ };
7651
+ };
7324
7652
  // src/sqliteStore.ts
7325
7653
  import { Database } from "bun:sqlite";
7326
7654
  var normalizeTableNameSegment = (value) => value.trim().replace(/[^a-zA-Z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "voice";
@@ -9909,8 +10237,10 @@ export {
9909
10237
  createOpenAIVoiceAssistantModel,
9910
10238
  createJSONVoiceAssistantModel,
9911
10239
  createId,
10240
+ createGeminiVoiceAssistantModel,
9912
10241
  createDomainPhraseHints,
9913
10242
  createDomainLexicon,
10243
+ createAnthropicVoiceAssistantModel,
9914
10244
  conditionAudioChunk,
9915
10245
  completeVoiceOpsTask,
9916
10246
  claimVoiceOpsTask,
@@ -14,5 +14,26 @@ export type OpenAIVoiceAssistantModelOptions = {
14
14
  onUsage?: (usage: Record<string, unknown>) => Promise<void> | void;
15
15
  temperature?: number;
16
16
  };
17
+ export type AnthropicVoiceAssistantModelOptions = {
18
+ apiKey: string;
19
+ baseUrl?: string;
20
+ fetch?: typeof fetch;
21
+ maxOutputTokens?: number;
22
+ model?: string;
23
+ onUsage?: (usage: Record<string, unknown>) => Promise<void> | void;
24
+ temperature?: number;
25
+ version?: string;
26
+ };
27
+ export type GeminiVoiceAssistantModelOptions = {
28
+ apiKey: string;
29
+ baseUrl?: string;
30
+ fetch?: typeof fetch;
31
+ maxOutputTokens?: number;
32
+ model?: string;
33
+ onUsage?: (usage: Record<string, unknown>) => Promise<void> | void;
34
+ temperature?: number;
35
+ };
17
36
  export declare const createJSONVoiceAssistantModel: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: VoiceJSONAssistantModelOptions<TContext, TSession, TResult>) => VoiceAgentModel<TContext, TSession, TResult>;
18
37
  export declare const createOpenAIVoiceAssistantModel: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: OpenAIVoiceAssistantModelOptions) => VoiceAgentModel<TContext, TSession, TResult>;
38
+ export declare const createAnthropicVoiceAssistantModel: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: AnthropicVoiceAssistantModelOptions) => VoiceAgentModel<TContext, TSession, TResult>;
39
+ export declare const createGeminiVoiceAssistantModel: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: GeminiVoiceAssistantModelOptions) => VoiceAgentModel<TContext, TSession, TResult>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.5",
3
+ "version": "0.0.22-beta.7",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",