@absolutejs/voice 0.0.22-beta.12 → 0.0.22-beta.13
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 +1 -1
- package/dist/index.js +61 -2
- package/dist/modelAdapters.d.ts +15 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -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, VoiceProviderRouterEvent, VoiceProviderRouterFallbackMode, VoiceProviderRouterOptions, VoiceProviderRouterPolicy, VoiceProviderRouterProviderProfile, VoiceJSONAssistantModelHandler, VoiceJSONAssistantModelOptions } from './modelAdapters';
|
|
29
|
+
export type { AnthropicVoiceAssistantModelOptions, GeminiVoiceAssistantModelOptions, OpenAIVoiceAssistantModelOptions, VoiceProviderRouterEvent, VoiceProviderRouterFallbackMode, VoiceProviderRouterHealthOptions, VoiceProviderRouterOptions, VoiceProviderRouterPolicy, VoiceProviderRouterProviderHealth, 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
|
@@ -7246,6 +7246,57 @@ var createVoiceProviderRouter = (options) => {
|
|
|
7246
7246
|
} : options.policy;
|
|
7247
7247
|
const strategy = policy?.strategy ?? "prefer-selected";
|
|
7248
7248
|
const fallbackMode = policy?.fallbackMode ?? options.fallbackMode ?? "provider-error";
|
|
7249
|
+
const healthOptions = typeof options.providerHealth === "object" ? options.providerHealth : options.providerHealth ? {} : undefined;
|
|
7250
|
+
const healthState = new Map;
|
|
7251
|
+
const now = () => healthOptions?.now?.() ?? Date.now();
|
|
7252
|
+
const failureThreshold = Math.max(1, healthOptions?.failureThreshold ?? 1);
|
|
7253
|
+
const cooldownMs = Math.max(0, healthOptions?.cooldownMs ?? 30000);
|
|
7254
|
+
const rateLimitCooldownMs = Math.max(0, healthOptions?.rateLimitCooldownMs ?? 60000);
|
|
7255
|
+
const getHealth = (provider) => {
|
|
7256
|
+
const existing = healthState.get(provider);
|
|
7257
|
+
if (existing) {
|
|
7258
|
+
return existing;
|
|
7259
|
+
}
|
|
7260
|
+
const next = {
|
|
7261
|
+
consecutiveFailures: 0,
|
|
7262
|
+
provider,
|
|
7263
|
+
status: "healthy"
|
|
7264
|
+
};
|
|
7265
|
+
healthState.set(provider, next);
|
|
7266
|
+
return next;
|
|
7267
|
+
};
|
|
7268
|
+
const isSuppressed = (provider) => {
|
|
7269
|
+
if (!healthOptions) {
|
|
7270
|
+
return false;
|
|
7271
|
+
}
|
|
7272
|
+
const health = getHealth(provider);
|
|
7273
|
+
return typeof health.suppressedUntil === "number" && health.suppressedUntil > now();
|
|
7274
|
+
};
|
|
7275
|
+
const recordProviderSuccess = (provider) => {
|
|
7276
|
+
if (!healthOptions) {
|
|
7277
|
+
return;
|
|
7278
|
+
}
|
|
7279
|
+
const health = getHealth(provider);
|
|
7280
|
+
health.consecutiveFailures = 0;
|
|
7281
|
+
health.status = "healthy";
|
|
7282
|
+
health.suppressedUntil = undefined;
|
|
7283
|
+
};
|
|
7284
|
+
const recordProviderError = (provider, isProviderError, rateLimited) => {
|
|
7285
|
+
if (!healthOptions || !isProviderError) {
|
|
7286
|
+
return;
|
|
7287
|
+
}
|
|
7288
|
+
const currentTime = now();
|
|
7289
|
+
const health = getHealth(provider);
|
|
7290
|
+
health.consecutiveFailures += 1;
|
|
7291
|
+
health.lastFailureAt = currentTime;
|
|
7292
|
+
if (rateLimited) {
|
|
7293
|
+
health.lastRateLimitedAt = currentTime;
|
|
7294
|
+
}
|
|
7295
|
+
if (rateLimited || health.consecutiveFailures >= failureThreshold) {
|
|
7296
|
+
health.status = "suppressed";
|
|
7297
|
+
health.suppressedUntil = currentTime + (rateLimited ? rateLimitCooldownMs : cooldownMs);
|
|
7298
|
+
}
|
|
7299
|
+
};
|
|
7249
7300
|
const resolveAllowedProviders = async (input) => {
|
|
7250
7301
|
const allowProviders = policy?.allowProviders ?? options.allowProviders;
|
|
7251
7302
|
const allowed = typeof allowProviders === "function" ? await allowProviders(input) : allowProviders;
|
|
@@ -7270,10 +7321,16 @@ var createVoiceProviderRouter = (options) => {
|
|
|
7270
7321
|
const rankedProviders = sortProviders([
|
|
7271
7322
|
...fallbackOrder ?? providerIds
|
|
7272
7323
|
]).filter((provider) => allowedProviders.has(provider));
|
|
7273
|
-
const
|
|
7324
|
+
const healthyRankedProviders = healthOptions ? rankedProviders.filter((provider) => !isSuppressed(provider)) : rankedProviders;
|
|
7325
|
+
const candidateRankedProviders = healthyRankedProviders.length ? healthyRankedProviders : rankedProviders;
|
|
7326
|
+
const preferred = selectedProvider && allowedProviders.has(selectedProvider) && (!healthOptions || !isSuppressed(selectedProvider)) ? selectedProvider : candidateRankedProviders[0] ?? firstProvider;
|
|
7274
7327
|
const seen = new Set;
|
|
7275
7328
|
const order = [];
|
|
7276
|
-
const candidates = strategy === "ordered" ?
|
|
7329
|
+
const candidates = strategy === "ordered" ? candidateRankedProviders : [
|
|
7330
|
+
preferred,
|
|
7331
|
+
...candidateRankedProviders,
|
|
7332
|
+
...providerIds.filter((provider) => !healthOptions || !isSuppressed(provider))
|
|
7333
|
+
];
|
|
7277
7334
|
for (const provider of candidates) {
|
|
7278
7335
|
if (!provider || seen.has(provider) || !allowedProviders.has(provider) || !options.providers[provider]) {
|
|
7279
7336
|
continue;
|
|
@@ -7304,6 +7361,7 @@ var createVoiceProviderRouter = (options) => {
|
|
|
7304
7361
|
const startedAt = Date.now();
|
|
7305
7362
|
try {
|
|
7306
7363
|
const output = await model.generate(input);
|
|
7364
|
+
recordProviderSuccess(provider);
|
|
7307
7365
|
await emit({
|
|
7308
7366
|
at: Date.now(),
|
|
7309
7367
|
elapsedMs: Date.now() - startedAt,
|
|
@@ -7320,6 +7378,7 @@ var createVoiceProviderRouter = (options) => {
|
|
|
7320
7378
|
const isProviderError = options.isProviderError?.(error, provider) ?? true;
|
|
7321
7379
|
const rateLimited = options.isRateLimitError?.(error, provider) ?? defaultIsRateLimitError(error);
|
|
7322
7380
|
const shouldFallback = fallbackMode === "provider-error" ? isProviderError : fallbackMode === "rate-limit" ? isProviderError && rateLimited : false;
|
|
7381
|
+
recordProviderError(provider, isProviderError, rateLimited);
|
|
7323
7382
|
const nextProvider = hasNextProvider ? order[index + 1] : undefined;
|
|
7324
7383
|
await emit({
|
|
7325
7384
|
at: Date.now(),
|
package/dist/modelAdapters.d.ts
CHANGED
|
@@ -56,6 +56,20 @@ export type VoiceProviderRouterProviderProfile = {
|
|
|
56
56
|
latencyMs?: number;
|
|
57
57
|
priority?: number;
|
|
58
58
|
};
|
|
59
|
+
export type VoiceProviderRouterHealthOptions = {
|
|
60
|
+
cooldownMs?: number;
|
|
61
|
+
failureThreshold?: number;
|
|
62
|
+
now?: () => number;
|
|
63
|
+
rateLimitCooldownMs?: number;
|
|
64
|
+
};
|
|
65
|
+
export type VoiceProviderRouterProviderHealth<TProvider extends string = string> = {
|
|
66
|
+
consecutiveFailures: number;
|
|
67
|
+
lastFailureAt?: number;
|
|
68
|
+
lastRateLimitedAt?: number;
|
|
69
|
+
provider: TProvider;
|
|
70
|
+
status: 'healthy' | 'suppressed';
|
|
71
|
+
suppressedUntil?: number;
|
|
72
|
+
};
|
|
59
73
|
export type VoiceProviderRouterOptions<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown, TProvider extends string = string> = {
|
|
60
74
|
allowProviders?: readonly TProvider[] | ((input: VoiceAgentModelInput<TContext, TSession>) => readonly TProvider[] | Promise<readonly TProvider[]>);
|
|
61
75
|
fallback?: TProvider[] | ((input: VoiceAgentModelInput<TContext, TSession>) => readonly TProvider[] | Promise<readonly TProvider[]>);
|
|
@@ -64,6 +78,7 @@ export type VoiceProviderRouterOptions<TContext = unknown, TSession extends Voic
|
|
|
64
78
|
isRateLimitError?: (error: unknown, provider: TProvider) => boolean;
|
|
65
79
|
onProviderEvent?: (event: VoiceProviderRouterEvent<TProvider>, input: VoiceAgentModelInput<TContext, TSession>) => Promise<void> | void;
|
|
66
80
|
policy?: VoiceProviderRouterPolicy<TContext, TSession, TProvider>;
|
|
81
|
+
providerHealth?: boolean | VoiceProviderRouterHealthOptions;
|
|
67
82
|
providerProfiles?: Partial<Record<TProvider, VoiceProviderRouterProviderProfile>>;
|
|
68
83
|
providers: Partial<Record<TProvider, VoiceAgentModel<TContext, TSession, TResult>>>;
|
|
69
84
|
selectProvider?: (input: VoiceAgentModelInput<TContext, TSession>) => TProvider | undefined | Promise<TProvider | undefined>;
|