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

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") {
@@ -7217,6 +7241,124 @@ var messageToOpenAIInput = (message) => {
7217
7241
  role: message.role === "system" ? "developer" : message.role
7218
7242
  };
7219
7243
  };
7244
+ var messageToAnthropicMessage = (message) => {
7245
+ if (message.role === "system") {
7246
+ return;
7247
+ }
7248
+ if (message.role === "tool") {
7249
+ if (!message.toolCallId) {
7250
+ return {
7251
+ content: `Tool result from ${message.name ?? "tool"}: ${message.content}`,
7252
+ role: "user"
7253
+ };
7254
+ }
7255
+ return {
7256
+ content: [
7257
+ {
7258
+ content: message.content,
7259
+ tool_use_id: message.toolCallId,
7260
+ type: "tool_result"
7261
+ }
7262
+ ],
7263
+ role: "user"
7264
+ };
7265
+ }
7266
+ const toolCalls = getMessageToolCalls(message);
7267
+ if (message.role === "assistant" && toolCalls.length) {
7268
+ return {
7269
+ content: [
7270
+ ...message.content ? [
7271
+ {
7272
+ text: message.content,
7273
+ type: "text"
7274
+ }
7275
+ ] : [],
7276
+ ...toolCalls.map((toolCall) => ({
7277
+ id: toolCall.id ?? crypto.randomUUID(),
7278
+ input: toolCall.args,
7279
+ name: toolCall.name,
7280
+ type: "tool_use"
7281
+ }))
7282
+ ],
7283
+ role: "assistant"
7284
+ };
7285
+ }
7286
+ return {
7287
+ content: message.content,
7288
+ role: message.role
7289
+ };
7290
+ };
7291
+ var toGeminiSchema = (schema) => {
7292
+ const next = {};
7293
+ for (const [key, value] of Object.entries(schema)) {
7294
+ if (key === "additionalProperties") {
7295
+ continue;
7296
+ }
7297
+ if (key === "type" && typeof value === "string") {
7298
+ next[key] = value.toUpperCase();
7299
+ continue;
7300
+ }
7301
+ if (Array.isArray(value)) {
7302
+ next[key] = value.map((item) => item && typeof item === "object" ? toGeminiSchema(item) : item);
7303
+ continue;
7304
+ }
7305
+ if (value && typeof value === "object") {
7306
+ next[key] = toGeminiSchema(value);
7307
+ continue;
7308
+ }
7309
+ next[key] = value;
7310
+ }
7311
+ return next;
7312
+ };
7313
+ var messageToGeminiContent = (message) => {
7314
+ if (message.role === "system") {
7315
+ return;
7316
+ }
7317
+ if (message.role === "tool") {
7318
+ return {
7319
+ parts: [
7320
+ {
7321
+ functionResponse: {
7322
+ id: message.toolCallId,
7323
+ name: message.name ?? "tool",
7324
+ response: {
7325
+ result: parseJSONValue(message.content)
7326
+ }
7327
+ }
7328
+ }
7329
+ ],
7330
+ role: "user"
7331
+ };
7332
+ }
7333
+ const toolCalls = getMessageToolCalls(message);
7334
+ if (message.role === "assistant" && toolCalls.length) {
7335
+ return {
7336
+ parts: [
7337
+ ...message.content ? [
7338
+ {
7339
+ text: message.content
7340
+ }
7341
+ ] : [],
7342
+ ...toolCalls.map((toolCall) => ({
7343
+ functionCall: {
7344
+ args: toolCall.args,
7345
+ id: toolCall.id,
7346
+ name: toolCall.name
7347
+ }
7348
+ }))
7349
+ ],
7350
+ role: "model"
7351
+ };
7352
+ }
7353
+ return {
7354
+ parts: [
7355
+ {
7356
+ text: message.content
7357
+ }
7358
+ ],
7359
+ role: message.role === "assistant" ? "model" : "user"
7360
+ };
7361
+ };
7220
7362
  var extractText = (response) => {
7221
7363
  if (typeof response.output_text === "string") {
7222
7364
  return response.output_text;
@@ -7305,7 +7447,7 @@ var createOpenAIVoiceAssistantModel = (options) => {
7305
7447
  method: "POST"
7306
7448
  });
7307
7449
  if (!response.ok) {
7308
- throw new Error(`OpenAI voice assistant model failed: HTTP ${response.status}`);
7450
+ throw createHTTPError("OpenAI", response);
7309
7451
  }
7310
7452
  const body = await response.json();
7311
7453
  if (body.usage && typeof body.usage === "object") {
@@ -7321,6 +7463,178 @@ var createOpenAIVoiceAssistantModel = (options) => {
7321
7463
  }
7322
7464
  };
7323
7465
  };
