feat(llm)!: multi-provider support — Anthropic / OpenAI / xAI / Gemini#27
Merged
Conversation
Until now the agent runner was hardcoded to Anthropic's Messages
API. Every operator had to choose between "use Claude" and "don't
deploy." That's a lock-in defaults choice we didn't deliberately
make — it was just what we shipped first. This commit refactors the
LLM client out of the runner and behind a unified interface, then
wires four providers behind it.
Backwards compatible: existing setups using `--api-key sk-ant-...`
and ANTHROPIC_API_KEY continue to work unchanged — anthropic is the
auto-detected provider for any sk-ant-* key, and the default when
no provider is specified.
## Architecture
openclaw/starter/agent/src/llm/
types.ts — LlmClient interface, LlmMessage/LlmTool/etc.
anthropic.ts — wraps @anthropic-ai/sdk
openai.ts — wraps openai SDK (used for OpenAI AND xAI)
gemini.ts — wraps @google/generative-ai
factory.ts — createLlmClientFromEnv(provider, key, model?)
index.ts — re-exports
The unified message/tool shape mirrors Anthropic's Messages API
(that's what the runner originally targeted). OpenAI / xAI / Gemini
impls translate to their own formats inside .complete() and back.
xAI is OpenAI-API-compatible at https://api.x.ai/v1 — same SDK,
different baseURL. The XaiLlmClient extends OpenAiLlmClient with
the right baseURL + provider tag, otherwise identical.
## Provider selection (priority order)
1. --provider flag (start.sh) → AGENT_LLM_PROVIDER
2. AGENT_LLM_PROVIDER env var
3. Auto-detect from API key prefix:
sk-ant-... → anthropic
xai-... → xai
sk-proj-* / sk-* → openai
AI... (long) → gemini
4. Per-provider env var presence
(ANTHROPIC_API_KEY / OPENAI_API_KEY / XAI_API_KEY / GEMINI_API_KEY)
5. Fallback: anthropic
## Default models (overridable via --model or AGENT_MODEL)
anthropic → claude-sonnet-4-6
openai → gpt-5
xai → grok-3-latest
gemini → gemini-2.5-flash
## start.sh
--provider <name> new flag, explicit override
--api-key <key> same flag, auto-routes to the right env var
for the resolved provider
If the operator passes --api-key sk-ant-… start.sh sets
ANTHROPIC_API_KEY. If they pass xai-… it sets XAI_API_KEY. And so
on. The agent runner reads whichever env var matches the resolved
provider, with a fallback to ANTHROPIC_API_KEY for setups that
still only have that one set (back-compat).
Boot log now shows the resolved LLM:
[agent-runner] LLM: openai (gpt-5)
## .env.example
Renames the LLM-key section to make multi-provider obvious:
AGENT_LLM_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=
XAI_API_KEY=
GEMINI_API_KEY=
AGENT_MODEL now defaults to empty — the runner picks the right
default for the resolved provider unless overridden.
## Frontend
Get-Started Step 4 and Register page Deploy block both show the
four provider variants explicitly. The flags table on Register
gets a new --provider row.
## What's NOT changed
The OpenClaw plugin itself (@xpr-agents/openclaw) is unchanged.
Plugin = tools + skills; LLM client lives in the agent runner only.
Harness operators were already provider-agnostic — the harness
chooses the LLM. This change only affects the standalone scaffold.
## Caveats
- Anthropic's built-in `web_search_20250305` server-side tool is
no longer exposed (no cross-provider equivalent). Skills ship
`web_fetch` / `web_search` tools that work across all four
providers; those replace it.
- Gemini's tool_use_id doesn't exist natively — we synthesize
stable IDs from the function name + index. Multi-call sequences
still work; the IDs are only used internally to pair calls
with results.
- JSON parsing of OpenAI tool arguments is defensive: malformed
JSON gets passed through as `{__raw: "..."}` rather than
silently dropped. The tool handler can reject it cleanly.
## Versions
@xpr-agents/openclaw → 0.5.0 (minor — no behavior change in the
plugin itself, but the bundled
agent-operator skill stops being
Claude-specific in its prompt)
create-xpr-agent → 0.7.0 (minor — start.sh + .env.example
gain new env vars; --api-key
auto-routes by prefix; old
setups keep working)
## Verification
- All 4 provider clients smoke-tested locally — factory resolves
correct provider + default model under each env-var combination
- detectProviderFromKey() returns expected provider for every
realistic key prefix (sk-ant-, xai-, sk-proj-, sk-, AI…)
- Agent runner compiles clean (`tsc` no errors)
- 80 openclaw tests pass (unchanged — plugin code didn't change)
- Frontend builds clean, all 13 routes prerendered
## docs/video-script.txt (also in this commit)
Full video brief for handing to a video creator agent, including
the new autopilot + skills-as-revenue sections and the multi-LLM
provider flag walkthrough in Step 4.
paulgnz
added a commit
that referenced
this pull request
May 18, 2026
PR #27 wired Anthropic / OpenAI / xAI / Gemini support into the agent runner, start.sh, frontend pages, and the new video script — but missed every README and QUICKSTART. Those are the surfaces npm displays on package landing pages, which is where most operators read first. This commit fixes that gap. Each README now shows the full four-provider matrix instead of just Anthropic: Provider | Key prefix | Default model | Get a key Anthropic | sk-ant-… | claude-sonnet-4-6 | console.anthropic.com OpenAI | sk-…/sk-proj- | gpt-5 | platform.openai.com xAI | xai-… | grok-3-latest | console.x.ai Gemini | AI… | gemini-2.5-flash | aistudio.google.com And the deploy example block shows all four `./start.sh` variants in sequence instead of just `sk-ant-`, so operators can immediately see which one matches the key prefix they have. ## Files updated - README.md (repo root) — Two-paths table + Deploy block - openclaw/README.md (@xpr-agents/openclaw npm landing) - create-xpr-agent/README.md (create-xpr-agent npm landing) - create-xpr-agent/template/README.md (scaffolded README in every new agent directory) - create-xpr-agent/template/QUICKSTART.md (scaffolded walkthrough) - openclaw/starter/README.md (synced) - openclaw/starter/QUICKSTART.md (synced) ## Env-var tables The template README's env-var table now lists ANTHROPIC_API_KEY, OPENAI_API_KEY, XAI_API_KEY, GEMINI_API_KEY as "one-of" requirements (set exactly one), plus AGENT_LLM_PROVIDER for explicit override. AGENT_MODEL now reads "per-provider default" with the four defaults named inline. ## start.sh flags table The QUICKSTART start.sh table now shows --provider as a separate flag, --api-key as multi-provider, and the per-provider model defaults under --model. ## Versions - @xpr-agents/openclaw → 0.5.1 (README content only) - create-xpr-agent → 0.7.1 (template README + QUICKSTART + main package README) ## Verification - All four remaining "Anthropic" / "console.anthropic.com" references are intentional — they're rows in the multi-provider tables alongside OpenAI/xAI/Gemini - 80 openclaw tests pass - CI scaffold-sanity diffs covered (template ↔ openclaw/starter)
paulgnz
added a commit
that referenced
this pull request
May 22, 2026
…-4.3 (#31) Live smoke test of the multi-LLM path (PR #27) caught two real issues that the type system + factory tests didn't. ## Bug — OpenAI completion fails with 400 on GPT-5 GPT-5 (and the o-series) rejects the legacy `max_tokens` field with: 400 Unsupported parameter: 'max_tokens' is not supported with this model. Use 'max_completion_tokens' instead. The OpenAI SDK passes the field through unmodified, so `LlmClient` needed to send the right name. xAI's OpenAI-compatible API still uses `max_tokens` though, so the fix branches on provider flavor: - provider === 'openai' → max_completion_tokens - provider === 'xai' → max_tokens (unchanged) End-to-end smoke tests now green for both providers: [1] factory: openai / gpt-5 [3] tool-use stop: tool_use, 1 tool call → get_time [4] tool-result roundtrip: "It's 07:00 UTC..." [1] factory: xai / grok-4.3 [2] text stop: end_turn, "verified" in 1 token [3] tool-use: 1 call to get_time [4] roundtrip: "The current time is 2026-05-22T07:00:00Z" Note for operators on GPT-5: the model uses internal reasoning tokens that count against `max_tokens`. With a tight ceiling (say 30) it may return empty content with stop_reason=max_tokens. The default 4096 is fine; just don't lower it aggressively when using gpt-5. ## Default model bump — grok-3-latest → grok-4.3 xAI released grok-4.3 since we shipped 0.5.0; updating the default gives new operators the current generation. Existing users who set AGENT_MODEL=grok-3-latest explicitly are unaffected. Updated in: - openclaw/starter/agent/src/llm/types.ts (DEFAULT_MODELS.xai) - README + QUICKSTART tables across all surfaces - start.sh help text (3 occurrences per file) ## Versions @xpr-agents/openclaw → 0.5.2 (patch — fix only) create-xpr-agent → 0.7.2 (patch — model default in scaffold) ## What's still untested Gemini path is in code but unverified end-to-end (no key available during this session). Most-different SDK + message shape of the four impls; treat as "best-effort" until someone exercises it with a real key. The structural code path matches the OpenAI one, but Gemini's `functionResponse` part shape + 'function' role for tool results is unique to that impl. ## Verification - 80 openclaw tests pass - Agent runner builds clean (tsc no errors) - Frontend rebuild clean (all 13 routes prerendered) - OpenAI smoke test (gpt-5): factory + text + tool-use + tool-result roundtrip ✓ - xAI smoke test (grok-4.3): same four checkpoints ✓
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The agent runner was hardcoded to Anthropic's Messages API. This PR refactors the LLM client out from behind a unified interface and wires four providers. Backwards compatible: existing setups using
--api-key sk-ant-...+ANTHROPIC_API_KEYkeep working unchanged.What you can now do
Provider is auto-detected from the API key prefix. Override with
--provider <anthropic|openai|xai|gemini>to be explicit.Architecture
openclaw/starter/agent/src/llm/— new directory:types.ts—LlmClientinterface + unified message/tool/response shapes (mirrors Anthropic's Messages API since that's what the runner originally targeted)anthropic.ts— wraps@anthropic-ai/sdkopenai.ts— wrapsopenaiSDK. Also serves xAI via theXaiLlmClientsubclass which pointsbaseURLathttps://api.x.ai/v1(xAI is OpenAI-API-compatible including tool calling)gemini.ts— wraps@google/generative-aiwith function-calling translationfactory.ts—createLlmClientFromEnv()resolves provider + model + key from env/flagsThe runner only knows about the unified types now. Provider SDKs aren't imported outside
llm/<provider>.ts.Resolution priority
--providerflag →AGENT_LLM_PROVIDERenv varAGENT_LLM_PROVIDERenv varANTHROPIC_API_KEY/OPENAI_API_KEY/XAI_API_KEY/GEMINI_API_KEY)anthropicDefault models (override via
--modelorAGENT_MODEL)claude-sonnet-4-6gpt-5grok-3-latestgemini-2.5-flashBoot signal
(was:
[agent-runner] Model: claude-sonnet-4-6)Versions
@xpr-agents/openclaw→ 0.5.0 (minor — no behavior change in the plugin itself; the bundled agent-operator skill stops being Claude-specific)create-xpr-agent→ 0.7.0 (minor —start.sh+.env.examplegain new env vars; back-compat preserved)Frontend
/get-startedStep 4 and/registerDeploy block both show the four provider variants./registerflags table gets a new--providerrow.Also in this commit
docs/video-script.txt— full video brief ready to hand to a video creator agent. 3:30 long-form + 90s short-form cuts. New sections: "It earns on autopilot — the job board loop" and "Upgrade with skills — turn expertise into revenue."Caveats
web_search_20250305server-side tool is no longer exposed (no cross-provider equivalent). Skills shipweb_fetch/web_searchtools that work across all four providers.{name}-{index}-{timestamp}. Multi-call sequences still work.{__raw: "..."}rather than silently dropped.Verification
detectProviderFromKey()returns expected provider for every realistic key prefixtscno errors)