Skip to content

Commit 0c4de75

Browse files
committed
fix(refine-skill): accept ANTHROPIC_OAUTH_TOKEN as valid claude credential; strip groq/ollama/local model prefix
- providers.js: requiredEnvVars (plural, any-of) returns [ANTHROPIC_API_KEY, ANTHROPIC_OAUTH_TOKEN] for claude-* models. validate.js accepts either; old single-required check rejected OAuth-only environments. Old requiredEnvVar() kept for back-compat (returns first element). - refine.sh: groq/foo, ollama/foo, local/foo now strip the provider prefix before passing to pi (pi prefixes internally; double prefix produced 'Model groq/groq/foo not found').
1 parent aefd337 commit 0c4de75

4 files changed

Lines changed: 42 additions & 20 deletions

File tree

docker/bin/refine.sh

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,18 @@ done
8585
}
8686

8787
# --- derive provider from model (mirrors host providers.js) ---
88+
# For groq/ollama/local, the model id includes a "<provider>/" prefix in our
89+
# user-facing convention. Pi expects --provider and --model separately and
90+
# prefixes internally, so strip the prefix when present.
8891
case "$model" in
8992
claude-*) provider="anthropic" ;;
9093
gpt-*|o1*|o3*) provider="openai" ;;
9194
gemini-*) provider="google" ;;
9295
grok-*) provider="xai" ;;
9396
mistral-*) provider="mistral" ;;
94-
groq/*) provider="groq" ;;
95-
ollama/*) provider="ollama" ;;
96-
local/*) provider="local" ;;
97+
groq/*) provider="groq"; model="${model#groq/}" ;;
98+
ollama/*) provider="ollama"; model="${model#ollama/}" ;;
99+
local/*) provider="local"; model="${model#local/}" ;;
97100
stub|stub:*) provider="stub" ;; # for tests
98101
*) provider="anthropic" ;; # fallback; pi will error if mismatched
99102
esac

src/providers.js

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,33 @@
1-
// Model-prefix routing. Each entry: [regex, envVar, providerName].
2-
// envVar is what we forward + verify. providerName is what pi expects via --provider.
1+
// Model-prefix routing. Each entry: [regex, requiredEnvVars (any-of), providerName].
2+
// requiredEnvVars is an array — if any one is present, validation passes.
33
const PREFIX_TABLE = [
4-
[/^claude-/, "ANTHROPIC_API_KEY", "anthropic"],
5-
[/^gpt-/, "OPENAI_API_KEY", "openai"],
6-
[/^o[13](-|$)/, "OPENAI_API_KEY", "openai"],
7-
[/^gemini-/, "GEMINI_API_KEY", "google"],
8-
[/^grok-/, "XAI_API_KEY", "xai"],
9-
[/^mistral-/, "MISTRAL_API_KEY", "mistral"],
10-
[/^groq\//, "GROQ_API_KEY", "groq"],
11-
[/^ollama\//, null, "ollama"],
12-
[/^local\//, null, "local"],
4+
[/^claude-/, ["ANTHROPIC_API_KEY", "ANTHROPIC_OAUTH_TOKEN"], "anthropic"],
5+
[/^gpt-/, ["OPENAI_API_KEY"], "openai"],
6+
[/^o[13](-|$)/, ["OPENAI_API_KEY"], "openai"],
7+
[/^gemini-/, ["GEMINI_API_KEY"], "google"],
8+
[/^grok-/, ["XAI_API_KEY"], "xai"],
9+
[/^mistral-/, ["MISTRAL_API_KEY"], "mistral"],
10+
[/^groq\//, ["GROQ_API_KEY"], "groq"],
11+
[/^ollama\//, [], "ollama"],
12+
[/^local\//, [], "local"],
1313
];
1414

15+
// Back-compat: returns the FIRST required env var, or null if none.
1516
export function requiredEnvVar(model) {
16-
for (const [re, envVar] of PREFIX_TABLE) {
17-
if (re.test(model)) return envVar;
17+
for (const [re, vars] of PREFIX_TABLE) {
18+
if (re.test(model)) return vars[0] ?? null;
1819
}
1920
return null;
2021
}
2122

23+
// Returns full any-of list (or [] if no specific requirement).
24+
export function requiredEnvVars(model) {
25+
for (const [re, vars] of PREFIX_TABLE) {
26+
if (re.test(model)) return vars;
27+
}
28+
return [];
29+
}
30+
2231
export function providerName(model) {
2332
for (const [re, , provider] of PREFIX_TABLE) {
2433
if (re.test(model)) return provider;

src/validate.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import fs from "node:fs";
22
import path from "node:path";
3-
import { requiredEnvVar } from "./providers.js";
3+
import { requiredEnvVars } from "./providers.js";
44

55
export function validatePath(inputPath) {
66
let absPath;
@@ -43,12 +43,13 @@ const KNOWN_PROVIDER_KEYS = [
4343
];
4444

4545
export function validateModelEnv(model, env) {
46-
const required = requiredEnvVar(model);
47-
if (required && !env[required]) {
46+
const required = requiredEnvVars(model);
47+
if (required.length > 0 && !required.some((v) => env[v])) {
48+
const list = required.join(" or ");
4849
return {
4950
ok: false,
5051
code: 3,
51-
error: `--model ${model} requires ${required} in environment`,
52+
error: `--model ${model} requires ${list} in environment`,
5253
};
5354
}
5455
const envVarsToForward = KNOWN_PROVIDER_KEYS.filter((v) => env[v]);

test/validate.test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,18 @@ test("validateModelEnv fails when required key missing", () => {
4848
assert.equal(result.ok, false);
4949
assert.equal(result.code, 3);
5050
assert.match(result.error, /ANTHROPIC_API_KEY/);
51+
assert.match(result.error, /ANTHROPIC_OAUTH_TOKEN/);
5152
assert.match(result.error, /claude-/);
5253
});
5354

55+
test("validateModelEnv passes when ANTHROPIC_OAUTH_TOKEN present (no API key)", () => {
56+
const result = validateModelEnv("claude-sonnet-4-5", {
57+
ANTHROPIC_OAUTH_TOKEN: "oat-...",
58+
});
59+
assert.equal(result.ok, true);
60+
assert.ok(result.envVarsToForward.includes("ANTHROPIC_OAUTH_TOKEN"));
61+
});
62+
5463
test("validateModelEnv forwards multiple present provider keys", () => {
5564
const result = validateModelEnv("claude-sonnet-4-5", {
5665
ANTHROPIC_API_KEY: "sk-...",

0 commit comments

Comments
 (0)