7466
+ var extractAnthropicText = (response) => {
7467
+ const content = Array.isArray(response.content) ? response.content : [];
7468
+ return content.map((item) => item && typeof item === "object" && item.type === "text" && typeof item.text === "string" ? item.text : "").filter(Boolean).join(`
7469
+ `);
7470
+ };
7471
+ var extractAnthropicToolCalls = (response) => {
7472
+ const content = Array.isArray(response.content) ? response.content : [];
7473
+ const toolCalls = [];
7474
+ for (const item of content) {
7475
+ if (!item || typeof item !== "object") {
7476
+ continue;
7477
+ }
7478
+ const record = item;
7479
+ if (record.type !== "tool_use" || typeof record.name !== "string") {
7480
+ continue;
7481
+ }
7482
+ toolCalls.push({
7483
+ args: record.input && typeof record.input === "object" ? record.input : {},
7484
+ id: typeof record.id === "string" ? record.id : undefined,
7485
+ name: record.name
7486
+ });
7487
+ }
7488
+ return toolCalls;
7489
+ };
7490
+ var createAnthropicVoiceAssistantModel = (options) => {
7491
+ const fetchImpl = options.fetch ?? globalThis.fetch;
7492
+ const baseUrl = options.baseUrl ?? "https://api.anthropic.com/v1";
7493
+ const model = options.model ?? "claude-sonnet-4-5";
7494
+ return {
7495
+ generate: async (input) => {
7496
+ const response = await fetchImpl(`${baseUrl.replace(/\/$/, "")}/messages`, {
7497
+ body: JSON.stringify({
7498
+ max_tokens: options.maxOutputTokens ?? 1024,
7499
+ messages: input.messages.map(messageToAnthropicMessage).filter(Boolean),
7500
+ model,
7501
+ system: [input.system, ROUTE_RESULT_INSTRUCTION].filter(Boolean).join(`
7502
+
7503
+ `),
7504
+ temperature: options.temperature,
7505
+ tool_choice: input.tools.length ? { type: "auto" } : { type: "none" },
7506
+ tools: input.tools.map((tool) => ({
7507
+ description: tool.description,
7508
+ input_schema: tool.parameters ?? {
7509
+ additionalProperties: true,
7510
+ type: "object"
7511
+ },
7512
+ name: tool.name
7513
+ }))
7514
+ }),
7515
+ headers: {
7516
+ "anthropic-version": options.version ?? "2023-06-01",
7517
+ "content-type": "application/json",
7518
+ "x-api-key": options.apiKey
7519
+ },
7520
+ method: "POST"
7521
+ });
7522
+ if (!response.ok) {
7523
+ throw createHTTPError("Anthropic", response);
7524
+ }
7525
+ const body = await response.json();
7526
+ if (body.usage && typeof body.usage === "object") {
7527
+ await options.onUsage?.(body.usage);
7528
+ }
7529
+ const toolCalls = extractAnthropicToolCalls(body);
7530
+ if (toolCalls.length) {
7531
+ return {
7532
+ assistantText: extractAnthropicText(body) || undefined,
7533
+ toolCalls
7534
+ };
7535
+ }
7536
+ return normalizeRouteOutput(parseJSON(extractAnthropicText(body)));
7537
+ }
7538
+ };
7539
+ };
7540
+ var extractGeminiCandidateParts = (response) => {
7541
+ const candidates = Array.isArray(response.candidates) ? response.candidates : [];
7542
+ const first = candidates[0];
7543
+ if (!first || typeof first !== "object") {
7544
+ return [];
7545
+ }
7546
+ const content = first.content;
7547
+ if (!content || typeof content !== "object") {
7548
+ return [];
7549
+ }
7550
+ const parts = content.parts;
7551
+ return Array.isArray(parts) ? parts : [];
7552
+ };
7553
+ var extractGeminiText = (response) => extractGeminiCandidateParts(response).map((part) => part && typeof part === "object" && typeof part.text === "string" ? part.text : "").filter(Boolean).join(`
7554
+ `);
7555
+ var extractGeminiToolCalls = (response) => {
7556
+ const toolCalls = [];
7557
+ for (const part of extractGeminiCandidateParts(response)) {
7558
+ if (!part || typeof part !== "object") {
7559
+ continue;
7560
+ }
7561
+ const functionCall = part.functionCall;
7562
+ if (!functionCall || typeof functionCall !== "object") {
7563
+ continue;
7564
+ }
7565
+ const record = functionCall;
7566
+ if (typeof record.name !== "string") {
7567
+ continue;
7568
+ }
7569
+ toolCalls.push({
7570
+ args: record.args && typeof record.args === "object" ? record.args : {},
7571
+ id: typeof record.id === "string" ? record.id : undefined,
7572
+ name: record.name
7573
+ });
7574
+ }
7575
+ return toolCalls;
7576
+ };
7577
+ var createGeminiVoiceAssistantModel = (options) => {
7578
+ const fetchImpl = options.fetch ?? globalThis.fetch;
7579
+ const baseUrl = options.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta";
7580
+ const model = options.model ?? "gemini-2.5-flash";
7581
+ return {
7582
+ generate: async (input) => {
7583
+ const endpoint = `${baseUrl.replace(/\/$/, "")}/models/${encodeURIComponent(model)}:generateContent?key=${encodeURIComponent(options.apiKey)}`;
7584
+ const response = await fetchImpl(endpoint, {
7585
+ body: JSON.stringify({
7586
+ contents: input.messages.map(messageToGeminiContent).filter(Boolean),
7587
+ generationConfig: {
7588
+ maxOutputTokens: options.maxOutputTokens,
7589
+ responseMimeType: "application/json",
7590
+ responseSchema: toGeminiSchema(OUTPUT_SCHEMA),
7591
+ temperature: options.temperature
7592
+ },
7593
+ systemInstruction: {
7594
+ parts: [
7595
+ {
7596
+ text: [input.system, ROUTE_RESULT_INSTRUCTION].filter(Boolean).join(`
7597
+
7598
+ `)
7599
+ }
7600
+ ]
7601
+ },
7602
+ tools: input.tools.length ? [
7603
+ {
7604
+ functionDeclarations: input.tools.map((tool) => ({
7605
+ description: tool.description,
7606
+ name: tool.name,
7607
+ parameters: toGeminiSchema(tool.parameters ?? {
7608
+ additionalProperties: true,
7609
+ type: "object"
7610
+ })
7611
+ }))
7612
+ }
7613
+ ] : undefined
7614
+ }),
7615
+ headers: {
7616
+ "content-type": "application/json"
7617
+ },
7618
+ method: "POST"
7619
+ });
7620
+ if (!response.ok) {
7621
+ throw createHTTPError("Gemini", response);
7622
+ }
7623
+ const body = await response.json();
7624
+ if (body.usageMetadata && typeof body.usageMetadata === "object") {
7625
+ await options.onUsage?.(body.usageMetadata);
7626
+ }
7627
+ const toolCalls = extractGeminiToolCalls(body);
7628
+ if (toolCalls.length) {
7629
+ return {
7630
+ assistantText: extractGeminiText(body) || undefined,
7631
+ toolCalls
7632
+ };
7633
+ }
7634
+ return normalizeRouteOutput(parseJSON(extractGeminiText(body)));
7635
+ }
7636
+ };
7637
+ };
7324
7638
  // src/sqliteStore.ts
7325
7639
  import { Database } from "bun:sqlite";
7326
7640
  var normalizeTableNameSegment = (value) => value.trim().replace(/[^a-zA-Z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "voice";
@@ -9909,8 +10223,10 @@ export {
9909
10223
  createOpenAIVoiceAssistantModel,
9910
10224
  createJSONVoiceAssistantModel,
9911
10225
  createId,
10226
+ createGeminiVoiceAssistantModel,
9912
10227
  createDomainPhraseHints,
9913
10228
  createDomainLexicon,
10229
+ createAnthropicVoiceAssistantModel,
9914
10230
  conditionAudioChunk,
9915
10231
  completeVoiceOpsTask,
9916
10232
  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.6",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",