@absolutejs/voice 0.0.22-beta.111 → 0.0.22-beta.112

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.
@@ -1,4 +1,5 @@
1
1
  import { Elysia } from 'elysia';
2
+ import type { VoiceRedisTaskLeaseCoordinator } from './queue';
2
3
  export type VoiceCampaignStatus = 'canceled' | 'completed' | 'draft' | 'paused' | 'running';
3
4
  export type VoiceCampaignRecipientStatus = 'canceled' | 'completed' | 'failed' | 'pending' | 'queued';
4
5
  export type VoiceCampaignAttemptStatus = 'canceled' | 'failed' | 'queued' | 'running' | 'succeeded';
@@ -157,9 +158,45 @@ export type VoiceCampaignRoutesOptions = VoiceCampaignRuntimeOptions & {
157
158
  path?: string;
158
159
  title?: string;
159
160
  };
161
+ export type VoiceCampaignWorkerOptions = {
162
+ campaignIds?: string[];
163
+ leaseMs?: number;
164
+ leases: VoiceRedisTaskLeaseCoordinator;
165
+ maxCampaigns?: number;
166
+ runtime?: VoiceCampaignRuntime;
167
+ statuses?: VoiceCampaignStatus[];
168
+ store?: VoiceCampaignStore;
169
+ workerId: string;
170
+ };
171
+ export type VoiceCampaignWorkerResult = {
172
+ attempted: number;
173
+ campaigns: number;
174
+ errors: Array<{
175
+ campaignId: string;
176
+ error: string;
177
+ }>;
178
+ skipped: number;
179
+ started: VoiceCampaignAttempt[];
180
+ };
181
+ export type VoiceCampaignWorker = {
182
+ drain: () => Promise<VoiceCampaignWorkerResult>;
183
+ };
184
+ export type VoiceCampaignWorkerLoopOptions = {
185
+ onError?: (error: unknown) => void | Promise<void>;
186
+ pollIntervalMs?: number;
187
+ worker: VoiceCampaignWorker;
188
+ };
189
+ export type VoiceCampaignWorkerLoop = {
190
+ isRunning: () => boolean;
191
+ start: () => void;
192
+ stop: () => void;
193
+ tick: () => Promise<VoiceCampaignWorkerResult>;
194
+ };
160
195
  export declare const createVoiceMemoryCampaignStore: () => VoiceCampaignStore;
161
196
  export declare const summarizeVoiceCampaigns: (records: VoiceCampaignRecord[]) => VoiceCampaignSummary;
162
197
  export declare const createVoiceCampaign: (options: VoiceCampaignRuntimeOptions) => VoiceCampaignRuntime;
198
+ export declare const createVoiceCampaignWorker: (options: VoiceCampaignWorkerOptions) => VoiceCampaignWorker;
199
+ export declare const createVoiceCampaignWorkerLoop: (options: VoiceCampaignWorkerLoopOptions) => VoiceCampaignWorkerLoop;
163
200
  export declare const runVoiceCampaignProof: (options?: VoiceCampaignProofOptions) => Promise<VoiceCampaignProofReport>;
164
201
  export declare const renderVoiceCampaignsHTML: (records: VoiceCampaignRecord[], options?: {
165
202
  title?: string;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { voice } from './plugin';
2
2
  export { createVoiceAppKit, createVoiceAppKitRoutes, summarizeVoiceAppKitStatus } from './appKit';
3
- export { createVoiceCampaign, createVoiceCampaignRoutes, createVoiceMemoryCampaignStore, renderVoiceCampaignsHTML, runVoiceCampaignProof, summarizeVoiceCampaigns } from './campaign';
3
+ export { createVoiceCampaign, createVoiceCampaignRoutes, createVoiceCampaignWorker, createVoiceCampaignWorkerLoop, createVoiceMemoryCampaignStore, renderVoiceCampaignsHTML, runVoiceCampaignProof, summarizeVoiceCampaigns } from './campaign';
4
4
  export { createVoiceAssistant, createVoiceExperiment, summarizeVoiceAssistantRuns } from './assistant';
5
5
  export { createVoiceAssistantHealthHTMLHandler, createVoiceAssistantHealthJSONHandler, createVoiceAssistantHealthRoutes, renderVoiceAssistantHealthHTML, summarizeVoiceAssistantHealth } from './assistantHealth';
6
6
  export { createVoiceBargeInRoutes, renderVoiceBargeInHTML, summarizeVoiceBargeIn } from './bargeInRoutes';
package/dist/index.js CHANGED
@@ -10794,6 +10794,90 @@ var createVoiceCampaign = (options) => {
10794
10794
  }
10795
10795
  };
10796
10796
  };
