batchwork 0.0.0 → 1.0.0

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.
Files changed (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -0
  3. package/dist/batch.d.ts +27 -0
  4. package/dist/batch.d.ts.map +1 -0
  5. package/dist/body.d.ts +18 -0
  6. package/dist/body.d.ts.map +1 -0
  7. package/dist/chunk-ab2d71gk.js +325 -0
  8. package/dist/chunk-ab2d71gk.js.map +12 -0
  9. package/dist/chunk-kv3847wy.js +1048 -0
  10. package/dist/chunk-kv3847wy.js.map +24 -0
  11. package/dist/chunk-v0bahtg2.js +7 -0
  12. package/dist/chunk-v0bahtg2.js.map +9 -0
  13. package/dist/chunk-zp2cxkyb.js +226 -0
  14. package/dist/chunk-zp2cxkyb.js.map +13 -0
  15. package/dist/errors.d.ts +16 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/http.d.ts +5 -0
  18. package/dist/http.d.ts.map +1 -0
  19. package/dist/index.d.ts +6 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +30 -0
  22. package/dist/index.js.map +9 -0
  23. package/dist/job.d.ts +31 -0
  24. package/dist/job.d.ts.map +1 -0
  25. package/dist/jsonl.d.ts +17 -0
  26. package/dist/jsonl.d.ts.map +1 -0
  27. package/dist/model.d.ts +32 -0
  28. package/dist/model.d.ts.map +1 -0
  29. package/dist/next/index.d.ts +59 -0
  30. package/dist/next/index.d.ts.map +1 -0
  31. package/dist/next/index.js +61 -0
  32. package/dist/next/index.js.map +10 -0
  33. package/dist/postgres/batch-store.d.ts +19 -0
  34. package/dist/postgres/batch-store.d.ts.map +1 -0
  35. package/dist/postgres/index.d.ts +6 -0
  36. package/dist/postgres/index.d.ts.map +1 -0
  37. package/dist/postgres/index.js +101 -0
  38. package/dist/postgres/index.js.map +12 -0
  39. package/dist/postgres/migrate.d.ts +15 -0
  40. package/dist/postgres/migrate.d.ts.map +1 -0
  41. package/dist/postgres/types.d.ts +26 -0
  42. package/dist/postgres/types.d.ts.map +1 -0
  43. package/dist/providers/adapter.d.ts +24 -0
  44. package/dist/providers/adapter.d.ts.map +1 -0
  45. package/dist/providers/anthropic.d.ts +3 -0
  46. package/dist/providers/anthropic.d.ts.map +1 -0
  47. package/dist/providers/google.d.ts +8 -0
  48. package/dist/providers/google.d.ts.map +1 -0
  49. package/dist/providers/groq.d.ts +8 -0
  50. package/dist/providers/groq.d.ts.map +1 -0
  51. package/dist/providers/index.d.ts +5 -0
  52. package/dist/providers/index.d.ts.map +1 -0
  53. package/dist/providers/mistral.d.ts +8 -0
  54. package/dist/providers/mistral.d.ts.map +1 -0
  55. package/dist/providers/openai-compatible.d.ts +41 -0
  56. package/dist/providers/openai-compatible.d.ts.map +1 -0
  57. package/dist/providers/openai.d.ts +7 -0
  58. package/dist/providers/openai.d.ts.map +1 -0
  59. package/dist/providers/shared.d.ts +23 -0
  60. package/dist/providers/shared.d.ts.map +1 -0
  61. package/dist/providers/together.d.ts +8 -0
  62. package/dist/providers/together.d.ts.map +1 -0
  63. package/dist/providers/xai.d.ts +8 -0
  64. package/dist/providers/xai.d.ts.map +1 -0
  65. package/dist/redis/batch-store.d.ts +15 -0
  66. package/dist/redis/batch-store.d.ts.map +1 -0
  67. package/dist/redis/index.d.ts +4 -0
  68. package/dist/redis/index.d.ts.map +1 -0
  69. package/dist/redis/index.js +55 -0
  70. package/dist/redis/index.js.map +11 -0
  71. package/dist/redis/types.d.ts +19 -0
  72. package/dist/redis/types.d.ts.map +1 -0
  73. package/dist/server/events.d.ts +5 -0
  74. package/dist/server/events.d.ts.map +1 -0
  75. package/dist/server/index.d.ts +7 -0
  76. package/dist/server/index.d.ts.map +1 -0
  77. package/dist/server/index.js +19 -0
  78. package/dist/server/index.js.map +9 -0
  79. package/dist/server/poller.d.ts +60 -0
  80. package/dist/server/poller.d.ts.map +1 -0
  81. package/dist/server/signing.d.ts +20 -0
  82. package/dist/server/signing.d.ts.map +1 -0
  83. package/dist/server/store.d.ts +4 -0
  84. package/dist/server/store.d.ts.map +1 -0
  85. package/dist/server/types.d.ts +37 -0
  86. package/dist/server/types.d.ts.map +1 -0
  87. package/dist/types.d.ts +118 -0
  88. package/dist/types.d.ts.map +1 -0
  89. package/dist/util.d.ts +10 -0
  90. package/dist/util.d.ts.map +1 -0
  91. package/package.json +119 -7
@@ -0,0 +1,24 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/errors.ts", "../src/job.ts", "../src/http.ts", "../src/jsonl.ts", "../src/util.ts", "../src/providers/anthropic.ts", "../src/providers/google.ts", "../src/providers/shared.ts", "../src/providers/openai-compatible.ts", "../src/providers/groq.ts", "../src/providers/mistral.ts", "../src/providers/openai.ts", "../src/providers/together.ts", "../src/providers/xai.ts", "../src/providers/index.ts"],
4
+ "sourcesContent": [
5
+ "// oxlint-disable max-classes-per-file -- the error hierarchy is co-located by design.\n\n/** Base error for all batchwork failures. */\nexport class BatchworkError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"BatchworkError\";\n }\n}\n\n/** Thrown when a model resolves to a provider without a batch adapter. */\nexport class UnsupportedProviderError extends BatchworkError {\n readonly provider: string;\n\n constructor(provider: string) {\n super(\n `batchwork: provider \"${provider}\" is not supported yet. Supported providers: openai, anthropic, google, groq, mistral, together, xai.`\n );\n this.name = \"UnsupportedProviderError\";\n this.provider = provider;\n }\n}\n\n/** Thrown when an optional provider package is not installed. */\nexport class MissingDependencyError extends BatchworkError {\n constructor(pkg: string, provider: string) {\n super(\n `batchwork: install \\`${pkg}\\` to batch ${provider} models (\\`npm install ${pkg}\\`).`\n );\n this.name = \"MissingDependencyError\";\n }\n}\n",
6
+ "import { BatchworkError } from \"./errors\";\nimport type { BatchAdapter } from \"./providers/adapter\";\nimport type {\n BatchProvider,\n BatchRequestCounts,\n BatchResult,\n BatchSnapshot,\n BatchStatus,\n ProviderCredentials,\n WaitOptions,\n} from \"./types\";\n\nconst DEFAULT_POLL_INTERVAL_MS = 15_000;\n\nconst TERMINAL_STATUSES: ReadonlySet<BatchStatus> = new Set<BatchStatus>([\n \"completed\",\n \"failed\",\n \"expired\",\n \"cancelled\",\n]);\n\n/** Whether a status means the batch has finished processing. */\nexport const isTerminalStatus = (status: BatchStatus): boolean =>\n TERMINAL_STATUSES.has(status);\n\nconst delay = (ms: number, signal?: AbortSignal): Promise<void> =>\n // oxlint-disable-next-line promise/avoid-new -- wrapping the callback-based setTimeout.\n new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new BatchworkError(\"batchwork: wait aborted.\"));\n return;\n }\n const timer = setTimeout(resolve, ms);\n signal?.addEventListener(\n \"abort\",\n () => {\n clearTimeout(timer);\n reject(new BatchworkError(\"batchwork: wait aborted.\"));\n },\n { once: true }\n );\n });\n\n/**\n * A handle to a submitted batch. Returned by `batch()` and `getBatch()`. Use it\n * to poll status, wait for completion, stream results, or cancel.\n */\nexport class BatchJob {\n readonly provider: BatchProvider;\n readonly id: string;\n\n readonly #adapter: BatchAdapter;\n readonly #credentials: ProviderCredentials;\n #snapshot: BatchSnapshot;\n\n constructor(\n adapter: BatchAdapter,\n credentials: ProviderCredentials,\n snapshot: BatchSnapshot\n ) {\n this.#adapter = adapter;\n this.#credentials = credentials;\n this.#snapshot = snapshot;\n this.id = snapshot.id;\n this.provider = snapshot.provider;\n }\n\n /** The most recently observed status. */\n get status(): BatchStatus {\n return this.#snapshot.status;\n }\n\n /** The most recently observed per-request tallies. */\n get requestCounts(): BatchRequestCounts {\n return this.#snapshot.requestCounts;\n }\n\n /** The most recently observed snapshot. */\n get snapshot(): BatchSnapshot {\n return this.#snapshot;\n }\n\n /** Refresh the status from the provider and return the new snapshot. */\n async poll(): Promise<BatchSnapshot> {\n this.#snapshot = await this.#adapter.retrieve(this.id, this.#credentials);\n return this.#snapshot;\n }\n\n /** Poll until the batch reaches a terminal status, then return the snapshot. */\n async wait(options: WaitOptions = {}): Promise<BatchSnapshot> {\n const interval = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const deadline = options.timeoutMs\n ? Date.now() + options.timeoutMs\n : undefined;\n\n let snapshot = await this.poll();\n options.onPoll?.(snapshot);\n\n while (!isTerminalStatus(snapshot.status)) {\n if (options.signal?.aborted) {\n throw new BatchworkError(\"batchwork: wait aborted.\");\n }\n if (deadline !== undefined && Date.now() > deadline) {\n throw new BatchworkError(\n `batchwork: timed out waiting for batch \"${this.id}\".`\n );\n }\n // oxlint-disable-next-line no-await-in-loop -- polling is inherently sequential.\n await delay(interval, options.signal);\n // oxlint-disable-next-line no-await-in-loop -- polling is inherently sequential.\n snapshot = await this.poll();\n options.onPoll?.(snapshot);\n }\n\n return snapshot;\n }\n\n /** Stream normalized results as they are read. Order is not guaranteed. */\n results(): AsyncGenerator<BatchResult> {\n return this.#adapter.results(this.id, this.#credentials);\n }\n\n /** Collect all results into an array, keyed by `customId`. */\n async collect(): Promise<BatchResult[]> {\n const out: BatchResult[] = [];\n for await (const result of this.results()) {\n out.push(result);\n }\n return out;\n }\n\n /** Request cancellation, then refresh status. */\n async cancel(): Promise<BatchSnapshot> {\n await this.#adapter.cancel(this.id, this.#credentials);\n return await this.poll();\n }\n}\n",
7
+ "import { BatchworkError } from \"./errors\";\n\nconst safeText = async (response: Response): Promise<string> => {\n try {\n return await response.text();\n } catch {\n return \"<no body>\";\n }\n};\n\nconst assertOk = async (url: string, init: RequestInit, response: Response) => {\n if (!response.ok) {\n const detail = await safeText(response);\n throw new BatchworkError(\n `batchwork: ${init.method ?? \"GET\"} ${url} failed with ${response.status}: ${detail}`\n );\n }\n};\n\n/** Make a request and parse a JSON response, throwing on non-2xx. */\nexport const requestJson = async <T>(\n url: string,\n init: RequestInit\n): Promise<T> => {\n const response = await fetch(url, init);\n await assertOk(url, init, response);\n return (await response.json()) as T;\n};\n\n/** Make a request and return the raw body stream, throwing on non-2xx. */\nexport const requestStream = async (\n url: string,\n init: RequestInit\n): Promise<ReadableStream<Uint8Array>> => {\n const response = await fetch(url, init);\n await assertOk(url, init, response);\n if (!response.body) {\n throw new BatchworkError(`batchwork: ${url} returned an empty body.`);\n }\n return response.body;\n};\n",
8
+ "/**\n * JSONL (newline-delimited JSON) helpers. OpenAI batch input/output and\n * Anthropic batch results are all JSONL, so batchwork builds and parses it for\n * the user.\n */\n\nconst NEWLINE = \"\\n\";\n\n/** Serialize an array of values to a JSONL string (trailing newline included). */\nexport const encodeJsonl = (items: readonly unknown[]): string => {\n if (items.length === 0) {\n return \"\";\n }\n const body = items.map((item) => JSON.stringify(item)).join(NEWLINE);\n return `${body}${NEWLINE}`;\n};\n\n/** Parse a complete JSONL string into an array, skipping blank lines. */\nexport const parseJsonl = <T = unknown>(text: string): T[] => {\n const results: T[] = [];\n for (const line of text.split(NEWLINE)) {\n const trimmed = line.trim();\n if (trimmed.length > 0) {\n results.push(JSON.parse(trimmed) as T);\n }\n }\n return results;\n};\n\nconst isReadableStream = (\n source: ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>\n): source is ReadableStream<Uint8Array> =>\n \"getReader\" in source &&\n typeof (source as ReadableStream<Uint8Array>).getReader === \"function\";\n\n// oxlint-disable-next-line func-style -- generators cannot be arrow functions.\nasync function* toByteIterable(\n source: ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>\n): AsyncGenerator<Uint8Array> {\n // Prefer an explicit reader: a web `ReadableStream` (e.g. `fetch` response\n // bodies) is not reliably async-iterable at runtime across platforms.\n if (isReadableStream(source)) {\n const reader = source.getReader();\n try {\n let chunk = await reader.read();\n while (!chunk.done) {\n if (chunk.value) {\n yield chunk.value;\n }\n // oxlint-disable-next-line no-await-in-loop -- a stream is read sequentially.\n chunk = await reader.read();\n }\n } finally {\n reader.releaseLock();\n }\n return;\n }\n\n yield* source;\n}\n\n/**\n * Stream-parse JSONL from a byte stream, yielding one parsed value per line as\n * it arrives. Memory-efficient for large result files.\n *\n * @yields {T} the parsed value for each non-empty line.\n */\n// oxlint-disable-next-line func-style -- generators cannot be arrow functions.\nexport async function* streamJsonl<T = unknown>(\n source: ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>\n): AsyncGenerator<T> {\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n for await (const chunk of toByteIterable(source)) {\n buffer += decoder.decode(chunk, { stream: true });\n let newlineIndex = buffer.indexOf(NEWLINE);\n while (newlineIndex !== -1) {\n const line = buffer.slice(0, newlineIndex).trim();\n buffer = buffer.slice(newlineIndex + 1);\n if (line.length > 0) {\n yield JSON.parse(line) as T;\n }\n newlineIndex = buffer.indexOf(NEWLINE);\n }\n }\n\n buffer += decoder.decode();\n const tail = buffer.trim();\n if (tail.length > 0) {\n yield JSON.parse(tail) as T;\n }\n}\n",
9
+ "/** Small, defensive helpers for reading loosely-typed provider JSON. */\n\nexport const asRecord = (value: unknown): Record<string, unknown> => {\n if (typeof value === \"object\" && value !== null) {\n return value as Record<string, unknown>;\n }\n return {};\n};\n\nexport const asString = (value: unknown): string | undefined =>\n typeof value === \"string\" ? value : undefined;\n\nexport const asNumber = (value: unknown): number | undefined =>\n typeof value === \"number\" ? value : undefined;\n\nexport const asArray = (value: unknown): unknown[] =>\n Array.isArray(value) ? value : [];\n\n/** Return a shallow copy of `obj` without `key`. */\nexport const omit = (\n obj: Record<string, unknown>,\n key: string\n): Record<string, unknown> => {\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (k !== key) {\n result[k] = v;\n }\n }\n return result;\n};\n\n/** Coerce a provider timestamp (ISO string or unix seconds) to a `Date`. */\nexport const toDate = (value: unknown): Date | undefined => {\n if (typeof value === \"string\") {\n return new Date(value);\n }\n if (typeof value === \"number\") {\n return new Date(value * 1000);\n }\n};\n",
10
+ "import { BatchworkError } from \"../errors\";\nimport { requestJson, requestStream } from \"../http\";\nimport { streamJsonl } from \"../jsonl\";\nimport type {\n BatchResult,\n BatchSnapshot,\n BatchStatus,\n BatchUsage,\n ProviderCredentials,\n} from \"../types\";\nimport { asArray, asNumber, asRecord, asString, omit, toDate } from \"../util\";\nimport type { BatchAdapter, SubmitInput } from \"./adapter\";\n\nconst ANTHROPIC_BASE = \"https://api.anthropic.com\";\nconst ANTHROPIC_VERSION = \"2023-06-01\";\n\nconst apiKey = (credentials: ProviderCredentials): string => {\n const key = credentials.apiKey ?? process.env.ANTHROPIC_API_KEY;\n if (!key) {\n throw new BatchworkError(\n \"batchwork: missing Anthropic API key. Set ANTHROPIC_API_KEY or pass `apiKey`.\"\n );\n }\n return key;\n};\n\nconst baseUrl = (credentials: ProviderCredentials): string =>\n credentials.baseURL ?? ANTHROPIC_BASE;\n\nconst headers = (credentials: ProviderCredentials): Record<string, string> => ({\n \"anthropic-version\": ANTHROPIC_VERSION,\n \"content-type\": \"application/json\",\n \"x-api-key\": apiKey(credentials),\n ...credentials.headers,\n});\n\nconst mapStatus = (status: string | undefined): BatchStatus => {\n if (status === \"ended\") {\n return \"completed\";\n }\n if (status === \"canceling\") {\n return \"cancelling\";\n }\n return \"in_progress\";\n};\n\nconst normalizeSnapshot = (raw: unknown): BatchSnapshot => {\n const obj = asRecord(raw);\n const counts = asRecord(obj.request_counts);\n const succeeded = asNumber(counts.succeeded) ?? 0;\n const errored = asNumber(counts.errored) ?? 0;\n const processing = asNumber(counts.processing) ?? 0;\n const canceled = asNumber(counts.canceled) ?? 0;\n const expired = asNumber(counts.expired) ?? 0;\n\n return {\n completedAt: toDate(obj.ended_at),\n createdAt: toDate(obj.created_at),\n expiresAt: toDate(obj.expires_at),\n id: asString(obj.id) ?? \"\",\n provider: \"anthropic\",\n raw,\n requestCounts: {\n canceled,\n completed: succeeded,\n expired,\n failed: errored,\n processing,\n total: succeeded + errored + processing + canceled + expired,\n },\n status: mapStatus(asString(obj.processing_status)),\n };\n};\n\nconst textFromMessage = (message: unknown): string | undefined => {\n const text = asArray(asRecord(message).content)\n .map((block) => asRecord(block))\n .filter((block) => block.type === \"text\")\n .map((block) => asString(block.text) ?? \"\")\n .join(\"\");\n return text.length > 0 ? text : undefined;\n};\n\nconst usageFromMessage = (message: unknown): BatchUsage | undefined => {\n const usage = asRecord(asRecord(message).usage);\n const inputTokens = asNumber(usage.input_tokens);\n const outputTokens = asNumber(usage.output_tokens);\n if (inputTokens === undefined && outputTokens === undefined) {\n return;\n }\n return {\n inputTokens,\n outputTokens,\n totalTokens: (inputTokens ?? 0) + (outputTokens ?? 0),\n };\n};\n\nconst normalizeResult = (line: unknown): BatchResult => {\n const obj = asRecord(line);\n const customId = asString(obj.custom_id) ?? \"\";\n const result = asRecord(obj.result);\n const type = asString(result.type);\n\n if (type === \"succeeded\") {\n return {\n customId,\n response: result.message,\n status: \"succeeded\",\n text: textFromMessage(result.message),\n usage: usageFromMessage(result.message),\n };\n }\n if (type === \"errored\") {\n const error = asRecord(result.error);\n const nested = asRecord(error.error);\n const source = Object.keys(nested).length > 0 ? nested : error;\n return {\n customId,\n error: {\n message: asString(source.message) ?? \"Request errored.\",\n type: asString(source.type),\n },\n response: result.error,\n status: \"errored\",\n };\n }\n if (type === \"expired\") {\n return { customId, status: \"expired\" };\n }\n return { customId, status: \"canceled\" };\n};\n\nconst submit = async (input: SubmitInput): Promise<BatchSnapshot> => {\n const requests = input.built.map((item) => ({\n custom_id: item.customId,\n params: omit(item.body, \"stream\"),\n }));\n const raw = await requestJson(\n `${baseUrl(input.credentials)}/v1/messages/batches`,\n {\n body: JSON.stringify({ requests }),\n headers: headers(input.credentials),\n method: \"POST\",\n }\n );\n return normalizeSnapshot(raw);\n};\n\nconst retrieve = async (\n id: string,\n credentials: ProviderCredentials\n): Promise<BatchSnapshot> => {\n const raw = await requestJson(\n `${baseUrl(credentials)}/v1/messages/batches/${id}`,\n { headers: headers(credentials) }\n );\n return normalizeSnapshot(raw);\n};\n\n// oxlint-disable-next-line func-style -- generators cannot be arrow functions.\nasync function* results(\n id: string,\n credentials: ProviderCredentials\n): AsyncGenerator<BatchResult> {\n const snapshot = await retrieve(id, credentials);\n const resultsUrl = asString(asRecord(snapshot.raw).results_url);\n if (!resultsUrl) {\n throw new BatchworkError(\n `batchwork: results are not ready for batch \"${id}\" (status: ${snapshot.status}).`\n );\n }\n const stream = await requestStream(resultsUrl, {\n headers: headers(credentials),\n });\n for await (const line of streamJsonl(stream)) {\n yield normalizeResult(line);\n }\n}\n\nconst cancel = async (\n id: string,\n credentials: ProviderCredentials\n): Promise<void> => {\n await requestJson(\n `${baseUrl(credentials)}/v1/messages/batches/${id}/cancel`,\n {\n headers: headers(credentials),\n method: \"POST\",\n }\n );\n};\n\nexport const anthropicAdapter: BatchAdapter = {\n cancel,\n id: \"anthropic\",\n results,\n retrieve,\n submit,\n};\n",
11
+ "import { BatchworkError } from \"../errors\";\nimport { requestJson } from \"../http\";\nimport type {\n BatchResult,\n BatchSnapshot,\n BatchStatus,\n BatchUsage,\n ProviderCredentials,\n} from \"../types\";\nimport { asArray, asNumber, asRecord, asString, omit } from \"../util\";\nimport type { BatchAdapter, SubmitInput } from \"./adapter\";\n\nconst GOOGLE_BASE = \"https://generativelanguage.googleapis.com/v1beta\";\n\nconst apiKey = (credentials: ProviderCredentials): string => {\n const key =\n credentials.apiKey ??\n process.env.GOOGLE_GENERATIVE_AI_API_KEY ??\n process.env.GEMINI_API_KEY;\n if (!key) {\n throw new BatchworkError(\n \"batchwork: missing Google Gemini API key. Set GOOGLE_GENERATIVE_AI_API_KEY (or GEMINI_API_KEY) or pass `apiKey`.\"\n );\n }\n return key;\n};\n\nconst baseUrl = (credentials: ProviderCredentials): string =>\n credentials.baseURL ?? GOOGLE_BASE;\n\nconst headers = (credentials: ProviderCredentials): Record<string, string> => ({\n \"content-type\": \"application/json\",\n \"x-goog-api-key\": apiKey(credentials),\n ...credentials.headers,\n});\n\n/** Gemini operation state. Live ops use `JOB_STATE_*`; match on the suffix. */\nconst mapState = (state: string | undefined, done: boolean): BatchStatus => {\n if (state) {\n if (state.endsWith(\"SUCCEEDED\")) {\n return \"completed\";\n }\n if (state.endsWith(\"FAILED\")) {\n return \"failed\";\n }\n if (state.endsWith(\"CANCELLED\")) {\n return \"cancelled\";\n }\n if (state.endsWith(\"EXPIRED\")) {\n return \"expired\";\n }\n if (state.endsWith(\"PENDING\")) {\n return \"validating\";\n }\n if (state.endsWith(\"RUNNING\")) {\n return \"in_progress\";\n }\n }\n return done ? \"completed\" : \"in_progress\";\n};\n\nconst inlinedResponses = (raw: unknown): unknown[] => {\n const obj = asRecord(raw);\n const response = asRecord(obj.response);\n const dest = asRecord(obj.dest);\n const responseInline =\n response.inlinedResponses ?? response.inlined_responses;\n const destInline = dest.inlinedResponses ?? dest.inlined_responses;\n const nestedResponseInline = asRecord(responseInline);\n const nestedDestInline = asRecord(destInline);\n return [\n ...asArray(responseInline),\n ...asArray(nestedResponseInline.inlinedResponses),\n ...asArray(nestedResponseInline.inlined_responses),\n ...asArray(destInline),\n ...asArray(nestedDestInline.inlinedResponses),\n ...asArray(nestedDestInline.inlined_responses),\n ];\n};\n\nconst normalizeSnapshot = (raw: unknown): BatchSnapshot => {\n const obj = asRecord(raw);\n const items = inlinedResponses(raw);\n const failed = items.filter((item) => asRecord(item).error).length;\n return {\n id: asString(obj.name) ?? \"\",\n provider: \"google\",\n raw,\n requestCounts: {\n completed: items.length - failed,\n failed,\n total: items.length,\n },\n status: mapState(\n asString(obj.state) ??\n asString(asRecord(obj.state).name) ??\n asString(asRecord(obj.metadata).state),\n obj.done === true\n ),\n };\n};\n\nconst textFromResponse = (response: unknown): string | undefined => {\n const candidate = asRecord(asArray(asRecord(response).candidates)[0]);\n const text = asArray(asRecord(candidate.content).parts)\n .map((part) => asString(asRecord(part).text) ?? \"\")\n .join(\"\");\n return text.length > 0 ? text : undefined;\n};\n\nconst usageFromResponse = (response: unknown): BatchUsage | undefined => {\n const usage = asRecord(asRecord(response).usageMetadata);\n const inputTokens = asNumber(usage.promptTokenCount);\n const outputTokens = asNumber(usage.candidatesTokenCount);\n const totalTokens = asNumber(usage.totalTokenCount);\n if (\n inputTokens === undefined &&\n outputTokens === undefined &&\n totalTokens === undefined\n ) {\n return;\n }\n return {\n inputTokens,\n outputTokens,\n totalTokens: totalTokens ?? (inputTokens ?? 0) + (outputTokens ?? 0),\n };\n};\n\nconst normalizeResult = (item: unknown): BatchResult => {\n const obj = asRecord(item);\n const customId =\n asString(asRecord(obj.metadata).key) ??\n asString(obj.key) ??\n asString(obj.custom_id) ??\n \"\";\n if (obj.error) {\n const error = asRecord(obj.error);\n return {\n customId,\n error: {\n code: asNumber(error.code) ?? asString(error.code),\n message: asString(error.message) ?? \"Request errored.\",\n type: asString(error.status),\n },\n response: obj.error,\n status: \"errored\",\n };\n }\n return {\n customId,\n response: obj.response,\n status: \"succeeded\",\n text: textFromResponse(obj.response),\n usage: usageFromResponse(obj.response),\n };\n};\n\nconst submit = async (input: SubmitInput): Promise<BatchSnapshot> => {\n const requests = input.built.map((item) => ({\n metadata: { key: item.customId },\n request: omit(item.body, \"stream\"),\n }));\n const raw = await requestJson(\n `${baseUrl(input.credentials)}/models/${input.modelId}:batchGenerateContent`,\n {\n body: JSON.stringify({\n batch: {\n display_name: \"batchwork\",\n input_config: { requests: { requests } },\n },\n }),\n headers: headers(input.credentials),\n method: \"POST\",\n }\n );\n return normalizeSnapshot(raw);\n};\n\nconst retrieve = async (\n id: string,\n credentials: ProviderCredentials\n): Promise<BatchSnapshot> => {\n const raw = await requestJson(`${baseUrl(credentials)}/${id}`, {\n headers: headers(credentials),\n });\n return normalizeSnapshot(raw);\n};\n\n// oxlint-disable-next-line func-style -- generators cannot be arrow functions.\nasync function* results(\n id: string,\n credentials: ProviderCredentials\n): AsyncGenerator<BatchResult> {\n const snapshot = await retrieve(id, credentials);\n const raw = asRecord(snapshot.raw);\n const response = asRecord(raw.response);\n const dest = asRecord(raw.dest);\n const responsesFile =\n asString(asRecord(response.responsesFile).name) ??\n asString(response.responsesFile) ??\n asString(asRecord(response.responses_file).name) ??\n asString(response.responses_file) ??\n asString(dest.fileName) ??\n asString(dest.file_name);\n if (responsesFile) {\n throw new BatchworkError(\n `batchwork: batch \"${id}\" returned file-mode results, which are not supported yet.`\n );\n }\n const items = inlinedResponses(raw);\n if (items.length === 0) {\n throw new BatchworkError(\n `batchwork: results are not ready for batch \"${id}\" (status: ${snapshot.status}).`\n );\n }\n for (const item of items) {\n yield normalizeResult(item);\n }\n}\n\nconst cancel = async (\n id: string,\n credentials: ProviderCredentials\n): Promise<void> => {\n await requestJson(`${baseUrl(credentials)}/${id}:cancel`, {\n headers: headers(credentials),\n method: \"POST\",\n });\n};\n\n/**\n * Google Gemini (Developer API) batch adapter: submits inline requests via\n * `:batchGenerateContent`, polls the returned long-running operation, then reads\n * the inline responses keyed by each request's `metadata.key`.\n */\nexport const googleAdapter: BatchAdapter = {\n cancel,\n id: \"google\",\n results,\n retrieve,\n submit,\n};\n",
12
+ "/**\n * Helpers shared across the OpenAI-shaped adapters (OpenAI, Groq, Together,\n * Mistral). These providers all upload JSONL via a Files API, return results as\n * JSONL keyed by `custom_id`, and shape each result line like OpenAI's\n * (`{ custom_id, response: { status_code, body }, error }`).\n */\n\nimport { BatchworkError } from \"../errors\";\nimport { requestJson, requestStream } from \"../http\";\nimport { streamJsonl } from \"../jsonl\";\nimport type {\n BatchResult,\n BatchResultError,\n BatchUsage,\n ProviderCredentials,\n} from \"../types\";\nimport { asArray, asNumber, asRecord, asString } from \"../util\";\n\nconst HTTP_OK_MIN = 200;\nconst HTTP_OK_MAX = 300;\n\n/** Resolve an API key from credentials or the provider's env var, or throw. */\nexport const resolveApiKey = (\n credentials: ProviderCredentials,\n envVar: string,\n label: string\n): string => {\n const key = credentials.apiKey ?? process.env[envVar];\n if (!key) {\n throw new BatchworkError(\n `batchwork: missing ${label} API key. Set ${envVar} or pass \\`apiKey\\`.`\n );\n }\n return key;\n};\n\nexport const textFromBody = (body: unknown): string | undefined => {\n const obj = asRecord(body);\n const choices = asArray(obj.choices);\n if (choices.length > 0) {\n const content = asString(asRecord(asRecord(choices[0]).message).content);\n if (content) {\n return content;\n }\n }\n return asString(obj.output_text);\n};\n\nexport const usageFromBody = (body: unknown): BatchUsage | undefined => {\n const usage = asRecord(asRecord(body).usage);\n const inputTokens =\n asNumber(usage.prompt_tokens) ?? asNumber(usage.input_tokens);\n const outputTokens =\n asNumber(usage.completion_tokens) ?? asNumber(usage.output_tokens);\n const totalTokens = asNumber(usage.total_tokens);\n if (\n inputTokens === undefined &&\n outputTokens === undefined &&\n totalTokens === undefined\n ) {\n return;\n }\n return {\n inputTokens,\n outputTokens,\n totalTokens: totalTokens ?? (inputTokens ?? 0) + (outputTokens ?? 0),\n };\n};\n\nconst errorFromValue = (value: unknown, fallback: string): BatchResultError => {\n const obj = asRecord(value);\n const nested = asRecord(obj.error);\n const source = nested.message ? nested : obj;\n return {\n code: asNumber(source.code) ?? asString(source.code),\n message: asString(source.message) ?? fallback,\n type: asString(source.type),\n };\n};\n\n/** Normalize an OpenAI-shaped result line into a {@link BatchResult}. */\nexport const normalizeOpenAIResult = (line: unknown): BatchResult => {\n const obj = asRecord(line);\n const customId = asString(obj.custom_id) ?? \"\";\n\n if (obj.error) {\n return {\n customId,\n error: errorFromValue(obj.error, \"Request errored.\"),\n response: obj.error,\n status: \"errored\",\n };\n }\n\n const response = asRecord(obj.response);\n const statusCode = asNumber(response.status_code) ?? 0;\n if (statusCode >= HTTP_OK_MIN && statusCode < HTTP_OK_MAX) {\n return {\n customId,\n response: response.body,\n status: \"succeeded\",\n text: textFromBody(response.body),\n usage: usageFromBody(response.body),\n };\n }\n\n return {\n customId,\n error: errorFromValue(\n response.body,\n `Request failed with status ${statusCode}.`\n ),\n response: response.body,\n status: \"errored\",\n };\n};\n\n// Upload a JSONL batch input file and return its id. Purpose defaults to `batch`.\nexport const uploadInputFile = async (\n jsonl: string,\n baseUrl: string,\n headers: Record<string, string>,\n options: { purpose?: string | null } = {}\n): Promise<string> => {\n const form = new FormData();\n const purpose = options.purpose === undefined ? \"batch\" : options.purpose;\n if (purpose !== null) {\n form.append(\"purpose\", purpose);\n }\n form.append(\n \"file\",\n new Blob([jsonl], { type: \"application/jsonl\" }),\n \"batchwork.jsonl\"\n );\n const raw = await requestJson<{ id: string }>(`${baseUrl}/files`, {\n body: form,\n headers,\n method: \"POST\",\n });\n return raw.id;\n};\n\n/**\n * Stream a JSONL result file's content as normalized OpenAI-shaped results.\n *\n * @yields {BatchResult} the normalized result for each line.\n */\n// oxlint-disable-next-line func-style -- generators cannot be arrow functions.\nexport async function* streamResultFile(\n fileId: string,\n baseUrl: string,\n headers: Record<string, string>\n): AsyncGenerator<BatchResult> {\n const stream = await requestStream(`${baseUrl}/files/${fileId}/content`, {\n headers,\n });\n for await (const line of streamJsonl(stream)) {\n yield normalizeOpenAIResult(line);\n }\n}\n",
13
+ "/**\n * Factory for OpenAI-compatible batch adapters. OpenAI, Groq, and Together AI\n * all share the same lifecycle — upload JSONL via the Files API, create a batch\n * referencing the file, poll a `status` field, then download output/error files\n * — differing only in base URL, credentials, and minor request-line shape.\n */\n\nimport { BatchworkError } from \"../errors\";\nimport { requestJson } from \"../http\";\nimport { encodeJsonl } from \"../jsonl\";\nimport type {\n BatchProvider,\n BatchResult,\n BatchSnapshot,\n BatchStatus,\n ProviderCredentials,\n} from \"../types\";\nimport { asNumber, asRecord, asString, omit, toDate } from \"../util\";\nimport type { BatchAdapter, SubmitInput } from \"./adapter\";\nimport { resolveApiKey, streamResultFile, uploadInputFile } from \"./shared\";\n\n// How a batch input line is shaped.\nexport type BatchLineFormat = \"body-only\" | \"method-url\";\n\nexport interface OpenAICompatibleConfig {\n apiKeyEnv: string;\n apiKeyLabel: string;\n baseUrl: string;\n // How long the provider may take to finish (OpenAI/Groq/Together).\n completionWindow?: string;\n // Files API purpose value for uploaded JSONL batch inputs.\n filePurpose?: string;\n id: BatchProvider;\n /**\n * Override the JSONL upload. Defaults to a direct multipart POST to the Files\n * API (OpenAI, Groq); Together replaces it with its presigned-URL flow.\n * Returns the uploaded file id.\n */\n uploadFile?: (args: {\n baseUrl: string;\n headers: Record<string, string>;\n jsonl: string;\n purpose: string;\n }) => Promise<string>;\n /**\n * Input-line shape. `method-url` writes `{ custom_id, method, url, body }`\n * (OpenAI, Groq); `body-only` writes `{ custom_id, body }` (Together).\n */\n lineFormat?: BatchLineFormat;\n /**\n * Map the captured endpoint path to the value the provider expects in the\n * batch `url`/`endpoint` field (e.g. Groq serves under `/openai/v1` but its\n * batch `url` must be `/v1/chat/completions`).\n */\n normalizeEndpoint?: (endpoint: string) => string;\n}\n\nconst DEFAULT_COMPLETION_WINDOW = \"24h\";\n\nconst mapStatus = (status: string | undefined): BatchStatus => {\n const normalized = status?.toLowerCase();\n switch (normalized) {\n case \"validating\":\n case \"in_progress\":\n case \"finalizing\":\n case \"completed\":\n case \"failed\":\n case \"expired\":\n case \"cancelling\":\n case \"cancelled\": {\n return normalized;\n }\n default: {\n return \"in_progress\";\n }\n }\n};\n\nconst normalizeSnapshot = (\n raw: unknown,\n provider: BatchProvider\n): BatchSnapshot => {\n const outer = asRecord(raw);\n const obj = asRecord(outer.job);\n const source = Object.keys(obj).length > 0 ? obj : outer;\n const counts = asRecord(source.request_counts);\n return {\n completedAt: toDate(source.completed_at),\n createdAt: toDate(source.created_at),\n expiresAt: toDate(source.expires_at),\n id: asString(source.id) ?? \"\",\n provider,\n raw: source,\n requestCounts: {\n completed: asNumber(counts.completed) ?? 0,\n failed: asNumber(counts.failed) ?? 0,\n total: asNumber(counts.total) ?? 0,\n },\n status: mapStatus(asString(source.status)),\n };\n};\n\n// Build an OpenAI-compatible adapter from a provider config.\nexport const createOpenAICompatibleAdapter = (\n config: OpenAICompatibleConfig\n): BatchAdapter => {\n const completionWindow = config.completionWindow ?? DEFAULT_COMPLETION_WINDOW;\n const lineFormat = config.lineFormat ?? \"method-url\";\n\n const baseUrl = (credentials: ProviderCredentials): string =>\n credentials.baseURL ?? config.baseUrl;\n\n const authHeaders = (\n credentials: ProviderCredentials\n ): Record<string, string> => ({\n Authorization: `Bearer ${resolveApiKey(credentials, config.apiKeyEnv, config.apiKeyLabel)}`,\n ...credentials.headers,\n });\n\n const submit = async (input: SubmitInput): Promise<BatchSnapshot> => {\n const endpoint = config.normalizeEndpoint\n ? config.normalizeEndpoint(input.endpoint)\n : input.endpoint;\n const jsonl = encodeJsonl(\n input.built.map((item) => {\n const body = omit(item.body, \"stream\");\n if (lineFormat === \"body-only\") {\n return { body, custom_id: item.customId };\n }\n return {\n body,\n custom_id: item.customId,\n method: \"POST\",\n url: endpoint,\n };\n })\n );\n const headers = authHeaders(input.credentials);\n const url = baseUrl(input.credentials);\n const purpose = config.filePurpose ?? \"batch\";\n const inputFileId = await (config.uploadFile\n ? config.uploadFile({ baseUrl: url, headers, jsonl, purpose })\n : uploadInputFile(jsonl, url, headers, { purpose }));\n const raw = await requestJson(`${url}/batches`, {\n body: JSON.stringify({\n completion_window: completionWindow,\n endpoint,\n input_file_id: inputFileId,\n metadata: input.metadata,\n }),\n headers: { ...headers, \"content-type\": \"application/json\" },\n method: \"POST\",\n });\n return normalizeSnapshot(raw, config.id);\n };\n\n const retrieve = async (\n id: string,\n credentials: ProviderCredentials\n ): Promise<BatchSnapshot> => {\n const raw = await requestJson(`${baseUrl(credentials)}/batches/${id}`, {\n headers: authHeaders(credentials),\n });\n return normalizeSnapshot(raw, config.id);\n };\n\n // oxlint-disable-next-line func-style -- generators cannot be arrow functions.\n async function* results(\n id: string,\n credentials: ProviderCredentials\n ): AsyncGenerator<BatchResult> {\n const snapshot = await retrieve(id, credentials);\n const raw = asRecord(snapshot.raw);\n const outputFileId = asString(raw.output_file_id);\n const errorFileId = asString(raw.error_file_id);\n\n if (!(outputFileId || errorFileId)) {\n throw new BatchworkError(\n `batchwork: results are not ready for batch \"${id}\" (status: ${snapshot.status}).`\n );\n }\n const headers = authHeaders(credentials);\n if (outputFileId) {\n yield* streamResultFile(outputFileId, baseUrl(credentials), headers);\n }\n if (errorFileId) {\n yield* streamResultFile(errorFileId, baseUrl(credentials), headers);\n }\n }\n\n const cancel = async (\n id: string,\n credentials: ProviderCredentials\n ): Promise<void> => {\n await requestJson(`${baseUrl(credentials)}/batches/${id}/cancel`, {\n headers: authHeaders(credentials),\n method: \"POST\",\n });\n };\n\n return { cancel, id: config.id, results, retrieve, submit };\n};\n",
14
+ "import { createOpenAICompatibleAdapter } from \"./openai-compatible\";\n\n/**\n * Groq batch adapter. Groq's batch API is OpenAI-compatible (Files API +\n * `/batches`), so it reuses the OpenAI-compatible flow. Groq is served under\n * `/openai/v1`, but its batch `url` must be `/v1/chat/completions`, so the\n * captured endpoint's `/openai` prefix is stripped.\n */\nexport const groqAdapter = createOpenAICompatibleAdapter({\n apiKeyEnv: \"GROQ_API_KEY\",\n apiKeyLabel: \"Groq\",\n baseUrl: \"https://api.groq.com/openai/v1\",\n id: \"groq\",\n lineFormat: \"method-url\",\n normalizeEndpoint: (endpoint) => endpoint.replace(/^\\/openai/u, \"\"),\n});\n",
15
+ "import { BatchworkError } from \"../errors\";\nimport { requestJson } from \"../http\";\nimport { encodeJsonl } from \"../jsonl\";\nimport type {\n BatchResult,\n BatchSnapshot,\n BatchStatus,\n ProviderCredentials,\n} from \"../types\";\nimport { asNumber, asRecord, asString, omit, toDate } from \"../util\";\nimport type { BatchAdapter, SubmitInput } from \"./adapter\";\nimport { resolveApiKey, streamResultFile, uploadInputFile } from \"./shared\";\n\nconst MISTRAL_BASE = \"https://api.mistral.ai/v1\";\n\nconst apiKey = (credentials: ProviderCredentials): string =>\n resolveApiKey(credentials, \"MISTRAL_API_KEY\", \"Mistral\");\n\nconst baseUrl = (credentials: ProviderCredentials): string =>\n credentials.baseURL ?? MISTRAL_BASE;\n\nconst authHeaders = (\n credentials: ProviderCredentials\n): Record<string, string> => ({\n Authorization: `Bearer ${apiKey(credentials)}`,\n ...credentials.headers,\n});\n\nconst mapStatus = (status: string | undefined): BatchStatus => {\n switch (status) {\n case \"QUEUED\": {\n return \"validating\";\n }\n case \"SUCCESS\": {\n return \"completed\";\n }\n case \"FAILED\": {\n return \"failed\";\n }\n case \"TIMEOUT_EXCEEDED\": {\n return \"expired\";\n }\n case \"CANCELLATION_REQUESTED\": {\n return \"cancelling\";\n }\n case \"CANCELLED\": {\n return \"cancelled\";\n }\n default: {\n return \"in_progress\";\n }\n }\n};\n\nconst normalizeSnapshot = (raw: unknown): BatchSnapshot => {\n const obj = asRecord(raw);\n const succeeded = asNumber(obj.succeeded_requests) ?? 0;\n const failed = asNumber(obj.failed_requests) ?? 0;\n return {\n completedAt: toDate(obj.completed_at),\n createdAt: toDate(obj.created_at),\n id: asString(obj.id) ?? \"\",\n provider: \"mistral\",\n raw,\n requestCounts: {\n completed: succeeded,\n failed,\n total: asNumber(obj.total_requests) ?? succeeded + failed,\n },\n status: mapStatus(asString(obj.status)),\n };\n};\n\nconst submit = async (input: SubmitInput): Promise<BatchSnapshot> => {\n // Mistral sets the model on the job, so strip it (and `stream`) from each line.\n const jsonl = encodeJsonl(\n input.built.map((item) => ({\n body: omit(omit(item.body, \"stream\"), \"model\"),\n custom_id: item.customId,\n }))\n );\n const inputFileId = await uploadInputFile(\n jsonl,\n baseUrl(input.credentials),\n authHeaders(input.credentials)\n );\n const raw = await requestJson(`${baseUrl(input.credentials)}/batch/jobs`, {\n body: JSON.stringify({\n endpoint: input.endpoint,\n input_files: [inputFileId],\n metadata: input.metadata,\n model: input.modelId,\n }),\n headers: {\n ...authHeaders(input.credentials),\n \"content-type\": \"application/json\",\n },\n method: \"POST\",\n });\n return normalizeSnapshot(raw);\n};\n\nconst retrieve = async (\n id: string,\n credentials: ProviderCredentials\n): Promise<BatchSnapshot> => {\n const raw = await requestJson(`${baseUrl(credentials)}/batch/jobs/${id}`, {\n headers: authHeaders(credentials),\n });\n return normalizeSnapshot(raw);\n};\n\n// oxlint-disable-next-line func-style -- generators cannot be arrow functions.\nasync function* results(\n id: string,\n credentials: ProviderCredentials\n): AsyncGenerator<BatchResult> {\n const snapshot = await retrieve(id, credentials);\n const raw = asRecord(snapshot.raw);\n const outputFileId = asString(raw.output_file);\n const errorFileId = asString(raw.error_file);\n const headers = authHeaders(credentials);\n if (outputFileId) {\n yield* streamResultFile(outputFileId, baseUrl(credentials), headers);\n }\n if (errorFileId) {\n yield* streamResultFile(errorFileId, baseUrl(credentials), headers);\n }\n if (!(outputFileId || errorFileId)) {\n throw new BatchworkError(\n `batchwork: results are not ready for batch \"${id}\" (status: ${snapshot.status}).`\n );\n }\n}\n\nconst cancel = async (\n id: string,\n credentials: ProviderCredentials\n): Promise<void> => {\n await requestJson(`${baseUrl(credentials)}/batch/jobs/${id}/cancel`, {\n headers: authHeaders(credentials),\n method: \"POST\",\n });\n};\n\n/**\n * Mistral batch adapter: uploads JSONL (`purpose=batch`), creates a job with the\n * model/endpoint set on the job, polls `status`, then downloads the OpenAI-shaped\n * output/error files.\n */\nexport const mistralAdapter: BatchAdapter = {\n cancel,\n id: \"mistral\",\n results,\n retrieve,\n submit,\n};\n",
16
+ "import { createOpenAICompatibleAdapter } from \"./openai-compatible\";\n\n/**\n * OpenAI batch adapter: builds JSONL, uploads it via the Files API\n * (`purpose=batch`), creates the batch, polls `status`, then downloads and\n * parses the output and error files.\n */\nexport const openaiAdapter = createOpenAICompatibleAdapter({\n apiKeyEnv: \"OPENAI_API_KEY\",\n apiKeyLabel: \"OpenAI\",\n baseUrl: \"https://api.openai.com/v1\",\n id: \"openai\",\n lineFormat: \"method-url\",\n});\n",
17
+ "import { BatchworkError } from \"../errors\";\nimport { requestJson } from \"../http\";\nimport { createOpenAICompatibleAdapter } from \"./openai-compatible\";\n\nconst INPUT_FILE_NAME = \"batchwork.jsonl\";\nconst HTTP_FOUND = 302;\n\nconst safeText = async (response: Response): Promise<string> => {\n try {\n return await response.text();\n } catch {\n return \"<no body>\";\n }\n};\n\n/**\n * Together uploads files via a presigned URL rather than a direct multipart\n * upload of the bytes. The flow is three steps:\n *\n * 1. `POST /files` with multipart *metadata only* (`purpose`, `file_name`,\n * `file_type` — no file part) responds `302` with a `Location` (the\n * presigned storage URL) and an `X-Together-File-Id` header.\n * 2. `PUT` the raw bytes to that `Location` (no auth — the URL is signed).\n * 3. `POST /files/{id}/preprocess` to finalize the upload.\n */\nconst uploadTogetherFile = async (args: {\n baseUrl: string;\n headers: Record<string, string>;\n jsonl: string;\n purpose: string;\n}): Promise<string> => {\n const metadata = new FormData();\n metadata.append(\"purpose\", args.purpose);\n metadata.append(\"file_name\", INPUT_FILE_NAME);\n metadata.append(\"file_type\", \"jsonl\");\n const init = await fetch(`${args.baseUrl}/files`, {\n body: metadata,\n // Let fetch set the multipart content-type/boundary; only auth is needed.\n headers: args.headers,\n method: \"POST\",\n // Together signals the presigned URL via a redirect we must read, not follow.\n redirect: \"manual\",\n });\n const location = init.headers.get(\"location\");\n const fileId = init.headers.get(\"x-together-file-id\");\n if (init.status !== HTTP_FOUND || !(location && fileId)) {\n throw new BatchworkError(\n `batchwork: Together upload could not be initiated (${init.status}): ${await safeText(init)}`\n );\n }\n\n // The presigned URL carries its own auth; sending Together's would break it.\n const upload = await fetch(location, { body: args.jsonl, method: \"PUT\" });\n if (!upload.ok) {\n throw new BatchworkError(\n `batchwork: Together file upload failed (${upload.status}): ${await safeText(upload)}`\n );\n }\n\n await requestJson(`${args.baseUrl}/files/${fileId}/preprocess`, {\n headers: args.headers,\n method: \"POST\",\n });\n return fileId;\n};\n\n/**\n * Together AI batch adapter. Together's batch API mirrors OpenAI's (Files API +\n * `/batches`) but uses the leaner `{ custom_id, body }` input-line shape, with\n * the endpoint declared on the batch rather than per line, and a presigned-URL\n * file upload in place of OpenAI's direct multipart POST.\n */\nexport const togetherAdapter = createOpenAICompatibleAdapter({\n apiKeyEnv: \"TOGETHER_API_KEY\",\n apiKeyLabel: \"Together AI\",\n baseUrl: \"https://api.together.xyz/v1\",\n filePurpose: \"batch-api\",\n id: \"together\",\n lineFormat: \"body-only\",\n uploadFile: uploadTogetherFile,\n});\n",
18
+ "import { requestJson } from \"../http\";\nimport { encodeJsonl } from \"../jsonl\";\nimport type {\n BatchResult,\n BatchSnapshot,\n BatchStatus,\n ProviderCredentials,\n} from \"../types\";\nimport { asNumber, asRecord, asString, omit, toDate } from \"../util\";\nimport type { BatchAdapter, SubmitInput } from \"./adapter\";\nimport {\n resolveApiKey,\n textFromBody,\n uploadInputFile,\n usageFromBody,\n} from \"./shared\";\n\nconst XAI_BASE = \"https://api.x.ai/v1\";\nconst RESULTS_PAGE_SIZE = 100;\n\nconst apiKey = (credentials: ProviderCredentials): string =>\n resolveApiKey(credentials, \"XAI_API_KEY\", \"xAI\");\n\nconst baseUrl = (credentials: ProviderCredentials): string =>\n credentials.baseURL ?? XAI_BASE;\n\nconst authHeaders = (\n credentials: ProviderCredentials\n): Record<string, string> => ({\n Authorization: `Bearer ${apiKey(credentials)}`,\n ...credentials.headers,\n});\n\nconst deriveStatus = (state: Record<string, unknown>): BatchStatus => {\n const pending = asNumber(state.num_pending);\n // No counts yet (e.g. immediately after creation): still processing.\n if (pending === undefined) {\n return \"in_progress\";\n }\n const total = asNumber(state.num_requests) ?? 0;\n const cancelled = asNumber(state.num_cancelled) ?? 0;\n if (total === 0) {\n return \"in_progress\";\n }\n if (pending > 0) {\n return \"in_progress\";\n }\n if (cancelled > 0 && cancelled === total) {\n return \"cancelled\";\n }\n return \"completed\";\n};\n\nconst normalizeSnapshot = (raw: unknown): BatchSnapshot => {\n const obj = asRecord(raw);\n const state = asRecord(obj.state);\n return {\n completedAt: toDate(obj.cancel_time),\n createdAt: toDate(obj.create_time),\n expiresAt: toDate(obj.expire_time ?? obj.expires_at),\n id: asString(obj.batch_id) ?? asString(obj.id) ?? \"\",\n provider: \"xai\",\n raw,\n requestCounts: {\n canceled: asNumber(state.num_cancelled) ?? 0,\n completed: asNumber(state.num_success) ?? 0,\n failed: asNumber(state.num_error) ?? 0,\n processing: asNumber(state.num_pending) ?? 0,\n total: asNumber(state.num_requests) ?? 0,\n },\n status: deriveStatus(state),\n };\n};\n\nconst normalizeResult = (item: unknown): BatchResult => {\n const obj = asRecord(item);\n const customId = asString(obj.batch_request_id) ?? \"\";\n const batchResult = asRecord(obj.batch_result);\n const resultError = batchResult.error;\n const errorMessage =\n asString(obj.error_message) ??\n asString(resultError) ??\n asString(asRecord(resultError).message);\n if (errorMessage) {\n return {\n customId,\n error: {\n message: errorMessage,\n type: asString(asRecord(resultError).type),\n },\n response: obj,\n status: \"errored\",\n };\n }\n // `batch_result.response` is keyed by operation type; chat lives under\n // `chat_get_completion`. Fall back to the first value for other op types.\n const response = asRecord(batchResult.response);\n const completion = response.chat_get_completion ?? Object.values(response)[0];\n return {\n customId,\n response: completion,\n status: \"succeeded\",\n text: textFromBody(completion),\n usage: usageFromBody(completion),\n };\n};\n\nconst submit = async (input: SubmitInput): Promise<BatchSnapshot> => {\n const jsonl = encodeJsonl(\n input.built.map((item) => ({\n body: omit(item.body, \"stream\"),\n custom_id: item.customId,\n method: \"POST\",\n url: input.endpoint,\n }))\n );\n const inputFileId = await uploadInputFile(\n jsonl,\n baseUrl(input.credentials),\n authHeaders(input.credentials),\n { purpose: null }\n );\n const raw = await requestJson(`${baseUrl(input.credentials)}/batches`, {\n body: JSON.stringify({ input_file_id: inputFileId, name: \"batchwork\" }),\n headers: {\n ...authHeaders(input.credentials),\n \"content-type\": \"application/json\",\n },\n method: \"POST\",\n });\n return normalizeSnapshot(raw);\n};\n\nconst retrieve = async (\n id: string,\n credentials: ProviderCredentials\n): Promise<BatchSnapshot> => {\n const raw = await requestJson(`${baseUrl(credentials)}/batches/${id}`, {\n headers: authHeaders(credentials),\n });\n return normalizeSnapshot(raw);\n};\n\n// oxlint-disable-next-line func-style -- generators cannot be arrow functions.\nasync function* results(\n id: string,\n credentials: ProviderCredentials\n): AsyncGenerator<BatchResult> {\n const headers = authHeaders(credentials);\n let token: string | undefined;\n do {\n const query = new URLSearchParams({ limit: String(RESULTS_PAGE_SIZE) });\n if (token) {\n query.set(\"pagination_token\", token);\n }\n // oxlint-disable-next-line no-await-in-loop -- pages are read sequentially.\n const raw = await requestJson<Record<string, unknown>>(\n `${baseUrl(credentials)}/batches/${id}/results?${query.toString()}`,\n { headers }\n );\n const page = asRecord(raw);\n for (const item of Array.isArray(page.results) ? page.results : []) {\n yield normalizeResult(item);\n }\n token = asString(page.pagination_token);\n } while (token);\n}\n\nconst cancel = async (\n id: string,\n credentials: ProviderCredentials\n): Promise<void> => {\n await requestJson(`${baseUrl(credentials)}/batches/${id}:cancel`, {\n headers: authHeaders(credentials),\n method: \"POST\",\n });\n};\n\n/**\n * xAI (Grok) batch adapter. Uses xAI's OpenAI-compatible file-upload path\n * (`/v1/files` + `/v1/batches` with `input_file_id`), but its status, paginated\n * `/results`, and `:cancel` shapes are proprietary.\n */\nexport const xaiAdapter: BatchAdapter = {\n cancel,\n id: \"xai\",\n results,\n retrieve,\n submit,\n};\n",
19
+ "import type { BatchProvider } from \"../types\";\nimport type { BatchAdapter } from \"./adapter\";\nimport { anthropicAdapter } from \"./anthropic\";\nimport { googleAdapter } from \"./google\";\nimport { groqAdapter } from \"./groq\";\nimport { mistralAdapter } from \"./mistral\";\nimport { openaiAdapter } from \"./openai\";\nimport { togetherAdapter } from \"./together\";\nimport { xaiAdapter } from \"./xai\";\n\nconst adapters: Record<BatchProvider, BatchAdapter> = {\n anthropic: anthropicAdapter,\n google: googleAdapter,\n groq: groqAdapter,\n mistral: mistralAdapter,\n openai: openaiAdapter,\n together: togetherAdapter,\n xai: xaiAdapter,\n};\n\nexport const getAdapter = (provider: BatchProvider): BatchAdapter =>\n adapters[provider];\n\nexport type { BatchAdapter, SubmitInput } from \"./adapter\";\n"
20
+ ],
21
+ "mappings": "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",
22
+ "debugId": "DA60AE45A8F12B3C64756E2164756E21",
23
+ "names": []
24
+ }
@@ -0,0 +1,7 @@
1
+ import { createRequire } from "node:module";
2
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
+
4
+ export { __require };
5
+
6
+ //# debugId=5F0D92CF7AD4DB9164756E2164756E21
7
+ //# sourceMappingURL=chunk-v0bahtg2.js.map
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [
5
+ ],
6
+ "mappings": "",
7
+ "debugId": "5F0D92CF7AD4DB9164756E2164756E21",
8
+ "names": []
9
+ }
@@ -0,0 +1,226 @@
1
+ import {
2
+ BatchworkError,
3
+ getAdapter,
4
+ isTerminalStatus
5
+ } from "./chunk-kv3847wy.js";
6
+
7
+ // src/server/events.ts
8
+ var EVENT_BY_STATUS = {
9
+ cancelled: "batch.cancelled",
10
+ completed: "batch.completed",
11
+ expired: "batch.expired",
12
+ failed: "batch.failed"
13
+ };
14
+ var toEvent = (provider, snapshot) => ({
15
+ completedAt: snapshot.completedAt?.toISOString(),
16
+ createdAt: snapshot.createdAt?.toISOString(),
17
+ id: snapshot.id,
18
+ provider,
19
+ requestCounts: snapshot.requestCounts,
20
+ type: EVENT_BY_STATUS[snapshot.status] ?? "batch.completed"
21
+ });
22
+
23
+ // src/server/signing.ts
24
+ var SIGNATURE_VERSION = "v1";
25
+ var TOLERANCE_SECONDS = 300;
26
+ var SECRET_PREFIX = "whsec_";
27
+ var encoder = new TextEncoder;
28
+ var base64ToBytes = (value) => Uint8Array.from(atob(value), (char) => char.codePointAt(0) ?? 0);
29
+ var bytesToBase64 = (bytes) => {
30
+ let binary = "";
31
+ for (const byte of bytes) {
32
+ binary += String.fromCodePoint(byte);
33
+ }
34
+ return btoa(binary);
35
+ };
36
+ var importKey = (secret) => {
37
+ const raw = secret.startsWith(SECRET_PREFIX) ? base64ToBytes(secret.slice(SECRET_PREFIX.length)) : encoder.encode(secret);
38
+ return crypto.subtle.importKey("raw", raw, { hash: "SHA-256", name: "HMAC" }, false, ["sign", "verify"]);
39
+ };
40
+ var signContent = async (secret, content) => {
41
+ const key = await importKey(secret);
42
+ const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(content));
43
+ return bytesToBase64(new Uint8Array(signature));
44
+ };
45
+ var verifyContent = async (secret, content, signature) => {
46
+ const key = await importKey(secret);
47
+ return crypto.subtle.verify("HMAC", key, base64ToBytes(signature), encoder.encode(content));
48
+ };
49
+ var signWebhook = async (secret, id, body, timestampSeconds) => {
50
+ const timestamp = Math.floor(timestampSeconds).toString();
51
+ const signature = await signContent(secret, `${id}.${timestamp}.${body}`);
52
+ return {
53
+ "webhook-id": id,
54
+ "webhook-signature": `${SIGNATURE_VERSION},${signature}`,
55
+ "webhook-timestamp": timestamp
56
+ };
57
+ };
58
+ var verifyWebhook = async (request, secret) => {
59
+ const id = request.headers.get("webhook-id");
60
+ const timestamp = request.headers.get("webhook-timestamp");
61
+ const signatureHeader = request.headers.get("webhook-signature");
62
+ if (!(id && timestamp && signatureHeader)) {
63
+ throw new BatchworkError("batchwork: missing webhook signature headers.");
64
+ }
65
+ const seconds = Number(timestamp);
66
+ if (!Number.isFinite(seconds) || Math.abs(Date.now() / 1000 - seconds) > TOLERANCE_SECONDS) {
67
+ throw new BatchworkError("batchwork: webhook timestamp outside tolerance.");
68
+ }
69
+ const body = await request.text();
70
+ const content = `${id}.${timestamp}.${body}`;
71
+ const signatures = signatureHeader.split(" ").map((part) => {
72
+ const comma = part.indexOf(",");
73
+ return comma === -1 ? part : part.slice(comma + 1);
74
+ });
75
+ let valid = false;
76
+ for (const signature of signatures) {
77
+ if (await verifyContent(secret, content, signature)) {
78
+ valid = true;
79
+ break;
80
+ }
81
+ }
82
+ if (!valid) {
83
+ throw new BatchworkError("batchwork: webhook signature verification failed.");
84
+ }
85
+ return { body, id, timestamp: seconds };
86
+ };
87
+ var verifyBatchWebhook = async (request, secret) => {
88
+ const { body } = await verifyWebhook(request, secret);
89
+ return JSON.parse(body);
90
+ };
91
+
92
+ // src/server/poller.ts
93
+ var sendWebhook = async (record, snapshot) => {
94
+ if (!record.webhookUrl) {
95
+ throw new BatchworkError("batchwork: tracked batch has no webhookUrl to deliver to.");
96
+ }
97
+ const body = JSON.stringify(toEvent(record.provider, snapshot));
98
+ const headers = {
99
+ "content-type": "application/json"
100
+ };
101
+ if (record.webhookSecret) {
102
+ Object.assign(headers, await signWebhook(record.webhookSecret, record.id, body, Date.now() / 1000));
103
+ }
104
+ const response = await fetch(record.webhookUrl, {
105
+ body,
106
+ headers,
107
+ method: "POST"
108
+ });
109
+ if (!response.ok) {
110
+ throw new BatchworkError(`batchwork: webhook delivery to ${record.webhookUrl} failed (${response.status}).`);
111
+ }
112
+ };
113
+ var createBatchPoller = (options) => {
114
+ const resolveCredentials = (provider) => {
115
+ if (typeof options.credentials === "function") {
116
+ return options.credentials(provider);
117
+ }
118
+ return options.credentials ?? {};
119
+ };
120
+ const sink = options.onComplete ?? sendWebhook;
121
+ const deliver = async (record, snapshot) => {
122
+ await sink(record, snapshot);
123
+ await options.store.set({
124
+ ...record,
125
+ deliveredAt: new Date().toISOString(),
126
+ status: snapshot.status
127
+ });
128
+ };
129
+ const track = async (target, opts) => {
130
+ const record = {
131
+ createdAt: new Date().toISOString(),
132
+ id: target.id,
133
+ provider: target.provider,
134
+ status: target.status ?? "in_progress",
135
+ webhookSecret: opts.secret,
136
+ webhookUrl: opts.webhookUrl
137
+ };
138
+ await options.store.set(record);
139
+ return record;
140
+ };
141
+ const process = async (record, delivered) => {
142
+ const adapter = getAdapter(record.provider);
143
+ const snapshot = await adapter.retrieve(record.id, resolveCredentials(record.provider));
144
+ if (isTerminalStatus(snapshot.status)) {
145
+ await deliver(record, snapshot);
146
+ delivered.push(record.id);
147
+ } else if (snapshot.status !== record.status) {
148
+ await options.store.set({ ...record, status: snapshot.status });
149
+ }
150
+ };
151
+ const tick = async () => {
152
+ const pending = await options.store.list({ delivered: false });
153
+ const delivered = [];
154
+ const failed = [];
155
+ for (const record of pending) {
156
+ try {
157
+ await process(record, delivered);
158
+ } catch (error) {
159
+ if (!options.onError) {
160
+ throw error;
161
+ }
162
+ options.onError(record, error);
163
+ failed.push({
164
+ error: error instanceof Error ? error.message : String(error),
165
+ id: record.id
166
+ });
167
+ }
168
+ }
169
+ return failed.length > 0 ? { checked: pending.length, delivered, failed } : { checked: pending.length, delivered };
170
+ };
171
+ const openaiWebhookHandler = (config) => async (request) => {
172
+ let verified;
173
+ try {
174
+ verified = await verifyWebhook(request, config.signingSecret);
175
+ } catch {
176
+ return new Response("invalid signature", { status: 400 });
177
+ }
178
+ const payload = JSON.parse(verified.body);
179
+ if (!payload.type?.startsWith("batch.")) {
180
+ return new Response("ignored", { status: 202 });
181
+ }
182
+ const id = payload.data?.id;
183
+ if (!id) {
184
+ return new Response("missing batch id", { status: 400 });
185
+ }
186
+ const record = await options.store.get(id);
187
+ if (!record || record.deliveredAt) {
188
+ return new Response("ok", { status: 200 });
189
+ }
190
+ const snapshot = await getAdapter("openai").retrieve(id, resolveCredentials("openai"));
191
+ if (isTerminalStatus(snapshot.status)) {
192
+ await deliver(record, snapshot);
193
+ }
194
+ return new Response("ok", { status: 200 });
195
+ };
196
+ return { deliver, openaiWebhookHandler, tick, track };
197
+ };
198
+
199
+ // src/server/store.ts
200
+ var createMemoryStore = () => {
201
+ const records = new Map;
202
+ return {
203
+ delete: (id) => {
204
+ records.delete(id);
205
+ return Promise.resolve();
206
+ },
207
+ get: (id) => Promise.resolve(records.get(id) ?? null),
208
+ list: (filter) => {
209
+ const all = [...records.values()];
210
+ if (filter?.delivered === undefined) {
211
+ return Promise.resolve(all);
212
+ }
213
+ const { delivered } = filter;
214
+ return Promise.resolve(all.filter((record) => record.deliveredAt !== undefined === delivered));
215
+ },
216
+ set: (record) => {
217
+ records.set(record.id, record);
218
+ return Promise.resolve();
219
+ }
220
+ };
221
+ };
222
+
223
+ export { toEvent, signWebhook, verifyWebhook, verifyBatchWebhook, createBatchPoller, createMemoryStore };
224
+
225
+ //# debugId=344750DFEF66E25964756E2164756E21
226
+ //# sourceMappingURL=chunk-zp2cxkyb.js.map
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/server/events.ts", "../src/server/signing.ts", "../src/server/poller.ts", "../src/server/store.ts"],
4
+ "sourcesContent": [
5
+ "import type { BatchProvider, BatchSnapshot, BatchStatus } from \"../types\";\nimport type { BatchWebhookEvent, BatchWebhookEventType } from \"./types\";\n\nconst EVENT_BY_STATUS: Partial<Record<BatchStatus, BatchWebhookEventType>> = {\n cancelled: \"batch.cancelled\",\n completed: \"batch.completed\",\n expired: \"batch.expired\",\n failed: \"batch.failed\",\n};\n\n/** Map a terminal snapshot to the unified webhook event batchwork delivers. */\nexport const toEvent = (\n provider: BatchProvider,\n snapshot: BatchSnapshot\n): BatchWebhookEvent => ({\n completedAt: snapshot.completedAt?.toISOString(),\n createdAt: snapshot.createdAt?.toISOString(),\n id: snapshot.id,\n provider,\n requestCounts: snapshot.requestCounts,\n type: EVENT_BY_STATUS[snapshot.status] ?? \"batch.completed\",\n});\n",
6
+ "import { BatchworkError } from \"../errors\";\nimport type { BatchWebhookEvent } from \"./types\";\n\n// Standard Webhooks-style HMAC-SHA256 signing, compatible with OpenAI's webhook\n// signatures (so the same verifier handles inbound OpenAI events and batchwork's\n// own outbound deliveries). Uses Web Crypto so it runs on edge runtimes.\n\nconst SIGNATURE_VERSION = \"v1\";\nconst TOLERANCE_SECONDS = 300;\nconst SECRET_PREFIX = \"whsec_\";\nconst encoder = new TextEncoder();\n\nconst base64ToBytes = (value: string): Uint8Array<ArrayBuffer> =>\n Uint8Array.from(atob(value), (char) => char.codePointAt(0) ?? 0);\n\nconst bytesToBase64 = (bytes: Uint8Array): string => {\n let binary = \"\";\n for (const byte of bytes) {\n binary += String.fromCodePoint(byte);\n }\n return btoa(binary);\n};\n\nconst importKey = (secret: string): Promise<CryptoKey> => {\n const raw = secret.startsWith(SECRET_PREFIX)\n ? base64ToBytes(secret.slice(SECRET_PREFIX.length))\n : encoder.encode(secret);\n return crypto.subtle.importKey(\n \"raw\",\n raw,\n { hash: \"SHA-256\", name: \"HMAC\" },\n false,\n [\"sign\", \"verify\"]\n );\n};\n\nconst signContent = async (\n secret: string,\n content: string\n): Promise<string> => {\n const key = await importKey(secret);\n const signature = await crypto.subtle.sign(\n \"HMAC\",\n key,\n encoder.encode(content)\n );\n return bytesToBase64(new Uint8Array(signature));\n};\n\nconst verifyContent = async (\n secret: string,\n content: string,\n signature: string\n): Promise<boolean> => {\n const key = await importKey(secret);\n return crypto.subtle.verify(\n \"HMAC\",\n key,\n base64ToBytes(signature),\n encoder.encode(content)\n );\n};\n\n/** Build Standard Webhooks signature headers for an outbound delivery. */\nexport const signWebhook = async (\n secret: string,\n id: string,\n body: string,\n timestampSeconds: number\n): Promise<Record<string, string>> => {\n const timestamp = Math.floor(timestampSeconds).toString();\n const signature = await signContent(secret, `${id}.${timestamp}.${body}`);\n return {\n \"webhook-id\": id,\n \"webhook-signature\": `${SIGNATURE_VERSION},${signature}`,\n \"webhook-timestamp\": timestamp,\n };\n};\n\nexport interface VerifiedWebhook {\n id: string;\n timestamp: number;\n body: string;\n}\n\n/**\n * Verify a Standard Webhooks-signed request and return its raw body. Throws if\n * headers are missing, the timestamp is outside tolerance, or no signature\n * matches. Consumes the request body.\n */\nexport const verifyWebhook = async (\n request: Request,\n secret: string\n): Promise<VerifiedWebhook> => {\n const id = request.headers.get(\"webhook-id\");\n const timestamp = request.headers.get(\"webhook-timestamp\");\n const signatureHeader = request.headers.get(\"webhook-signature\");\n if (!(id && timestamp && signatureHeader)) {\n throw new BatchworkError(\"batchwork: missing webhook signature headers.\");\n }\n\n const seconds = Number(timestamp);\n if (\n !Number.isFinite(seconds) ||\n Math.abs(Date.now() / 1000 - seconds) > TOLERANCE_SECONDS\n ) {\n throw new BatchworkError(\"batchwork: webhook timestamp outside tolerance.\");\n }\n\n const body = await request.text();\n const content = `${id}.${timestamp}.${body}`;\n const signatures = signatureHeader.split(\" \").map((part) => {\n const comma = part.indexOf(\",\");\n return comma === -1 ? part : part.slice(comma + 1);\n });\n\n let valid = false;\n for (const signature of signatures) {\n // oxlint-disable-next-line no-await-in-loop -- usually a single signature.\n if (await verifyContent(secret, content, signature)) {\n valid = true;\n break;\n }\n }\n if (!valid) {\n throw new BatchworkError(\n \"batchwork: webhook signature verification failed.\"\n );\n }\n\n return { body, id, timestamp: seconds };\n};\n\n/**\n * Verify and parse a batchwork webhook on your receiving endpoint. Returns the\n * unified {@link BatchWebhookEvent}.\n */\nexport const verifyBatchWebhook = async (\n request: Request,\n secret: string\n): Promise<BatchWebhookEvent> => {\n const { body } = await verifyWebhook(request, secret);\n return JSON.parse(body) as BatchWebhookEvent;\n};\n",
7
+ "import { BatchworkError } from \"../errors\";\nimport { isTerminalStatus } from \"../job\";\nimport { getAdapter } from \"../providers\";\nimport type {\n BatchProvider,\n BatchSnapshot,\n BatchStatus,\n ProviderCredentials,\n} from \"../types\";\nimport { toEvent } from \"./events\";\nimport { signWebhook, verifyWebhook } from \"./signing\";\nimport type { BatchStore, TrackedBatch } from \"./types\";\n\n/** Credentials for polling: a fixed config, or one resolved per provider. */\nexport type CredentialResolver =\n | ProviderCredentials\n | ((provider: BatchProvider) => ProviderCredentials);\n\n/**\n * Handles a batch reaching a terminal status. Replaces the default signed\n * webhook delivery — e.g. to invoke a callback instead (see `batchwork/next`).\n */\nexport type CompletionSink = (\n record: TrackedBatch,\n snapshot: BatchSnapshot\n) => Promise<void>;\n\nexport interface BatchPollerOptions {\n store: BatchStore;\n /** Falls back to provider env vars (e.g. `OPENAI_API_KEY`) when omitted. */\n credentials?: CredentialResolver;\n /** Replaces signed-webhook delivery when a batch finishes. */\n onComplete?: CompletionSink;\n /**\n * Called when processing a single batch throws during `tick`. When provided,\n * the tick reports the error and continues to the next batch; when omitted,\n * the error propagates out of `tick`.\n */\n onError?: (record: TrackedBatch, error: unknown) => void;\n}\n\nexport interface TrackTarget {\n id: string;\n provider: BatchProvider;\n status?: BatchStatus;\n}\n\nexport interface TrackOptions {\n /** Where to POST the completion webhook. Omit for callback-based delivery. */\n webhookUrl?: string;\n /** Signs the outbound webhook (Standard Webhooks HMAC) when provided. */\n secret?: string;\n}\n\nexport interface TickResult {\n checked: number;\n delivered: string[];\n /** Batches whose processing threw this tick (only when `onError` is set). */\n failed?: { id: string; error: string }[];\n}\n\nexport interface OpenAIWebhookOptions {\n /** The OpenAI webhook signing secret (`whsec_…`). */\n signingSecret: string;\n}\n\nexport interface BatchPoller {\n track: (target: TrackTarget, options: TrackOptions) => Promise<TrackedBatch>;\n tick: () => Promise<TickResult>;\n deliver: (record: TrackedBatch, snapshot: BatchSnapshot) => Promise<void>;\n openaiWebhookHandler: (\n options: OpenAIWebhookOptions\n ) => (request: Request) => Promise<Response>;\n}\n\n/** The default completion sink: POST a signed webhook to the tracked URL. */\nconst sendWebhook: CompletionSink = async (record, snapshot) => {\n if (!record.webhookUrl) {\n throw new BatchworkError(\n \"batchwork: tracked batch has no webhookUrl to deliver to.\"\n );\n }\n const body = JSON.stringify(toEvent(record.provider, snapshot));\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n };\n if (record.webhookSecret) {\n Object.assign(\n headers,\n await signWebhook(\n record.webhookSecret,\n record.id,\n body,\n Date.now() / 1000\n )\n );\n }\n const response = await fetch(record.webhookUrl, {\n body,\n headers,\n method: \"POST\",\n });\n if (!response.ok) {\n throw new BatchworkError(\n `batchwork: webhook delivery to ${record.webhookUrl} failed (${response.status}).`\n );\n }\n};\n\n/**\n * Create a managed poller: register submitted batches with `track`, then run\n * `tick` on a schedule (cron) to poll open batches and deliver one unified,\n * signed webhook per batch when it finishes. For OpenAI, mount\n * `openaiWebhookHandler` to skip polling and react to native webhooks instead.\n */\nexport const createBatchPoller = (options: BatchPollerOptions): BatchPoller => {\n const resolveCredentials = (provider: BatchProvider): ProviderCredentials => {\n if (typeof options.credentials === \"function\") {\n return options.credentials(provider);\n }\n return options.credentials ?? {};\n };\n\n const sink = options.onComplete ?? sendWebhook;\n\n const deliver = async (\n record: TrackedBatch,\n snapshot: BatchSnapshot\n ): Promise<void> => {\n // Run the side effect before marking delivered: if it throws, the record\n // stays pending and is retried on the next tick (at-least-once delivery).\n await sink(record, snapshot);\n await options.store.set({\n ...record,\n deliveredAt: new Date().toISOString(),\n status: snapshot.status,\n });\n };\n\n const track = async (\n target: TrackTarget,\n opts: TrackOptions\n ): Promise<TrackedBatch> => {\n const record: TrackedBatch = {\n createdAt: new Date().toISOString(),\n id: target.id,\n provider: target.provider,\n status: target.status ?? \"in_progress\",\n webhookSecret: opts.secret,\n webhookUrl: opts.webhookUrl,\n };\n await options.store.set(record);\n return record;\n };\n\n const process = async (\n record: TrackedBatch,\n delivered: string[]\n ): Promise<void> => {\n const adapter = getAdapter(record.provider);\n const snapshot = await adapter.retrieve(\n record.id,\n resolveCredentials(record.provider)\n );\n if (isTerminalStatus(snapshot.status)) {\n await deliver(record, snapshot);\n delivered.push(record.id);\n } else if (snapshot.status !== record.status) {\n await options.store.set({ ...record, status: snapshot.status });\n }\n };\n\n const tick = async (): Promise<TickResult> => {\n const pending = await options.store.list({ delivered: false });\n const delivered: string[] = [];\n const failed: { id: string; error: string }[] = [];\n for (const record of pending) {\n // oxlint-disable-next-line no-await-in-loop -- batches are polled serially\n // to avoid hammering provider rate limits; deliver before the next.\n try {\n // oxlint-disable-next-line no-await-in-loop -- see above.\n await process(record, delivered);\n } catch (error) {\n // Without an `onError` handler, preserve the propagate-the-throw\n // behavior; with one, report and continue so a single failing batch\n // can't starve the rest of the queue. Either way the record stays\n // pending (it was never marked delivered) and retries next tick.\n if (!options.onError) {\n throw error;\n }\n options.onError(record, error);\n failed.push({\n error: error instanceof Error ? error.message : String(error),\n id: record.id,\n });\n }\n }\n return failed.length > 0\n ? { checked: pending.length, delivered, failed }\n : { checked: pending.length, delivered };\n };\n\n const openaiWebhookHandler =\n (config: OpenAIWebhookOptions) =>\n async (request: Request): Promise<Response> => {\n let verified: { body: string };\n try {\n verified = await verifyWebhook(request, config.signingSecret);\n } catch {\n return new Response(\"invalid signature\", { status: 400 });\n }\n\n const payload = JSON.parse(verified.body) as {\n type?: string;\n data?: { id?: string };\n };\n if (!payload.type?.startsWith(\"batch.\")) {\n return new Response(\"ignored\", { status: 202 });\n }\n const id = payload.data?.id;\n if (!id) {\n return new Response(\"missing batch id\", { status: 400 });\n }\n const record = await options.store.get(id);\n if (!record || record.deliveredAt) {\n return new Response(\"ok\", { status: 200 });\n }\n const snapshot = await getAdapter(\"openai\").retrieve(\n id,\n resolveCredentials(\"openai\")\n );\n if (isTerminalStatus(snapshot.status)) {\n await deliver(record, snapshot);\n }\n return new Response(\"ok\", { status: 200 });\n };\n\n return { deliver, openaiWebhookHandler, tick, track };\n};\n",
8
+ "import type { BatchStore, TrackedBatch } from \"./types\";\n\n/** An in-memory `BatchStore`. Suitable for development and single-process use. */\nexport const createMemoryStore = (): BatchStore => {\n const records = new Map<string, TrackedBatch>();\n\n return {\n delete: (id) => {\n records.delete(id);\n return Promise.resolve();\n },\n get: (id) => Promise.resolve(records.get(id) ?? null),\n list: (filter) => {\n const all = [...records.values()];\n if (filter?.delivered === undefined) {\n return Promise.resolve(all);\n }\n const { delivered } = filter;\n return Promise.resolve(\n all.filter((record) => (record.deliveredAt !== undefined) === delivered)\n );\n },\n set: (record) => {\n records.set(record.id, record);\n return Promise.resolve();\n },\n };\n};\n"
9
+ ],
10
+ "mappings": "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",
11
+ "debugId": "344750DFEF66E25964756E2164756E21",
12
+ "names": []
13
+ }
@@ -0,0 +1,16 @@
1
+ /** Base error for all batchwork failures. */
2
+ export declare class BatchworkError extends Error {
3
+ constructor(message: string, options?: {
4
+ cause?: unknown;
5
+ });
6
+ }
7
+ /** Thrown when a model resolves to a provider without a batch adapter. */
8
+ export declare class UnsupportedProviderError extends BatchworkError {
9
+ readonly provider: string;
10
+ constructor(provider: string);
11
+ }
12
+ /** Thrown when an optional provider package is not installed. */
13
+ export declare class MissingDependencyError extends BatchworkError {
14
+ constructor(pkg: string, provider: string);
15
+ }
16
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,6CAA6C;AAC7C,qBAAa,cAAe,SAAQ,KAAK;IACvC,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAGzD;CACF;AAED,0EAA0E;AAC1E,qBAAa,wBAAyB,SAAQ,cAAc;IAC1D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,YAAY,QAAQ,EAAE,MAAM,EAM3B;CACF;AAED,iEAAiE;AACjE,qBAAa,sBAAuB,SAAQ,cAAc;IACxD,YAAY,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAKxC;CACF"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /** Make a request and parse a JSON response, throwing on non-2xx. */
2
+ export declare const requestJson: <T>(url: string, init: RequestInit) => Promise<T>;
3
+ /** Make a request and return the raw body stream, throwing on non-2xx. */
4
+ export declare const requestStream: (url: string, init: RequestInit) => Promise<ReadableStream<Uint8Array>>;
5
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAmBA,qEAAqE;AACrE,eAAO,MAAM,WAAW,GAAU,CAAC,OAC5B,MAAM,QACL,WAAW,KAChB,OAAO,CAAC,CAAC,CAIX,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,aAAa,QACnB,MAAM,QACL,WAAW,KAChB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAOpC,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { batch, cancelBatch, getBatch, getBatchResults } from "./batch";
2
+ export { BatchworkError, MissingDependencyError, UnsupportedProviderError, } from "./errors";
3
+ export { BatchJob, isTerminalStatus } from "./job";
4
+ export { resolveModel } from "./model";
5
+ export type { BatchDefaults, BatchOptions, BatchProvider, BatchRef, BatchRequest, BatchRequestCounts, BatchRequestSettings, BatchResult, BatchResultError, BatchResultStatus, BatchSnapshot, BatchStatus, BatchUsage, ProviderCredentials, ProviderOptions, WaitOptions, } from "./types";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,YAAY,EACV,aAAa,EACb,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,EACpB,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,UAAU,EACV,mBAAmB,EACnB,eAAe,EACf,WAAW,GACZ,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,30 @@
1
+ import {
2
+ batch,
3
+ cancelBatch,
4
+ getBatch,
5
+ getBatchResults,
6
+ resolveModel
7
+ } from "./chunk-ab2d71gk.js";
8
+ import {
9
+ BatchJob,
10
+ BatchworkError,
11
+ MissingDependencyError,
12
+ UnsupportedProviderError,
13
+ isTerminalStatus
14
+ } from "./chunk-kv3847wy.js";
15
+ import"./chunk-v0bahtg2.js";
16
+ export {
17
+ resolveModel,
18
+ isTerminalStatus,
19
+ getBatchResults,
20
+ getBatch,
21
+ cancelBatch,
22
+ batch,
23
+ UnsupportedProviderError,
24
+ MissingDependencyError,
25
+ BatchworkError,
26
+ BatchJob
27
+ };
28
+
29
+ //# debugId=503CE7D732E9F8F664756E2164756E21
30
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [
5
+ ],
6
+ "mappings": "",
7
+ "debugId": "503CE7D732E9F8F664756E2164756E21",
8
+ "names": []
9
+ }
package/dist/job.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ import type { BatchAdapter } from "./providers/adapter";
2
+ import type { BatchProvider, BatchRequestCounts, BatchResult, BatchSnapshot, BatchStatus, ProviderCredentials, WaitOptions } from "./types";
3
+ /** Whether a status means the batch has finished processing. */
4
+ export declare const isTerminalStatus: (status: BatchStatus) => boolean;
5
+ /**
6
+ * A handle to a submitted batch. Returned by `batch()` and `getBatch()`. Use it
7
+ * to poll status, wait for completion, stream results, or cancel.
8
+ */
9
+ export declare class BatchJob {
10
+ #private;
11
+ readonly provider: BatchProvider;
12
+ readonly id: string;
13
+ constructor(adapter: BatchAdapter, credentials: ProviderCredentials, snapshot: BatchSnapshot);
14
+ /** The most recently observed status. */
15
+ get status(): BatchStatus;
16
+ /** The most recently observed per-request tallies. */
17
+ get requestCounts(): BatchRequestCounts;
18
+ /** The most recently observed snapshot. */
19
+ get snapshot(): BatchSnapshot;
20
+ /** Refresh the status from the provider and return the new snapshot. */
21
+ poll(): Promise<BatchSnapshot>;
22
+ /** Poll until the batch reaches a terminal status, then return the snapshot. */
23
+ wait(options?: WaitOptions): Promise<BatchSnapshot>;
24
+ /** Stream normalized results as they are read. Order is not guaranteed. */
25
+ results(): AsyncGenerator<BatchResult>;
26
+ /** Collect all results into an array, keyed by `customId`. */
27
+ collect(): Promise<BatchResult[]>;
28
+ /** Request cancellation, then refresh status. */
29
+ cancel(): Promise<BatchSnapshot>;
30
+ }
31
+ //# sourceMappingURL=job.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job.d.ts","sourceRoot":"","sources":["../src/job.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EACV,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,WAAW,EACZ,MAAM,SAAS,CAAC;AAWjB,gEAAgE;AAChE,eAAO,MAAM,gBAAgB,WAAY,WAAW,KAAG,OACxB,CAAC;AAoBhC;;;GAGG;AACH,qBAAa,QAAQ;;IACnB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAMpB,YACE,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,mBAAmB,EAChC,QAAQ,EAAE,aAAa,EAOxB;IAED,yCAAyC;IACzC,IAAI,MAAM,IAAI,WAAW,CAExB;IAED,sDAAsD;IACtD,IAAI,aAAa,IAAI,kBAAkB,CAEtC;IAED,2CAA2C;IAC3C,IAAI,QAAQ,IAAI,aAAa,CAE5B;IAED,wEAAwE;IAClE,IAAI,IAAI,OAAO,CAAC,aAAa,CAAC,CAGnC;IAED,gFAAgF;IAC1E,IAAI,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,CA0B5D;IAED,2EAA2E;IAC3E,OAAO,IAAI,cAAc,CAAC,WAAW,CAAC,CAErC;IAED,8DAA8D;IACxD,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAMtC;IAED,iDAAiD;IAC3C,MAAM,IAAI,OAAO,CAAC,aAAa,CAAC,CAGrC;CACF"}