Skip to content

feat(xai): xai_deferred_chat tool — long-running completions via submit + poll - #23329

Closed
Julientalbot wants to merge 3 commits into
NousResearch:mainfrom
Julientalbot:feat/xai-deferred-completions
Closed

feat(xai): xai_deferred_chat tool — long-running completions via submit + poll#23329
Julientalbot wants to merge 3 commits into
NousResearch:mainfrom
Julientalbot:feat/xai-deferred-completions

Conversation

@Julientalbot

Copy link
Copy Markdown
Contributor

Summary

Adds xai_deferred_chat — a tool that runs a long-running chat completion against xAI in deferred mode: submits to POST /v1/chat/completions with deferred: true, polls GET /v1/chat/deferred-completion/{request_id} (202 → still pending, 200 → done) until the response is ready or the per-call budget is exhausted.

Why

xAI's deferred mode is built for completions that take many minutes (extended thinking, multi-agent reasoning, large-context calls) — long enough that holding an open HTTP connection is unreliable. Deferred decouples the request lifecycle from the network: the client can disconnect, crash, retry, or yield, and pick the result up later. This pairs naturally with Hermes' autonomous mode (long-running sessions) and with the grok-4.20-multi-agent-0309 model whose orchestrator legs can run several minutes.

This tool is the smallest possible primitive: one tool that submits + polls and blocks the caller until done. A future, more sophisticated transport could layer parallelism / persistence / cross-session resumption on top of the same primitives, but the existing autonomous + delegation patterns already get most of the value out of this minimal shape.

Changes (5 files, +653/-0)

tools/xai_deferred_tool.py (new, ~350 LoC)

Self-contained tool. Public surface:

xai_deferred_chat(
    prompt: str,
    *,
    model: str | None = None,                 # default: config xai_deferred.model or "grok-4.3"
    system: str | None = None,                # optional system prompt
    max_wait_seconds: int | None = None,      # default: config or 600 (10 min)
    poll_interval_seconds: float | None = None,  # default: config or 2.0
    extra_messages: list[dict] | None = None, # conversation history to prepend
    extra_body: dict | None = None,           # merged into submit body (deferred=true is locked)
) -> {"request_id": str, "completion": dict, "elapsed_seconds": float}
  • Reuses tools.xai_http.hermes_xai_user_agent for User-Agent.
  • extra_body cannot override deferred=true (the entire point of the tool).
  • Self-registers via tools.registry.registry.register with check_fn gating on XAI_API_KEY.
  • Configurable via config.yaml:
    xai_deferred:
      model: grok-4.20-multi-agent-0309
      max_wait_seconds: 1200
      poll_interval_seconds: 5

tests/tools/test_xai_deferred_tool.py (new, ~280 LoC)

24 unit tests, all using a tiny _FakeHttp monkeypatch (no real network):

  • Requirements & schema: with/without/blank API key; required + optional schema params.
  • Argument validation: empty/whitespace prompt, missing API key, negative max_wait_seconds, negative poll_interval_seconds.
  • Submit: deferred=true always set, default model resolution, explicit-model override, system message ordering, extra_body merging (cannot clobber deferred), 4xx HTTP error, missing request_id in response, network error.
  • Poll: completes on first 200, polls through multiple 202s, 5xx surface, timeout (mocked time.monotonic).
  • Headers: Authorization: Bearer <key> on both submit + poll, User-Agent: Hermes-Agent/<version> on submit.

Wiring

Validation

  • pytest tests/tools/test_xai_deferred_tool.py tests/tools/test_registry.py55/55 passing locally on top of current main.
  • The 24 new tool tests + the 31 existing registry tests all green.

Backward compatibility

  • Pure addition. New tool, new toolset entry, new test file. 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_deferred is not in the default toolset roster; users opt in via tools_config or by enabling the toolset in their profile.

Follow-up scope (not in this PR)

  • A streaming flavor that yields incremental token deltas as the completion progresses (the current xAI deferred GET only returns the final body).
  • A persistent flavor that survives Hermes restarts: store request_id to disk, resume polling on next launch. Useful for autonomous sessions whose backing process gets restarted mid-thought.
  • A transport-layer integration (so any model call can opt into deferred mode based on a config knob, not just calls routed through this tool).

…submit + poll

xAI deferred mode (POST /v1/chat/completions with deferred=true,
then GET /v1/chat/deferred-completion/{request_id}) decouples the
HTTP connection lifecycle from the actual completion. Useful for
extended-thinking, large-context, or multi-agent reasoning calls
that exceed normal HTTP timeouts.

Changes:
- tools/xai_deferred_tool.py: self-contained tool implementation
  - xai_deferred_chat(prompt, model, system, max_wait_seconds,
    poll_interval_seconds, extra_messages, extra_body) — submits
    in deferred mode and polls until ready or budget exhausted
  - Configurable model / max_wait_seconds / poll_interval_seconds
    via config.yaml under xai_deferred:
  - Reuses tools.xai_http.hermes_xai_user_agent for User-Agent
  - extra_body cannot override deferred=true (hard-coded)
  - Self-registers via tools.registry.registry.register
- tests/tools/test_xai_deferred_tool.py: 24 unit tests covering
  - check_xai_deferred_requirements (with/without/blank API key)
  - schema (required prompt, optional params advertised)
  - argument validation (empty prompt, missing key, negative
    max_wait, negative poll_interval)
  - submit semantics (deferred=true, default model, explicit
    model, system message, extra_body merging, 4xx error,
    missing request_id, network error)
  - poll semantics (200 first, 202 then 200, 5xx error, timeout)
  - headers (Authorization Bearer, User-Agent prefix)
- toolsets.py: add xai_deferred_chat to _HERMES_CORE_TOOLS and
  new xai_deferred toolset
- hermes_cli/tools_config.py: add xai_deferred to CONFIGURABLE_TOOLSETS
- tests/tools/test_registry.py: add tools.xai_deferred_tool to the
  manual builtin tool set snapshot

Requires XAI_API_KEY in ~/.hermes/.env.
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the contribution Julien. We're going to pass on this one. New agent-facing tools that issue side-channel chat completions to a specific provider duplicate what the main model/provider pipeline (and ) already does, but locked to xAI only. Every new model tool also bloats the tool schema sent on every API call. If there's a concrete workflow you can't reach via the normal model loop or delegate_task, happy to hear it — but as a generic capability we'd rather not add it. Closing with appreciation.

@teknium1 teknium1 closed this May 20, 2026
@Julientalbot

Copy link
Copy Markdown
Contributor Author

@teknium1 I understand thank you for taking the time to answer to me !

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