mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-19 13:23:50 +03:00
An OpenCode Zen free-tier 403 ("free tier can only be used from within
OpenCode") reached the end of the executor loop unrecognized: nothing was
persisted about it, and the account that had just been refused was marked
successful, which clears the failure history driving its cooldown backoff. A
refusal was therefore improving the rotation health of the account it hit.
The refusal is now recognized by its own predicate, returned unchanged without
rotating (it is request-scoped, so every sibling account returns the same
verdict), and classified as a non-banning routing error, so the connection
records lastErrorType/lastError/errorCode and stays active.
The account-health reset is also reserved for HTTP successes at both call sites
in the loop, since the same reset ran on any status the loop did not handle in a
dedicated branch.
Co-authored-by: Max <maxmad64@gmail.com>
135 lines
5.8 KiB
TypeScript
135 lines
5.8 KiB
TypeScript
import { describe, it } from "node:test";
|
|
import assert from "node:assert";
|
|
import {
|
|
isOpencodeFreeTierRefusal,
|
|
isOpencodeFreeTierRefusalForProvider,
|
|
isOpencodeGeoBlocked,
|
|
isOpencodeUserBlocked,
|
|
} from "../../open-sse/executors/opencodeGeoBlock.ts";
|
|
import {
|
|
classifyProviderError,
|
|
PROVIDER_ERROR_TYPES,
|
|
} from "../../open-sse/services/errorClassifier.ts";
|
|
|
|
// Verbatim upstream refusal (2026-09-17): the free tier rejects a request whose
|
|
// client identity or request shape does not match the OpenCode client contract.
|
|
const REFUSAL_BODY = JSON.stringify({
|
|
type: "error",
|
|
error: {
|
|
type: "FreeTierError",
|
|
message:
|
|
"Error from provider (Console): OpenCode's free tier can only be used from within OpenCode",
|
|
},
|
|
});
|
|
// What the error parser hands to the classifier: the relayed message alone.
|
|
const RELAYED_MESSAGE =
|
|
"Error from provider (Console): OpenCode's free tier can only be used from within OpenCode";
|
|
const AUTH_BODY = JSON.stringify({ error: { message: "invalid api key", type: "auth_error" } });
|
|
|
|
describe("isOpencodeFreeTierRefusal", () => {
|
|
it("matches the upstream refusal body on 403", () => {
|
|
assert.strictEqual(isOpencodeFreeTierRefusal(403, REFUSAL_BODY), true);
|
|
});
|
|
it("matches the relayed message alone (no type field available)", () => {
|
|
assert.strictEqual(isOpencodeFreeTierRefusal(403, RELAYED_MESSAGE), true);
|
|
});
|
|
it("classifies 451 exactly like 403 (one predicate, no status special case)", () => {
|
|
assert.strictEqual(isOpencodeFreeTierRefusal(451, REFUSAL_BODY), true);
|
|
assert.strictEqual(isOpencodeFreeTierRefusal(451, AUTH_BODY), false);
|
|
});
|
|
it("matches regardless of case", () => {
|
|
assert.strictEqual(isOpencodeFreeTierRefusal(403, REFUSAL_BODY.toUpperCase()), true);
|
|
});
|
|
it("leaves a geo-blocked body to the geo predicate even with the token present", () => {
|
|
const geo = JSON.stringify({
|
|
error: { type: "FreeTierError", message: "not available in your country" },
|
|
});
|
|
assert.strictEqual(isOpencodeGeoBlocked(403, geo), true);
|
|
assert.strictEqual(isOpencodeFreeTierRefusal(403, geo), false);
|
|
});
|
|
it("leaves a user_blocked body to its own predicate", () => {
|
|
const blocked = JSON.stringify({
|
|
error: { type: "FreeTierError", message: "[user_blocked] egress refused" },
|
|
});
|
|
assert.strictEqual(isOpencodeUserBlocked(403, blocked), true);
|
|
assert.strictEqual(isOpencodeFreeTierRefusal(403, blocked), false);
|
|
});
|
|
it("rejects a keyed fingerprint 1010 body even with the token present", () => {
|
|
assert.strictEqual(
|
|
isOpencodeFreeTierRefusal(403, `{"error_code":1010,"error":{"type":"FreeTierError"}}`),
|
|
false
|
|
);
|
|
});
|
|
it("rejects 403 without the signal", () => {
|
|
assert.strictEqual(isOpencodeFreeTierRefusal(403, AUTH_BODY), false);
|
|
});
|
|
it("rejects statuses other than 403 and 451", () => {
|
|
for (const status of [200, 400, 401, 426, 429, 500]) {
|
|
assert.strictEqual(isOpencodeFreeTierRefusal(status, REFUSAL_BODY), false);
|
|
}
|
|
});
|
|
it("rejects empty and null bodies", () => {
|
|
assert.strictEqual(isOpencodeFreeTierRefusal(403, ""), false);
|
|
assert.strictEqual(isOpencodeFreeTierRefusal(403, null), false);
|
|
});
|
|
});
|
|
|
|
// The executor predicate reads the whole upstream body; the classifier only ever
|
|
// receives the relayed message (parseUpstreamError keeps `error.type` aside). Both
|
|
// must agree on every vector, or one layer silently stops recognizing the refusal.
|
|
describe("free-tier refusal parity: executor predicate, classifier and auth guard", () => {
|
|
const VECTORS: Array<{ label: string; body: string; refusal: boolean }> = [
|
|
{ label: "full upstream body", body: REFUSAL_BODY, refusal: true },
|
|
{ label: "relayed message only", body: RELAYED_MESSAGE, refusal: true },
|
|
{ label: "plain auth refusal", body: AUTH_BODY, refusal: false },
|
|
{ label: "empty body", body: "", refusal: false },
|
|
];
|
|
for (const vector of VECTORS) {
|
|
it(`agrees on ${vector.label}`, () => {
|
|
assert.strictEqual(isOpencodeFreeTierRefusal(403, vector.body), vector.refusal);
|
|
const classified = classifyProviderError(403, vector.body, "opencode");
|
|
assert.strictEqual(
|
|
classified === PROVIDER_ERROR_TYPES.PROJECT_ROUTE_ERROR,
|
|
vector.refusal,
|
|
"classifier must recognize exactly the vectors the executor predicate recognizes"
|
|
);
|
|
// Third consumer: the auth guard that keeps the refusal off the account and the
|
|
// model. It wraps the predicate with the same provider scope the classifier uses,
|
|
// so all three have to agree or the refusal is handled at one layer and not another.
|
|
assert.strictEqual(
|
|
isOpencodeFreeTierRefusalForProvider("opencode", 403, vector.body),
|
|
vector.refusal,
|
|
"auth guard must recognize exactly the same vectors"
|
|
);
|
|
});
|
|
}
|
|
});
|
|
|
|
describe("free-tier refusal classification scope", () => {
|
|
for (const provider of ["opencode", "opencode-zen", "opencode-go"]) {
|
|
it(`classifies the refusal for ${provider} as a non-banning routing error`, () => {
|
|
assert.strictEqual(
|
|
classifyProviderError(403, RELAYED_MESSAGE, provider),
|
|
PROVIDER_ERROR_TYPES.PROJECT_ROUTE_ERROR
|
|
);
|
|
});
|
|
}
|
|
it("leaves the same sentence unclassified for another provider", () => {
|
|
assert.strictEqual(classifyProviderError(403, RELAYED_MESSAGE, "groq"), null);
|
|
assert.strictEqual(
|
|
isOpencodeFreeTierRefusalForProvider("groq", 403, RELAYED_MESSAGE),
|
|
false,
|
|
"the auth guard carries the same provider scope as the classifier"
|
|
);
|
|
});
|
|
it("does not recognize a 402, which stays a per-model credit failure", () => {
|
|
assert.strictEqual(
|
|
isOpencodeFreeTierRefusalForProvider("opencode", 402, RELAYED_MESSAGE),
|
|
false
|
|
);
|
|
});
|
|
it("keeps an ordinary api-key 403 unclassified", () => {
|
|
assert.strictEqual(classifyProviderError(403, AUTH_BODY, "opencode"), null);
|
|
});
|
|
});
|