10797
+ var getCampaignLeaseTaskId = (campaignId) => `voice-campaign:${campaignId}`;
10798
+ var createVoiceCampaignWorker = (options) => {
10799
+ const leaseMs = Math.max(1, options.leaseMs ?? 30000);
10800
+ const runtime = options.runtime ?? createVoiceCampaign({
10801
+ store: options.store ?? createVoiceMemoryCampaignStore()
10802
+ });
10803
+ const statuses = options.statuses ?? ["running"];
10804
+ const campaignIds = new Set(options.campaignIds);
10805
+ const maxCampaigns = Math.max(1, options.maxCampaigns ?? Number.MAX_SAFE_INTEGER);
10806
+ return {
10807
+ drain: async () => {
10808
+ const result = {
10809
+ attempted: 0,
10810
+ campaigns: 0,
10811
+ errors: [],
10812
+ skipped: 0,
10813
+ started: []
10814
+ };
10815
+ const campaigns = [...await runtime.list()].filter((record) => (campaignIds.size === 0 || campaignIds.has(record.campaign.id)) && statuses.includes(record.campaign.status)).sort((left, right) => left.campaign.createdAt - right.campaign.createdAt).slice(0, maxCampaigns);
10816
+ for (const record of campaigns) {
10817
+ const campaignId = record.campaign.id;
10818
+ const taskId = getCampaignLeaseTaskId(campaignId);
10819
+ const claimed = await options.leases.claim({
10820
+ leaseMs,
10821
+ taskId,
10822
+ workerId: options.workerId
10823
+ });
10824
+ if (!claimed) {
10825
+ result.skipped += 1;
10826
+ continue;
10827
+ }
10828
+ try {
10829
+ const tick = await runtime.tick(campaignId);
10830
+ result.campaigns += 1;
10831
+ result.attempted += tick.attempted;
10832
+ result.started.push(...tick.started);
10833
+ result.errors.push(...tick.errors.map((error) => ({
10834
+ campaignId,
10835
+ error: error.error
10836
+ })));
10837
+ } catch (error) {
10838
+ result.errors.push({
10839
+ campaignId,
10840
+ error: error instanceof Error ? error.message : String(error)
10841
+ });
10842
+ } finally {
10843
+ await options.leases.release({
10844
+ taskId,
10845
+ workerId: options.workerId
10846
+ });
10847
+ }
10848
+ }
10849
+ return result;
10850
+ }
10851
+ };
10852
+ };
10853
+ var createVoiceCampaignWorkerLoop = (options) => {
10854
+ const pollIntervalMs = Math.max(1, options.pollIntervalMs ?? 1000);
10855
+ let timer;
10856
+ let running = false;
10857
+ const tick = async () => options.worker.drain();
10858
+ return {
10859
+ isRunning: () => running,
10860
+ start: () => {
10861
+ if (timer) {
10862
+ return;
10863
+ }
10864
+ running = true;
10865
+ timer = setInterval(() => {
10866
+ tick().catch((error) => {
10867
+ options.onError?.(error);
10868
+ });
10869
+ }, pollIntervalMs);
10870
+ },
10871
+ stop: () => {
10872
+ if (timer) {
10873
+ clearInterval(timer);
10874
+ timer = undefined;
10875
+ }
10876
+ running = false;
10877
+ },
10878
+ tick
10879
+ };
10880
+ };
10797
10881
  var defaultProofRecipients = () => [
10798
10882
  {
10799
10883
  id: "campaign-proof-recipient-1",
@@ -19123,6 +19207,8 @@ export {
19123
19207
  createVoiceExperiment,
19124
19208
  createVoiceEvalRoutes,
19125
19209
  createVoiceDiagnosticsRoutes,
19210
+ createVoiceCampaignWorkerLoop,
19211
+ createVoiceCampaignWorker,
19126
19212
  createVoiceCampaignRoutes,
19127
19213
  createVoiceCampaign,
19128
19214
  createVoiceCallReviewRecorder,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.111",
3
+ "version": "0.0.22-beta.112",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",