mirror of
https://github.com/instructkr/claw-code.git
synced 2026-04-29 10:45:09 +08:00
feat: #154 — hint provider prefix and env var when model name looks like different provider
## Problem When a user types `claw --model gpt-4` or `--model qwen-plus`, they get: ``` error: invalid model syntax: 'gpt-4'. Expected provider/model (e.g., anthropic/claude-opus-4-6) or known alias ``` USAGE.md documents that "The error message now includes a hint that names the detected env var" — but this hint does not actually exist. The user has to re-read USAGE.md or guess the correct prefix. ## Fix Enhance `validate_model_syntax` to detect when a model name looks like it belongs to a different provider: 1. **OpenAI models** (starts with `gpt-` or `gpt_`): ``` Did you mean `openai/gpt-4`? (Requires OPENAI_API_KEY env var) ``` 2. **Qwen/DashScope models** (starts with `qwen`): ``` Did you mean `qwen/qwen-plus`? (Requires DASHSCOPE_API_KEY env var) ``` 3. **Grok/xAI models** (starts with `grok`): ``` Did you mean `xai/grok-3`? (Requires XAI_API_KEY env var) ``` Unrelated invalid models (e.g., `asdfgh`) do not get a spurious hint. ## Verification - `claw --model gpt-4` → hints `openai/gpt-4` + `OPENAI_API_KEY` - `claw --model qwen-plus` → hints `qwen/qwen-plus` + `DASHSCOPE_API_KEY` - `claw --model grok-3` → hints `xai/grok-3` + `XAI_API_KEY` - `claw --model asdfgh` → generic error (no hint) ## Tests Added 3 new assertions in `parses_multiple_diagnostic_subcommands`: - GPT model error hints openai/ prefix and OPENAI_API_KEY - Qwen model error hints qwen/ prefix and DASHSCOPE_API_KEY - Unrelated models don't get a spurious hint All 177 rusty-claude-cli tests pass. Closes ROADMAP #154.
This commit is contained in:
26
ROADMAP.md
26
ROADMAP.md
@@ -5934,3 +5934,29 @@ This creates a confusing gap: users build successfully but then get "command not
|
|||||||
**Blocker:** None. Pure documentation.
|
**Blocker:** None. Pure documentation.
|
||||||
|
|
||||||
**Source:** Clawhip nudge 2026-04-21 21:27 KST — onboarding gap from #claw-code observations earlier this month.
|
**Source:** Clawhip nudge 2026-04-21 21:27 KST — onboarding gap from #claw-code observations earlier this month.
|
||||||
|
|
||||||
|
## Pinpoint #154. Model syntax error doesn't hint at env var when multiple credentials present
|
||||||
|
|
||||||
|
**Gap.** When a user types `claw --model gpt-4` but only has `ANTHROPIC_API_KEY` set (no `OPENAI_API_KEY`), the error is:
|
||||||
|
```
|
||||||
|
error: invalid model syntax: 'gpt-4'. Expected provider/model (e.g., anthropic/claude-opus-4-6) or known alias (opus, sonnet, haiku)
|
||||||
|
```
|
||||||
|
|
||||||
|
But USAGE.md documents that "The error message now includes a hint that names the detected env var" — **this hint is not actually emitted.** The user gets a generic syntax error and has to re-read USAGE.md to discover they should type `openai/gpt-4` instead.
|
||||||
|
|
||||||
|
**Expected behavior (from USAGE.md):** When the user has multiple providers' env vars set, or when a model name looks like it belongs to a different provider (e.g., `gpt-4` looks like OpenAI), the error should hint:
|
||||||
|
- "Did you mean `openai/gpt-4`? (but `OPENAI_API_KEY` is not set)"
|
||||||
|
- or "You have `ANTHROPIC_API_KEY` set but `gpt-4` looks like an OpenAI model. Try `openai/gpt-4` with `OPENAI_API_KEY` exported"
|
||||||
|
|
||||||
|
**Current behavior:** Generic syntax error, user has to infer the fix from USAGE.md or guess.
|
||||||
|
|
||||||
|
**Fix shape (~20 lines).** Enhance `FormatError::InvalidModelSyntax` or the model-parsing validation to:
|
||||||
|
1. Detect if the model name looks like it belongs to a known provider (prefix `gpt-`, `openai/`, `qwen`, etc.)
|
||||||
|
2. If it does, check if that provider's env var is missing
|
||||||
|
3. Append a hint: "Did you mean \`{inferred_prefix}/{model}\`? (requires `{PROVIDER_KEY}` env var)"
|
||||||
|
|
||||||
|
**Acceptance:** `claw --model gpt-4` produces a hint about OpenAI prefix and missing `OPENAI_API_KEY`. Same for `qwen-plus` → hint about `DASHSCOPE_API_KEY`, etc.
|
||||||
|
|
||||||
|
**Blocker:** None. Pure error-message UX improvement.
|
||||||
|
|
||||||
|
**Source:** Clawhip nudge 2026-04-21 21:37 KST — discovered during dogfood probing of model validation.
|
||||||
|
|||||||
@@ -1401,10 +1401,27 @@ fn validate_model_syntax(model: &str) -> Result<(), String> {
|
|||||||
// Check provider/model format: provider_id/model_id
|
// Check provider/model format: provider_id/model_id
|
||||||
let parts: Vec<&str> = trimmed.split('/').collect();
|
let parts: Vec<&str> = trimmed.split('/').collect();
|
||||||
if parts.len() != 2 || parts[0].is_empty() || parts[1].is_empty() {
|
if parts.len() != 2 || parts[0].is_empty() || parts[1].is_empty() {
|
||||||
return Err(format!(
|
// #154: hint if the model looks like it belongs to a different provider
|
||||||
|
let mut err_msg = format!(
|
||||||
"invalid model syntax: '{}'. Expected provider/model (e.g., anthropic/claude-opus-4-6) or known alias (opus, sonnet, haiku)",
|
"invalid model syntax: '{}'. Expected provider/model (e.g., anthropic/claude-opus-4-6) or known alias (opus, sonnet, haiku)",
|
||||||
trimmed
|
trimmed
|
||||||
));
|
);
|
||||||
|
if trimmed.starts_with("gpt-") || trimmed.starts_with("gpt_") {
|
||||||
|
err_msg.push_str("\nDid you mean `openai/");
|
||||||
|
err_msg.push_str(trimmed);
|
||||||
|
err_msg.push_str("`? (Requires OPENAI_API_KEY env var)");
|
||||||
|
}
|
||||||
|
else if trimmed.starts_with("qwen") {
|
||||||
|
err_msg.push_str("\nDid you mean `qwen/");
|
||||||
|
err_msg.push_str(trimmed);
|
||||||
|
err_msg.push_str("`? (Requires DASHSCOPE_API_KEY env var)");
|
||||||
|
}
|
||||||
|
else if trimmed.starts_with("grok") {
|
||||||
|
err_msg.push_str("\nDid you mean `xai/");
|
||||||
|
err_msg.push_str(trimmed);
|
||||||
|
err_msg.push_str("`? (Requires XAI_API_KEY env var)");
|
||||||
|
}
|
||||||
|
return Err(err_msg);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -10301,6 +10318,34 @@ mod tests {
|
|||||||
.expect_err("`doctor garbage` should fail without --json hint");
|
.expect_err("`doctor garbage` should fail without --json hint");
|
||||||
assert!(!err_other.contains("--output-format json"),
|
assert!(!err_other.contains("--output-format json"),
|
||||||
"unrelated args should not trigger --json hint: {err_other}");
|
"unrelated args should not trigger --json hint: {err_other}");
|
||||||
|
// #154: model syntax error should hint at provider prefix when applicable
|
||||||
|
let err_gpt = parse_args(&["prompt".to_string(), "test".to_string(), "--model".to_string(), "gpt-4".to_string()])
|
||||||
|
.expect_err("`--model gpt-4` should fail with OpenAI hint");
|
||||||
|
assert!(
|
||||||
|
err_gpt.contains("Did you mean `openai/gpt-4`?"),
|
||||||
|
"GPT model error should hint openai/ prefix: {err_gpt}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
err_gpt.contains("OPENAI_API_KEY"),
|
||||||
|
"GPT model error should mention env var: {err_gpt}"
|
||||||
|
);
|
||||||
|
let err_qwen = parse_args(&["prompt".to_string(), "test".to_string(), "--model".to_string(), "qwen-plus".to_string()])
|
||||||
|
.expect_err("`--model qwen-plus` should fail with DashScope hint");
|
||||||
|
assert!(
|
||||||
|
err_qwen.contains("Did you mean `qwen/qwen-plus`?"),
|
||||||
|
"Qwen model error should hint qwen/ prefix: {err_qwen}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
err_qwen.contains("DASHSCOPE_API_KEY"),
|
||||||
|
"Qwen model error should mention env var: {err_qwen}"
|
||||||
|
);
|
||||||
|
// Unrelated invalid model should NOT get a hint
|
||||||
|
let err_garbage = parse_args(&["prompt".to_string(), "test".to_string(), "--model".to_string(), "asdfgh".to_string()])
|
||||||
|
.expect_err("`--model asdfgh` should fail");
|
||||||
|
assert!(
|
||||||
|
!err_garbage.contains("Did you mean"),
|
||||||
|
"Unrelated model errors should not get a hint: {err_garbage}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user