refactor: 移除顶层 defaults 配置段,简化为 target 显式字段 > 代码内置默认值
- 移除 DefaultsConfig 类型、ProbeConfig.defaults 字段 - 移除 CheckerSchemas.defaults、ResolveContext.defaults、CheckerValidationInput.defaults - 更新所有 checker schema/resolve/validate 删除 defaults 合并逻辑 - 更新 config-loader 不再读取传递 defaults - 更新测试、README、DEVELOPMENT、probes.example.yaml - 重新生成 probe-config.schema.json(不含 defaults) - 同步 delta specs 到主规范 - 归档 openspec change
This commit is contained in:
@@ -17,14 +17,12 @@ describe("LLM registry integration", () => {
|
||||
test("llm checker schemas 有效", () => {
|
||||
const checker = checkerRegistry.get("llm");
|
||||
expect(checker.schemas.config).toBeDefined();
|
||||
expect(checker.schemas.defaults).toBeDefined();
|
||||
expect(checker.schemas.expect).toBeDefined();
|
||||
});
|
||||
|
||||
test("llm checker validate 方法可用", () => {
|
||||
const checker = checkerRegistry.get("llm");
|
||||
const issues = checker.validate({
|
||||
defaults: {},
|
||||
targets: [],
|
||||
});
|
||||
expect(issues).toHaveLength(0);
|
||||
|
||||
@@ -42,7 +42,6 @@ function makeResolveContext(overrides?: Partial<ResolveContext>): ResolveContext
|
||||
return {
|
||||
configDir: "/tmp",
|
||||
defaultIntervalMs: 30000,
|
||||
defaults: {},
|
||||
defaultTimeoutMs: 10000,
|
||||
...overrides,
|
||||
};
|
||||
@@ -61,16 +60,15 @@ describe("LlmChecker schema", () => {
|
||||
expect(checker?.configKey).toBe("llm");
|
||||
});
|
||||
|
||||
test("schemas 包含 config、defaults、expect", () => {
|
||||
test("schemas 包含 config、expect", () => {
|
||||
expect(checker).toBeDefined();
|
||||
expect(Object.keys(checker!.schemas).sort()).toEqual(["config", "defaults", "expect"].sort());
|
||||
expect(Object.keys(checker!.schemas).sort()).toEqual(["config", "expect"].sort());
|
||||
});
|
||||
});
|
||||
|
||||
describe("LlmChecker validate", () => {
|
||||
test("合法 LLM target 无校验问题", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [makeRawTarget()],
|
||||
});
|
||||
expect(issues).toHaveLength(0);
|
||||
@@ -78,7 +76,6 @@ describe("LlmChecker validate", () => {
|
||||
|
||||
test("provider 非法报错", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [
|
||||
makeRawTarget({
|
||||
llm: { model: "m", prompt: "p", provider: "gemini", url: "https://x" },
|
||||
@@ -91,7 +88,6 @@ describe("LlmChecker validate", () => {
|
||||
|
||||
test("url 非法报错", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [
|
||||
makeRawTarget({
|
||||
llm: { model: "m", prompt: "p", provider: "openai", url: "ftp://bad" },
|
||||
@@ -104,7 +100,6 @@ describe("LlmChecker validate", () => {
|
||||
|
||||
test("model 为空报错", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [
|
||||
makeRawTarget({
|
||||
llm: { model: "", prompt: "p", provider: "openai", url: "https://x" },
|
||||
@@ -117,7 +112,6 @@ describe("LlmChecker validate", () => {
|
||||
|
||||
test("prompt 为空报错", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [
|
||||
makeRawTarget({
|
||||
llm: { model: "m", prompt: "", provider: "openai", url: "https://x" },
|
||||
@@ -130,7 +124,6 @@ describe("LlmChecker validate", () => {
|
||||
|
||||
test("mode 非法报错", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [
|
||||
makeRawTarget({
|
||||
llm: { mode: "batch", model: "m", prompt: "p", provider: "openai", url: "https://x" },
|
||||
@@ -143,7 +136,6 @@ describe("LlmChecker validate", () => {
|
||||
|
||||
test("openai provider 不允许 authToken", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [
|
||||
makeRawTarget({
|
||||
llm: { authToken: "tok", model: "m", prompt: "p", provider: "openai", url: "https://x" },
|
||||
@@ -155,7 +147,6 @@ describe("LlmChecker validate", () => {
|
||||
|
||||
test("anthropic 同时配置 key 和 authToken 报错", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [
|
||||
makeRawTarget({
|
||||
llm: { authToken: "tok", key: "k", model: "m", prompt: "p", provider: "anthropic", url: "https://x" },
|
||||
@@ -167,7 +158,6 @@ describe("LlmChecker validate", () => {
|
||||
|
||||
test("ignoreSSL 非布尔值报错", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [
|
||||
makeRawTarget({
|
||||
llm: { ignoreSSL: "yes", model: "m", prompt: "p", provider: "openai", url: "https://x" },
|
||||
@@ -179,7 +169,6 @@ describe("LlmChecker validate", () => {
|
||||
|
||||
test("options.maxOutputTokens 非正整数报错", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [
|
||||
makeRawTarget({
|
||||
llm: { model: "m", options: { maxOutputTokens: -1 }, prompt: "p", provider: "openai", url: "https://x" },
|
||||
@@ -191,7 +180,6 @@ describe("LlmChecker validate", () => {
|
||||
|
||||
test("options.stopSequences 非字符串数组报错", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [
|
||||
makeRawTarget({
|
||||
llm: { model: "m", options: { stopSequences: [123] }, prompt: "p", provider: "openai", url: "https://x" },
|
||||
@@ -203,7 +191,6 @@ describe("LlmChecker validate", () => {
|
||||
|
||||
test("expect.output 缺少规则类型报错", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [makeRawTarget({ expect: { output: [{}] } })],
|
||||
});
|
||||
expect(issues.some((i) => i.code === "empty-matcher")).toBe(true);
|
||||
@@ -211,7 +198,6 @@ describe("LlmChecker validate", () => {
|
||||
|
||||
test("expect.output 直接 matcher 混入 extractor 报错", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [makeRawTarget({ expect: { output: [{ contains: "y", json: { path: "$.status" } }] } })],
|
||||
});
|
||||
expect(issues.some((i) => i.code === "invalid-content-expectation")).toBe(true);
|
||||
@@ -219,7 +205,6 @@ describe("LlmChecker validate", () => {
|
||||
|
||||
test("expect.output regex ReDoS 报错", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [makeRawTarget({ expect: { output: [{ regex: "(a+)+" }] } })],
|
||||
});
|
||||
expect(issues.some((i) => i.code === "unsafe-regex")).toBe(true);
|
||||
@@ -227,7 +212,6 @@ describe("LlmChecker validate", () => {
|
||||
|
||||
test("expect.stream 在 mode:http 下报错", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [
|
||||
makeRawTarget({
|
||||
expect: { stream: { completed: true } },
|
||||
@@ -240,7 +224,6 @@ describe("LlmChecker validate", () => {
|
||||
|
||||
test("expect.stream 在 mode:stream 下合法", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {},
|
||||
targets: [
|
||||
makeRawTarget({
|
||||
expect: { stream: { completed: true } },
|
||||
@@ -250,24 +233,6 @@ describe("LlmChecker validate", () => {
|
||||
});
|
||||
expect(issues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("defaults.llm 合法配置", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: {
|
||||
llm: { headers: { "X-Custom": "val" }, ignoreSSL: false, mode: "http", options: { maxOutputTokens: 32 } },
|
||||
},
|
||||
targets: [makeRawTarget()],
|
||||
});
|
||||
expect(issues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("defaults.llm mode 非法报错", () => {
|
||||
const issues = validateLlmConfig({
|
||||
defaults: { llm: { mode: "batch" } },
|
||||
targets: [makeRawTarget()],
|
||||
});
|
||||
expect(issues.some((i) => i.path.includes("defaults.llm.mode"))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("LlmChecker resolve", () => {
|
||||
@@ -324,61 +289,6 @@ describe("LlmChecker resolve", () => {
|
||||
expect(resolved.expect?.stream).toEqual({ completed: true, firstTokenMs: { equals: 100 } });
|
||||
});
|
||||
|
||||
test("defaults.llm 与 target.llm 浅合并", () => {
|
||||
const raw = makeRawTarget({
|
||||
llm: {
|
||||
headers: { Authorization: "Bearer test" },
|
||||
model: "gpt-4o-mini",
|
||||
prompt: "Say OK",
|
||||
provider: "openai",
|
||||
url: "https://api.openai.com/v1",
|
||||
},
|
||||
});
|
||||
const ctx = makeResolveContext({
|
||||
defaults: {
|
||||
llm: {
|
||||
headers: { "X-Custom": "default" },
|
||||
ignoreSSL: true,
|
||||
mode: "stream",
|
||||
options: { maxOutputTokens: 64, temperature: 0.5 },
|
||||
},
|
||||
},
|
||||
});
|
||||
const resolved = asLlm(checker.resolve(raw, ctx));
|
||||
expect(resolved.llm.mode).toBe("stream");
|
||||
expect(resolved.llm.ignoreSSL).toBe(true);
|
||||
expect(resolved.llm.headers).toEqual({ Authorization: "Bearer test", "X-Custom": "default" });
|
||||
expect(resolved.llm.options.maxOutputTokens).toBe(64);
|
||||
expect(resolved.llm.options.temperature).toBe(0.5);
|
||||
});
|
||||
|
||||
test("target 字段覆盖 defaults", () => {
|
||||
const raw = makeRawTarget({
|
||||
llm: {
|
||||
ignoreSSL: false,
|
||||
mode: "http",
|
||||
model: "gpt-4o-mini",
|
||||
options: { maxOutputTokens: 8 },
|
||||
prompt: "Say OK",
|
||||
provider: "openai",
|
||||
url: "https://api.openai.com/v1",
|
||||
},
|
||||
});
|
||||
const ctx = makeResolveContext({
|
||||
defaults: {
|
||||
llm: {
|
||||
ignoreSSL: true,
|
||||
mode: "stream",
|
||||
options: { maxOutputTokens: 64 },
|
||||
},
|
||||
},
|
||||
});
|
||||
const resolved = asLlm(checker.resolve(raw, ctx));
|
||||
expect(resolved.llm.mode).toBe("http");
|
||||
expect(resolved.llm.ignoreSSL).toBe(false);
|
||||
expect(resolved.llm.options.maxOutputTokens).toBe(8);
|
||||
});
|
||||
|
||||
test("serialize 返回正确格式", () => {
|
||||
const resolved = asLlm(checker.resolve(makeRawTarget(), makeResolveContext()));
|
||||
const serialized = checker.serialize(resolved);
|
||||
@@ -398,25 +308,4 @@ describe("LlmChecker resolve", () => {
|
||||
const config = parseSerializedConfig(serialized.config);
|
||||
expect(config.key).toBe("***");
|
||||
});
|
||||
|
||||
test("providerOptions 浅合并", () => {
|
||||
const raw = makeRawTarget({
|
||||
llm: {
|
||||
model: "m",
|
||||
prompt: "p",
|
||||
provider: "openai",
|
||||
providerOptions: { openai: { store: true } },
|
||||
url: "https://x",
|
||||
},
|
||||
});
|
||||
const ctx = makeResolveContext({
|
||||
defaults: {
|
||||
llm: {
|
||||
providerOptions: { openai: { user: "default-user" } },
|
||||
},
|
||||
},
|
||||
});
|
||||
const resolved = asLlm(checker.resolve(raw, ctx));
|
||||
expect(resolved.llm.providerOptions).toEqual({ openai: { store: true } });
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user