chore: initial commit

Laimiao agent v2 service: agent orchestration, sessions, billing,
gateway client, skills catalog, auth, config, plus tests.
This commit is contained in:
light committed 2026-11-07 10:40:07 +08:00
commit 6ec9778d45
53 files changed
+12340

No files matched your search

+89
View File
@@ -0,0 +1,89 @@
import { appendFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { UsageLedger, type BeginUsage } from "../src/billing/ledger.js";
import { silentLogger, tempRoot } from "./helpers.js";
function usage(overrides: Partial<BeginUsage> = {}): BeginUsage {
return {
uid: "1",
team_id: "1",
session_id: "s1",
tool_call_id: "call-1",
skill_id: "demo",
skill_name: "Demo",
model: "seedream@5.0",
size: "1024x1536",
quantity: 2,
unit_credits: 30,
size_multiplier: 1,
total_credits: 60,
currency: "credits",
billing_mode: "dry_run",
...overrides,
};
}
describe("UsageLedger", () => {
it("records pending and succeeded usage and finds it by tool call", () => {
const ledger = new UsageLedger(join(tempRoot(), "ledger.jsonl"), silentLogger);
const record = ledger.begin(usage());
expect(record.status).toBe("pending");
ledger.settle(record.id, "succeeded", {
gateway_task_id: "task-1",
image_urls: ["https://img/1.png", "https://img/2.png"],
});
const found = ledger.findSucceededToolCall("1", "s1", "call-1");
expect(found?.id).toBe(record.id);
expect(found?.status).toBe("succeeded");
expect(found?.image_urls).toHaveLength(2);
expect(ledger.findSucceededToolCall("2", "s1", "call-1")).toBeUndefined();
});
it("persists records and rebuilds the idempotency index", () => {
const file = join(tempRoot(), "ledger.jsonl");
const ledger = new UsageLedger(file, silentLogger);
const record = ledger.begin(usage());
ledger.settle(record.id, "succeeded", { image_urls: ["https://img/1.png"] });
const reloaded = new UsageLedger(file, silentLogger);
expect(reloaded.list()).toHaveLength(1);
expect(reloaded.findSucceededToolCall("1", "s1", "call-1")?.id).toBe(record.id);
});
it("does not index failed calls and summarises by status and skill", () => {
const ledger = new UsageLedger(join(tempRoot(), "ledger.jsonl"), silentLogger);
const failed = ledger.begin(usage({ tool_call_id: "call-failed" }));
ledger.settle(failed.id, "failed", { error: "nope" });
const ok = ledger.begin(usage({ tool_call_id: "call-ok", skill_id: "poster", total_credits: 30, quantity: 1 }));
ledger.settle(ok.id, "succeeded", { image_urls: ["https://img/p.png"] });
const pending = ledger.begin(usage({ tool_call_id: "call-pending", skill_id: "poster", total_credits: 30 }));
expect(ledger.findSucceededToolCall("1", "s1", "call-failed")).toBeUndefined();
const summary = ledger.summary({ uid: "1" });
expect(summary.totals).toEqual({
records: 3,
images: 1,
credits_succeeded: 30,
credits_pending: 30,
credits_failed: 60,
});
expect(summary.by_skill).toEqual([
{ skill_id: "poster", skill_name: "Demo", records: 2, images: 1, credits: 30 },
{ skill_id: "demo", skill_name: "Demo", records: 1, images: 0, credits: 0 },
]);
expect(ledger.list({ status: "pending" }).map((item) => item.id)).toEqual([pending.id]);
expect(ledger.list({ limit: 1 })).toHaveLength(1);
});
it("skips malformed lines instead of losing the ledger", () => {
const file = join(tempRoot(), "ledger.jsonl");
const ledger = new UsageLedger(file, silentLogger);
ledger.begin(usage());
appendFileSync(file, "{bad json\n", "utf8");
const reloaded = new UsageLedger(file, silentLogger);
expect(reloaded.list()).toHaveLength(1);
});
});