fix: improve provider routing and assignments

This commit is contained in:
MUH. IQRAM BAHRING
2026-08-15 03:47:18 +08:00
parent d00205511a
commit df8c4ccb81
17 changed files with 471 additions and 51 deletions
@@ -0,0 +1,36 @@
import { describe, expect, it } from "vitest";
import { filterConnectionsForModel } from "../../src/sse/services/auth.js";
const connections = [
{ id: "flash-1", providerSpecificData: { freebuffModel: "deepseek/deepseek-v4-flash" } },
{ id: "mimo-1", providerSpecificData: { freebuffModel: "mimo/mimo-v2.5" } },
{ id: "unassigned", providerSpecificData: {} },
];
describe("Freebuff strict model assignment", () => {
it("keeps only accounts assigned to the requested model", () => {
const result = filterConnectionsForModel("freebuff", connections, "mimo/mimo-v2.5", {
providerStrategies: { freebuff: { strictModelAssignment: true } },
});
expect(result.map((connection) => connection.id)).toEqual(["mimo-1"]);
});
it("excludes unassigned accounts when strict mode is enabled", () => {
const result = filterConnectionsForModel("freebuff", connections, "deepseek/deepseek-v4-flash", {
providerStrategies: { freebuff: { strictModelAssignment: true } },
});
expect(result.map((connection) => connection.id)).toEqual(["flash-1"]);
});
it("preserves the existing pool when strict mode is disabled", () => {
expect(filterConnectionsForModel("freebuff", connections, "mimo/mimo-v2.5", {})).toBe(connections);
});
it("does not affect other providers when their toggle is off", () => {
expect(filterConnectionsForModel("codex", connections, "mimo/mimo-v2.5", {
providerStrategies: {},
})).toBe(connections);
});
});
+68 -6
View File
@@ -207,6 +207,43 @@ describe("freebuff executor wire shape", () => {
const ex = new FreebuffExecutor();
expect(ex.buildUrl()).toBe("https://www.codebuff.com/api/v1/chat/completions");
});
it("injects the end_turn tool into any tool-calling request (backend foreign_toolset gate)", () => {
const ex = new FreebuffExecutor();
const body = {
model: "deepseek/deepseek-v4-flash",
messages: [{ role: "user", content: "hi" }],
tools: [{ type: "function", function: { name: "read_file", description: "read" } }],
};
const out = ex.transformRequest(body.model, body, true, { providerSpecificData: { fingerprintId: "fp-1" } });
const names = out.tools.map((t) => t.function.name);
expect(names).toContain("read_file");
expect(names).toContain("end_turn");
expect(out.tools[out.tools.length - 1].function).toMatchObject({
name: "end_turn",
description: "Signal the end of the current task.",
});
});
it("does not inject end_turn when the request has no tools", () => {
const ex = new FreebuffExecutor();
const body = { model: "deepseek/deepseek-v4-flash", messages: [{ role: "user", content: "hi" }] };
const out = ex.transformRequest(body.model, body, true, { providerSpecificData: { fingerprintId: "fp-1" } });
expect(out.tools).toBeUndefined();
});
it("does not duplicate end_turn when the caller already declared it", () => {
const ex = new FreebuffExecutor();
const endTurn = { type: "function", function: { name: "end_turn", description: "Signal the end of the current task.", parameters: { type: "object", properties: {} } } };
const body = {
model: "deepseek/deepseek-v4-flash",
messages: [{ role: "user", content: "hi" }],
tools: [endTurn],
};
const out = ex.transformRequest(body.model, body, true, { providerSpecificData: { fingerprintId: "fp-1" } });
expect(out.tools).toHaveLength(1);
expect(out.tools[0].function.name).toBe("end_turn");
});
});
describe("freebuff session pre-flight", () => {
@@ -301,11 +338,11 @@ describe("freebuff free-tier system marker", () => {
describe("freebuff run registration", () => {
it("maps freebuff models to their root free agent ids", () => {
expect(rootAgentIdForModel("deepseek/deepseek-v4-flash")).toBe("base2-free-deepseek-flash");
expect(rootAgentIdForModel("deepseek/deepseek-v4-pro")).toBe("base2-free-deepseek");
expect(rootAgentIdForModel("mimo/mimo-v2.5")).toBe("base2-free-mimo");
expect(rootAgentIdForModel("minimax/minimax-m3")).toBe("base2-free-minimax-m3");
expect(rootAgentIdForModel("openai/gpt-5.6-luna")).toBe("base2-free-luna");
expect(rootAgentIdForModel("deepseek/deepseek-v4-flash")).toBe("base3-free-deepseek-flash");
expect(rootAgentIdForModel("deepseek/deepseek-v4-pro")).toBe("base3-free-deepseek");
expect(rootAgentIdForModel("mimo/mimo-v2.5")).toBe("base3-free-mimo");
expect(rootAgentIdForModel("minimax/minimax-m3")).toBe("base3-free-minimax-m3");
expect(rootAgentIdForModel("openai/gpt-5.6-luna")).toBe("base3-free-luna");
expect(rootAgentIdForModel("some/unknown-model")).toBe("base2-free");
});
@@ -320,7 +357,7 @@ describe("freebuff run registration", () => {
expect(opts.headers.Authorization).toBe("Bearer tok-1");
const payload = JSON.parse(opts.body);
expect(payload.action).toBe("START");
expect(payload.agentId).toBe("base2-free-deepseek-flash");
expect(payload.agentId).toBe("base3-free-deepseek-flash");
expect(payload.ancestorRunIds).toEqual([]);
});
@@ -586,3 +623,28 @@ describe("freebuff executor execute", () => {
expect(JSON.parse(finishes[0][1].body).status).toBe("cancelled");
});
});
describe("freebuff executor parseError", () => {
it("explains a 404 'No endpoints found' as the toolset gate, not a credential problem", async () => {
const ex = new FreebuffExecutor();
const res = jsonResponse(
{ error: { message: "No endpoints found for deepseek/deepseek-v4-flash.", code: 404, type: null, param: null } },
{ status: 404, ok: false },
);
const parsed = await ex.parseError(res, JSON.stringify({ error: { message: "No endpoints found for deepseek/deepseek-v4-flash.", code: 404 } }));
expect(parsed.status).toBe(404);
expect(parsed.message).toMatch(/end_turn/i);
expect(parsed.message).not.toMatch(/credential/i);
expect(parsed.resetsAtMs).toBeGreaterThan(Date.now());
});
it("passes other statuses through untouched", async () => {
const ex = new FreebuffExecutor();
const res = jsonResponse({ error: "bad" }, { status: 500, ok: false });
const parsed = await ex.parseError(res, JSON.stringify({ error: "bad" }));
expect(parsed.status).toBe(500);
expect(parsed.message).toContain("bad");
expect(parsed.resetsAtMs).toBeUndefined();
});
});
+28
View File
@@ -48,3 +48,31 @@ describe("classifyOAuthProbeResult (grok-cli)", () => {
expect(r).toEqual({ valid: true, error: null, soft: false });
});
});
describe("classifyOAuthProbeResult (freebuff)", () => {
const FREEBUFF_PROBE = {
acceptStatuses: [403, 404],
softFailMessage: { 403: "gated" },
};
it("treats 404 (no session row) as silent success", () => {
const r = classifyOAuthProbeResult({ ok: false, status: 404 }, FREEBUFF_PROBE, "");
expect(r).toEqual({ valid: true, error: null, soft: false });
});
it("treats 403 (region/account gate) as soft success", () => {
const r = classifyOAuthProbeResult(
{ ok: false, status: 403 },
FREEBUFF_PROBE,
JSON.stringify({ status: "country_blocked", countryCode: "XX" }),
);
expect(r.valid).toBe(true);
expect(r.soft).toBe(true);
expect(r.error).toMatch(/gated/);
});
it("treats 401 as hard auth failure", () => {
const r = classifyOAuthProbeResult({ ok: false, status: 401 }, FREEBUFF_PROBE, "unauthorized");
expect(r).toEqual({ valid: false, error: "Token invalid or revoked", soft: false });
});
});
+25
View File
@@ -9,6 +9,7 @@ import {
pruneExpired,
resetPoolFitness,
} from "open-sse/services/proxyPoolFitness.js";
import { pickProxyPoolId } from "../../src/lib/network/connectionProxy.js";
describe("proxy pool fitness registry", () => {
beforeEach(() => resetPoolFitness());
@@ -39,6 +40,30 @@ describe("proxy pool fitness registry", () => {
expect(snap.p1["fb::m2"]).toBeUndefined();
});
it("does not reuse a pool when every smart candidate is unfit", () => {
markPoolUnfit("p1", "freebuff::openai/gpt-5.6-luna", Date.now() + 60_000, "limited_ip");
markPoolUnfit("p2", "freebuff::openai/gpt-5.6-luna", Date.now() + 60_000, "limited_ip");
expect(pickProxyPoolId(
["p1", "p2"],
"smart",
"freebuff",
{ scope: "freebuff::openai/gpt-5.6-luna" },
)).toBeNull();
});
it("preserves fail-open smart fallback for non-Freebuff providers", () => {
markPoolUnfit("p1", "opencode::sonnet-4.6", Date.now() + 60_000, "ip-limit");
markPoolUnfit("p2", "opencode::sonnet-4.6", Date.now() + 60_000, "ip-limit");
expect(pickProxyPoolId(
["p1", "p2"],
"smart",
"opencode",
{ scope: "opencode::sonnet-4.6" },
)).toBe("p1");
});
it("clear per scope, clear-all per provider, clear-all global, pruneExpired", () => {
markPoolUnfit("p1", "freebuff::m1", Date.now() + 60_000);
markPoolUnfit("p1", "kiro::m3", Date.now() + 60_000);