45bfae6e94
Initial import of all source code, config, and README assets: the packages workspace (cli, core, server, web, docs, landing, skills), build scripts, tooling config, and CI workflows. Includes the data-layout revision made on this branch: the local data root defaults to ~/.penguin/data (PENGUIN_HOME still overrides; the installer keeps its binaries in ~/.penguin), and every Agent lives under <project>/agents/<agent>/ — path helpers, the three agent-enumeration scans, the system prompt, built-in Skills, tests and docs all follow the new layout. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018ihk8iQuo3kv2aPjAYEPuR
69 lines
2.7 KiB
TypeScript
69 lines
2.7 KiB
TypeScript
/**
|
|
* Unit tests for `config model list` rendering: provider and model_id are separate
|
|
* columns (stored fields as-is, with the default model marked `*` before the provider
|
|
* column; the request column was removed along with concatenated storage); vision falls
|
|
* back to the catalog matched by the (provider, model_id) pair; api_key is masked
|
|
* inline; fully empty columns are omitted automatically.
|
|
*/
|
|
import { describe, expect, it } from "vitest";
|
|
import type { ProjectConfig } from "@prismshadow/penguin-core";
|
|
import { formatModelRows } from "../src/commands/config.js";
|
|
|
|
describe("formatModelRows", () => {
|
|
const cfg: ProjectConfig = {
|
|
default_model: { provider: "anthropic", model_id: "claude-sonnet-4-6" },
|
|
models: [
|
|
{
|
|
provider: "anthropic",
|
|
model_id: "claude-sonnet-4-6",
|
|
context_window: 1000000,
|
|
pricing: { unit: "usd_per_mtok", cache_read: 0.3, cache_write: 3.75, output: 15 },
|
|
},
|
|
{
|
|
provider: "custom",
|
|
model_id: "my-proxy-model",
|
|
client_type: "openai",
|
|
vision: false,
|
|
api_key: "sk-test-abcd-1234",
|
|
},
|
|
],
|
|
};
|
|
|
|
it("provider 与 model_id 双列展示;预置模型 vision 经目录成对匹配,默认模型以 * 标记", () => {
|
|
const lines = formatModelRows(cfg);
|
|
expect(lines).toHaveLength(2);
|
|
expect(lines[0]).toMatch(/^\* anthropic\s+claude-sonnet-4-6\s+vision=Y/);
|
|
expect(lines[0]).toContain("price=0.3/3.75/15");
|
|
// The request column was removed; no <provider>/<id> concatenation appears anymore.
|
|
expect(lines[0]).not.toContain("request=");
|
|
expect(lines[0]).not.toContain("anthropic/claude-sonnet-4-6");
|
|
});
|
|
|
|
it("自定义模型 vision 按标注(显式 false 记 -);内联 api_key 掩码显示", () => {
|
|
const lines = formatModelRows(cfg);
|
|
expect(lines[1]).toMatch(/^ {2}custom\s+my-proxy-model\s+vision=-/);
|
|
expect(lines[1]).toContain("client_type=openai");
|
|
expect(lines[1]).toContain("api_key=****1234");
|
|
expect(lines[1]).not.toContain("sk-test-abcd-1234");
|
|
});
|
|
|
|
it("同名 model_id 双 provider 并存时各占一行,默认标记只落在成对命中的那行", () => {
|
|
const lines = formatModelRows({
|
|
default_model: { provider: "deepseek", model_id: "m1" },
|
|
models: [
|
|
{ provider: "deepseek", model_id: "m1" },
|
|
{ provider: "siliconflow", model_id: "m1" },
|
|
],
|
|
});
|
|
expect(lines[0]).toMatch(/^\* deepseek\s+m1\s+vision=Y/);
|
|
expect(lines[1]).toMatch(/^ {2}siliconflow\s+m1\s+vision=Y/);
|
|
});
|
|
|
|
it("无标注按「缺省=支持」记 Y;全空列省略", () => {
|
|
const lines = formatModelRows({
|
|
models: [{ provider: "custom", model_id: "m1" }],
|
|
});
|
|
expect(lines[0]).toBe(" custom m1 vision=Y api_key=-");
|
|
});
|
|
});
|