mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-26 17:12:27 +03:00
fix(command-code): fallback to /alpha/generate for Go plan without Provider API access (#11455)
Validated in a combined sub-batch worktree off release/v3.8.51 tip. This PR's branch also carried ~88 already-merged commits from a stale rebase (phantom-diff); cherry-picked only the genuine value commit. That commit's own test file (command-code-executor.test.ts) predated #11421's async getExecutor() change and had 7 test failures from unresolved-Promise call sites (`.execute()` on a still-pending getExecutor() Promise, and in one case on execute() itself not being awaited) — fixed all 7 call sites to properly await both async calls, verified 16/16 pass, and pushed both the cherry-pick and the fix to this branch. - Focused tests: command-code-executor.test.ts (16/16) + provider-validation-specialty.test.ts — 140/140 combined, part of sub-batch's full run - typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity — all OK - Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff Thanks for tracing the Go-plan 403 to the v3.8.50 endpoint migration and building a clean fallback that preserves the new Provider-tier path while restoring CLI compatibility for Go plan.
This commit is contained in:
@@ -232,7 +232,7 @@ test("Command Code executor passes the upstream OpenAI SSE stream through untouc
|
||||
});
|
||||
};
|
||||
|
||||
const { response } = (await getExecutor("command-code")).execute({
|
||||
const { response } = await (await getExecutor("command-code")).execute({
|
||||
model: "gpt-5.4",
|
||||
stream: true,
|
||||
credentials: { apiKey: "cc_test_key" },
|
||||
@@ -254,7 +254,9 @@ test("Command Code executor passes the upstream OpenAI JSON through untouched (n
|
||||
id: "chatcmpl-1",
|
||||
object: "chat.completion",
|
||||
model: "gpt-5.4-mini",
|
||||
choices: [{ index: 0, message: { role: "assistant", content: "Hello" }, finish_reason: "stop" }],
|
||||
choices: [
|
||||
{ index: 0, message: { role: "assistant", content: "Hello" }, finish_reason: "stop" },
|
||||
],
|
||||
usage: { prompt_tokens: 3, completion_tokens: 2, total_tokens: 5 },
|
||||
};
|
||||
let capturedStreamFlag: unknown = null;
|
||||
@@ -266,7 +268,7 @@ test("Command Code executor passes the upstream OpenAI JSON through untouched (n
|
||||
});
|
||||
};
|
||||
|
||||
const { response } = (await getExecutor("command-code")).execute({
|
||||
const { response } = await (await getExecutor("command-code")).execute({
|
||||
model: "gpt-5.4-mini",
|
||||
stream: false,
|
||||
credentials: { apiKey: "cc_test_key" },
|
||||
@@ -279,7 +281,7 @@ test("Command Code executor passes the upstream OpenAI JSON through untouched (n
|
||||
|
||||
test("Command Code executor surfaces upstream errors", async () => {
|
||||
globalThis.fetch = async () => new Response("bad key", { status: 401, statusText: "Unauthorized" });
|
||||
const upstreamFailure = (await getExecutor("command-code")).execute({
|
||||
const upstreamFailure = await (await getExecutor("command-code")).execute({
|
||||
model: "gpt-5.4-mini",
|
||||
stream: false,
|
||||
credentials: { apiKey: "cc_test_key" },
|
||||
@@ -350,7 +352,7 @@ test("Command Code stream preserves the upstream OpenAI usage chunk (passthrough
|
||||
globalThis.fetch = async () =>
|
||||
new Response(sse, { status: 200, headers: { "Content-Type": "text/event-stream" } });
|
||||
|
||||
const { response } = (await getExecutor("command-code")).execute({
|
||||
const { response } = await (await getExecutor("command-code")).execute({
|
||||
model: "gpt-5.4-mini",
|
||||
stream: true,
|
||||
credentials: { apiKey: "cc_test_key" },
|
||||
@@ -364,4 +366,131 @@ test("Command Code stream preserves the upstream OpenAI usage chunk (passthrough
|
||||
assert.ok(text.includes('"cached_tokens":4'));
|
||||
assert.ok(text.includes('"reasoning_tokens":1'));
|
||||
assert.ok(text.includes("data: [DONE]"));
|
||||
});
|
||||
});
|
||||
|
||||
test("Command Code executor falls back to /alpha/generate on 403 (e.g. Go plan without Provider API access) for streaming", async () => {
|
||||
const calls: Array<{
|
||||
url: string;
|
||||
headers: Record<string, string>;
|
||||
body: Record<string, unknown>;
|
||||
}> = [];
|
||||
globalThis.fetch = async (url, init = {}) => {
|
||||
const urlStr = String(url);
|
||||
calls.push({
|
||||
url: urlStr,
|
||||
headers: (init.headers || {}) as Record<string, string>,
|
||||
body: JSON.parse(String(init.body)) as Record<string, unknown>,
|
||||
});
|
||||
|
||||
if (urlStr.includes("/provider/v1/chat/completions")) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: {
|
||||
message:
|
||||
"Your Go plan doesn't include API access. Upgrade to Provider or higher at https://commandcode.ai/billing to use these endpoints.",
|
||||
type: "permission_error",
|
||||
code: "upgrade_required",
|
||||
},
|
||||
}),
|
||||
{ status: 403, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
if (urlStr.includes("/alpha/generate")) {
|
||||
const cliSse =
|
||||
'data: {"type":"text-delta","text":"Hello from CLI fallback"}\n\n' +
|
||||
'data: {"type":"finish","finishReason":"stop","usage":{"inputTokens":5,"outputTokens":4,"totalTokens":9}}\n\n';
|
||||
return new Response(cliSse, {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "text/event-stream" },
|
||||
});
|
||||
}
|
||||
|
||||
return new Response("Not found", { status: 404 });
|
||||
};
|
||||
|
||||
const { response, url, headers } = await (await getExecutor("command-code")).execute({
|
||||
model: "deepseek/deepseek-v4-flash",
|
||||
stream: true,
|
||||
credentials: { apiKey: "cc_go_plan_key" },
|
||||
body: { messages: [{ role: "user", content: "Hi" }] },
|
||||
});
|
||||
|
||||
assert.equal(calls.length, 2, "probed /provider/v1 first, then fell back to /alpha/generate");
|
||||
assert.ok(calls[0].url.includes("/provider/v1/chat/completions"));
|
||||
assert.ok(calls[1].url.includes("/alpha/generate"));
|
||||
assert.equal(calls[1].headers["x-cli-environment"], "external");
|
||||
assert.equal(calls[1].headers["x-command-code-version"], "1.15.1");
|
||||
assert.equal((calls[1].body.config as Record<string, unknown>).environment, "external");
|
||||
|
||||
assert.ok(url.includes("/alpha/generate"));
|
||||
assert.equal(headers["x-cli-environment"], "external");
|
||||
const text = await response.text();
|
||||
assert.ok(text.includes("Hello from CLI fallback"));
|
||||
assert.ok(text.includes("data: [DONE]"));
|
||||
});
|
||||
|
||||
test("Command Code executor falls back to /alpha/generate on 403 (Go plan) for non-stream JSON", async () => {
|
||||
const calls: string[] = [];
|
||||
globalThis.fetch = async (url) => {
|
||||
const urlStr = String(url);
|
||||
calls.push(urlStr);
|
||||
|
||||
if (urlStr.includes("/provider/v1/chat/completions")) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: { message: "upgrade_required", code: "upgrade_required" } }),
|
||||
{ status: 403, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
if (urlStr.includes("/alpha/generate")) {
|
||||
const cliSse =
|
||||
'data: {"type":"text-delta","text":"Non-stream answer"}\n\n' +
|
||||
'data: {"type":"finish","finishReason":"stop","usage":{"inputTokens":3,"outputTokens":2,"totalTokens":5}}\n\n';
|
||||
return new Response(cliSse, {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "text/event-stream" },
|
||||
});
|
||||
}
|
||||
|
||||
return new Response("Not found", { status: 404 });
|
||||
};
|
||||
|
||||
const { response } = await (await getExecutor("command-code")).execute({
|
||||
model: "gpt-5.4",
|
||||
stream: false,
|
||||
credentials: { apiKey: "cc_go_plan_key" },
|
||||
body: { messages: [{ role: "user", content: "Hi" }] },
|
||||
});
|
||||
|
||||
assert.equal(calls.length, 2);
|
||||
const json = (await response.json()) as Record<string, unknown>;
|
||||
assert.equal(json.object, "chat.completion");
|
||||
const choices = json.choices as Array<{ message: { content: string } }>;
|
||||
assert.equal(choices[0].message.content, "Non-stream answer");
|
||||
const usage = json.usage as { total_tokens: number };
|
||||
assert.equal(usage.total_tokens, 5);
|
||||
});
|
||||
|
||||
test("Command Code executor surfaces fallback error when both /provider/v1 and /alpha/generate fail", async () => {
|
||||
globalThis.fetch = async (url) => {
|
||||
const urlStr = String(url);
|
||||
if (urlStr.includes("/provider/v1/chat/completions")) {
|
||||
return new Response("forbidden", { status: 403 });
|
||||
}
|
||||
if (urlStr.includes("/alpha/generate")) {
|
||||
return new Response("insufficient credits on fallback", { status: 400 });
|
||||
}
|
||||
return new Response("error", { status: 500 });
|
||||
};
|
||||
|
||||
const result = await (await getExecutor("command-code")).execute({
|
||||
model: "gpt-5.4",
|
||||
stream: false,
|
||||
credentials: { apiKey: "cc_key" },
|
||||
body: { messages: [{ role: "user", content: "Hi" }] },
|
||||
});
|
||||
|
||||
assert.equal(result.response.status, 400);
|
||||
assert.equal(await result.response.text(), "insufficient credits on fallback");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user