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,111 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { SkillCatalog } from "../src/skills/catalog.js";
|
||||
import { FakeSkillRepository, makeCatalog, silentLogger, skillFixture } from "./helpers.js";
|
||||
|
||||
describe("SkillCatalog (database)", () => {
|
||||
it("loads valid skills and exposes pricing metadata", async () => {
|
||||
const catalog = await makeCatalog([
|
||||
skillFixture({
|
||||
key: "poster",
|
||||
config: { pricePerImage: 30 },
|
||||
content: "---\ndefault_size: 1024x1536\ndefault_n: 2\nwatermark: false\n---\n# Skill",
|
||||
}),
|
||||
skillFixture({ key: "space", config: { pricePerImage: 50 } }),
|
||||
]);
|
||||
|
||||
expect(catalog.diagnostics()).toEqual([]);
|
||||
expect(catalog.list().map((skill) => skill.key)).toEqual(["poster", "space"]);
|
||||
expect(catalog.get("poster")?.displayName).toBe("Test poster");
|
||||
expect(catalog.toPublic(catalog.get("poster")!)).toMatchObject({
|
||||
key: "poster",
|
||||
display_name: "Test poster",
|
||||
price: { per_image: 30 },
|
||||
defaults: { size: "1024x1536", n: 2, watermark: false },
|
||||
});
|
||||
expect(catalog.promptCatalog()).toContain("skill_id: poster");
|
||||
expect(catalog.promptCatalog()).toContain("单价: 30 credits/张");
|
||||
});
|
||||
|
||||
it("parses size, quantity, watermark and tags from skill body", async () => {
|
||||
const catalog = await makeCatalog([
|
||||
skillFixture({
|
||||
key: "body-params",
|
||||
content: "---\ndefault_size: 1536x864\ndefault_n: 1\nmax_images: 3\ntags: [旅游, 海报]\n---\n# Body",
|
||||
}),
|
||||
skillFixture({
|
||||
key: "inline-params",
|
||||
content: "# 技能\n默认尺寸:1024x1820\n生成数量:2\n水印:否\n标签:电商、海报",
|
||||
}),
|
||||
]);
|
||||
|
||||
const body = catalog.get("body-params");
|
||||
expect(body?.manifest.defaults).toMatchObject({ size: "1536x864", n: 1 });
|
||||
expect(body?.manifest.max_images).toBe(3);
|
||||
expect(body?.manifest.tags).toEqual(["旅游", "海报"]);
|
||||
|
||||
const inline = catalog.get("inline-params");
|
||||
expect(inline?.manifest.defaults).toMatchObject({ size: "1024x1820", n: 2, watermark: false });
|
||||
expect(inline?.manifest.tags).toEqual(["电商", "海报"]);
|
||||
});
|
||||
|
||||
it("falls back to defaults when agent_skill_config is absent", async () => {
|
||||
const catalog = await makeCatalog([skillFixture({ key: "no-config", config: null })], { defaultPrice: 17 });
|
||||
const skill = catalog.get("no-config");
|
||||
expect(skill?.usesDefaults).toBe(true);
|
||||
expect(skill?.manifest.price.per_image).toBe(17);
|
||||
expect(skill?.manifest.model).toBe("gpt-image-2");
|
||||
expect(catalog.diagnostics().join("\n")).toContain("no agent_skill_config");
|
||||
});
|
||||
|
||||
it("rejects disabled, missing content, and invalid keys", async () => {
|
||||
const catalog = await makeCatalog([
|
||||
skillFixture({ key: "good" }),
|
||||
skillFixture({ key: "no-description", description: "" }),
|
||||
skillFixture({ key: "disabled", config: { enabled: false } }),
|
||||
skillFixture({ key: "library-disabled", libraryEnabled: false }),
|
||||
skillFixture({ key: "empty", content: "" }),
|
||||
skillFixture({ key: "bad_name" }),
|
||||
]);
|
||||
|
||||
expect(catalog.list().map((skill) => skill.key)).toEqual(["good", "no-description"]);
|
||||
expect(catalog.get("disabled")).toBeUndefined();
|
||||
expect(catalog.get("library-disabled")).toBeUndefined();
|
||||
const diagnostics = catalog.diagnostics().join("\n");
|
||||
expect(diagnostics).toContain("content is empty");
|
||||
expect(diagnostics).toContain("description is empty");
|
||||
expect(diagnostics).toContain("lowercase kebab-case");
|
||||
});
|
||||
|
||||
it("rejects invalid database config without crashing", async () => {
|
||||
const catalog = await makeCatalog([
|
||||
skillFixture({ key: "bad-price", config: { pricePerImage: -1 } }),
|
||||
]);
|
||||
|
||||
expect(catalog.list()).toHaveLength(0);
|
||||
expect(catalog.diagnostics().join("\n")).toContain("bad-price");
|
||||
});
|
||||
|
||||
it("refreshes the in-memory catalog from the repository", async () => {
|
||||
const repository = new FakeSkillRepository([skillFixture({ key: "first" })]);
|
||||
const catalog = new SkillCatalog({
|
||||
repository,
|
||||
defaultPrice: 10,
|
||||
defaultModel: "seedream@5.0",
|
||||
defaultSize: "1024x1024",
|
||||
currency: "credits",
|
||||
logger: silentLogger,
|
||||
});
|
||||
await catalog.load();
|
||||
expect(catalog.list().map((skill) => skill.key)).toEqual(["first"]);
|
||||
|
||||
repository.setSkills([skillFixture({ key: "second", config: { pricePerImage: 20 } })]);
|
||||
await catalog.refresh();
|
||||
expect(catalog.list().map((skill) => skill.key)).toEqual(["second"]);
|
||||
expect(catalog.get("first")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("requires at least one enabled skill", async () => {
|
||||
const catalog = await makeCatalog([]);
|
||||
expect(() => catalog.requireReady()).toThrow(/no enabled skills/);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user