mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-05 16:34:00 +01:00
fix(context-bill): --exact resolves a billed path one link at a time, so a shared deep chain keeps its measurements
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
398ca092df
commit
2b68acb2d5
2 files changed
+43
-1
No files matched your search
+16
-1
@@ -243,6 +243,21 @@ function entryKind(p: string, e: fs.Dirent): "file" | "dir" | null {
|
||||
* followed directory symlink that contains the tree being walked (`..`, `/`,
|
||||
* `$HOME`) would climb out of that tree, so walkers refuse it.
|
||||
*/
|
||||
/**
|
||||
* The realpath of `p`, resolved one component at a time the way the walk
|
||||
* reads directories, so a lexical path that crosses more links than the
|
||||
* kernel's limit still resolves.
|
||||
*/
|
||||
function realpathStepwise(p: string): string {
|
||||
const abs = path.resolve(p);
|
||||
const { root } = path.parse(abs);
|
||||
let real = fs.realpathSync(root);
|
||||
for (const segment of abs.slice(root.length).split(path.sep)) {
|
||||
if (segment) real = fs.realpathSync(path.join(real, segment));
|
||||
}
|
||||
return real;
|
||||
}
|
||||
|
||||
function encloses(outer: string, inner: string): boolean {
|
||||
const rel = path.relative(outer, inner);
|
||||
return rel === "" || (rel !== ".." && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel));
|
||||
@@ -1019,7 +1034,7 @@ export async function measureExactTokens(
|
||||
const isFm = key.endsWith(fmSuffix);
|
||||
let real: string;
|
||||
try {
|
||||
real = fs.realpathSync(isFm ? key.slice(0, -fmSuffix.length) : key);
|
||||
real = realpathStepwise(isFm ? key.slice(0, -fmSuffix.length) : key);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1406,6 +1406,33 @@ describe("--exact (opt-in measurement; offline here via an injected fetch)", ()
|
||||
expect(rows).toBe(depth + 1);
|
||||
});
|
||||
|
||||
it("prices a second skill's links into a chain deeper than the kernel's limit from the one measurement", async () => {
|
||||
if (process.platform === "win32") return;
|
||||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "context-bill-exact-shared-deep-"));
|
||||
const levels = path.join(tmp, "levels");
|
||||
const depth = 50;
|
||||
for (let i = 0; i < depth; i++) {
|
||||
fs.mkdirSync(path.join(levels, `L${i}`), { recursive: true });
|
||||
fs.writeFileSync(path.join(levels, `L${i}`, `f${i}.md`), `F${i}-${"x".repeat(30)}`);
|
||||
}
|
||||
for (let i = 0; i < depth - 1; i++) fs.symlinkSync(path.join(levels, `L${i + 1}`), path.join(levels, `L${i}`, "next"));
|
||||
markCheckout(levels);
|
||||
const root = path.join(tmp, "skills");
|
||||
for (const name of ["qa", "qb"]) {
|
||||
fs.mkdirSync(path.join(root, name), { recursive: true });
|
||||
fs.writeFileSync(path.join(root, name, "SKILL.md"), `---\nname: ${name}\ndescription: d\n---\n# ${name}\n`);
|
||||
fs.symlinkSync(path.join(levels, "L0"), path.join(root, name, "next"));
|
||||
}
|
||||
fs.symlinkSync(levels, path.join(root, "gstack"));
|
||||
const measured = await measureExactTokens(root, { model: "m", apiKey: "sk-test", fetchImpl: fakeFetch(), egressHome });
|
||||
const bill = buildBill(root, { tokensOf: measured.tokensOf, tokenSource: measured.tokenSource });
|
||||
fs.rmSync(tmp, { recursive: true, force: true });
|
||||
expect([...measured.missedKeys]).toEqual([]);
|
||||
const [qa, qb] = ["qa", "qb"].map((n) => bill.skills.find((s) => s.name === n)!);
|
||||
expect(qb.totalMdBytes).toBe(qa.totalMdBytes);
|
||||
expect(qb.totalMdTokens).toBe(qa.totalMdTokens);
|
||||
});
|
||||
|
||||
it("names a missing tree, without a raw ENOENT or any request", async () => {
|
||||
const parent = fs.mkdtempSync(path.join(os.tmpdir(), "context-bill-missing-"));
|
||||
const out = capture();
|
||||
|
||||
Reference in new issue
Block a user