Resolve conflicts

This commit is contained in:
diegosouzapw
2026-04-29 08:55:12 -03:00
76 changed files with 2676 additions and 499 deletions

View File

@@ -167,7 +167,7 @@ async function mockHealthPageApis(page: Page) {
});
});
await page.route("**/api/v1/db/health", async (route) => {
await page.route("**/api/db/health", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",

View File

@@ -46,6 +46,10 @@ test("cloakAntigravityToolPayload cloaks custom tools, preserves native tools an
assert.ok(names.includes(`workspace_read${AG_TOOL_SUFFIX}`));
assert.ok(names.includes("run_command"));
assert.ok(names.includes("browser_subagent"));
assert.ok(names.includes("mcp_sequential_thinking_sequentialthinking"));
for (const name of names) {
assert.match(name, /^[a-zA-Z0-9_]+$/);
}
assert.equal(
result.body.request.contents[0].parts[0].functionCall.name,
`workspace_read${AG_TOOL_SUFFIX}`

View File

@@ -153,6 +153,7 @@ const cases: Case[] = [
expectedClass: "MANAGEMENT",
},
{ name: "/api/keys MANAGEMENT", path: "/api/keys", expectedClass: "MANAGEMENT" },
{ name: "/api/db/health MANAGEMENT", path: "/api/db/health", expectedClass: "MANAGEMENT" },
{ name: "/api/settings MANAGEMENT", path: "/api/settings", expectedClass: "MANAGEMENT" },
{ name: "/api/audit MANAGEMENT", path: "/api/audit", expectedClass: "MANAGEMENT" },

View File

@@ -174,6 +174,20 @@ test("runAuthzPipeline allows dashboard sessions to read model catalog aliases",
assert.equal(response.headers.get("x-omniroute-route-class"), "CLIENT_API");
});
test("runAuthzPipeline allows dashboard sessions to reach DB health management API", async () => {
await forceAuthRequired();
const response = await pipeline.runAuthzPipeline(
request("http://localhost/api/db/health", {
headers: { cookie: await dashboardCookie() },
}),
{ enforce: true }
);
assert.equal(response.status, 200);
assert.equal(response.headers.get("x-omniroute-route-class"), "MANAGEMENT");
});
test("runAuthzPipeline refreshes dashboard JWTs near expiry", async () => {
await forceAuthRequired();
const secret = new TextEncoder().encode(process.env.JWT_SECRET);

View File

@@ -226,7 +226,7 @@ test("getCliRuntimeStatus ignores suspicious known-path binaries and symlink esc
const suspiciousStatus = await cliRuntime.getCliRuntimeStatus("qoder");
assert.equal(suspiciousStatus.installed, false);
assert.equal(suspiciousStatus.reason, "not_found");
assert.equal(suspiciousStatus.reason, "suspicious_size");
if (process.platform !== "win32") {
const escapePrefix = createTempDir("omniroute-cli-escape-");
@@ -246,7 +246,7 @@ test("getCliRuntimeStatus ignores suspicious known-path binaries and symlink esc
const escapedStatus = await escapedRuntime.getCliRuntimeStatus("qoder");
assert.equal(escapedStatus.installed, false);
assert.equal(escapedStatus.reason, "not_found");
assert.equal(escapedStatus.reason, "symlink_escape");
}
});

View File

@@ -3,29 +3,44 @@ import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { SignJWT } from "jose";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-db-health-route-"));
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "task-303-api-key-secret";
const core = await import("../../src/lib/db/core.ts");
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const routeModule = await import("../../src/app/api/v1/db/health/route.ts");
const routeModule = await import("../../src/app/api/db/health/route.ts");
const ORIGINAL_JWT_SECRET = process.env.JWT_SECRET;
const ORIGINAL_INITIAL_PASSWORD = process.env.INITIAL_PASSWORD;
const TEST_JWT_SECRET = "db-health-route-jwt-secret";
const TEST_INITIAL_PASSWORD = "db-health-route-password";
async function resetStorage() {
core.resetDbInstance();
apiKeysDb.resetApiKeyState();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
process.env.JWT_SECRET = TEST_JWT_SECRET;
process.env.INITIAL_PASSWORD = TEST_INITIAL_PASSWORD;
}
function makeRequest(method, token) {
return new Request("http://localhost/api/v1/db/health", {
function makeRequest(method, cookie) {
return new Request("http://localhost/api/db/health", {
method,
headers: token ? { Authorization: `Bearer ${token}` } : {},
headers: cookie ? { cookie } : {},
});
}
async function dashboardCookie() {
const secret = new TextEncoder().encode(TEST_JWT_SECRET);
const token = await new SignJWT({ authenticated: true })
.setProtectedHeader({ alg: "HS256" })
.setExpirationTime("1h")
.sign(secret);
return `auth_token=${token}`;
}
function insertBrokenRows(db) {
db.prepare(
`INSERT INTO quota_snapshots
@@ -43,35 +58,27 @@ test.beforeEach(async () => {
test.after(async () => {
core.resetDbInstance();
apiKeysDb.resetApiKeyState();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
if (ORIGINAL_JWT_SECRET === undefined) delete process.env.JWT_SECRET;
else process.env.JWT_SECRET = ORIGINAL_JWT_SECRET;
if (ORIGINAL_INITIAL_PASSWORD === undefined) delete process.env.INITIAL_PASSWORD;
else process.env.INITIAL_PASSWORD = ORIGINAL_INITIAL_PASSWORD;
});
test("GET /api/v1/db/health requires authentication", async () => {
const previousInitialPassword = process.env.INITIAL_PASSWORD;
process.env.INITIAL_PASSWORD = "route-health-auth";
test("GET /api/db/health requires authentication", async () => {
const response = await routeModule.GET(makeRequest("GET"));
const body = (await response.json()) as any;
try {
const response = await routeModule.GET(makeRequest("GET"));
const body = (await response.json()) as any;
assert.equal(response.status, 401);
assert.equal(body.error.message, "Authentication required");
} finally {
if (previousInitialPassword === undefined) {
delete process.env.INITIAL_PASSWORD;
} else {
process.env.INITIAL_PASSWORD = previousInitialPassword;
}
}
assert.equal(response.status, 401);
assert.equal(body.error.message, "Authentication required");
});
test("GET /api/v1/db/health diagnoses without mutating database rows", async () => {
const authKey = await apiKeysDb.createApiKey("Health Route", "machine-route-health");
test("GET /api/db/health diagnoses without mutating database rows", async () => {
const cookie = await dashboardCookie();
const db = core.getDbInstance();
insertBrokenRows(db);
const response = await routeModule.GET(makeRequest("GET", authKey.key));
const response = await routeModule.GET(makeRequest("GET", cookie));
const body = (await response.json()) as any;
assert.equal(response.status, 200);
@@ -81,12 +88,12 @@ test("GET /api/v1/db/health diagnoses without mutating database rows", async ()
assert.equal((db.prepare("SELECT COUNT(*) AS count FROM domain_budgets").get() as any).count, 1);
});
test("POST /api/v1/db/health repairs broken rows for authenticated callers", async () => {
const authKey = await apiKeysDb.createApiKey("Health Route", "machine-route-health");
test("POST /api/db/health repairs broken rows for authenticated callers", async () => {
const cookie = await dashboardCookie();
const db = core.getDbInstance();
insertBrokenRows(db);
const response = await routeModule.POST(makeRequest("POST", authKey.key));
const response = await routeModule.POST(makeRequest("POST", cookie));
const body = (await response.json()) as any;
assert.equal(response.status, 200);

View File

@@ -115,7 +115,7 @@ test("replaceCustomModels preserves compat fields and respects the empty-list gu
{
id: "gpt-4.1",
name: "GPT-4.1 Refreshed",
source: "api-sync",
source: "imported",
supportsThinking: true,
},
]);
@@ -147,12 +147,12 @@ test("removing a custom model also removes its compat override", async () => {
test("synced available models are unioned across connections and cleaned per connection", async () => {
await modelsDb.replaceSyncedAvailableModelsForConnection("openai", "conn-a", [
{ id: "gpt-4.1", name: "GPT-4.1", source: "api-sync" },
{ id: "gpt-4.1-mini", name: "GPT-4.1 Mini", source: "api-sync" },
{ id: "gpt-4.1", name: "GPT-4.1", source: "imported" },
{ id: "gpt-4.1-mini", name: "GPT-4.1 Mini", source: "imported" },
]);
const union = await modelsDb.replaceSyncedAvailableModelsForConnection("openai", "conn-b", [
{ id: "gpt-4.1-mini", name: "GPT-4.1 Mini", source: "api-sync" },
{ id: "o3-mini", name: "o3-mini", source: "api-sync" },
{ id: "gpt-4.1-mini", name: "GPT-4.1 Mini", source: "imported" },
{ id: "o3-mini", name: "o3-mini", source: "imported" },
]);
const remaining = await modelsDb.deleteSyncedAvailableModelsForConnection("openai", "conn-a");
const allProviders = await modelsDb.getAllSyncedAvailableModels();

View File

@@ -12,6 +12,10 @@ import {
isCodexResponsesWebSocketRequired,
parseCodexQuotaHeaders,
} from "../../open-sse/executors/codex.ts";
import {
clearRememberedResponseFunctionCallsForTesting,
rememberResponseFunctionCalls,
} from "../../open-sse/services/responsesToolCallState.ts";
import {
DEFAULT_THINKING_CONFIG,
setThinkingBudgetConfig,
@@ -22,6 +26,7 @@ import { CODEX_CHAT_DEFAULT_INSTRUCTIONS } from "../../open-sse/config/codexInst
test.afterEach(() => {
setThinkingBudgetConfig(DEFAULT_THINKING_CONFIG);
__setCodexWebSocketTransportForTesting(undefined);
clearRememberedResponseFunctionCallsForTesting();
});
async function withEnv(entries: Record<string, string | undefined>, fn: () => any) {
@@ -296,6 +301,48 @@ test("CodexExecutor.transformRequest preserves store-enabled responses state whe
assert.equal(result.previous_response_id, "resp_prev_123");
});
test("CodexExecutor.transformRequest rehydrates missing function_call items for stateful tool outputs", () => {
const executor = new CodexExecutor();
rememberResponseFunctionCalls("resp_prev_tool_123", [
{
type: "function_call",
call_id: "call_tool_123",
name: "workspace_read_file",
arguments: '{"path":"README.md"}',
},
]);
const body = {
_nativeCodexPassthrough: true,
previous_response_id: "resp_prev_tool_123",
input: [
{
type: "function_call_output",
call_id: "call_tool_123",
output: '{"ok":true}',
},
],
stream: false,
};
const result = executor.transformRequest("gpt-5.5-low", body, false, {
requestEndpointPath: "/responses",
});
assert.equal(result.previous_response_id, undefined);
assert.equal(result.store, false);
assert.deepEqual(result.input[0], {
type: "function_call",
call_id: "call_tool_123",
name: "workspace_read_file",
arguments: '{"path":"README.md"}',
});
assert.deepEqual(result.input[1], {
type: "function_call_output",
call_id: "call_tool_123",
output: '{"ok":true}',
});
});
test("CodexExecutor.transformRequest applies per-connection reasoning and service tier defaults", () => {
const executor = new CodexExecutor();
const result = executor.transformRequest(

View File

@@ -67,10 +67,10 @@ test("GET /api/memory filters by q and returns matching stats", async () => {
assert.equal(response.status, 200);
const body = (await response.json()) as any;
assert.deepEqual(
body.data.map((memory) => memory.key),
["typescript:tooling", "typescript:guide"]
);
assert.deepEqual(body.data.map((memory) => memory.key).sort(), [
"typescript:guide",
"typescript:tooling",
]);
assert.equal(body.total, 2);
assert.equal(body.stats.total, 2);
assert.deepEqual(body.stats.byType, { factual: 1, semantic: 1 });

View File

@@ -177,18 +177,21 @@ test("listMemories filters by api key, type and session while preserving newest-
test("listMemories supports limit and offset pagination even when only offset is provided", async () => {
insertMemoryRow({
id: "page-1",
key: "pagination:1",
content: "oldest",
createdAt: "2026-04-01T00:00:00.000Z",
updatedAt: "2026-04-01T00:00:00.000Z",
});
insertMemoryRow({
id: "page-2",
key: "pagination:2",
content: "middle",
createdAt: "2026-04-02T00:00:00.000Z",
updatedAt: "2026-04-02T00:00:00.000Z",
});
insertMemoryRow({
id: "page-3",
key: "pagination:3",
content: "newest",
createdAt: "2026-04-03T00:00:00.000Z",
updatedAt: "2026-04-03T00:00:00.000Z",
@@ -251,18 +254,21 @@ test("listMemories applies query filtering before pagination and type stats", as
test("listMemories supports page-based pagination (page 1)", async () => {
insertMemoryRow({
id: "pg-1",
key: "page:test:1",
content: "first",
createdAt: "2026-04-01T00:00:00.000Z",
updatedAt: "2026-04-01T00:00:00.000Z",
});
insertMemoryRow({
id: "pg-2",
key: "page:test:2",
content: "second",
createdAt: "2026-04-02T00:00:00.000Z",
updatedAt: "2026-04-02T00:00:00.000Z",
});
insertMemoryRow({
id: "pg-3",
key: "page:test:3",
content: "third",
createdAt: "2026-04-03T00:00:00.000Z",
updatedAt: "2026-04-03T00:00:00.000Z",
@@ -279,18 +285,21 @@ test("listMemories supports page-based pagination (page 1)", async () => {
test("listMemories supports page-based pagination (page 2 returns remainder)", async () => {
insertMemoryRow({
id: "pg-1",
key: "page:test:1",
content: "first",
createdAt: "2026-04-01T00:00:00.000Z",
updatedAt: "2026-04-01T00:00:00.000Z",
});
insertMemoryRow({
id: "pg-2",
key: "page:test:2",
content: "second",
createdAt: "2026-04-02T00:00:00.000Z",
updatedAt: "2026-04-02T00:00:00.000Z",
});
insertMemoryRow({
id: "pg-3",
key: "page:test:3",
content: "third",
createdAt: "2026-04-03T00:00:00.000Z",
updatedAt: "2026-04-03T00:00:00.000Z",
@@ -307,6 +316,7 @@ test("listMemories supports page-based pagination (page 2 returns remainder)", a
test("listMemories returns empty data for a page beyond the result set", async () => {
insertMemoryRow({
id: "pg-1",
key: "page:test:1",
content: "only entry",
createdAt: "2026-04-01T00:00:00.000Z",
updatedAt: "2026-04-01T00:00:00.000Z",
@@ -320,12 +330,14 @@ test("listMemories returns empty data for a page beyond the result set", async (
test("listMemories page parameter defaults to page 1 when omitted with limit", async () => {
insertMemoryRow({
id: "pg-1",
key: "page:test:1",
content: "first",
createdAt: "2026-04-01T00:00:00.000Z",
updatedAt: "2026-04-01T00:00:00.000Z",
});
insertMemoryRow({
id: "pg-2",
key: "page:test:2",
content: "second",
createdAt: "2026-04-02T00:00:00.000Z",
updatedAt: "2026-04-02T00:00:00.000Z",

View File

@@ -9,8 +9,8 @@ import {
test("model catalog source normalization groups manual and synced rows separately", () => {
assert.equal(normalizeModelCatalogSource("manual"), "custom");
assert.equal(normalizeModelCatalogSource("imported"), "api-sync");
assert.equal(normalizeModelCatalogSource("api-sync"), "api-sync");
assert.equal(normalizeModelCatalogSource("imported"), "imported");
assert.equal(normalizeModelCatalogSource("api-sync"), "imported");
assert.equal(normalizeModelCatalogSource("fallback"), "fallback");
assert.equal(normalizeModelCatalogSource("alias"), "alias");
assert.equal(normalizeModelCatalogSource(undefined), "system");
@@ -19,7 +19,7 @@ test("model catalog source normalization groups manual and synced rows separatel
test("model catalog source labels stay user-facing", () => {
assert.equal(getModelCatalogSourceLabel("system"), "Built-in");
assert.equal(getModelCatalogSourceLabel("custom"), "Custom");
assert.equal(getModelCatalogSourceLabel("api-sync"), "Synced");
assert.equal(getModelCatalogSourceLabel("imported"), "Imported");
assert.equal(getModelCatalogSourceLabel("fallback"), "Fallback");
assert.equal(getModelCatalogSourceLabel("alias"), "Alias");
});
@@ -29,7 +29,7 @@ test("model catalog query matches id, display name, alias and source label", ()
modelId: "qwen/qwen3-coder-480b-a35b-instruct",
modelName: "Qwen3 Coder 480B",
alias: "best-qwen",
source: "api-sync",
source: "imported",
};
assert.equal(matchesModelCatalogQuery("", target), true);

View File

@@ -5,9 +5,9 @@ const { getModelCatalogSourceLabel, normalizeModelCatalogSource } =
await import("../../src/shared/utils/modelCatalogSearch.ts");
test("model catalog source normalizes synced import variants consistently", () => {
assert.equal(normalizeModelCatalogSource("api-sync"), "api-sync");
assert.equal(normalizeModelCatalogSource("auto-sync"), "api-sync");
assert.equal(normalizeModelCatalogSource("imported"), "api-sync");
assert.equal(getModelCatalogSourceLabel("auto-sync"), "Synced");
assert.equal(getModelCatalogSourceLabel("imported"), "Synced");
assert.equal(normalizeModelCatalogSource("api-sync"), "imported");
assert.equal(normalizeModelCatalogSource("imported"), "imported");
assert.equal(normalizeModelCatalogSource("auto-sync"), "imported");
assert.equal(getModelCatalogSourceLabel("auto-sync"), "Imported");
assert.equal(getModelCatalogSourceLabel("imported"), "Imported");
});

View File

@@ -53,11 +53,11 @@ test("model sync route skips success log when fetched models do not change store
apiKey: "test-key",
});
await modelsDb.replaceCustomModels("openrouter", [
await modelsDb.replaceSyncedAvailableModelsForConnection("openrouter", connection.id, [
{
id: "custom-model-1",
name: "Custom Model 1",
source: "auto-sync",
source: "imported",
},
]);
@@ -85,6 +85,7 @@ test("model sync route skips success log when fetched models do not change store
const body = (await response.json()) as any;
assert.equal(body.logged, false);
assert.deepEqual(body.modelChanges, { added: 0, removed: 0, updated: 0, total: 0 });
assert.deepEqual(body.models, []);
const logs = await callLogs.getCallLogs({ model: "model-sync", limit: 10 });
assert.equal(logs.length, 0);
@@ -250,6 +251,45 @@ test("model sync route falls back to the upstream HTTP status when the models pa
assert.equal(logs[0].error, "HTTP 429");
});
test("model sync route reports invalid JSON /models responses without losing upstream status", async () => {
await resetStorage();
const connection = await providersDb.createProviderConnection({
provider: "openrouter",
authType: "apikey",
name: "Invalid JSON Sync",
apiKey: "test-key",
});
globalThis.fetch = async (url) => {
assert.equal(
String(url),
`http://localhost/api/providers/${connection.id}/models?refresh=true`
);
return new Response("<html>bad gateway</html>", {
status: 200,
headers: { "content-type": "text/html" },
});
};
const response = await modelSyncRoute.POST(
new Request(`http://localhost/api/providers/${connection.id}/sync-models`, {
method: "POST",
headers: scheduler.buildModelSyncInternalHeaders(),
}),
{ params: { id: connection.id } }
);
const body = (await response.json()) as any;
const logs = await callLogs.getCallLogs({ model: "model-sync", limit: 10 });
assert.equal(response.status, 502);
assert.equal(body.error, "Invalid JSON response from /models");
assert.equal(body.upstreamStatus, 200);
assert.equal(logs.length, 1);
assert.equal(logs[0].status, 200);
assert.equal(logs[0].error, "Invalid JSON response from /models");
});
test("model sync route preserves previously synced models when the upstream omits the models list", async () => {
await resetStorage();
@@ -260,11 +300,11 @@ test("model sync route preserves previously synced models when the upstream omit
apiKey: "test-key",
});
await modelsDb.replaceCustomModels("openrouter", [
await modelsDb.replaceSyncedAvailableModelsForConnection("openrouter", connection.id, [
{
id: "persisted-model",
name: "Persisted Model",
source: "auto-sync",
source: "imported",
},
]);
@@ -290,13 +330,12 @@ test("model sync route preserves previously synced models when the upstream omit
assert.equal(body.syncedModels, 1);
assert.equal(body.logged, false);
assert.deepEqual(body.modelChanges, { added: 0, removed: 0, updated: 0, total: 0 });
assert.deepEqual(body.models, [
assert.deepEqual(body.models, []);
assert.deepEqual(await modelsDb.getSyncedAvailableModels("openrouter"), [
{
id: "persisted-model",
name: "Persisted Model",
source: "auto-sync",
apiFormat: "chat-completions",
supportedEndpoints: ["chat"],
source: "imported",
},
]);
assert.equal(logs.length, 0);
@@ -348,24 +387,12 @@ test("model sync route writes synced available models for Gemini connections", a
assert.equal(body.syncedModels, 1);
assert.equal(body.logged, true);
assert.deepEqual(body.modelChanges, { added: 1, removed: 0, updated: 0, total: 1 });
assert.deepEqual(body.models, [
{
id: "gemini-custom-preview",
name: "Gemini Custom Preview",
source: "api-sync",
apiFormat: "chat-completions",
supportedEndpoints: ["chat", "embeddings"],
inputTokenLimit: 32768,
outputTokenLimit: 8192,
description: "Custom Gemini preview model",
supportsThinking: true,
},
]);
assert.deepEqual(body.models, []);
assert.deepEqual(synced, [
{
id: "gemini-custom-preview",
name: "Gemini Custom Preview",
source: "api-sync",
source: "imported",
supportedEndpoints: ["chat", "embeddings"],
inputTokenLimit: 32768,
outputTokenLimit: 8192,
@@ -421,7 +448,7 @@ test("model sync route writes synced available models for non-Gemini providers t
{
id: "glm-5.1",
name: "GLM 5.1",
source: "api-sync",
source: "imported",
supportedEndpoints: ["chat"],
inputTokenLimit: 262144,
},
@@ -439,6 +466,7 @@ test("model sync route import mode merges discovered models without deleting man
});
await modelsDb.addCustomModel("openrouter", "manual-only", "Manual Only", "manual");
await modelsDb.addCustomModel("openrouter", "router-v4", "Manual Router V4", "manual");
await localDb.setModelAlias("manual-only", "openrouter/manual-only");
globalThis.fetch = async (url) => {
@@ -467,16 +495,21 @@ test("model sync route import mode merges discovered models without deleting man
assert.equal(body.updatedCount, 0);
assert.equal(body.syncedAliases, 1);
assert.deepEqual(body.modelChanges, { added: 1, removed: 0, updated: 0, total: 1 });
assert.deepEqual(body.customModelChanges, { added: 0, removed: 1, updated: 0, total: 1 });
assert.deepEqual(
body.models.map((model) => ({ id: model.id, source: model.source })),
[
{ id: "manual-only", source: "manual" },
{ id: "router-v4", source: "api-sync" },
]
[{ id: "manual-only", source: "manual" }]
);
assert.deepEqual(
body.importedModels.map((model) => ({ id: model.id, source: model.source })),
[{ id: "router-v4", source: "api-sync" }]
[{ id: "router-v4", source: "imported" }]
);
assert.deepEqual(
(await modelsDb.getSyncedAvailableModels("openrouter")).map((model) => ({
id: model.id,
source: model.source,
})),
[{ id: "router-v4", source: "imported" }]
);
assert.equal(aliases["manual-only"], "openrouter/manual-only");
assert.equal(aliases["router-v4"], "openrouter/router-v4");
@@ -492,12 +525,11 @@ test("model sync route import mode ignores supported endpoint ordering changes",
apiKey: "test-key",
});
await modelsDb.replaceCustomModels("openrouter", [
await modelsDb.replaceSyncedAvailableModelsForConnection("openrouter", connection.id, [
{
id: "router-v4",
name: "Router V4",
source: "api-sync",
apiFormat: "chat-completions",
source: "imported",
supportedEndpoints: ["chat", "embeddings"],
},
]);
@@ -536,12 +568,13 @@ test("model sync route import mode ignores supported endpoint ordering changes",
assert.equal(body.logged, false);
assert.deepEqual(body.importedModels, []);
assert.deepEqual(
body.models.map((model) => ({
(await modelsDb.getSyncedAvailableModels("openrouter")).map((model) => ({
id: model.id,
supportedEndpoints: model.supportedEndpoints,
})),
[{ id: "router-v4", supportedEndpoints: ["chat", "embeddings"] }]
);
assert.deepEqual(body.models, []);
assert.equal(logs.length, 0);
});
@@ -555,12 +588,11 @@ test("model sync route import mode reports updates without counting them as new
apiKey: "test-key",
});
await modelsDb.replaceCustomModels("openrouter", [
await modelsDb.replaceSyncedAvailableModelsForConnection("openrouter", connection.id, [
{
id: "router-v4",
name: "Router V4",
source: "api-sync",
apiFormat: "chat-completions",
source: "imported",
supportedEndpoints: ["chat"],
},
]);
@@ -597,7 +629,7 @@ test("model sync route import mode reports updates without counting them as new
assert.deepEqual(body.importedChanges, { added: 0, updated: 1, unchanged: 0, total: 1 });
assert.deepEqual(body.importedModels, []);
assert.deepEqual(
body.models.map((model) => ({
(await modelsDb.getSyncedAvailableModels("openrouter")).map((model) => ({
id: model.id,
name: model.name,
supportedEndpoints: model.supportedEndpoints,
@@ -610,6 +642,7 @@ test("model sync route import mode reports updates without counting them as new
},
]
);
assert.deepEqual(body.models, []);
assert.equal(body.logged, true);
assert.equal(logs.length, 1);
});
@@ -624,16 +657,16 @@ test("model sync route records added, removed, and updated model diffs with fall
accessToken: "sync-token",
});
await modelsDb.replaceCustomModels("openrouter", [
await modelsDb.replaceSyncedAvailableModelsForConnection("openrouter", connection.id, [
{
id: "persisted-model",
name: "Persisted Model",
source: "auto-sync",
source: "imported",
},
{
id: "removed-model",
name: "Removed Model",
source: "auto-sync",
source: "imported",
},
]);
@@ -673,7 +706,7 @@ test("model sync route records added, removed, and updated model diffs with fall
assert.equal(body.logged, true);
assert.deepEqual(body.modelChanges, { added: 1, removed: 1, updated: 1, total: 3 });
assert.deepEqual(
body.models.map((model) => ({
(await modelsDb.getSyncedAvailableModels("openrouter")).map((model) => ({
id: model.id,
name: model.name,
supportedEndpoints: model.supportedEndpoints,
@@ -689,7 +722,7 @@ test("model sync route records added, removed, and updated model diffs with fall
{
id: "fallback-model",
name: "Fallback Model",
supportedEndpoints: ["chat"],
supportedEndpoints: undefined,
description: "Fallback from model field",
},
]
@@ -753,17 +786,12 @@ test("model sync route forwards cookies, filters built-ins, and syncs aliases fo
assert.equal(body.provider, "openrouter");
assert.equal(body.syncedModels, 3);
assert.equal(body.availableModelsCount, 3);
assert.equal(body.syncedAliases, 2);
assert.equal(body.syncedAliases, 3);
assert.equal(body.logged, true);
assert.deepEqual(body.modelChanges, { added: 2, removed: 0, updated: 0, total: 2 });
assert.deepEqual(
body.models.map((model) => ({ id: model.id, name: model.name })),
[
{ id: "router-v2", name: "Router V2" },
{ id: "router-v3", name: "Router V3" },
]
);
assert.deepEqual(body.modelChanges, { added: 3, removed: 0, updated: 0, total: 3 });
assert.deepEqual(body.models, []);
assert.equal(aliases["stale-model"], undefined);
assert.equal(aliases["auto"], "openrouter/auto");
assert.equal(aliases["openrouter-router-v2"], "openrouter/router-v2");
assert.equal(aliases["router-v3"], "openrouter/router-v3");
assert.equal(logs.length, 1);
@@ -782,6 +810,7 @@ test("model sync route reports synced managed models separately from preserved m
});
await modelsDb.addCustomModel("openrouter", "manual-only", "Manual Only", "manual");
await modelsDb.addCustomModel("openrouter", "router-v4", "Manual Router V4", "manual");
globalThis.fetch = async (url) => {
assert.equal(
@@ -807,12 +836,17 @@ test("model sync route reports synced managed models separately from preserved m
assert.equal(body.availableModelsCount, 2);
assert.equal(body.importedCount, 1);
assert.equal(body.updatedCount, 0);
assert.deepEqual(body.customModelChanges, { added: 0, removed: 1, updated: 0, total: 1 });
assert.deepEqual(
body.models.map((model) => ({ id: model.id, source: model.source })),
[
{ id: "manual-only", source: "manual" },
{ id: "router-v4", source: "api-sync" },
]
[{ id: "manual-only", source: "manual" }]
);
assert.deepEqual(
(await modelsDb.getSyncedAvailableModels("openrouter")).map((model) => ({
id: model.id,
source: model.source,
})),
[{ id: "router-v4", source: "imported" }]
);
});
@@ -868,33 +902,71 @@ test("model sync route uses provider-node prefixes when syncing compatible-provi
assert.equal(aliases["cm-sonnet-4-6"], "anthropic-compatible-demo/sonnet-4-6");
});
test("model sync route returns 500 and records a failure when the internal models fetch throws", async () => {
test("model sync route falls back to in-process discovery when internal self-fetch throws", async () => {
await resetStorage();
const connection = await providersDb.createProviderConnection({
provider: "openrouter",
provider: "openai-compatible-aio",
authType: "apikey",
name: "Exploding Sync",
name: "AIO Import",
apiKey: "test-key",
providerSpecificData: {
prefix: "aio",
apiType: "chat",
baseUrl: "https://api.bltcy.ai/v1",
nodeName: "aio",
autoSync: true,
},
});
globalThis.fetch = async () => {
throw new Error("network exploded");
const fetchCalls: string[] = [];
globalThis.fetch = async (url) => {
const urlString = String(url);
fetchCalls.push(urlString);
if (urlString === `http://localhost/api/providers/${connection.id}/models?refresh=true`) {
throw new Error("fetch failed");
}
assert.equal(urlString, "https://api.bltcy.ai/v1/models");
return Response.json({
data: [{ id: "aio-model", name: "AIO Model" }],
});
};
const response = await modelSyncRoute.POST(
new Request(`http://localhost/api/providers/${connection.id}/sync-models`, {
new Request(`http://localhost/api/providers/${connection.id}/sync-models?mode=import`, {
method: "POST",
headers: scheduler.buildModelSyncInternalHeaders(),
}),
{ params: { id: connection.id } }
);
const body = (await response.json()) as any;
const logs = await callLogs.getCallLogs({ model: "model-sync", limit: 10 });
const body = (await response.json()) as {
importedCount: number;
importedModels: Array<{ id: string; source: string }>;
};
const customModels = (await modelsDb.getCustomModels("openai-compatible-aio")) as Array<{
id: string;
source: string;
}>;
const availableModels = await modelsDb.getSyncedAvailableModels("openai-compatible-aio");
assert.equal(response.status, 500);
assert.equal(body.error, "network exploded");
assert.equal(logs.length, 1);
assert.equal(logs[0].status, 500);
assert.equal(logs[0].provider, "openrouter");
assert.equal(response.status, 200);
assert.equal(body.importedCount, 1);
assert.deepEqual(
body.importedModels.map((model) => ({ id: model.id, source: model.source })),
[{ id: "aio-model", source: "imported" }]
);
assert.deepEqual(
customModels.map((model) => ({ id: model.id, source: model.source })),
[]
);
assert.deepEqual(
availableModels.map((model) => ({ id: model.id, source: model.source })),
[{ id: "aio-model", source: "imported" }]
);
assert.deepEqual(fetchCalls, [
`http://localhost/api/providers/${connection.id}/models?refresh=true`,
"https://api.bltcy.ai/v1/models",
]);
});

View File

@@ -354,21 +354,21 @@ test("v1 models catalog includes synced Gemini models and duplicates audio model
{
id: "gemini-audio-live",
name: "Gemini Audio Live",
source: "api-sync",
source: "imported",
supportedEndpoints: ["audio"],
inputTokenLimit: 4096,
},
{
id: "text-embedding-004",
name: "Text Embedding 004",
source: "api-sync",
source: "imported",
supportedEndpoints: ["embeddings"],
inputTokenLimit: 2048,
},
{
id: "gemini-hidden",
name: "Gemini Hidden",
source: "api-sync",
source: "imported",
supportedEndpoints: ["chat"],
},
]
@@ -402,7 +402,7 @@ test("v1 models catalog keeps Gemini chat models untyped when synced endpoints a
{
id: "gemini-2.5-pro-live",
name: "Gemini 2.5 Pro Live",
source: "api-sync",
source: "imported",
inputTokenLimit: 8192,
},
]);
@@ -430,7 +430,7 @@ test("v1 models catalog includes synced non-Gemini provider models from discover
{
id: "glm-5.1",
name: "GLM 5.1",
source: "api-sync",
source: "imported",
supportedEndpoints: ["chat"],
inputTokenLimit: 262144,
},

View File

@@ -0,0 +1,123 @@
import test from "node:test";
import assert from "node:assert/strict";
import { kiro } from "@/lib/oauth/providers/kiro";
test("kiro.requestDeviceCode returns resolved region for IDC token endpoint", async () => {
const originalFetch = global.fetch;
const fetchCalls: Array<{ url: string; body?: string }> = [];
global.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
const url = String(input);
fetchCalls.push({ url, body: typeof init?.body === "string" ? init.body : undefined });
if (url.endsWith("/client/register")) {
return new Response(JSON.stringify({ clientId: "client-ap", clientSecret: "secret-ap" }), {
status: 200,
});
}
if (url.endsWith("/device_authorization")) {
return new Response(
JSON.stringify({
deviceCode: "dev-code",
userCode: "user-code",
verificationUri: "https://d-tenant.awsapps.com/start/#/device",
verificationUriComplete: "https://d-tenant.awsapps.com/start/#/device?user_code=ABCD",
expiresIn: 600,
interval: 1,
}),
{ status: 200 }
);
}
return new Response("not-found", { status: 404 });
}) as typeof fetch;
try {
const result = await kiro.requestDeviceCode({
registerClientUrl: "https://oidc.ap-southeast-1.amazonaws.com/client/register",
deviceAuthUrl: "https://oidc.ap-southeast-1.amazonaws.com/device_authorization",
tokenUrl: "https://oidc.ap-southeast-1.amazonaws.com/token",
startUrl: "https://d-tenant.awsapps.com/start",
clientName: "kiro-oauth-client",
clientType: "public",
scopes: ["codewhisperer:completions"],
grantTypes: ["urn:ietf:params:oauth:grant-type:device_code", "refresh_token"],
skipIssuerUrlForRegistration: true,
});
assert.equal(result._region, "ap-southeast-1");
assert.equal(result._clientId, "client-ap");
assert.equal(result._clientSecret, "secret-ap");
const registerBody = JSON.parse(fetchCalls[0]?.body || "{}");
assert.equal(registerBody.issuerUrl, undefined);
} finally {
global.fetch = originalFetch;
}
});
test("kiro.pollToken uses region provided by extraData", async () => {
const originalFetch = global.fetch;
let requestedUrl = "";
global.fetch = (async (input: RequestInfo | URL) => {
requestedUrl = String(input);
return new Response(
JSON.stringify({
accessToken: "access",
refreshToken: "refresh",
expiresIn: 3600,
}),
{ status: 200 }
);
}) as typeof fetch;
try {
const result = await kiro.pollToken(
{ tokenUrl: "https://oidc.us-east-1.amazonaws.com/token" },
"device-code",
null,
{ _clientId: "cid", _clientSecret: "csecret", _region: "ap-southeast-1" }
);
assert.equal(requestedUrl, "https://oidc.ap-southeast-1.amazonaws.com/token");
assert.equal(result.ok, true);
assert.equal(result.data.access_token, "access");
assert.equal(result.data._region, "ap-southeast-1");
} finally {
global.fetch = originalFetch;
}
});
test("kiro.mapTokens persists region into providerSpecificData", () => {
const mapped = kiro.mapTokens({
access_token: "at",
refresh_token: "rt",
expires_in: 3600,
_clientId: "cid",
_clientSecret: "csec",
_region: "ap-southeast-1",
});
assert.equal(mapped.accessToken, "at");
assert.equal(mapped.refreshToken, "rt");
assert.equal(mapped.expiresIn, 3600);
assert.equal(mapped.providerSpecificData.clientId, "cid");
assert.equal(mapped.providerSpecificData.clientSecret, "csec");
assert.equal(mapped.providerSpecificData.region, "ap-southeast-1");
});
test("kiro.mapTokens defaults region to undefined when not provided", () => {
const mapped = kiro.mapTokens({
access_token: "at",
refresh_token: "rt",
expires_in: 3600,
_clientId: "cid",
_clientSecret: "csec",
});
assert.equal(mapped.providerSpecificData.region, undefined);
});

View File

@@ -461,7 +461,7 @@ test("provider models route caches discovered opencode-go models per connection"
assert.equal(firstResponse.status, 200);
assert.equal(firstBody.source, "api");
assert.deepEqual(firstBody.models, [{ id: "glm-5.1", name: "GLM 5.1" }]);
assert.deepEqual(cachedModels, [{ id: "glm-5.1", name: "GLM 5.1", source: "api-sync" }]);
assert.deepEqual(cachedModels, [{ id: "glm-5.1", name: "GLM 5.1", source: "imported" }]);
globalThis.fetch = async () => {
throw new Error("cached route should not hit upstream");
@@ -472,7 +472,7 @@ test("provider models route caches discovered opencode-go models per connection"
assert.equal(cachedResponse.status, 200);
assert.equal(cachedBody.source, "cache");
assert.deepEqual(cachedBody.models, [{ id: "glm-5.1", name: "GLM 5.1", source: "api-sync" }]);
assert.deepEqual(cachedBody.models, [{ id: "glm-5.1", name: "GLM 5.1", source: "imported" }]);
assert.equal(fetchCalls, 1);
});
@@ -481,7 +481,7 @@ test("provider models route falls back to cached models when a refresh fails", a
apiKey: "opencode-go-key",
});
await modelsDb.replaceSyncedAvailableModelsForConnection("opencode-go", connection.id, [
{ id: "cached-go", name: "Cached Go", source: "api-sync" },
{ id: "cached-go", name: "Cached Go", source: "imported" },
]);
let fetchCalls = 0;
@@ -496,7 +496,7 @@ test("provider models route falls back to cached models when a refresh fails", a
assert.equal(response.status, 200);
assert.equal(body.source, "cache");
assert.match(body.warning, /cached catalog/i);
assert.deepEqual(body.models, [{ id: "cached-go", name: "Cached Go", source: "api-sync" }]);
assert.deepEqual(body.models, [{ id: "cached-go", name: "Cached Go", source: "imported" }]);
assert.equal(fetchCalls, 1);
});
@@ -505,7 +505,7 @@ test("provider models route clears cached discovery when a refresh returns no re
apiKey: "opencode-go-key",
});
await modelsDb.replaceSyncedAvailableModelsForConnection("opencode-go", connection.id, [
{ id: "cached-go", name: "Cached Go", source: "api-sync" },
{ id: "cached-go", name: "Cached Go", source: "imported" },
]);
globalThis.fetch = async () => {

View File

@@ -6,6 +6,7 @@ import {
createStreamController,
createDisconnectAwareStream,
} from "../../open-sse/utils/streamHandler.ts";
import { createPassthroughStreamWithLogger } from "../../open-sse/utils/stream.ts";
import { wantsProgress, createProgressTransform } from "../../open-sse/utils/progressTracker.ts";
@@ -50,6 +51,88 @@ test("createProgressTransform maps SSE text output to valid byte stream with pro
assert.match(result, /done":true/);
});
test("createPassthroughStreamWithLogger omits [DONE] for Responses clients", async () => {
const transform = createPassthroughStreamWithLogger(
"codex",
null,
null,
"gpt-5.5-low",
null,
null,
null,
null,
null,
"openai-responses"
);
const writer = transform.writable.getWriter();
await writer.write(
new TextEncoder().encode(
[
"event: response.completed",
'data: {"type":"response.completed","response":{"id":"resp_1","model":"gpt-5.5-low","status":"completed","output":[],"usage":{"input_tokens":1,"output_tokens":1,"total_tokens":2}}}',
"",
].join("\n")
)
);
await writer.close();
const reader = transform.readable.getReader();
const decoder = new TextDecoder();
let result = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
result += decoder.decode(value);
}
assert.match(result, /event: response\.completed/);
assert.doesNotMatch(result, /data: \[DONE\]/);
});
test("createPassthroughStreamWithLogger synthesizes reasoning summary events from reasoning output items", async () => {
const transform = createPassthroughStreamWithLogger(
"codex",
null,
null,
"gpt-5.5-low",
null,
null,
null,
null,
null,
"openai-responses"
);
const writer = transform.writable.getWriter();
await writer.write(
new TextEncoder().encode(
[
"event: response.output_item.done",
'data: {"type":"response.output_item.done","response_id":"resp_reasoning_1","output_index":0,"item":{"id":"rs_resp_reasoning_1_0","type":"reasoning","summary":[{"type":"summary_text","text":"Reasoning summary text"}]}}',
"",
].join("\n")
)
);
await writer.close();
const reader = transform.readable.getReader();
const decoder = new TextDecoder();
let result = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
result += decoder.decode(value);
}
assert.match(result, /event: response\.reasoning_summary_text\.delta/);
assert.match(result, /"delta":"Reasoning summary text"/);
assert.match(result, /event: response\.reasoning_summary_part\.done/);
assert.match(result, /event: response\.output_item\.done/);
});
test("createStreamController returns valid controller", () => {
let completeLogged = false;
let disconnectLogged = false;
@@ -61,8 +144,8 @@ test("createStreamController returns valid controller", () => {
};
const sc = createStreamController({
connectionId: "conn_1",
onStreamComplete: () => {},
provider: "test",
model: "conn_1",
});
assert.equal(typeof sc.signal, "object");

View File

@@ -464,6 +464,47 @@ test("refreshKiroToken uses the AWS OIDC flow when client credentials are presen
});
});
test("refreshKiroToken uses stored region for AWS OIDC refresh without authMethod", async () => {
const log = createLog();
const calls: any[] = [];
await withMockedFetch(
async (url, options = {}) => {
calls.push({ url, options });
return jsonResponse({
accessToken: "kiro-aws-access",
refreshToken: "kiro-aws-refresh-next",
expiresIn: 900,
});
},
async () => {
const result = await refreshKiroToken(
"kiro-refresh",
{
clientId: "aws-client",
clientSecret: "aws-secret",
region: "ap-southeast-1",
},
log
);
assert.deepEqual(result, {
accessToken: "kiro-aws-access",
refreshToken: "kiro-aws-refresh-next",
expiresIn: 900,
});
}
);
assert.equal(calls[0].url, "https://oidc.ap-southeast-1.amazonaws.com/token");
assert.deepEqual(JSON.parse(calls[0].options.body), {
clientId: "aws-client",
clientSecret: "aws-secret",
refreshToken: "kiro-refresh",
grantType: "refresh_token",
});
});
test("refreshKiroToken falls back to the social-auth refresh endpoint", async () => {
const log = createLog();
const calls: any[] = [];

View File

@@ -76,7 +76,7 @@ test("Claude -> Gemini maps system, thinking, tool use, tool result and tools",
);
assert.deepEqual(result.systemInstruction, {
role: "user",
role: "system",
parts: [{ text: "Rules" }],
});
assert.equal(result.contents[0].role, "model");
@@ -92,6 +92,7 @@ test("Claude -> Gemini maps system, thinking, tool use, tool result and tools",
},
});
assert.equal(result.generationConfig.maxOutputTokens, 256);
assert.match((result as any).tools[0].functionDeclarations[0].name, /^[a-zA-Z0-9_]+$/);
assert.equal(result.generationConfig.temperature, 0.4);
assert.equal(result.generationConfig.topP, 0.8);
assert.deepEqual(result.generationConfig.thinkingConfig, {
@@ -105,6 +106,19 @@ test("Claude -> Gemini maps system, thinking, tool use, tool result and tools",
});
});
test("Claude -> Gemini clamps maxOutputTokens to the model cap", () => {
const result = claudeToGeminiRequest(
"gemini-2.5-flash",
{
messages: [{ role: "user", content: [{ type: "text", text: "Hello" }] }],
max_tokens: 999999,
},
false
);
assert.equal(result.generationConfig.maxOutputTokens, 8192);
});
test("Claude -> Gemini converts text and base64 images to Gemini parts", () => {
const result = claudeToGeminiRequest(
"gemini-2.5-flash",

View File

@@ -224,7 +224,7 @@ test("OpenAI -> Gemini request maps messages, merged system instructions, tools
false
);
assert.equal((result as any).systemInstruction.role, "user");
assert.equal((result as any).systemInstruction.role, "system");
assert.deepEqual((result as any).systemInstruction.parts, [
{ text: "Rule A" },
{ text: "Rule B" },
@@ -558,11 +558,15 @@ test("OpenAI -> Antigravity uses the Claude bridge for Claude-family models", ()
assert.equal(result.project, "proj-claude");
assert.equal(result.userAgent, "antigravity");
assert.equal((result as any).request?.systemInstruction.role, "system");
assert.equal(
(result as any).request?.systemInstruction.parts[0].text,
ANTIGRAVITY_DEFAULT_SYSTEM
);
assert.equal((result as any).request?.systemInstruction.parts[1].text, "Project rules");
assert.equal((result as any).request?.generationConfig.maxOutputTokens, 8192);
assert.equal((result as any).request?.generationConfig.temperature, 1);
assert.equal((result as any).request?.generationConfig.thinkingConfig, undefined);
const modelTurn = result.request.contents.find(
(content) => content.role === "model" && content.parts.some((part) => part.functionCall)
@@ -624,6 +628,7 @@ test("OpenAI -> Antigravity Claude bridge sanitizes long names and preserves res
const sanitizedToolName = (result as any).request?.tools[0].functionDeclarations[0].name;
assert.equal(sanitizedToolName.length, 64);
assert.match(sanitizedToolName, /^[a-zA-Z0-9_]+$/);
assert.equal((result as any)._toolNameMap.get(sanitizedToolName), longToolName);
const modelTurn = result.request.contents.find(
@@ -638,3 +643,22 @@ test("OpenAI -> Antigravity Claude bridge sanitizes long names and preserves res
assert.ok(toolTurn, "expected a tool response turn");
assert.equal(getFunctionResponse(toolTurn.parts[0]).name, sanitizedToolName);
});
test("OpenAI -> Antigravity Claude bridge clamps output tokens and keeps thinking budget separate", () => {
const result = openaiToAntigravityRequest(
"claude-3-7-sonnet",
{
messages: [{ role: "user", content: "Summarize this" }],
max_completion_tokens: 32000,
reasoning_effort: "high",
},
false,
{ projectId: "proj-claude-thinking" } as any
);
assert.equal((result as any).request?.generationConfig.maxOutputTokens, 8192);
assert.deepEqual((result as any).request?.generationConfig.thinkingConfig, {
thinkingBudget: 131072,
includeThoughts: true,
});
});

View File

@@ -0,0 +1,55 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-v1beta-models-"));
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "v1beta-models-test-secret";
const core = await import("../../src/lib/db/core.ts");
const modelsDb = await import("../../src/lib/db/models.ts");
const v1betaModelsRoute = await import("../../src/app/api/v1beta/models/route.ts");
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
test.beforeEach(async () => {
await resetStorage();
});
test.after(async () => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
test("v1beta models route deduplicates custom models against built-in and synced entries", async () => {
await modelsDb.replaceSyncedAvailableModelsForConnection("openai", "conn-main", [
{
id: "gpt-4o",
name: "GPT-4o From Sync",
source: "imported",
},
{
id: "review-sync-only",
name: "Review Sync Only",
source: "imported",
},
]);
await modelsDb.addCustomModel("openai", "gpt-4o", "GPT-4o Manual Duplicate");
await modelsDb.addCustomModel("openai", "review-sync-only", "Review Manual Duplicate");
await modelsDb.addCustomModel("openai", "review-manual-only", "Review Manual Only");
const response = await v1betaModelsRoute.GET();
const body = (await response.json()) as { models: Array<{ name: string }> };
const names = body.models.map((model) => model.name);
assert.equal(response.status, 200);
assert.equal(names.filter((name) => name === "models/openai/gpt-4o").length, 1);
assert.equal(names.filter((name) => name === "models/openai/review-sync-only").length, 1);
assert.equal(names.filter((name) => name === "models/openai/review-manual-only").length, 1);
});

View File

@@ -16,7 +16,7 @@ test("xiaomi-mimo registry uses the current default base URL and MiMo V2 models"
assert.equal(entry.baseUrl, "https://api.xiaomimimo.com/v1");
assert.deepEqual(
entry.models.map((model) => model.id),
["mimo-v2-pro", "mimo-v2-omni", "mimo-v2-tts"]
["mimo-v2.5-pro", "mimo-v2.5", "mimo-v2-omni", "mimo-v2-flash"]
);
});
@@ -24,7 +24,7 @@ test("xiaomi-mimo executor appends /chat/completions for regional base URLs", ()
const executor = new DefaultExecutor("xiaomi-mimo");
assert.equal(
executor.buildUrl("mimo-v2-pro", true, 0, {
executor.buildUrl("mimo-v2.5", true, 0, {
providerSpecificData: {
baseUrl: "https://token-plan-ams.xiaomimimo.com/v1",
},
@@ -33,7 +33,7 @@ test("xiaomi-mimo executor appends /chat/completions for regional base URLs", ()
);
assert.equal(
executor.buildUrl("mimo-v2-pro", true, 0, {
executor.buildUrl("mimo-v2.5", true, 0, {
providerSpecificData: {
baseUrl: "https://token-plan-cn.xiaomimimo.com/v1/chat/completions",
},