Merge PR #7025: fix(coding-agent): coalesce models config resource probe (@paralin)

This commit is contained in:
can1357
2026-07-29 23:09:10 +02:00
3 changed files with 87 additions and 39 deletions
+1
View File
@@ -27,6 +27,7 @@
- Reduced bash, grep, and glob tool guidance while preserving supported internal-URL routes and making shell/eval boundaries and broad-search timeout avoidance explicit.
- Fixed the spawn-based models config validator laziness test flaking under CPU contention by giving its two sequential probe processes an explicit 60s per-test timeout instead of sharing bun's 5s default ([#7018](https://github.com/can1357/oh-my-pi/issues/7018)).
- Coalesced the models configuration resource probe into one child process to avoid startup contention while preserving retained-resource coverage.
## [17.1.8] - 2026-07-28
@@ -9,14 +9,52 @@ interface HeapSnapshot {
snapshot: { meta: { node_fields: string[] } };
}
const root = process.argv[2];
const mode = process.argv[3];
if (!root || (mode !== "missing" && mode !== "custom")) {
throw new Error("Expected an isolated config root and missing|custom mode");
interface ProbeResult {
retainedHeapNodes: number;
schemaIdentityStable?: boolean;
model?: {
provider: string;
id: string;
baseUrl: string;
api: string;
thinking?: unknown;
};
}
const configPath = path.join(root, mode, "models.yml");
if (mode === "custom") {
const root = process.argv[2];
if (!root) {
throw new Error("Expected an isolated config root");
}
function retainedHeapNodes(): number {
Bun.gc(true);
const snapshot = JSON.parse(Bun.generateHeapSnapshot("v8")) as HeapSnapshot;
return snapshot.nodes.length / snapshot.snapshot.meta.node_fields.length;
}
async function measureMissing(): Promise<ProbeResult> {
let model: ProbeResult["model"];
{
const authStorage = await AuthStorage.create(":memory:");
try {
const registry = new ModelRegistry(authStorage, path.join(root, "missing", "models.yml"));
const found = registry.find("anthropic", "claude-sonnet-4-5");
model = found && {
provider: found.provider,
id: found.id,
baseUrl: found.baseUrl,
api: found.api,
thinking: found.thinking,
};
} finally {
authStorage.close();
}
}
return { retainedHeapNodes: retainedHeapNodes(), model };
}
async function writeCustomConfig(): Promise<string> {
const configPath = path.join(root, "custom", "models.yml");
await Bun.write(
configPath,
YAML.stringify(
@@ -45,34 +83,38 @@ if (mode === "custom") {
2,
),
);
return configPath;
}
const authStorage = await AuthStorage.create(":memory:");
try {
const registry = new ModelRegistry(authStorage, configPath);
const model =
mode === "custom" ? registry.find("lazy-models", "lazy-model") : registry.find("anthropic", "claude-sonnet-4-5");
const firstSchema = mode === "custom" ? ModelsConfigFile.relocate(configPath).schema : undefined;
const secondSchema =
mode === "custom" ? ModelsConfigFile.relocate(path.join(root, "second", "models.yml")).schema : undefined;
Bun.gc(true);
const snapshot = JSON.parse(Bun.generateHeapSnapshot("v8")) as HeapSnapshot;
const nodeWidth = snapshot.snapshot.meta.node_fields.length;
process.stdout.write(
JSON.stringify({
retainedHeapNodes: snapshot.nodes.length / nodeWidth,
schemaIdentityStable: mode === "custom" ? firstSchema === secondSchema : undefined,
model: model && {
provider: model.provider,
id: model.id,
baseUrl: model.baseUrl,
api: model.api,
thinking: model.thinking,
},
}),
);
} finally {
authStorage.close();
async function measureCustom(configPath: string): Promise<ProbeResult> {
let model: ProbeResult["model"];
let schemaIdentityStable = false;
{
const authStorage = await AuthStorage.create(":memory:");
try {
const registry = new ModelRegistry(authStorage, configPath);
const found = registry.find("lazy-models", "lazy-model");
model = found && {
provider: found.provider,
id: found.id,
baseUrl: found.baseUrl,
api: found.api,
thinking: found.thinking,
};
const firstSchema = ModelsConfigFile.relocate(configPath).schema;
const secondSchema = ModelsConfigFile.relocate(path.join(root, "second", "models.yml")).schema;
schemaIdentityStable = firstSchema === secondSchema;
} finally {
authStorage.close();
}
}
return {
retainedHeapNodes: retainedHeapNodes(),
schemaIdentityStable,
model,
};
}
const missing = await measureMissing();
const custom = await measureCustom(await writeCustomConfig());
process.stdout.write(JSON.stringify({ missing, custom }));
@@ -14,10 +14,15 @@ interface ProbeResult {
};
}
interface ProbeResults {
missing: ProbeResult;
custom: ProbeResult;
}
const probePath = path.join(import.meta.dir, "fixtures", "models-config-validator-construction-probe.ts");
async function runProbe(root: string, mode: "missing" | "custom"): Promise<ProbeResult> {
const proc = Bun.spawn([process.execPath, probePath, root, mode], {
async function runProbe(root: string): Promise<ProbeResults> {
const proc = Bun.spawn([process.execPath, probePath, root], {
cwd: path.join(import.meta.dir, "../../.."),
stdout: "pipe",
stderr: "pipe",
@@ -28,14 +33,13 @@ async function runProbe(root: string, mode: "missing" | "custom"): Promise<Probe
proc.exited,
]);
expect(exitCode, stderr).toBe(0);
return JSON.parse(stdout) as ProbeResult;
return JSON.parse(stdout) as ProbeResults;
}
test("models config validation resources are retained only for a custom config", async () => {
const tempDir = TempDir.createSync("@models-config-validator-");
try {
const missing = await runProbe(tempDir.path(), "missing");
const custom = await runProbe(tempDir.path(), "custom");
const { missing, custom } = await runProbe(tempDir.path());
expect(missing.model).toMatchObject({
provider: "anthropic",
@@ -55,6 +59,7 @@ test("models config validation resources are retained only for a custom config",
},
});
expect(custom.schemaIdentityStable).toBe(true);
expect(missing.retainedHeapNodes).toBeLessThan(custom.retainedHeapNodes);
expect(
custom.retainedHeapNodes - missing.retainedHeapNodes,
"custom config validation should retain its schema bundle",