@absolutejs/voice 0.0.22-beta.4 → 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,6 +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 { createAnthropicVoiceAssistantModel, createGeminiVoiceAssistantModel, createJSONVoiceAssistantModel, createOpenAIVoiceAssistantModel } from './modelAdapters';
6
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';
7
8
  export { createVoiceSQLiteExternalObjectMapStore, createVoiceSQLiteIntegrationEventStore, createVoiceSQLiteReviewStore, createVoiceSQLiteRuntimeStorage, createVoiceSQLiteSessionStore, createVoiceSQLiteTaskStore, createVoiceSQLiteTraceSinkDeliveryStore, createVoiceSQLiteTraceEventStore } from './sqliteStore';
8
9
  export { createVoicePostgresExternalObjectMapStore, createVoicePostgresIntegrationEventStore, createVoicePostgresReviewStore, createVoicePostgresRuntimeStorage, createVoicePostgresSessionStore, createVoicePostgresTaskStore, createVoicePostgresTraceSinkDeliveryStore, createVoicePostgresTraceEventStore } from './postgresStore';
@@ -25,6 +26,7 @@ export { resolveTurnDetectionConfig, TURN_PROFILE_DEFAULTS } from './turnProfile
25
26
  export { createVoiceCallReviewFromLiveTelephonyReport, createVoiceCallReviewRecorder, renderVoiceCallReviewHTML, renderVoiceCallReviewMarkdown } from './testing/review';
26
27
  export type { VoiceAssistant, VoiceAssistantArtifactPlan, VoiceAssistantExperiment, VoiceAssistantExperimentOptions, VoiceAssistantGuardrailInput, VoiceAssistantGuardrails, VoiceAssistantMemoryLifecycle, VoiceAssistantMemoryLifecycleInput, VoiceAssistantOptions, VoiceAssistantOutputGuardrailInput, VoiceAssistantPreset, VoiceAssistantRunsSummary, VoiceAssistantRunSummary, VoiceAssistantVariant } from './assistant';
27
28
  export type { VoiceAssistantMemoryBinding, VoiceAssistantMemoryHandle, VoiceAssistantMemoryOptions, VoiceAssistantMemoryRecord, VoiceAssistantMemoryStore } from './assistantMemory';
29
+ export type { AnthropicVoiceAssistantModelOptions, GeminiVoiceAssistantModelOptions, OpenAIVoiceAssistantModelOptions, VoiceJSONAssistantModelHandler, VoiceJSONAssistantModelOptions } from './modelAdapters';
28
30
  export type { VoiceAgent, VoiceAgentMessage, VoiceAgentMessageRole, VoiceAgentModel, VoiceAgentModelInput, VoiceAgentModelOutput, VoiceAgentOptions, VoiceAgentRunResult, VoiceAgentSquadOptions, VoiceAgentTool, VoiceAgentToolCall, VoiceAgentToolResult } from './agent';
29
31
  export type { VoiceOpsRuntime, VoiceOpsRuntimeConfig, VoiceOpsRuntimeSummary, VoiceOpsRuntimeSinkWorkerConfig, VoiceOpsRuntimeTaskWorkerConfig, VoiceOpsRuntimeTickResult, VoiceOpsRuntimeWebhookWorkerConfig } from './opsRuntime';
30
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
  }
@@ -7073,6 +7084,557 @@ var createStoredVoiceExternalObjectMap = (mapping) => createVoiceExternalObjectM
7073
7084
  sourceId: mapping.sourceId,
7074
7085
  sourceType: mapping.sourceType
7075
7086
  });
