mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-14 13:22:37 +00:00
test(opencode): reproduce #6006, a model OpenCode 2 dropped stays in the picker and fails to apply
Run with OPENCODE_V2_BIN pointing at an OpenCode 2.x binary. Fails on main.
This commit is contained in:
+115
@@ -0,0 +1,115 @@
|
||||
import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
import { OpenCodeV2AgentClient } from "../agent/providers/opencode/v2/agent.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
|
||||
// Reproduction for #6006. Needs an OpenCode 2.x binary in OPENCODE_V2_BIN.
|
||||
const OPENCODE_V2_BIN = process.env.OPENCODE_V2_BIN;
|
||||
|
||||
const PROVIDER = "paseo-catalog-drift";
|
||||
const KEPT_MODEL = "kept-model";
|
||||
const RETIRED_MODEL = "retired-model";
|
||||
|
||||
function catalog(modelIds: string[]) {
|
||||
return {
|
||||
[PROVIDER]: {
|
||||
id: PROVIDER,
|
||||
env: ["PASEO_CATALOG_DRIFT_KEY"],
|
||||
npm: "@ai-sdk/openai-compatible",
|
||||
api: "http://127.0.0.1:9/v1",
|
||||
name: "Catalog drift",
|
||||
models: Object.fromEntries(
|
||||
modelIds.map((id) => [
|
||||
id,
|
||||
{
|
||||
id,
|
||||
name: id,
|
||||
attachment: false,
|
||||
reasoning: false,
|
||||
tool_call: true,
|
||||
temperature: true,
|
||||
release_date: "2025-06-17",
|
||||
last_updated: "2025-06-17",
|
||||
modalities: { input: ["text"], output: ["text"] },
|
||||
open_weights: false,
|
||||
limit: { context: 128_000, output: 8_000 },
|
||||
cost: { input: 0, output: 0 },
|
||||
},
|
||||
]),
|
||||
),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test.runIf(OPENCODE_V2_BIN)(
|
||||
"a model OpenCode dropped from its catalog stays in the picker and fails to apply",
|
||||
async () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const root = realpathSync(mkdtempSync(path.join(tmpdir(), "opencode-v2-catalog-drift-")));
|
||||
const modelsPath = path.join(root, "models.json");
|
||||
const cwd = path.join(root, "work");
|
||||
mkdirSync(cwd);
|
||||
writeFileSync(modelsPath, JSON.stringify(catalog([KEPT_MODEL, RETIRED_MODEL])));
|
||||
const provider = new OpenCodeV2AgentClient({
|
||||
logger,
|
||||
settings: {
|
||||
command: { mode: "replace", argv: [OPENCODE_V2_BIN!] },
|
||||
env: {
|
||||
OPENCODE_MODELS_PATH: modelsPath,
|
||||
OPENCODE_DISABLE_MODELS_FETCH: "1",
|
||||
OPENCODE_DISABLE_AUTO_UPDATE: "1",
|
||||
PASEO_CATALOG_DRIFT_KEY: "test",
|
||||
XDG_CONFIG_HOME: path.join(root, "config"),
|
||||
XDG_DATA_HOME: path.join(root, "data"),
|
||||
XDG_CACHE_HOME: path.join(root, "cache"),
|
||||
},
|
||||
},
|
||||
});
|
||||
const daemon = await createTestPaseoDaemon({ agentClients: { opencode: provider }, logger });
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
try {
|
||||
await client.connect();
|
||||
await client.fetchAgents({ subscribe: {} });
|
||||
|
||||
const before = await client.listProviderModels("opencode", { cwd });
|
||||
expect(before.models?.map((entry) => entry.id)).toContain(`${PROVIDER}/${RETIRED_MODEL}`);
|
||||
|
||||
// OpenCode's catalog drops the model while Paseo sits idle.
|
||||
writeFileSync(modelsPath, JSON.stringify(catalog([KEPT_MODEL])));
|
||||
|
||||
const agent = await client.createAgent({
|
||||
provider: "opencode",
|
||||
cwd,
|
||||
title: "catalog drift",
|
||||
model: `${PROVIDER}/${KEPT_MODEL}`,
|
||||
modeId: "build",
|
||||
});
|
||||
|
||||
const listed = await client.listProviderModels("opencode", { cwd });
|
||||
const offered = listed.models?.map((entry) => entry.id) ?? [];
|
||||
let switchError: string | null = null;
|
||||
if (offered.includes(`${PROVIDER}/${RETIRED_MODEL}`)) {
|
||||
try {
|
||||
await client.setAgentModel(agent.id, `${PROVIDER}/${RETIRED_MODEL}`);
|
||||
} catch (error) {
|
||||
switchError = error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
}
|
||||
// Every model the picker offers must be one OpenCode accepts.
|
||||
expect({ offered, switchError }).toEqual({
|
||||
offered: [`${PROVIDER}/${KEPT_MODEL}`],
|
||||
switchError: null,
|
||||
});
|
||||
} finally {
|
||||
await client.close().catch(() => undefined);
|
||||
await daemon.close().catch(() => undefined);
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
},
|
||||
180_000,
|
||||
);
|
||||
Reference in New Issue
Block a user