Skip to content

feat(xai): xai_batch_chat tool — submit many completions to xAI Batch API - #23333

Closed
Julientalbot wants to merge 4 commits into
NousResearch:mainfrom
Julientalbot:feat/xai-batch-api
Closed

feat(xai): xai_batch_chat tool — submit many completions to xAI Batch API#23333
Julientalbot wants to merge 4 commits into
NousResearch:mainfrom
Julientalbot:feat/xai-batch-api

Conversation

@Julientalbot

Copy link
Copy Markdown
Contributor

Summary

Adds xai_batch_chat — a tool that wraps xAI's Batch API end-to-end (POST /v1/batches → add inline requests → poll → paginated retrieval) behind a single sync entry point. Use it to process dozens-to-thousands of chat completions in one shot at reduced pricing and with higher rate limits than the synchronous endpoint, with up to a 24h SLA per xAI.

Pairs with #23329 (xai_deferred_chat): deferred handles one long completion, batch handles many short-to-medium ones in parallel.

Why

The Batch API is the only xAI surface where the cost / rate-limit math actually works out for large evaluation, classification, or backfill workloads. Hermes' autonomous mode and delegation features can already orchestrate long-running tasks, but every call still hits the synchronous endpoint with full per-minute rate limits. With this tool an agent (or a cron-scheduled hermes session) can fan out, e.g., a 5 000-row classification job in one POST and walk away.

Changes (5 files, +820/-0)

tools/xai_batch_tool.py (new, ~440 LoC)

Single public entry point:

xai_batch_chat(
    requests: list[dict],          # each: {"prompt"|"messages", "system"?, "model"?, "request_id"?, "extra_body"?}
    *,
    name: str | None = None,       # batch display name (auto-generated if absent)
    model: str | None = None,      # default for any request without its own
    wait: bool = True,             # block until done? (default yes)
    max_wait_seconds: int | None = None,    # default: 86400 (24h, matches xAI SLA)
    poll_interval_seconds: float | None = None,  # default: 30s
) -> {
    "batch_id": str,
    "state": {"num_requests": int, "num_pending": int, "num_success": int, "num_error": int, ...},
    # Only when wait=True:
    "results": [{"request_id": str, "response": dict | None, "error": dict | None}, ...],
}
  • Each request dict accepts either a prompt (with optional system) or a full messages list.
  • Per-request model overrides the tool-level default.
  • Caller-provided request_id survives round-trip; missing IDs are auto-generated as req-{i:05d}-{hex}.
  • wait=False returns immediately after submit + add + state-fetch — useful for fire-and-forget patterns where the caller resumes polling later (e.g. cron-driven).
  • Configurable via config.yaml:
    xai_batch:
      model: grok-4-1-fast-non-reasoning
      max_wait_seconds: 7200
      poll_interval_seconds: 60
  • Reuses tools.xai_http.hermes_xai_user_agent for User-Agent.
  • Self-registers via tools.registry.registry.register with check_fn gating on XAI_API_KEY.

tests/tools/test_xai_batch_tool.py (new, ~340 LoC)

19 unit tests using a scripted httpx.request fake (no real network):

  • Requirements & schema: check_xai_batch_requirements returns the right state with/without XAI_API_KEY; schema advertises required + optional params correctly.
  • Argument validation: empty requests, missing API key, requests missing both prompt and messages, negative max_wait_seconds.
  • Submit semantics: full create → add wiring (verifies the inline payload shape — batch_request_id, batch_request: {method, url, body}); default model resolution; per-request model overrides; messages overrides prompt; HTTP 4xx surfaces.
  • wait=False: returns after submit + state-fetch; no poll, no results.
  • Poll: walks num_pending from 2 → 1 → 0 then proceeds; timeout via mocked time.monotonic.
  • Results pagination: multi-page walk with pagination_token; missing results yield response: None for the unfound request_id (graceful degradation).
  • Headers: Authorization: Bearer <key> and User-Agent: Hermes-Agent/<version> on every HTTP call.

Wiring

Validation

  • pytest tests/tools/test_xai_batch_tool.py tests/tools/test_registry.py50/50 passing locally on top of current main.

