* Poll a batch until processing_status is 'ended' or timeout.
(
client: Anthropic,
batchId: string,
options: { pollIntervalMs: number; timeoutMs: number; operationName: string },
)
| 112 | * Poll a batch until processing_status is 'ended' or timeout. |
| 113 | */ |
| 114 | async function pollForCompletion( |
| 115 | client: Anthropic, |
| 116 | batchId: string, |
| 117 | options: { pollIntervalMs: number; timeoutMs: number; operationName: string }, |
| 118 | ): Promise<MessageBatch> { |
| 119 | const deadline = Date.now() + options.timeoutMs; |
| 120 | |
| 121 | while (true) { |
| 122 | const batch = await client.messages.batches.retrieve(batchId); |
| 123 | |
| 124 | if (batch.processing_status === 'ended') { |
| 125 | logger.info( |
| 126 | { |
| 127 | batchId, |
| 128 | operation: options.operationName, |
| 129 | counts: batch.request_counts, |
| 130 | }, |
| 131 | 'Batch processing ended', |
| 132 | ); |
| 133 | return batch; |
| 134 | } |
| 135 | |
| 136 | if (Date.now() >= deadline) { |
| 137 | break; |
| 138 | } |
| 139 | |
| 140 | const remaining = batch.request_counts.processing; |
| 141 | const succeeded = batch.request_counts.succeeded; |
| 142 | logger.debug( |
| 143 | { batchId, processing: remaining, succeeded, operation: options.operationName }, |
| 144 | 'Batch still processing', |
| 145 | ); |
| 146 | |
| 147 | await sleep(Math.min(options.pollIntervalMs, deadline - Date.now())); |
| 148 | } |
| 149 | |
| 150 | // Timeout — cancel and throw |
| 151 | logger.warn({ batchId, operation: options.operationName }, 'Batch timed out, canceling'); |
| 152 | try { |
| 153 | await client.messages.batches.cancel(batchId); |
| 154 | } catch (cancelErr) { |
| 155 | logger.warn({ batchId, error: cancelErr }, 'Failed to cancel timed-out batch'); |
| 156 | } |
| 157 | throw new Error(`Batch ${batchId} timed out after ${options.timeoutMs}ms`); |
| 158 | } |
| 159 | |
| 160 | /** |
| 161 | * Stream JSONL results from a completed batch and return as a Map. |
no test coverage detected