Batch content capture
Prompt samples from batch and flex jobs with trackBatchRequest and trackBatchResult.
wrap() samples prompts and completions from live calls. Batch and flex jobs have no live call, and submit() and the track*Batch helpers record usage only, so batch work produces no prompt samples. If you loop over a job's results yourself, use these two functions instead.
They need @superpenguin/js 0.15.0 or later (trackBatchResult with request is available from 0.13.0). Capture still requires the org opt-in in Settings; see Content capture and privacy.
When to use it
Use it for batch and flex jobs whose output files hold only responses. The prompt is in the input file you submitted, and the result arrives hours later without it. Instead of keeping every prompt until its result returns, send each half when you already have it. The server joins the two halves on the line's idempotency key.
Recommended: send each half when you have it
import { flush, init, trackBatchRequest, trackBatchResult } from "@superpenguin/js";
init({ apiKey: process.env.SP_API_KEY });
// At submit time, once the batch id is known: the prompt half.
for (const line of inputLines) {
await trackBatchRequest({
key: `${batch.id}:${line.custom_id}`, // the key the usage event will carry
request: line, // the input line (its body is read)
metadata: { feature: "ai_extractor" },
provider: "sail_research",
});
}
// On completion: usage, plus the output half. No request needed.
for (const line of outputLines) {
const event = buildUsageEvent(line); // idempotency_key = `${batch.id}:${custom_id}`
await trackBatchResult({ usageEvent: event, response: line.response.body });
}
await flush();The key passed to trackBatchRequest must equal the usage event's idempotency_key, for example `${batchId}:${customId}`. This is the key trackOpenAIBatch uses. If the batch id is not known until submit returns, call trackBatchRequest after submit.
The server pairs a result with a prompt half sent up to 3 days earlier. Batch jobs finish within 24 hours, so a normal job always pairs.
trackBatchRequest
trackBatchRequest(input: BatchRequestInput): Promise<boolean>| Field | Description |
|---|---|
key | Required. The line's idempotency key, the same one its usage event carries. |
request | Required. The input line's request body, or the whole input line (its body is used). |
metadata | Attribution such as feature, customer_id and prompt_key. It outranks an active withMetadata scope. Where the result's usage event also sets a field, the usage event wins. |
provider, baseUrl | The provider, as on the usage event. |
client | Event client; defaults to the process-level client. |
Resolves to true when the prompt half was queued for upload, and false when the line is not sampled or capture is off. It never throws.
One call, if you still hold the prompts
If you keep the request body until the result returns, pass it as request and one call sends the usage event and one complete sample:
for (const line of outputLines) { // the job's output JSONL, parsed
const customId = line.custom_id;
const event = buildUsageEvent(line); // the object you pass to submit() today
await trackBatchResult({
request: requestLines[customId].body, // the matching input line's body
response: line.response.body,
usageEvent: event,
});
}
await flush();This path samples at the org's adaptive rate, the same rate wrap() uses. Reporting the same result twice stores one sample.
trackBatchResult
trackBatchResult(input: BatchResultInput): Promise<boolean>| Field | Description |
|---|---|
response | Required. The result's response body, or the whole output line (its response.body is used). |
request | The input line's request body, or the whole input line. Omit it after trackBatchRequest to send only the output half. |
usageEvent | The usage event you already pass to submit(). It is submitted unchanged. |
key | Join key of the output half when request is omitted. Defaults to the usage event's idempotency_key. |
provider, batchId, customId | Without usageEvent, the SDK builds the event the way trackOpenAIBatch does and needs all three. customId is taken from a whole input or output line when not given. |
completedAt | The job's completion time (Date, unix seconds or ISO string). Backdates a built event; also stamps the sample when the usage event has no created_at. |
metadata | Attribution for a built event. With usageEvent, it only fills sample attribution the event does not carry. |
baseUrl, region, deploymentType, serviceTier | Pricing fields for a built event. |
client | Event client; defaults to the process-level client. |
Resolves to true when a usage event was submitted. A built event is skipped for a non-200 result line, matching trackOpenAIBatch. Without usageEvent, and without provider, batchId and customId, it throws.
Chat (messages, choices) and Responses (instructions, input, output) bodies are both read, including the system prompt. A withMetadata scope cannot override the usage event's attribution on the sample.
Guarantees
- Spend never depends on capture.
trackBatchResultsubmits the usage event first and unchanged, exactly assubmit()does. Capture runs afterwards, and a capture error never affects the usage event. - Only sampled lines send anything. In the split pattern, both calls hash the key against your organization's stable batch rate, so they make the same decision without shared state. An unsampled line reads no text and makes no network call. The SDK fetches your organization's capture settings once and caches them.
- The same privacy rules apply. The org opt-in, redaction and encryption at rest apply as for every sample.
configureContentCapture()can still narrow capture:allow: falsevetoes both halves,recordPrompt: falsedrops the prompt half,recordOutcome: falsedrops the output half, andsampleRateMultiplierscales the batch rate. - Unpaired halves expire. A half without its partner, such as a prompt half whose result never arrives, is discarded after 14 days.
- Capture is optional. Skipping
trackBatchRequestjust means no samples for those lines. Usage and cost are recorded the same way.
Related
- Batch and service-tier pricing for the
track*Batchusage helpers - Python batch content capture