Skip to content

Commit 1bf781f

Browse files
CrazyBoyMclaude
andcommitted
Refactor model config system to align with OpenClaw
Rewrite models.json with provider-nested schema (18 providers, cost data, compat flags, provider/model alias format). Update agent.sh model resolution with _parse_model_ref(), _model_get_compat_field(), max_completion_tokens support for o-series models. Default model changed to claude-opus-4-6. Update gateway API, web UI, onboard wizard, tests, and docs accordingly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e710bdb commit 1bf781f

10 files changed

Lines changed: 540 additions & 273 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ Config file: `~/.bashclaw/bashclaw.json`
496496
{
497497
"agents": {
498498
"defaults": {
499-
"model": "claude-sonnet-4-20250514",
499+
"model": "claude-opus-4-6",
500500
"maxTurns": 50,
501501
"contextTokens": 200000,
502502
"tools": ["web_fetch", "web_search", "memory", "shell"]

README_CN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ plugin_register_provider "my_llm" "My LLM" '["model-a"]' '{"envKey":"MY_KEY"}'
496496
{
497497
"agents": {
498498
"defaults": {
499-
"model": "claude-sonnet-4-20250514",
499+
"model": "claude-opus-4-6",
500500
"maxTurns": 50,
501501
"contextTokens": 200000,
502502
"tools": ["web_fetch", "web_search", "memory", "shell"]

gateway/http_handler.sh

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -484,18 +484,19 @@ _handle_api_models() {
484484
# Return models with provider info and aliases
485485
local response
486486
response="$(printf '%s' "$catalog" | jq '{
487-
models: [.models | to_entries[] | {
488-
id: .key,
489-
provider: .value.provider,
490-
max_tokens: .value.max_tokens,
491-
context_window: .value.context_window,
492-
supports_images: .value.supports_images,
493-
supports_thinking: .value.supports_thinking
487+
models: [.providers | to_entries[] | .key as $prov | .value.models[]? | {
488+
id: .id,
489+
name: .name,
490+
provider: $prov,
491+
max_tokens: .max_tokens,
492+
context_window: .context_window,
493+
reasoning: .reasoning,
494+
input: .input
494495
}],
495496
aliases: .aliases,
496497
providers: [.providers | to_entries[] | {
497498
id: .key,
498-
api_format: .value.api_format,
499+
api: .value.api,
499500
api_key_env: .value.api_key_env,
500501
has_key: false
501502
}]

install.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ _create_default_config() {
306306
{
307307
"agents": {
308308
"defaults": {
309-
"model": "claude-sonnet-4-20250514",
309+
"model": "claude-opus-4-6",
310310
"maxTurns": 50,
311311
"contextTokens": 200000
312312
},

lib/agent.sh

Lines changed: 133 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,28 @@ _models_catalog_load() {
4242
printf '%s' "$_MODELS_CATALOG_CACHE"
4343
}
4444

45-
# Resolve model alias to actual model ID
45+
# Parse "provider/model" reference format.
46+
# If input contains "/", split into provider and model.
47+
# Otherwise resolve alias first, then look up provider from catalog.
48+
# Sets two variables: _PARSED_PROVIDER and _PARSED_MODEL
49+
_parse_model_ref() {
50+
local raw="$1"
51+
_PARSED_PROVIDER=""
52+
_PARSED_MODEL=""
53+
54+
# Resolve alias first
55+
local resolved
56+
resolved="$(_model_resolve_alias "$raw")"
57+
58+
if [[ "$resolved" == *"/"* ]]; then
59+
_PARSED_PROVIDER="${resolved%%/*}"
60+
_PARSED_MODEL="${resolved#*/}"
61+
else
62+
_PARSED_MODEL="$resolved"
63+
fi
64+
}
65+
66+
# Resolve model alias to actual model ref (may include provider/ prefix)
4667
_model_resolve_alias() {
4768
local model="$1"
4869
local catalog
@@ -56,21 +77,48 @@ _model_resolve_alias() {
5677
fi
5778
}
5879

80+
# Look up which provider owns a model ID by searching all providers
5981
_model_provider() {
6082
local model="$1"
6183
local catalog
6284
catalog="$(_models_catalog_load)"
6385
local provider
64-
provider="$(printf '%s' "$catalog" | jq -r --arg m "$model" '.models[$m].provider // empty' 2>/dev/null)"
86+
provider="$(printf '%s' "$catalog" | jq -r --arg m "$model" '
87+
.providers | to_entries[] | select(.value.models[]?.id == $m) | .key
88+
' 2>/dev/null | head -1)"
6589
printf '%s' "$provider"
6690
}
6791

68-
_model_max_tokens() {
92+
# Get a field from a model definition across all providers
93+
_model_get_field() {
6994
local model="$1"
95+
local field="$2"
7096
local catalog
7197
catalog="$(_models_catalog_load)"
98+
local val
99+
val="$(printf '%s' "$catalog" | jq -r --arg m "$model" --arg f "$field" '
100+
[.providers[].models[] | select(.id == $m)] | .[0][$f] // empty
101+
' 2>/dev/null)"
102+
printf '%s' "$val"
103+
}
104+
105+
# Get a compat flag from a model definition
106+
_model_get_compat_field() {
107+
local model="$1"
108+
local field="$2"
109+
local catalog
110+
catalog="$(_models_catalog_load)"
111+
local val
112+
val="$(printf '%s' "$catalog" | jq -r --arg m "$model" --arg f "$field" '
113+
[.providers[].models[] | select(.id == $m)] | .[0].compat[$f] // empty
114+
' 2>/dev/null)"
115+
printf '%s' "$val"
116+
}
117+
118+
_model_max_tokens() {
119+
local model="$1"
72120
local tokens
73-
tokens="$(printf '%s' "$catalog" | jq -r --arg m "$model" '.models[$m].max_tokens // empty' 2>/dev/null)"
121+
tokens="$(_model_get_field "$model" "max_tokens")"
74122
if [[ -z "$tokens" ]]; then
75123
printf '4096'
76124
else
@@ -80,10 +128,8 @@ _model_max_tokens() {
80128

81129
_model_context_window() {
82130
local model="$1"
83-
local catalog
84-
catalog="$(_models_catalog_load)"
85131
local window
86-
window="$(printf '%s' "$catalog" | jq -r --arg m "$model" '.models[$m].context_window // empty' 2>/dev/null)"
132+
window="$(_model_get_field "$model" "context_window")"
87133
if [[ -z "$window" ]]; then
88134
printf '128000'
89135
else
@@ -102,17 +148,25 @@ agent_resolve_model() {
102148
local model
103149
model="$(config_agent_get "$agent_id" "model" "")"
104150
if [[ -z "$model" ]]; then
105-
model="${MODEL_ID:-claude-sonnet-4-20250514}"
151+
model="${MODEL_ID:-claude-opus-4-6}"
106152
fi
107153

108-
# Resolve alias to actual model ID
109-
model="$(_model_resolve_alias "$model")"
110-
printf '%s' "$model"
154+
# Parse provider/model ref (resolves aliases internally)
155+
_parse_model_ref "$model"
156+
printf '%s' "$_PARSED_MODEL"
111157
}
112158

113159
agent_resolve_provider() {
114160
local model="$1"
115161

162+
# If caller already set _PARSED_PROVIDER from _parse_model_ref, use it
163+
if [[ -n "${_PARSED_PROVIDER:-}" ]]; then
164+
printf '%s' "$_PARSED_PROVIDER"
165+
_PARSED_PROVIDER=""
166+
return
167+
fi
168+
169+
# Look up provider from catalog
116170
local provider
117171
provider="$(_model_provider "$model")"
118172
if [[ -n "$provider" ]]; then
@@ -122,14 +176,20 @@ agent_resolve_provider() {
122176

123177
# Infer provider from model name patterns
124178
case "$model" in
125-
claude-*|claude3*) printf 'anthropic'; return ;;
126-
gpt-*|o1*|o3*|o4*) printf 'openai'; return ;;
127-
gemini-*) printf 'google'; return ;;
128-
deepseek-*) printf 'deepseek'; return ;;
129-
qwen-*|qwq-*) printf 'qwen'; return ;;
130-
glm-*) printf 'zhipu'; return ;;
131-
moonshot-*|kimi-*) printf 'moonshot'; return ;;
132-
MiniMax-*|minimax-*|abab*) printf 'minimax'; return ;;
179+
claude-*) printf 'anthropic'; return ;;
180+
gpt-*|o1*|o3*|o4*) printf 'openai'; return ;;
181+
gemini-*) printf 'google'; return ;;
182+
deepseek-*) printf 'deepseek'; return ;;
183+
qwen-*|qwq-*) printf 'qwen'; return ;;
184+
glm-*) printf 'zhipu'; return ;;
185+
moonshot-*|kimi-*) printf 'moonshot'; return ;;
186+
MiniMax-*|minimax-*|abab*) printf 'minimax'; return ;;
187+
mimo-*) printf 'xiaomi'; return ;;
188+
ernie-*) printf 'qianfan'; return ;;
189+
nvidia/*) printf 'nvidia'; return ;;
190+
llama-*|meta/*) printf 'groq'; return ;;
191+
grok-*) printf 'xai'; return ;;
192+
mistral-*) printf 'mistral'; return ;;
133193
esac
134194

135195
# If OPENROUTER_API_KEY is set and model is unknown, assume openrouter
@@ -150,7 +210,6 @@ agent_resolve_api_key() {
150210
local catalog
151211
catalog="$(_models_catalog_load)"
152212

153-
# Look up the env var name for this provider from models.json
154213
local key_env
155214
key_env="$(printf '%s' "$catalog" | jq -r --arg p "$provider" \
156215
'.providers[$p].api_key_env // empty' 2>/dev/null)"
@@ -159,10 +218,24 @@ agent_resolve_api_key() {
159218
log_fatal "Unknown provider: $provider (not found in models.json)"
160219
fi
161220

162-
# Read the actual key value from the environment
163221
local key
164222
eval "key=\"\${${key_env}:-}\""
165223

224+
# Backward compat: check legacy env var names
225+
if [[ -z "$key" ]]; then
226+
case "$provider" in
227+
google) key="${GOOGLE_API_KEY:-}" ;;
228+
zhipu) key="${ZHIPU_API_KEY:-}" ;;
229+
esac
230+
fi
231+
232+
# Ollama/vLLM: API key is optional for local providers
233+
if [[ -z "$key" ]]; then
234+
case "$provider" in
235+
ollama|vllm) key="no-key-required"; return 0 ;;
236+
esac
237+
fi
238+
166239
if [[ -z "$key" ]]; then
167240
log_fatal "${key_env} is required for ${provider} provider"
168241
fi
@@ -176,24 +249,31 @@ _provider_api_url() {
176249
local catalog
177250
catalog="$(_models_catalog_load)"
178251

179-
# Check for env var override first
180-
local url_env
181-
url_env="$(printf '%s' "$catalog" | jq -r --arg p "$provider" \
182-
'.providers[$p].api_url_env // empty' 2>/dev/null)"
252+
local url_default
253+
url_default="$(printf '%s' "$catalog" | jq -r --arg p "$provider" \
254+
'.providers[$p].base_url // empty' 2>/dev/null)"
255+
256+
# Check env var override: {PROVIDER}_BASE_URL
257+
local env_key
258+
case "$provider" in
259+
anthropic) env_key="ANTHROPIC_BASE_URL" ;;
260+
openai) env_key="OPENAI_BASE_URL" ;;
261+
google) env_key="GOOGLE_AI_BASE_URL" ;;
262+
openrouter) env_key="OPENROUTER_BASE_URL" ;;
263+
ollama) env_key="OLLAMA_BASE_URL" ;;
264+
vllm) env_key="VLLM_BASE_URL" ;;
265+
*) env_key="" ;;
266+
esac
183267

184-
if [[ -n "$url_env" ]]; then
268+
if [[ -n "$env_key" ]]; then
185269
local url_override
186-
eval "url_override=\"\${${url_env}:-}\""
270+
eval "url_override=\"\${${env_key}:-}\""
187271
if [[ -n "$url_override" ]]; then
188272
printf '%s' "$url_override"
189273
return
190274
fi
191275
fi
192276

193-
# Fall back to default URL
194-
local url_default
195-
url_default="$(printf '%s' "$catalog" | jq -r --arg p "$provider" \
196-
'.providers[$p].api_url_default // empty' 2>/dev/null)"
197277
printf '%s' "$url_default"
198278
}
199279

@@ -205,7 +285,7 @@ _provider_api_format() {
205285
catalog="$(_models_catalog_load)"
206286
local fmt
207287
fmt="$(printf '%s' "$catalog" | jq -r --arg p "$provider" \
208-
'.providers[$p].api_format // empty' 2>/dev/null)"
288+
'.providers[$p].api // empty' 2>/dev/null)"
209289

210290
if [[ -z "$fmt" ]]; then
211291
printf 'openai'
@@ -214,6 +294,15 @@ _provider_api_format() {
214294
fi
215295
}
216296

297+
# Get the API version header value for a provider (e.g. anthropic)
298+
_provider_api_version() {
299+
local provider="$1"
300+
local catalog
301+
catalog="$(_models_catalog_load)"
302+
printf '%s' "$(printf '%s' "$catalog" | jq -r --arg p "$provider" \
303+
'.providers[$p].api_version // empty' 2>/dev/null)"
304+
}
305+
217306
# Resolve the next fallback model from the configured fallback chain.
218307
# Returns the fallback model name, or empty if none available.
219308
agent_resolve_fallback_model() {
@@ -817,6 +906,14 @@ agent_call_openai() {
817906

818907
local api_url="${api_base}/v1/chat/completions"
819908

909+
# Check if model uses max_completion_tokens (o-series models)
910+
local max_tokens_field="max_tokens"
911+
local compat_field
912+
compat_field="$(_model_get_compat_field "$model" "max_tokens_field")"
913+
if [[ -n "$compat_field" ]]; then
914+
max_tokens_field="$compat_field"
915+
fi
916+
820917
local oai_messages
821918
oai_messages="$(printf '%s' "$messages" | jq --arg sys "$system_prompt" \
822919
'[{role: "system", content: $sys}] + .')"
@@ -841,10 +938,11 @@ agent_call_openai() {
841938
--argjson max_tokens "$max_tokens" \
842939
--argjson temp "$temperature" \
843940
--argjson tools "$oai_tools" \
941+
--arg mtf "$max_tokens_field" \
844942
'{
845943
model: $model,
846944
messages: $messages,
847-
max_tokens: $max_tokens,
945+
($mtf): $max_tokens,
848946
temperature: $temp,
849947
tools: $tools
850948
}')"
@@ -854,10 +952,11 @@ agent_call_openai() {
854952
--argjson messages "$oai_messages" \
855953
--argjson max_tokens "$max_tokens" \
856954
--argjson temp "$temperature" \
955+
--arg mtf "$max_tokens_field" \
857956
'{
858957
model: $model,
859958
messages: $messages,
860-
max_tokens: $max_tokens,
959+
($mtf): $max_tokens,
861960
temperature: $temp
862961
}')"
863962
fi

lib/cmd_onboard.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ _onboard_api_key() {
8686
printf 'Choose provider:\n'
8787
printf ' 1) Anthropic (Claude)\n'
8888
printf ' 2) OpenAI (GPT)\n'
89+
printf ' 3) DeepSeek\n'
8990
printf 'Choice [1]: '
9091
local choice
9192
read -r choice
@@ -137,6 +138,20 @@ _onboard_api_key() {
137138
config_set '.agents.defaults.model' '"gpt-4o"'
138139
printf 'API key saved to %s\n' "$env_file"
139140
;;
141+
3)
142+
printf 'Enter your DeepSeek API key: '
143+
local api_key
144+
read -r -s api_key
145+
printf '\n'
146+
if [[ -z "$api_key" ]]; then
147+
log_warn "No API key provided, skipping"
148+
return 0
149+
fi
150+
printf 'DEEPSEEK_API_KEY=%s\n' "$api_key" >> "$env_file"
151+
chmod 600 "$env_file" 2>/dev/null || true
152+
config_set '.agents.defaults.model' '"deepseek-chat"'
153+
printf 'API key saved to %s\n' "$env_file"
154+
;;
140155
*)
141156
log_warn "Invalid choice, skipping API key setup"
142157
;;

lib/config.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ config_init_default() {
224224
return 1
225225
fi
226226

227-
local model="${MODEL_ID:-claude-sonnet-4-20250514}"
227+
local model="${MODEL_ID:-claude-opus-4-6}"
228228
ensure_dir "$(dirname "$path")"
229229

230230
cat > "$path" <<ENDJSON

0 commit comments

Comments
 (0)