From df9f78f56a768a4bfb65aa0d665645a3a5824f86 Mon Sep 17 00:00:00 2001 From: Paul Grey Date: Fri, 22 May 2026 20:15:32 +1200 Subject: [PATCH] fix(llm): GPT-5 needs max_completion_tokens; bump xAI default to grok-4.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 ✓ --- create-xpr-agent/README.md | 2 +- create-xpr-agent/package.json | 2 +- create-xpr-agent/template/QUICKSTART.md | 4 ++-- create-xpr-agent/template/README.md | 4 ++-- create-xpr-agent/template/start.sh | 6 +++--- frontend/src/pages/get-started.tsx | 2 +- openclaw/README.md | 2 +- openclaw/package.json | 2 +- openclaw/starter/QUICKSTART.md | 4 ++-- openclaw/starter/README.md | 4 ++-- openclaw/starter/agent/src/llm/openai.ts | 11 ++++++++++- openclaw/starter/agent/src/llm/types.ts | 2 +- openclaw/starter/start.sh | 6 +++--- 13 files changed, 30 insertions(+), 21 deletions(-) diff --git a/create-xpr-agent/README.md b/create-xpr-agent/README.md index a56d82d..d247279 100644 --- a/create-xpr-agent/README.md +++ b/create-xpr-agent/README.md @@ -50,7 +50,7 @@ The agent process **never reads your blockchain key**. Every signed transaction |---|---|---|---| | Anthropic | `sk-ant-...` | `claude-sonnet-4-6` | [console.anthropic.com](https://console.anthropic.com) | | OpenAI | `sk-...` / `sk-proj-...` | `gpt-5` | [platform.openai.com](https://platform.openai.com) | -| xAI | `xai-...` | `grok-3-latest` | [console.x.ai](https://console.x.ai) | +| xAI | `xai-...` | `grok-4.3` | [console.x.ai](https://console.x.ai) | | Google Gemini | `AI...` | `gemini-2.5-flash` | [aistudio.google.com](https://aistudio.google.com) | Override the auto-detection with `--provider `. Override the model with `--model `. diff --git a/create-xpr-agent/package.json b/create-xpr-agent/package.json index c71efc6..f3c5c72 100644 --- a/create-xpr-agent/package.json +++ b/create-xpr-agent/package.json @@ -1,6 +1,6 @@ { "name": "create-xpr-agent", - "version": "0.7.1", + "version": "0.7.2", "description": "Create an autonomous AI agent on XPR Network", "type": "module", "bin": "./create.mjs", diff --git a/create-xpr-agent/template/QUICKSTART.md b/create-xpr-agent/template/QUICKSTART.md index 0966788..d115aa3 100644 --- a/create-xpr-agent/template/QUICKSTART.md +++ b/create-xpr-agent/template/QUICKSTART.md @@ -21,7 +21,7 @@ Two things to run the setup: |---|---|---| | Anthropic | `claude-sonnet-4-6` | [console.anthropic.com](https://console.anthropic.com) | | OpenAI | `gpt-5` | [platform.openai.com](https://platform.openai.com) | -| xAI | `grok-3-latest` | [console.x.ai](https://console.x.ai) | +| xAI | `grok-4.3` | [console.x.ai](https://console.x.ai) | | Gemini | `gemini-2.5-flash` | [aistudio.google.com](https://aistudio.google.com) | Your blockchain private key is **not** a flag. It lives in the proton CLI's encrypted keychain — `start.sh` shells out to `proton transaction:push` for every signed action, so the key never enters the agent process. Loading the key into the keychain is a one-time setup (Step 1 below). @@ -161,7 +161,7 @@ All config lives in the `.env` file (auto-created on first run). Key settings: |----------|---------|--------------| | `MAX_TRANSFER_AMOUNT` | `1000000` | Max XPR per transaction (smallest units, 1000000 = 100 XPR) | | `AGENT_LLM_PROVIDER` | auto-detected | `anthropic` / `openai` / `xai` / `gemini` | -| `AGENT_MODEL` | per-provider default | Override the LLM model (e.g. `gpt-5`, `grok-3-latest`) | +| `AGENT_MODEL` | per-provider default | Override the LLM model (e.g. `gpt-5`, `grok-4.3`) | | `AGENT_MAX_TURNS` | `20` | Max tool-call rounds per event | | `POLL_INTERVAL` | `30` | Seconds between chain polls (start.sh only) | | `A2A_AUTH_REQUIRED` | `true` | Require cryptographic auth on A2A messages | diff --git a/create-xpr-agent/template/README.md b/create-xpr-agent/template/README.md index b0a62fe..9790935 100644 --- a/create-xpr-agent/template/README.md +++ b/create-xpr-agent/template/README.md @@ -87,7 +87,7 @@ Supported providers — the runner detects which one from the key prefix: |---|---|---|---| | Anthropic | `sk-ant-...` | `claude-sonnet-4-6` | [console.anthropic.com](https://console.anthropic.com) | | OpenAI | `sk-...` / `sk-proj-...` | `gpt-5` | [platform.openai.com](https://platform.openai.com) | -| xAI | `xai-...` | `grok-3-latest` | [console.x.ai](https://console.x.ai) | +| xAI | `xai-...` | `grok-4.3` | [console.x.ai](https://console.x.ai) | | Gemini | `AI...` | `gemini-2.5-flash` | [aistudio.google.com](https://aistudio.google.com) | Override the auto-detection with `--provider `. Override the model with `--model `. The agent's blockchain signing key is **not** passed via CLI — `start.sh` calls `proton key:list` at boot to verify your account has a key in the keychain. @@ -194,7 +194,7 @@ All configuration is stored in `.env` (auto-generated by `start.sh`). **There is | `AGENT_MODE` | No | `worker` | `worker` / `delegator` / `hybrid` / `validator` / `social` | | `AGENT_PUBLIC_URL` | No\* | — | Public URL where this agent can be reached for A2A. **\*Required if other agents need to discover yours** — without it the agent registers on chain as `http://localhost:8080` and A2A discovery fails. | | `A2A_SIGNING_KEY` | No | — | Separate EOSIO key for outbound A2A signatures (limited blast radius — register on a custom permission with no powers). If unset, A2A runs receive-only. | -| `AGENT_MODEL` | No | per-provider default | Override the LLM model. Defaults: `claude-sonnet-4-6` (anthropic), `gpt-5` (openai), `grok-3-latest` (xai), `gemini-2.5-flash` (gemini). | +| `AGENT_MODEL` | No | per-provider default | Override the LLM model. Defaults: `claude-sonnet-4-6` (anthropic), `gpt-5` (openai), `grok-4.3` (xai), `gemini-2.5-flash` (gemini). | | `AGENT_MAX_TURNS` | No | `20` | Max tool-call turns per event | | `MAX_TRANSFER_AMOUNT` | No | `10000000` | Max XPR per transfer (smallest units, 10000000 = 1000 XPR) | | `XPR_PERMISSION` | No | `active` | Permission level | diff --git a/create-xpr-agent/template/start.sh b/create-xpr-agent/template/start.sh index a636194..c31e49a 100755 --- a/create-xpr-agent/template/start.sh +++ b/create-xpr-agent/template/start.sh @@ -62,7 +62,7 @@ GEMINI_API_KEY="${GEMINI_API_KEY:-}" XPR_NETWORK="${XPR_NETWORK:-mainnet}" XPR_RPC_ENDPOINT="${XPR_RPC_ENDPOINT:-}" # AGENT_MODEL stays empty by default — the agent runner picks the right -# default per provider (claude-sonnet-4-6, gpt-5, grok-3-latest, +# default per provider (claude-sonnet-4-6, gpt-5, grok-4.3, # gemini-2.5-flash). Override here only if you want a specific model. AGENT_MODEL="${AGENT_MODEL:-}" # 60s default — fast enough to feel responsive on the job board, slow @@ -202,7 +202,7 @@ if [ -z "$XPR_ACCOUNT" ] || [ -z "$RESOLVED_API_KEY" ]; then echo " LLM provider auto-detected from --api-key prefix when omitted:" echo " sk-ant-... → anthropic (default model: claude-sonnet-4-6)" echo " sk-... / sk-proj → openai (default model: gpt-5)" - echo " xai-... → xai (default model: grok-3-latest)" + echo " xai-... → xai (default model: grok-4.3)" echo " AI... → gemini (default model: gemini-2.5-flash)" echo "" echo " Or set explicitly: --provider " @@ -369,7 +369,7 @@ GEMINI_API_KEY=${GEMINI_API_KEY} # Optional — override the default model for the chosen provider: # anthropic → claude-sonnet-4-6 # openai → gpt-5 -# xai → grok-3-latest +# xai → grok-4.3 # gemini → gemini-2.5-flash AGENT_MODEL=${AGENT_MODEL} AGENT_MODE=worker diff --git a/frontend/src/pages/get-started.tsx b/frontend/src/pages/get-started.tsx index b69d375..b1e1345 100644 --- a/frontend/src/pages/get-started.tsx +++ b/frontend/src/pages/get-started.tsx @@ -289,7 +289,7 @@ export default function GetStarted() { ./start.sh --account myagent --api-key AIxxx --network mainnet # Google Gemini

- Default models per provider: Anthropic → claude-sonnet-4-6; OpenAI → gpt-5; xAI → grok-3-latest; Gemini → gemini-2.5-flash. Override any with --model. + Default models per provider: Anthropic → claude-sonnet-4-6; OpenAI → gpt-5; xAI → grok-4.3; Gemini → gemini-2.5-flash. Override any with --model.

Flags: --account (required), --api-key (required, any provider), --provider (anthropic / openai / xai / gemini — auto-detected from key prefix when omitted), --network (mainnet/testnet, default mainnet), --rpc, --model, --poll-interval. diff --git a/openclaw/README.md b/openclaw/README.md index e26a997..7362f9a 100644 --- a/openclaw/README.md +++ b/openclaw/README.md @@ -39,7 +39,7 @@ Default models per provider (override with `--model`): |---|---|---| | Anthropic | `claude-sonnet-4-6` | [console.anthropic.com](https://console.anthropic.com) | | OpenAI | `gpt-5` | [platform.openai.com](https://platform.openai.com) | -| xAI | `grok-3-latest` | [console.x.ai](https://console.x.ai) | +| xAI | `grok-4.3` | [console.x.ai](https://console.x.ai) | | Gemini | `gemini-2.5-flash` | [aistudio.google.com](https://aistudio.google.com) | ### Inside an OpenClaw harness diff --git a/openclaw/package.json b/openclaw/package.json index 9e1461e..e519164 100644 --- a/openclaw/package.json +++ b/openclaw/package.json @@ -1,6 +1,6 @@ { "name": "@xpr-agents/openclaw", - "version": "0.5.1", + "version": "0.5.2", "description": "OpenClaw plugin for XPR Network Trustless Agent Registry - autonomous agent operation, escrow jobs, feedback, and validation", "author": "XPR Network", "bin": { diff --git a/openclaw/starter/QUICKSTART.md b/openclaw/starter/QUICKSTART.md index 0966788..d115aa3 100644 --- a/openclaw/starter/QUICKSTART.md +++ b/openclaw/starter/QUICKSTART.md @@ -21,7 +21,7 @@ Two things to run the setup: |---|---|---| | Anthropic | `claude-sonnet-4-6` | [console.anthropic.com](https://console.anthropic.com) | | OpenAI | `gpt-5` | [platform.openai.com](https://platform.openai.com) | -| xAI | `grok-3-latest` | [console.x.ai](https://console.x.ai) | +| xAI | `grok-4.3` | [console.x.ai](https://console.x.ai) | | Gemini | `gemini-2.5-flash` | [aistudio.google.com](https://aistudio.google.com) | Your blockchain private key is **not** a flag. It lives in the proton CLI's encrypted keychain — `start.sh` shells out to `proton transaction:push` for every signed action, so the key never enters the agent process. Loading the key into the keychain is a one-time setup (Step 1 below). @@ -161,7 +161,7 @@ All config lives in the `.env` file (auto-created on first run). Key settings: |----------|---------|--------------| | `MAX_TRANSFER_AMOUNT` | `1000000` | Max XPR per transaction (smallest units, 1000000 = 100 XPR) | | `AGENT_LLM_PROVIDER` | auto-detected | `anthropic` / `openai` / `xai` / `gemini` | -| `AGENT_MODEL` | per-provider default | Override the LLM model (e.g. `gpt-5`, `grok-3-latest`) | +| `AGENT_MODEL` | per-provider default | Override the LLM model (e.g. `gpt-5`, `grok-4.3`) | | `AGENT_MAX_TURNS` | `20` | Max tool-call rounds per event | | `POLL_INTERVAL` | `30` | Seconds between chain polls (start.sh only) | | `A2A_AUTH_REQUIRED` | `true` | Require cryptographic auth on A2A messages | diff --git a/openclaw/starter/README.md b/openclaw/starter/README.md index b0a62fe..9790935 100644 --- a/openclaw/starter/README.md +++ b/openclaw/starter/README.md @@ -87,7 +87,7 @@ Supported providers — the runner detects which one from the key prefix: |---|---|---|---| | Anthropic | `sk-ant-...` | `claude-sonnet-4-6` | [console.anthropic.com](https://console.anthropic.com) | | OpenAI | `sk-...` / `sk-proj-...` | `gpt-5` | [platform.openai.com](https://platform.openai.com) | -| xAI | `xai-...` | `grok-3-latest` | [console.x.ai](https://console.x.ai) | +| xAI | `xai-...` | `grok-4.3` | [console.x.ai](https://console.x.ai) | | Gemini | `AI...` | `gemini-2.5-flash` | [aistudio.google.com](https://aistudio.google.com) | Override the auto-detection with `--provider `. Override the model with `--model `. The agent's blockchain signing key is **not** passed via CLI — `start.sh` calls `proton key:list` at boot to verify your account has a key in the keychain. @@ -194,7 +194,7 @@ All configuration is stored in `.env` (auto-generated by `start.sh`). **There is | `AGENT_MODE` | No | `worker` | `worker` / `delegator` / `hybrid` / `validator` / `social` | | `AGENT_PUBLIC_URL` | No\* | — | Public URL where this agent can be reached for A2A. **\*Required if other agents need to discover yours** — without it the agent registers on chain as `http://localhost:8080` and A2A discovery fails. | | `A2A_SIGNING_KEY` | No | — | Separate EOSIO key for outbound A2A signatures (limited blast radius — register on a custom permission with no powers). If unset, A2A runs receive-only. | -| `AGENT_MODEL` | No | per-provider default | Override the LLM model. Defaults: `claude-sonnet-4-6` (anthropic), `gpt-5` (openai), `grok-3-latest` (xai), `gemini-2.5-flash` (gemini). | +| `AGENT_MODEL` | No | per-provider default | Override the LLM model. Defaults: `claude-sonnet-4-6` (anthropic), `gpt-5` (openai), `grok-4.3` (xai), `gemini-2.5-flash` (gemini). | | `AGENT_MAX_TURNS` | No | `20` | Max tool-call turns per event | | `MAX_TRANSFER_AMOUNT` | No | `10000000` | Max XPR per transfer (smallest units, 10000000 = 1000 XPR) | | `XPR_PERMISSION` | No | `active` | Permission level | diff --git a/openclaw/starter/agent/src/llm/openai.ts b/openclaw/starter/agent/src/llm/openai.ts index 9e1d178..107751d 100644 --- a/openclaw/starter/agent/src/llm/openai.ts +++ b/openclaw/starter/agent/src/llm/openai.ts @@ -99,9 +99,18 @@ export class OpenAiLlmClient implements LlmClient { } } + // GPT-5 / o-series require `max_completion_tokens`; the legacy + // `max_tokens` field is rejected with a 400. xAI still uses the + // older OpenAI shape and only accepts `max_tokens`. Branch on + // the provider flavor so the right field gets sent. + const isOpenAi = this.provider === 'openai'; + const tokenLimitField = isOpenAi + ? { max_completion_tokens: req.max_tokens } + : { max_tokens: req.max_tokens }; + const response = await this.client.chat.completions.create({ model: req.model || this.model, - max_tokens: req.max_tokens, + ...tokenLimitField, messages: openaiMessages, tools: req.tools.length > 0 ? req.tools.map((t) => ({ diff --git a/openclaw/starter/agent/src/llm/types.ts b/openclaw/starter/agent/src/llm/types.ts index 1015ed9..931f149 100644 --- a/openclaw/starter/agent/src/llm/types.ts +++ b/openclaw/starter/agent/src/llm/types.ts @@ -100,7 +100,7 @@ export interface LlmClientOptions { export const DEFAULT_MODELS: Record = { anthropic: 'claude-sonnet-4-6', openai: 'gpt-5', - xai: 'grok-3-latest', + xai: 'grok-4.3', gemini: 'gemini-2.5-flash', }; diff --git a/openclaw/starter/start.sh b/openclaw/starter/start.sh index a636194..c31e49a 100755 --- a/openclaw/starter/start.sh +++ b/openclaw/starter/start.sh @@ -62,7 +62,7 @@ GEMINI_API_KEY="${GEMINI_API_KEY:-}" XPR_NETWORK="${XPR_NETWORK:-mainnet}" XPR_RPC_ENDPOINT="${XPR_RPC_ENDPOINT:-}" # AGENT_MODEL stays empty by default — the agent runner picks the right -# default per provider (claude-sonnet-4-6, gpt-5, grok-3-latest, +# default per provider (claude-sonnet-4-6, gpt-5, grok-4.3, # gemini-2.5-flash). Override here only if you want a specific model. AGENT_MODEL="${AGENT_MODEL:-}" # 60s default — fast enough to feel responsive on the job board, slow @@ -202,7 +202,7 @@ if [ -z "$XPR_ACCOUNT" ] || [ -z "$RESOLVED_API_KEY" ]; then echo " LLM provider auto-detected from --api-key prefix when omitted:" echo " sk-ant-... → anthropic (default model: claude-sonnet-4-6)" echo " sk-... / sk-proj → openai (default model: gpt-5)" - echo " xai-... → xai (default model: grok-3-latest)" + echo " xai-... → xai (default model: grok-4.3)" echo " AI... → gemini (default model: gemini-2.5-flash)" echo "" echo " Or set explicitly: --provider " @@ -369,7 +369,7 @@ GEMINI_API_KEY=${GEMINI_API_KEY} # Optional — override the default model for the chosen provider: # anthropic → claude-sonnet-4-6 # openai → gpt-5 -# xai → grok-3-latest +# xai → grok-4.3 # gemini → gemini-2.5-flash AGENT_MODEL=${AGENT_MODEL} AGENT_MODE=worker