7087
+ // src/modelAdapters.ts
7088
+ var OUTPUT_SCHEMA = {
7089
+ additionalProperties: false,
7090
+ properties: {
7091
+ assistantText: {
7092
+ type: "string"
7093
+ },
7094
+ complete: {
7095
+ type: "boolean"
7096
+ },
7097
+ escalate: {
7098
+ additionalProperties: false,
7099
+ properties: {
7100
+ metadata: {
7101
+ additionalProperties: true,
7102
+ type: "object"
7103
+ },
7104
+ reason: {
7105
+ type: "string"
7106
+ }
7107
+ },
7108
+ required: ["reason"],
7109
+ type: "object"
7110
+ },
7111
+ noAnswer: {
7112
+ additionalProperties: false,
7113
+ properties: {
7114
+ metadata: {
7115
+ additionalProperties: true,
7116
+ type: "object"
7117
+ }
7118
+ },
7119
+ type: "object"
7120
+ },
7121
+ result: {
7122
+ additionalProperties: true,
7123
+ type: "object"
7124
+ },
7125
+ transfer: {
7126
+ additionalProperties: false,
7127
+ properties: {
7128
+ metadata: {
7129
+ additionalProperties: true,
7130
+ type: "object"
7131
+ },
7132
+ reason: {
7133
+ type: "string"
7134
+ },
7135
+ target: {
7136
+ type: "string"
7137
+ }
7138
+ },
7139
+ required: ["target"],
7140
+ type: "object"
7141
+ },
7142
+ voicemail: {
7143
+ additionalProperties: false,
7144
+ properties: {
7145
+ metadata: {
7146
+ additionalProperties: true,
7147
+ type: "object"
7148
+ }
7149
+ },
7150
+ type: "object"
7151
+ }
7152
+ },
7153
+ type: "object"
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.";
7156
+ var parseJSON = (value) => {
7157
+ try {
7158
+ const parsed = JSON.parse(value);
7159
+ return parsed && typeof parsed === "object" ? parsed : {};
7160
+ } catch {
7161
+ return {
7162
+ assistantText: value
7163
+ };
7164
+ }
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}`);
7178
+ var normalizeRouteOutput = (output) => {
7179
+ const result = {};
7180
+ if (typeof output.assistantText === "string") {
7181
+ result.assistantText = output.assistantText;
7182
+ }
7183
+ if (typeof output.complete === "boolean") {
7184
+ result.complete = output.complete;
7185
+ }
7186
+ if (output.result !== undefined) {
7187
+ result.result = output.result;
7188
+ }
7189
+ if (output.transfer && typeof output.transfer === "object") {
7190
+ const transfer = output.transfer;
7191
+ if (typeof transfer.target === "string") {
7192
+ result.transfer = {
7193
+ metadata: transfer.metadata && typeof transfer.metadata === "object" ? transfer.metadata : undefined,
7194
+ reason: typeof transfer.reason === "string" ? transfer.reason : undefined,
7195
+ target: transfer.target
7196
+ };
7197
+ }
7198
+ }
7199
+ if (output.escalate && typeof output.escalate === "object") {
7200
+ const escalate = output.escalate;
7201
+ if (typeof escalate.reason === "string") {
7202
+ result.escalate = {
7203
+ metadata: escalate.metadata && typeof escalate.metadata === "object" ? escalate.metadata : undefined,
7204
+ reason: escalate.reason
7205
+ };
7206
+ }
7207
+ }
7208
+ if (output.voicemail && typeof output.voicemail === "object") {
7209
+ const voicemail = output.voicemail;
7210
+ result.voicemail = {
7211
+ metadata: voicemail.metadata && typeof voicemail.metadata === "object" ? voicemail.metadata : undefined
7212
+ };
7213
+ }
7214
+ if (output.noAnswer && typeof output.noAnswer === "object") {
7215
+ const noAnswer = output.noAnswer;
7216
+ result.noAnswer = {
7217
+ metadata: noAnswer.metadata && typeof noAnswer.metadata === "object" ? noAnswer.metadata : undefined
7218
+ };
7219
+ }
7220
+ return result;
7221
+ };
7222
+ var createJSONVoiceAssistantModel = (options) => ({
7223
+ generate: async (input) => {
7224
+ const output = await options.generate(input);
7225
+ if ("assistantText" in output || "toolCalls" in output || "complete" in output || "transfer" in output || "escalate" in output) {
7226
+ return output;
7227
+ }
7228
+ return options.mapOutput?.(output) ?? normalizeRouteOutput(output);
7229
+ }
7230
+ });
7231
+ var messageToOpenAIInput = (message) => {
7232
+ if (message.role === "tool") {
7233
+ return {
7234
+ call_id: message.toolCallId ?? message.name ?? crypto.randomUUID(),
7235
+ output: message.content,
7236
+ type: "function_call_output"
7237
+ };
7238
+ }
7239
+ return {
7240
+ content: message.content,
7241
+ role: message.role === "system" ? "developer" : message.role
7242
+ };
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
+ };
7362
+ var extractText = (response) => {
7363
+ if (typeof response.output_text === "string") {
7364
+ return response.output_text;
7365
+ }
7366
+ const output = Array.isArray(response.output) ? response.output : [];
7367
+ for (const item of output) {
7368
+ if (!item || typeof item !== "object") {
7369
+ continue;
7370
+ }
7371
+ const record = item;
7372
+ const content = Array.isArray(record.content) ? record.content : [];
7373
+ for (const contentItem of content) {
7374
+ if (!contentItem || typeof contentItem !== "object") {
7375
+ continue;
7376
+ }
7377
+ const contentRecord = contentItem;
7378
+ if (typeof contentRecord.text === "string") {
7379
+ return contentRecord.text;
7380
+ }
7381
+ }
7382
+ }
7383
+ return "";
7384
+ };
7385
+ var extractToolCalls = (response) => {
7386
+ const output = Array.isArray(response.output) ? response.output : [];
7387
+ const toolCalls = [];
7388
+ for (const item of output) {
7389
+ if (!item || typeof item !== "object") {
7390
+ continue;
7391
+ }
7392
+ const record = item;
7393
+ if (record.type !== "function_call" || typeof record.name !== "string") {
7394
+ continue;
7395
+ }
7396
+ const args = typeof record.arguments === "string" ? parseJSON(record.arguments) : {};
7397
+ toolCalls.push({
7398
+ args,
7399
+ id: typeof record.call_id === "string" ? record.call_id : typeof record.id === "string" ? record.id : undefined,
7400
+ name: record.name
7401
+ });
7402
+ }
7403
+ return toolCalls;
7404
+ };
7405
+ var createOpenAIVoiceAssistantModel = (options) => {
7406
+ const fetchImpl = options.fetch ?? globalThis.fetch;
7407
+ const baseUrl = options.baseUrl ?? "https://api.openai.com/v1";
7408
+ const model = options.model ?? "gpt-4.1-mini";
7409
+ return {
7410
+ generate: async (input) => {
7411
+ const response = await fetchImpl(`${baseUrl.replace(/\/$/, "")}/responses`, {
7412
+ body: JSON.stringify({
7413
+ input: input.messages.map(messageToOpenAIInput),
7414
+ instructions: [
7415
+ input.system,
7416
+ "Return a JSON object with assistantText, complete, transfer, escalate, voicemail, noAnswer, and result when you are not calling tools."
7417
+ ].filter(Boolean).join(`
7418
+
7419
+ `),
7420
+ max_output_tokens: options.maxOutputTokens,
7421
+ model,
7422
+ temperature: options.temperature,
7423
+ text: {
7424
+ format: {
7425
+ name: "voice_route_result",
7426
+ schema: OUTPUT_SCHEMA,
7427
+ strict: false,
7428
+ type: "json_schema"
7429
+ }
7430
+ },
7431
+ tool_choice: input.tools.length ? "auto" : "none",
7432
+ tools: input.tools.map((tool) => ({
7433
+ description: tool.description,
7434
+ name: tool.name,
7435
+ parameters: tool.parameters ?? {
7436
+ additionalProperties: true,
7437
+ type: "object"
7438
+ },
7439
+ strict: false,
7440
+ type: "function"
7441
+ }))
7442
+ }),
7443
+ headers: {
7444
+ authorization: `Bearer ${options.apiKey}`,
7445
+ "content-type": "application/json"
7446
+ },
7447
+ method: "POST"
7448
+ });
7449
+ if (!response.ok) {
7450
+ throw createHTTPError("OpenAI", response);
7451
+ }
7452
+ const body = await response.json();
7453
+ if (body.usage && typeof body.usage === "object") {
7454
+ await options.onUsage?.(body.usage);
7455
+ }
7456
+ const toolCalls = extractToolCalls(body);
7457
+ if (toolCalls.length) {
7458
+ return {
7459
+ toolCalls
7460
+ };
7461
+ }
7462
+ return normalizeRouteOutput(parseJSON(extractText(body)));
7463
+ }
7464
+ };
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
+ };
7076
7638
  // src/sqliteStore.ts
7077
7639
  import { Database } from "bun:sqlite";
7078
7640
  var normalizeTableNameSegment = (value) => value.trim().replace(/[^a-zA-Z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "voice";
@@ -9658,9 +10220,13 @@ export {
9658
10220
  createStoredVoiceCallReviewArtifact,
9659
10221
  createRiskyTurnCorrectionHandler,
9660
10222
  createPhraseHintCorrectionHandler,
10223
+ createOpenAIVoiceAssistantModel,
10224
+ createJSONVoiceAssistantModel,
9661
10225
  createId,
10226
+ createGeminiVoiceAssistantModel,
9662
10227
  createDomainPhraseHints,
9663
10228
  createDomainLexicon,
10229
+ createAnthropicVoiceAssistantModel,
9664
10230
  conditionAudioChunk,
9665
10231
  completeVoiceOpsTask,
9666
10232
  claimVoiceOpsTask,
@@ -0,0 +1,39 @@
1
+ import type { VoiceAgentModel, VoiceAgentModelInput, VoiceAgentModelOutput } from './agent';
2
+ import type { VoiceSessionRecord } from './types';
3
+ export type VoiceJSONAssistantModelHandler<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = (input: VoiceAgentModelInput<TContext, TSession>) => Promise<Record<string, unknown> | VoiceAgentModelOutput<TResult>> | Record<string, unknown> | VoiceAgentModelOutput<TResult>;
4
+ export type VoiceJSONAssistantModelOptions<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
5
+ generate: VoiceJSONAssistantModelHandler<TContext, TSession, TResult>;
6
+ mapOutput?: (output: Record<string, unknown>) => VoiceAgentModelOutput<TResult>;
7
+ };
8
+ export type OpenAIVoiceAssistantModelOptions = {
9
+ apiKey: string;
10
+ baseUrl?: string;
11
+ fetch?: typeof fetch;
12
+ maxOutputTokens?: number;
13
+ model?: string;
14
+ onUsage?: (usage: Record<string, unknown>) => Promise<void> | void;
15
+ temperature?: number;
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
+ };
36
+ export declare const createJSONVoiceAssistantModel: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: VoiceJSONAssistantModelOptions<TContext, TSession, TResult>) => VoiceAgentModel<TContext, TSession, TResult>;
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.4",
3
+ "version": "0.0.22-beta.6",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",