|
|
@@ -466,7 +466,7 @@ describe("ArchitectAgent", () => {
|
|
|
expect(result.pendingHooks).toContain("| H01 | 1 | mystery | open | 0 | 10章 | 中程 | 初始钩子 |");
|
|
|
});
|
|
|
|
|
|
- it("falls back to default current_state when section is missing from LLM output", async () => {
|
|
|
+ it("throws when a required foundation section is missing", async () => {
|
|
|
const agent = new ArchitectAgent({
|
|
|
client: {
|
|
|
provider: "openai",
|
|
|
@@ -484,16 +484,16 @@ describe("ArchitectAgent", () => {
|
|
|
});
|
|
|
|
|
|
const book: BookConfig = {
|
|
|
- id: "missing-state-book",
|
|
|
- title: "缺失状态卡",
|
|
|
+ id: "broken-book",
|
|
|
+ title: "Broken Book",
|
|
|
platform: "other",
|
|
|
genre: "other",
|
|
|
status: "active",
|
|
|
targetChapters: 20,
|
|
|
chapterWordCount: 2200,
|
|
|
language: "zh",
|
|
|
- createdAt: "2026-04-15T00:00:00.000Z",
|
|
|
- updatedAt: "2026-04-15T00:00:00.000Z",
|
|
|
+ createdAt: "2026-03-29T00:00:00.000Z",
|
|
|
+ updatedAt: "2026-03-29T00:00:00.000Z",
|
|
|
};
|
|
|
|
|
|
vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
|
@@ -505,27 +505,19 @@ describe("ArchitectAgent", () => {
|
|
|
"=== SECTION: volume_outline ===",
|
|
|
"# 卷纲",
|
|
|
"",
|
|
|
- "=== SECTION: book_rules ===",
|
|
|
- "---",
|
|
|
- "version: \"1.0\"",
|
|
|
- "---",
|
|
|
+ "=== SECTION: current_state ===",
|
|
|
+ "# 当前状态",
|
|
|
"",
|
|
|
"=== SECTION: pending_hooks ===",
|
|
|
- "| hook_id | 起始章节 | 类型 | 状态 | 最近推进 | 预期回收 | 备注 |",
|
|
|
- "| --- | --- | --- | --- | --- | --- | --- |",
|
|
|
- "| H01 | 1 | 主线 | 未开启 | 0 | 10章 | 初始钩子 |",
|
|
|
+ "# 伏笔池",
|
|
|
].join("\n"),
|
|
|
usage: ZERO_USAGE,
|
|
|
});
|
|
|
|
|
|
- const result = await agent.generateFoundation(book);
|
|
|
-
|
|
|
- expect(result.currentState).toContain("当前章节 | 0");
|
|
|
- expect(result.currentState).toContain("当前位置");
|
|
|
- expect(result.currentState).toContain("主角状态");
|
|
|
+ await expect(agent.generateFoundation(book)).rejects.toThrow(/book_rules/i);
|
|
|
});
|
|
|
|
|
|
- it("falls back to default pending_hooks when section is missing from LLM output", async () => {
|
|
|
+ it("passes maxTokens 16384 when generating foundation", async () => {
|
|
|
const agent = new ArchitectAgent({
|
|
|
client: {
|
|
|
provider: "openai",
|
|
|
@@ -543,19 +535,19 @@ describe("ArchitectAgent", () => {
|
|
|
});
|
|
|
|
|
|
const book: BookConfig = {
|
|
|
- id: "missing-hooks-book",
|
|
|
- title: "缺失伏笔池",
|
|
|
+ id: "max-tokens-book",
|
|
|
+ title: "Max Tokens Book",
|
|
|
platform: "other",
|
|
|
genre: "other",
|
|
|
status: "active",
|
|
|
targetChapters: 20,
|
|
|
chapterWordCount: 2200,
|
|
|
language: "zh",
|
|
|
- createdAt: "2026-04-15T00:00:00.000Z",
|
|
|
- updatedAt: "2026-04-15T00:00:00.000Z",
|
|
|
+ createdAt: "2026-03-29T00:00:00.000Z",
|
|
|
+ updatedAt: "2026-03-29T00:00:00.000Z",
|
|
|
};
|
|
|
|
|
|
- vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
|
+ const chatSpy = vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
|
.mockResolvedValue({
|
|
|
content: [
|
|
|
"=== SECTION: story_bible ===",
|
|
|
@@ -571,18 +563,24 @@ describe("ArchitectAgent", () => {
|
|
|
"",
|
|
|
"=== SECTION: current_state ===",
|
|
|
"# 当前状态",
|
|
|
+ "",
|
|
|
+ "=== SECTION: pending_hooks ===",
|
|
|
+ "| hook_id | 起始章节 | 类型 | 状态 | 最近推进 | 预期回收 | 备注 |",
|
|
|
+ "| --- | --- | --- | --- | --- | --- | --- |",
|
|
|
+ "| H01 | 1 | mystery | open | 0 | 10章 | 初始钩子 |",
|
|
|
].join("\n"),
|
|
|
usage: ZERO_USAGE,
|
|
|
});
|
|
|
|
|
|
- const result = await agent.generateFoundation(book);
|
|
|
+ await agent.generateFoundation(book);
|
|
|
|
|
|
- expect(result.pendingHooks).toContain("hook_id");
|
|
|
- expect(result.pendingHooks).toContain("起始章节");
|
|
|
- expect(result.pendingHooks).toContain("回收节奏");
|
|
|
+ expect(chatSpy).toHaveBeenCalledWith(
|
|
|
+ expect.any(Array),
|
|
|
+ expect.objectContaining({ temperature: 0.8, maxTokens: 16384 }),
|
|
|
+ );
|
|
|
});
|
|
|
|
|
|
- it("falls back to defaults when both current_state and pending_hooks are missing", async () => {
|
|
|
+ it("passes maxTokens 16384 when generating foundation from import", async () => {
|
|
|
const agent = new ArchitectAgent({
|
|
|
client: {
|
|
|
provider: "openai",
|
|
|
@@ -600,19 +598,19 @@ describe("ArchitectAgent", () => {
|
|
|
});
|
|
|
|
|
|
const book: BookConfig = {
|
|
|
- id: "missing-both-book",
|
|
|
- title: "都缺失",
|
|
|
+ id: "import-max-tokens-book",
|
|
|
+ title: "Import Max Tokens Book",
|
|
|
platform: "other",
|
|
|
genre: "other",
|
|
|
status: "active",
|
|
|
targetChapters: 20,
|
|
|
chapterWordCount: 2200,
|
|
|
language: "zh",
|
|
|
- createdAt: "2026-04-15T00:00:00.000Z",
|
|
|
- updatedAt: "2026-04-15T00:00:00.000Z",
|
|
|
+ createdAt: "2026-03-29T00:00:00.000Z",
|
|
|
+ updatedAt: "2026-03-29T00:00:00.000Z",
|
|
|
};
|
|
|
|
|
|
- vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
|
+ const chatSpy = vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
|
.mockResolvedValue({
|
|
|
content: [
|
|
|
"=== SECTION: story_bible ===",
|
|
|
@@ -625,17 +623,27 @@ describe("ArchitectAgent", () => {
|
|
|
"---",
|
|
|
"version: \"1.0\"",
|
|
|
"---",
|
|
|
+ "",
|
|
|
+ "=== SECTION: current_state ===",
|
|
|
+ "# 当前状态",
|
|
|
+ "",
|
|
|
+ "=== SECTION: pending_hooks ===",
|
|
|
+ "| hook_id | 起始章节 | 类型 | 状态 | 最近推进 | 预期回收 | 备注 |",
|
|
|
+ "| --- | --- | --- | --- | --- | --- | --- |",
|
|
|
+ "| H01 | 1 | mystery | open | 0 | 10章 | 初始钩子 |",
|
|
|
].join("\n"),
|
|
|
usage: ZERO_USAGE,
|
|
|
});
|
|
|
|
|
|
- const result = await agent.generateFoundation(book);
|
|
|
+ await agent.generateFoundationFromImport(book, "第一章正文");
|
|
|
|
|
|
- expect(result.currentState).toContain("当前章节 | 0");
|
|
|
- expect(result.pendingHooks).toContain("hook_id");
|
|
|
+ expect(chatSpy).toHaveBeenCalledWith(
|
|
|
+ expect.any(Array),
|
|
|
+ expect.objectContaining({ temperature: 0.5, maxTokens: 16384 }),
|
|
|
+ );
|
|
|
});
|
|
|
|
|
|
- it("throws when a required foundation section is missing", async () => {
|
|
|
+ it("passes maxTokens 16384 when generating fanfic foundation", async () => {
|
|
|
const agent = new ArchitectAgent({
|
|
|
client: {
|
|
|
provider: "openai",
|
|
|
@@ -653,10 +661,10 @@ describe("ArchitectAgent", () => {
|
|
|
});
|
|
|
|
|
|
const book: BookConfig = {
|
|
|
- id: "broken-book",
|
|
|
- title: "Broken Book",
|
|
|
+ id: "fanfic-max-tokens-book",
|
|
|
+ title: "Fanfic Max Tokens Book",
|
|
|
platform: "other",
|
|
|
- genre: "other",
|
|
|
+ genre: "fanfic",
|
|
|
status: "active",
|
|
|
targetChapters: 20,
|
|
|
chapterWordCount: 2200,
|
|
|
@@ -665,7 +673,7 @@ describe("ArchitectAgent", () => {
|
|
|
updatedAt: "2026-03-29T00:00:00.000Z",
|
|
|
};
|
|
|
|
|
|
- vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
|
+ const chatSpy = vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
|
.mockResolvedValue({
|
|
|
content: [
|
|
|
"=== SECTION: story_bible ===",
|
|
|
@@ -674,15 +682,27 @@ describe("ArchitectAgent", () => {
|
|
|
"=== SECTION: volume_outline ===",
|
|
|
"# 卷纲",
|
|
|
"",
|
|
|
+ "=== SECTION: book_rules ===",
|
|
|
+ "---",
|
|
|
+ "version: \"1.0\"",
|
|
|
+ "---",
|
|
|
+ "",
|
|
|
"=== SECTION: current_state ===",
|
|
|
"# 当前状态",
|
|
|
"",
|
|
|
"=== SECTION: pending_hooks ===",
|
|
|
- "# 伏笔池",
|
|
|
+ "| hook_id | 起始章节 | 类型 | 状态 | 最近推进 | 预期回收 | 备注 |",
|
|
|
+ "| --- | --- | --- | --- | --- | --- | --- |",
|
|
|
+ "| H01 | 1 | mystery | open | 0 | 10章 | 初始钩子 |",
|
|
|
].join("\n"),
|
|
|
usage: ZERO_USAGE,
|
|
|
});
|
|
|
|
|
|
- await expect(agent.generateFoundation(book)).rejects.toThrow(/book_rules/i);
|
|
|
+ await agent.generateFanficFoundation(book, "正典文本", "canon");
|
|
|
+
|
|
|
+ expect(chatSpy).toHaveBeenCalledWith(
|
|
|
+ expect.any(Array),
|
|
|
+ expect.objectContaining({ temperature: 0.7, maxTokens: 16384 }),
|
|
|
+ );
|
|
|
});
|
|
|
});
|