Skip to content

feat: add Claude (Anthropic) batch processing status mapping #121

Description

@kvithayathil

Parent

#30 — Unify OCR provider status mapping

Summary

Map Anthropic Claude Message Batches API lifecycle states to votecatcher MatchingStatus values.

Claude Message Batches API Lifecycle

Per the Claude Batch Processing docs:

  • 50% cost discount vs real-time inference
  • Up to 100,000 requests per batch or 256 MB, whichever is reached first
  • Most batches complete within 1 hour; snip 24h max processing window before expiry
  • Results available for 29 days after creation
  • Supports all active models and all Messages API features (vision, tool use, system messages, multi-turn)
  • Prompt caching supported (discounts stack with batch discount)

Batch Processing Status Flow

in_progress → ended

processing_status transitions: in_progressended (all requests finished or 24h elapsed).

Status Values

Claude Batch processing_status Description Proposed Mapping
in_progress Batch is being processed OCR_IN_PROGRESS
ended All requests finished (or 24h expired) OCR_COMPLETED

Per-Request Result Types (after ended)

Result Type Description Proposed Mapping
succeeded Request completed successfully OCR_COMPLETED
errored Request failed (invalid request or server error) OCR_FAILED
canceled Batch cancelled before this request processed OCR_CANCELLED
expired 24h window elapsed before request processed OCR_TIMED_OUT

Key API Operations

  1. Create: client.messages.batches.create(requests=[...]) — list of {custom_id, params} objects
  2. Poll: client.messages.batches.retrieve(batch_id) → check .processing_status
  3. Results: Stream results via client.messages.batches.results(batch_id).jsonl format
  4. Cancel: client.messages.batches.cancel(batch_id) — status transitions to canceling then ended
  5. List: client.messages.batches.list(limit=20) — auto-paginating

Request Format

{
  "custom_id": "my-request-1",
  "params": {
    "model": "claude-sonnet-4-20250514",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "..."}]
  }
}

Result Format

Results streamed as .jsonl — order not guaranteed, match by custom_id:

{"custom_id":"request-2","result":{"type":"succeeded","message":{...}}}
{"custom_id":"request-1","result":{"type":"errored","error":{"type":"invalid_request_error",...}}}

Expiration Behavior

  • Batches expire if not completed within 24 hours
  • Unprocessed requests return result type expired
  • Completed requests within an expired batch are still billed

Prompt Caching

  • Supports cache_control: {"type": "ephemeral"} in batch requests
  • Cache hits are best-effort due to concurrent async processing
  • Typical hit rates: 30%–98% depending on traffic patterns
  • Consider 1-hour cache duration for better hit rates with batches

Proposed Implementation

Since Claude uses a two-level status model (batch-level processing_status + per-request result types), the mapping needs to handle both:

  1. Batch-level polling: in_progressOCR_IN_PROGRESS, ended → check request_counts for final status
  2. Per-request results: Map each result type to the appropriate MatchingStatus
  3. Cancel flow: canceling intermediate state → OCR_CANCELLED

Status Mapping

STATUS_MAPPING: dict[str, MatchingStatus] = {
    "in_progress": MatchingStatus.OCR_IN_PROGRESS,
    "canceling": MatchingStatus.OCR_CANCELLED,
    "ended": MatchingStatus.OCR_COMPLETED,  # check request_counts for failures
}

RESULT_TYPE_MAPPING: dict[str, MatchingStatus] = {
    "succeeded": MatchingStatus.OCR_COMPLETED,
    "errored": MatchingStatus.OCR_FAILED,
    "canceled": MatchingStatus.OCR_CANCELLED,
    "expired": MatchingStatus.OCR_TIMED_OUT,
}

Tasks

  • Add STATUS_MAPPING for batch-level processing_status values
  • Add RESULT_TYPE_MAPPING for per-request result types
  • Implement OcrStatusMapper.register("claude", STATUS_MAPPING) once feat: unify OCR provider status mapping with generic fallback #30 mapper is built
  • Handle two-level status model: batch polling uses STATUS_MAPPING, result parsing uses RESULT_TYPE_MAPPING
  • Parse request_counts from batch object for progress tracking during in_progress
  • Handle errored result type — distinguish invalid_request_error (fix needed) vs server error (retry)
  • Add tests for all batch status values + unknown status fallback
  • Add tests for all 4 result types
  • Add test for canceling intermediate state
  • Consider prompt caching support for shared context across batch requests

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions