@absolutejs/voice 0.0.22-beta.10 → 0.0.22-beta.12

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 { createAnthropicVoiceAssistantModel, createGeminiVoiceAssistantModel, createJSONVoiceAssistantModel, createOpenAIVoiceAssistantModel } from './modelAdapters';
6
+ export { createAnthropicVoiceAssistantModel, createGeminiVoiceAssistantModel, createJSONVoiceAssistantModel, createOpenAIVoiceAssistantModel, createVoiceProviderRouter } 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 { AnthropicVoiceAssistantModelOptions, GeminiVoiceAssistantModelOptions, OpenAIVoiceAssistantModelOptions, VoiceJSONAssistantModelHandler, VoiceJSONAssistantModelOptions } from './modelAdapters';
29
+ export type { AnthropicVoiceAssistantModelOptions, GeminiVoiceAssistantModelOptions, OpenAIVoiceAssistantModelOptions, VoiceProviderRouterEvent, VoiceProviderRouterFallbackMode, VoiceProviderRouterOptions, VoiceProviderRouterPolicy, VoiceProviderRouterProviderProfile, 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
@@ -7183,6 +7183,8 @@ var createHTTPError = (provider, response) => new Error(`${provider} voice assis
7183
7183
  var sleep4 = (ms) => new Promise((resolve2) => {
7184
7184
  setTimeout(resolve2, ms);
7185
7185
  });
7186
+ var errorMessage = (error) => error instanceof Error ? error.message : String(error);
7187
+ var defaultIsRateLimitError = (error) => /(\b429\b|rate limit|quota|too many requests)/i.test(errorMessage(error));
7186
7188
  var normalizeRouteOutput = (output) => {
7187
7189
  const result = {};
7188
7190
  if (typeof output.assistantText === "string") {
@@ -7236,6 +7238,108 @@ var createJSONVoiceAssistantModel = (options) => ({
7236
7238
  return options.mapOutput?.(output) ?? normalizeRouteOutput(output);
7237
7239
  }
7238
7240
  });
7241
+ var createVoiceProviderRouter = (options) => {
7242
+ const providerIds = Object.keys(options.providers);
7243
+ const firstProvider = providerIds[0];
7244
+ const policy = typeof options.policy === "string" ? {
7245
+ strategy: options.policy
7246
+ } : options.policy;
7247
+ const strategy = policy?.strategy ?? "prefer-selected";
7248
+ const fallbackMode = policy?.fallbackMode ?? options.fallbackMode ?? "provider-error";
7249
+ const resolveAllowedProviders = async (input) => {
7250
+ const allowProviders = policy?.allowProviders ?? options.allowProviders;
7251
+ const allowed = typeof allowProviders === "function" ? await allowProviders(input) : allowProviders;
7252
+ return new Set(allowed ?? providerIds);
7253
+ };
7254
+ const sortProviders = (providers) => {
7255
+ if (strategy !== "prefer-cheapest" && strategy !== "prefer-fastest") {
7256
+ return providers;
7257
+ }
7258
+ return [...providers].sort((left, right) => {
7259
+ const leftProfile = options.providerProfiles?.[left];
7260
+ const rightProfile = options.providerProfiles?.[right];
7261
+ const leftValue = strategy === "prefer-cheapest" ? leftProfile?.cost ?? Number.MAX_SAFE_INTEGER : leftProfile?.latencyMs ?? Number.MAX_SAFE_INTEGER;
7262
+ const rightValue = strategy === "prefer-cheapest" ? rightProfile?.cost ?? Number.MAX_SAFE_INTEGER : rightProfile?.latencyMs ?? Number.MAX_SAFE_INTEGER;
7263
+ return leftValue - rightValue || (leftProfile?.priority ?? Number.MAX_SAFE_INTEGER) - (rightProfile?.priority ?? Number.MAX_SAFE_INTEGER);
7264
+ });
7265
+ };
7266
+ const resolveOrder = async (input) => {
7267
+ const selectedProvider = await options.selectProvider?.(input);
7268
+ const allowedProviders = await resolveAllowedProviders(input);
7269
+ const fallbackOrder = typeof options.fallback === "function" ? await options.fallback(input) : options.fallback;
7270
+ const rankedProviders = sortProviders([
7271
+ ...fallbackOrder ?? providerIds
7272
+ ]).filter((provider) => allowedProviders.has(provider));
7273
+ const preferred = selectedProvider && allowedProviders.has(selectedProvider) ? selectedProvider : rankedProviders[0] ?? firstProvider;
7274
+ const seen = new Set;
7275
+ const order = [];
7276
+ const candidates = strategy === "ordered" ? rankedProviders : [preferred, ...rankedProviders, ...providerIds];
7277
+ for (const provider of candidates) {
7278
+ if (!provider || seen.has(provider) || !allowedProviders.has(provider) || !options.providers[provider]) {
7279
+ continue;
7280
+ }
7281
+ seen.add(provider);
7282
+ order.push(provider);
7283
+ }
7284
+ return {
7285
+ order,
7286
+ selectedProvider: preferred
7287
+ };
7288
+ };
7289
+ const emit = async (event, input) => {
7290
+ await options.onProviderEvent?.(event, input);
7291
+ };
7292
+ return {
7293
+ generate: async (input) => {
7294
+ const { order, selectedProvider } = await resolveOrder(input);
7295
+ if (!selectedProvider || order.length === 0) {
7296
+ throw new Error("Voice provider router has no available providers.");
7297
+ }
7298
+ let lastError;
7299
+ for (const [index, provider] of order.entries()) {
7300
+ const model = options.providers[provider];
7301
+ if (!model) {
7302
+ continue;
7303
+ }
7304
+ const startedAt = Date.now();
7305
+ try {
7306
+ const output = await model.generate(input);
7307
+ await emit({
7308
+ at: Date.now(),
7309
+ elapsedMs: Date.now() - startedAt,
7310
+ fallbackProvider: provider === selectedProvider ? undefined : provider,
7311
+ provider,
7312
+ recovered: provider !== selectedProvider,
7313
+ selectedProvider,
7314
+ status: provider === selectedProvider ? "success" : "fallback"
7315
+ }, input);
7316
+ return output;
7317
+ } catch (error) {
7318
+ lastError = error;
7319
+ const hasNextProvider = index < order.length - 1;
7320
+ const isProviderError = options.isProviderError?.(error, provider) ?? true;
7321
+ const rateLimited = options.isRateLimitError?.(error, provider) ?? defaultIsRateLimitError(error);
7322
+ const shouldFallback = fallbackMode === "provider-error" ? isProviderError : fallbackMode === "rate-limit" ? isProviderError && rateLimited : false;
7323
+ const nextProvider = hasNextProvider ? order[index + 1] : undefined;
7324
+ await emit({
7325
+ at: Date.now(),
7326
+ elapsedMs: Date.now() - startedAt,
7327
+ error: errorMessage(error),
7328
+ fallbackProvider: shouldFallback ? nextProvider : undefined,
7329
+ provider,
7330
+ rateLimited,
7331
+ selectedProvider,
7332
+ status: "error"
7333
+ }, input);
7334
+ if (!hasNextProvider || !shouldFallback) {
7335
+ throw error;
7336
+ }
7337
+ }
7338
+ }
7339
+ throw lastError ?? new Error("Voice provider router did not run a provider.");
7340
+ }
7341
+ };
7342
+ };
7239
7343
  var messageToOpenAIInput = (message) => {
7240
7344
  if (message.role === "tool") {
7241
7345
  return [
@@ -8868,10 +8972,10 @@ var createVoiceOpsTaskProcessorWorker = (options) => ({
8868
8972
  result.completed += 1;
8869
8973
  } catch (error) {
8870
8974
  await options.onError?.(error, task);
8871
- const errorMessage = error instanceof Error ? error.message : String(error);
8975
+ const errorMessage2 = error instanceof Error ? error.message : String(error);
8872
8976
  const failedTask = failVoiceOpsTask(task, {
8873
8977
  actor: task.claimedBy ?? "ops-worker",
8874
- error: errorMessage
8978
+ error: errorMessage2
8875
8979
  });
8876
8980
  if (shouldDeadLetterTask(failedTask, options.maxFailures)) {
8877
8981
  const deadLetterTask = deadLetterVoiceOpsTask(failedTask, {
@@ -10198,6 +10302,7 @@ export {
10198
10302
  createVoiceReviewSavedEvent,
10199
10303
  createVoiceRedisTaskLeaseCoordinator,
10200
10304
  createVoiceRedisIdempotencyStore,
10305
+ createVoiceProviderRouter,
10201
10306
  createVoicePostgresTraceSinkDeliveryStore,
10202
10307
  createVoicePostgresTraceEventStore,
10203
10308
  createVoicePostgresTaskStore,
@@ -34,7 +34,42 @@ export type GeminiVoiceAssistantModelOptions = {
34
34
  onUsage?: (usage: Record<string, unknown>) => Promise<void> | void;
35
35
  temperature?: number;
36
36
  };
37
+ export type VoiceProviderRouterEvent<TProvider extends string = string> = {
38
+ at: number;
39
+ elapsedMs: number;
40
+ error?: string;
41
+ fallbackProvider?: TProvider;
42
+ provider: TProvider;
43
+ rateLimited?: boolean;
44
+ recovered?: boolean;
45
+ selectedProvider: TProvider;
46
+ status: 'error' | 'fallback' | 'success';
47
+ };
48
+ export type VoiceProviderRouterFallbackMode = 'never' | 'provider-error' | 'rate-limit';
49
+ export type VoiceProviderRouterPolicy<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TProvider extends string = string> = 'ordered' | 'prefer-cheapest' | 'prefer-fastest' | 'prefer-selected' | {
50
+ allowProviders?: readonly TProvider[] | ((input: VoiceAgentModelInput<TContext, TSession>) => readonly TProvider[] | Promise<readonly TProvider[]>);
51
+ fallbackMode?: VoiceProviderRouterFallbackMode;
52
+ strategy?: 'ordered' | 'prefer-cheapest' | 'prefer-fastest' | 'prefer-selected';
53
+ };
54
+ export type VoiceProviderRouterProviderProfile = {
55
+ cost?: number;
56
+ latencyMs?: number;
57
+ priority?: number;
58
+ };
59
+ export type VoiceProviderRouterOptions<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown, TProvider extends string = string> = {
60
+ allowProviders?: readonly TProvider[] | ((input: VoiceAgentModelInput<TContext, TSession>) => readonly TProvider[] | Promise<readonly TProvider[]>);
61
+ fallback?: TProvider[] | ((input: VoiceAgentModelInput<TContext, TSession>) => readonly TProvider[] | Promise<readonly TProvider[]>);
62
+ fallbackMode?: VoiceProviderRouterFallbackMode;
63
+ isProviderError?: (error: unknown, provider: TProvider) => boolean;
64
+ isRateLimitError?: (error: unknown, provider: TProvider) => boolean;
65
+ onProviderEvent?: (event: VoiceProviderRouterEvent<TProvider>, input: VoiceAgentModelInput<TContext, TSession>) => Promise<void> | void;
66
+ policy?: VoiceProviderRouterPolicy<TContext, TSession, TProvider>;
67
+ providerProfiles?: Partial<Record<TProvider, VoiceProviderRouterProviderProfile>>;
68
+ providers: Partial<Record<TProvider, VoiceAgentModel<TContext, TSession, TResult>>>;
69
+ selectProvider?: (input: VoiceAgentModelInput<TContext, TSession>) => TProvider | undefined | Promise<TProvider | undefined>;
70
+ };
37
71
  export declare const createJSONVoiceAssistantModel: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: VoiceJSONAssistantModelOptions<TContext, TSession, TResult>) => VoiceAgentModel<TContext, TSession, TResult>;
72
+ export declare const createVoiceProviderRouter: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown, TProvider extends string = string>(options: VoiceProviderRouterOptions<TContext, TSession, TResult, TProvider>) => VoiceAgentModel<TContext, TSession, TResult>;
38
73
  export declare const createOpenAIVoiceAssistantModel: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: OpenAIVoiceAssistantModelOptions) => VoiceAgentModel<TContext, TSession, TResult>;
39
74
  export declare const createAnthropicVoiceAssistantModel: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: AnthropicVoiceAssistantModelOptions) => VoiceAgentModel<TContext, TSession, TResult>;
40
75
  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.10",
3
+ "version": "0.0.22-beta.12",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",