agent-tools-params.test.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import { describe, it, expect, vi, beforeEach } from "vitest";
  2. import { createSubAgentTool } from "../agent/agent-tools.js";
  3. describe("SubAgentParams schema", () => {
  4. const mockPipeline = {} as any;
  5. const tool = createSubAgentTool(mockPipeline, null);
  6. const schema = tool.parameters;
  7. const props = (schema as any).properties;
  8. it("has architect params: title, genre, platform, language, targetChapters", () => {
  9. expect(props.title).toBeDefined();
  10. expect(props.genre).toBeDefined();
  11. expect(props.platform).toBeDefined();
  12. expect(props.language).toBeDefined();
  13. expect(props.targetChapters).toBeDefined();
  14. });
  15. it("has writer/architect param: chapterWordCount", () => {
  16. expect(props.chapterWordCount).toBeDefined();
  17. });
  18. it("has reviser param: mode", () => {
  19. expect(props.mode).toBeDefined();
  20. });
  21. it("has exporter params: format, approvedOnly", () => {
  22. expect(props.format).toBeDefined();
  23. expect(props.approvedOnly).toBeDefined();
  24. });
  25. it("has existing params: agent, instruction, bookId, chapterNumber", () => {
  26. expect(props.agent).toBeDefined();
  27. expect(props.instruction).toBeDefined();
  28. expect(props.bookId).toBeDefined();
  29. expect(props.chapterNumber).toBeDefined();
  30. });
  31. it("all new params have description with agent scope", () => {
  32. expect(props.title.description).toMatch(/architect/i);
  33. expect(props.genre.description).toMatch(/architect/i);
  34. expect(props.mode.description).toMatch(/reviser/i);
  35. expect(props.format.description).toMatch(/exporter/i);
  36. });
  37. });
  38. describe("architect agent — BookConfig construction", () => {
  39. let initBookMock: ReturnType<typeof vi.fn>;
  40. let tool: ReturnType<typeof createSubAgentTool>;
  41. beforeEach(() => {
  42. initBookMock = vi.fn(async () => {});
  43. const mockPipeline = { initBook: initBookMock } as any;
  44. tool = createSubAgentTool(mockPipeline, null);
  45. });
  46. it("passes complete BookConfig with schema params", async () => {
  47. await tool.execute("tc1", {
  48. agent: "architect",
  49. instruction: "Create a xuanhuan novel",
  50. title: "天道独行",
  51. genre: "xuanhuan",
  52. platform: "tomato",
  53. language: "zh",
  54. targetChapters: 100,
  55. chapterWordCount: 4000,
  56. });
  57. expect(initBookMock).toHaveBeenCalledOnce();
  58. const [bookConfig, options] = initBookMock.mock.calls[0];
  59. expect(bookConfig.title).toBe("天道独行");
  60. expect(bookConfig.genre).toBe("xuanhuan");
  61. expect(bookConfig.platform).toBe("tomato");
  62. expect(bookConfig.language).toBe("zh");
  63. expect(bookConfig.targetChapters).toBe(100);
  64. expect(bookConfig.chapterWordCount).toBe(4000);
  65. expect(bookConfig.status).toBe("outlining");
  66. expect(bookConfig.createdAt).toBeDefined();
  67. expect(options.externalContext).toBe("Create a xuanhuan novel");
  68. });
  69. it("uses defaults when optional params are omitted", async () => {
  70. await tool.execute("tc2", { agent: "architect", instruction: "Create a book", title: "Test Book" });
  71. const [bookConfig] = initBookMock.mock.calls[0];
  72. expect(bookConfig.genre).toBe("general");
  73. expect(bookConfig.platform).toBe("other");
  74. expect(bookConfig.language).toBe("zh");
  75. expect(bookConfig.targetChapters).toBe(200);
  76. expect(bookConfig.chapterWordCount).toBe(3000);
  77. });
  78. });
  79. describe("writer agent — wordCount passthrough", () => {
  80. let writeNextChapterMock: ReturnType<typeof vi.fn>;
  81. let tool: ReturnType<typeof createSubAgentTool>;
  82. beforeEach(() => {
  83. writeNextChapterMock = vi.fn(async () => ({ wordCount: 3000 }));
  84. const mockPipeline = { writeNextChapter: writeNextChapterMock } as any;
  85. tool = createSubAgentTool(mockPipeline, "existing-book");
  86. });
  87. it("passes chapterWordCount as wordCount", async () => {
  88. await tool.execute("tc1", { agent: "writer", instruction: "Write", bookId: "my-book", chapterWordCount: 5000 });
  89. expect(writeNextChapterMock).toHaveBeenCalledWith("my-book", 5000);
  90. });
  91. it("passes undefined when chapterWordCount omitted", async () => {
  92. await tool.execute("tc2", { agent: "writer", instruction: "Write", bookId: "my-book" });
  93. expect(writeNextChapterMock).toHaveBeenCalledWith("my-book", undefined);
  94. });
  95. });
  96. describe("auditor agent — rich return value", () => {
  97. it("returns issue details with severity", async () => {
  98. const auditDraftMock = vi.fn(async () => ({
  99. chapterNumber: 3, passed: false,
  100. issues: [
  101. { severity: "warning", description: "Pacing too fast" },
  102. { severity: "critical", description: "Name inconsistency" },
  103. ],
  104. }));
  105. const tool = createSubAgentTool({ auditDraft: auditDraftMock } as any, "book");
  106. const result = await tool.execute("tc1", { agent: "auditor", instruction: "Audit", bookId: "my-book", chapterNumber: 3 });
  107. const text = (result.content[0] as { type: "text"; text: string }).text;
  108. expect(text).toContain("FAILED");
  109. expect(text).toContain("2 issue(s)");
  110. expect(text).toContain("[warning]");
  111. expect(text).toContain("[critical]");
  112. expect(text).toContain("Pacing too fast");
  113. });
  114. });
  115. describe("reviser agent — mode field", () => {
  116. let reviseDraftMock: ReturnType<typeof vi.fn>;
  117. let tool: ReturnType<typeof createSubAgentTool>;
  118. beforeEach(() => {
  119. reviseDraftMock = vi.fn(async () => ({}));
  120. tool = createSubAgentTool({ reviseDraft: reviseDraftMock } as any, "book");
  121. });
  122. it("uses mode param directly", async () => {
  123. await tool.execute("tc1", { agent: "reviser", instruction: "Fix", bookId: "my-book", chapterNumber: 5, mode: "anti-detect" });
  124. expect(reviseDraftMock).toHaveBeenCalledWith("my-book", 5, "anti-detect");
  125. });
  126. it("defaults to spot-fix", async () => {
  127. await tool.execute("tc2", { agent: "reviser", instruction: "Fix", bookId: "my-book" });
  128. expect(reviseDraftMock).toHaveBeenCalledWith("my-book", undefined, "spot-fix");
  129. });
  130. });