chore: initial commit
Laimiao agent v2 service: agent orchestration, sessions, billing, gateway client, skills catalog, auth, config, plus tests.
This commit is contained in:
commit
6ec9778d45
53 files changed
+12340
No files matched your search
@@ -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);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user