Backward compatibility

  • Pure addition. No existing modules touched beyond the three single-line additions to toolsets.py, tools_config.py, and the registry snapshot list.
  • Tool is check_fn-gated on XAI_API_KEY — invisible to users without an xAI key.
  • xai_batch is not in any default toolset roster; users opt in via tools_config or by enabling the toolset in their profile.

Scope deliberately not in this PR

  • File-based input (uploading a JSONL via Files API for >50 000 requests). This first PR uses the inline POST /v1/batches/{id}/requests path only — the file path requires Files API plumbing that doesn't currently exist anywhere in the codebase.
  • Image / video / multi-modal batch results. The result mapper picks response from the result envelope, which works for chat completions; a richer adapter that surfaces image_response.url / video_response.url / usage is a clean follow-up.
  • Cancellation, listing, request-level metadata. xai_batch_chat is a one-shot orchestration tool. Exposing xai_batch_cancel(batch_id), xai_batch_list(), and xai_batch_get(batch_id) as separate tools is straightforward but would inflate this PR; happy to do those follow-ups.

…atch API

xAI Batch API processes large volumes of chat completions asynchronously
with reduced pricing and higher rate limits than the synchronous endpoint.
Most batches complete within 24h per xAI's SLA.

Lifecycle: POST /v1/batches (create empty batch with name) → POST
/v1/batches/{id}/requests (add chat-completion requests inline, max 25MB
per request) → GET /v1/batches/{id} (poll state counters until num_pending
== 0) → GET /v1/batches/{id}/results (paginated retrieval).

Changes:
- tools/xai_batch_tool.py: self-contained tool implementation
  - xai_batch_chat(requests, name, model, wait, max_wait_seconds,
    poll_interval_seconds) — one entry point covering submit + add +
    optional poll + paginated retrieval
  - Each request dict supports prompt | messages, system, model,
    request_id (auto-generated if absent), extra_body
  - Per-request model overrides the tool-level default
  - wait=True (default): blocks until done, returns results in
    submission order; wait=False: returns batch_id immediately
    so the caller can resume polling later
  - Configurable via config.yaml under xai_batch:
    {model, max_wait_seconds, poll_interval_seconds}
  - Reuses tools.xai_http.hermes_xai_user_agent for User-Agent
  - Self-registers via tools.registry.registry.register
- tests/tools/test_xai_batch_tool.py: 19 unit tests with a scripted
  httpx.request fake covering
  - check_xai_batch_requirements (with/without API key)
  - schema (required requests, optional params)
  - argument validation (empty list, missing key, missing prompt,
    negative max_wait)
  - submit semantics (create + add wiring, default model, per-request
    model, messages overrides prompt, 4xx error)
  - wait=False (returns after submit + state)
  - poll semantics (pending → 0, timeout)
  - results pagination (multi-page walk, missing-result fallback)
  - headers (Authorization Bearer, User-Agent prefix on every call)
- toolsets.py: add xai_batch_chat to _HERMES_CORE_TOOLS and new
  xai_batch toolset
- hermes_cli/tools_config.py: add xai_batch to CONFIGURABLE_TOOLSETS
- tests/tools/test_registry.py: add tools.xai_batch_tool to manual
  builtin tool set snapshot

Pairs naturally with NousResearch#23329 (xai_deferred_chat) — deferred handles a single
long completion, batch handles many short-to-medium ones in parallel.

Requires XAI_API_KEY in ~/.hermes/.env.
@alt-glitch alt-glitch added type/feature New feature or request comp/tools Tool registry, model_tools, toolsets provider/xai xAI (Grok) P3 Low — cosmetic, nice to have labels May 10, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks Julien. We're going to pass on this one. xAI's Batch API is an offline cost-optimization for users running large jobs — not really an agent-facing runtime tool. Exposing it as a model tool would add schema bloat for a capability that's better used as a CLI command or external script. If you want batch processing inside Hermes, the closer fit is the existing infrastructure. Closing with appreciation.

@teknium1 teknium1 closed this May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/tools Tool registry, model_tools, toolsets P3 Low — cosmetic, nice to have provider/xai xAI (Grok) type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants