@waterwx/dsh-novel-forge 1.1.9 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -1116,7 +1116,8 @@ async function complete(ctx, config, options) {
1116
1116
  messages,
1117
1117
  system: options.system,
1118
1118
  maxTokens: options.maxTokens ?? config.maxTokens,
1119
- temperature: options.temperature ?? .7
1119
+ temperature: options.temperature ?? .7,
1120
+ reasoningEffort: ReasoningEffortId(config.reasoningEffort ?? "off")
1120
1121
  };
1121
1122
  const assembler = new BlockAssembler();
1122
1123
  for await (const chunk of ctx.llm.stream(request)) assembler.push(chunk);
@@ -2747,15 +2748,21 @@ async function auditBatch(ctx, config, project, outputDir, batch) {
2747
2748
  return issues;
2748
2749
  }
2749
2750
  /** 全书一致性质检:LLM 分批扫描已生成章节 + 设定 + 事实库,聚合矛盾清单。 */
2750
- async function auditBook(ctx, config, project, outputDir) {
2751
+ async function auditBook(ctx, config, project, outputDir, onProgress) {
2751
2752
  const written = project.chapters.filter((c) => c.status !== "pending" && c.status !== "generating");
2752
- if (written.length === 0) return [];
2753
+ if (written.length === 0) {
2754
+ onProgress?.(0, 0);
2755
+ return [];
2756
+ }
2757
+ const totalBatches = Math.ceil(written.length / AUDIT_BATCH_SIZE);
2753
2758
  const all = [];
2759
+ onProgress?.(0, totalBatches);
2754
2760
  for (let i = 0; i < written.length; i += AUDIT_BATCH_SIZE) {
2755
2761
  const batch = written.slice(i, i + AUDIT_BATCH_SIZE);
2756
2762
  try {
2757
2763
  all.push(...await auditBatch(ctx, config, project, outputDir, batch));
2758
2764
  } catch {}
2765
+ onProgress?.(Math.min(Math.ceil((i + AUDIT_BATCH_SIZE) / AUDIT_BATCH_SIZE), totalBatches), totalBatches);
2759
2766
  }
2760
2767
  return all.slice(0, 50);
2761
2768
  }
@@ -4522,6 +4529,12 @@ const DEFAULT_PLAN_COUNT = 30;
4522
4529
  */
4523
4530
  function makeRoutes(deps) {
4524
4531
  const { ctx, getConfig, patchConfig } = deps;
4532
+ /** 全书质检实时状态(内存态,重启后回到 idle;用于 /status 暴露进度)。 */
4533
+ let auditState = {
4534
+ status: "idle",
4535
+ totalBatches: 0,
4536
+ completedBatches: 0
4537
+ };
4525
4538
  /** Guard helper: fence + method check. */
4526
4539
  const guard = (req, res, method) => {
4527
4540
  if (!isLoopbackRequest(req)) {
@@ -4573,7 +4586,8 @@ function makeRoutes(deps) {
4573
4586
  ...project,
4574
4587
  facts: (project.facts ?? []).slice(-80)
4575
4588
  } : void 0,
4576
- generatedFiles: listChapterFiles(config.outputDir)
4589
+ generatedFiles: listChapterFiles(config.outputDir),
4590
+ audit: auditState
4577
4591
  });
4578
4592
  }
4579
4593
  };
@@ -5478,13 +5492,41 @@ function makeRoutes(deps) {
5478
5492
  const config = getConfig();
5479
5493
  const project = requireProject(res);
5480
5494
  if (project === void 0) return;
5495
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
5496
+ auditState = {
5497
+ status: "running",
5498
+ startedAt,
5499
+ totalBatches: 0,
5500
+ completedBatches: 0
5501
+ };
5481
5502
  try {
5503
+ const issues = await auditBook(ctx, config, project, config.outputDir, (completed, total) => {
5504
+ auditState.totalBatches = total;
5505
+ auditState.completedBatches = completed;
5506
+ });
5507
+ const auditedChapters = project.chapters.filter((c) => c.status !== "pending" && c.status !== "generating").length;
5508
+ auditState = {
5509
+ status: "done",
5510
+ startedAt,
5511
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
5512
+ totalBatches: auditState.totalBatches,
5513
+ completedBatches: auditState.totalBatches,
5514
+ auditedChapters,
5515
+ issuesCount: issues.length,
5516
+ issues
5517
+ };
5482
5518
  writeJson(res, 200, {
5483
- issues: await auditBook(ctx, config, project, config.outputDir),
5484
- auditedChapters: project.chapters.filter((c) => c.status !== "pending" && c.status !== "generating").length,
5485
- auditedAt: (/* @__PURE__ */ new Date()).toISOString()
5519
+ issues,
5520
+ auditedChapters,
5521
+ auditedAt: auditState.finishedAt
5486
5522
  });
5487
5523
  } catch (error) {
5524
+ auditState = {
5525
+ ...auditState,
5526
+ status: "error",
5527
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
5528
+ error: error.message
5529
+ };
5488
5530
  writeJson(res, 500, { error: error.message });
5489
5531
  }
5490
5532
  }
@@ -6042,7 +6084,7 @@ function makeRoutes(deps) {
6042
6084
  return;
6043
6085
  }
6044
6086
  }
6045
- const missing = project.chapters.filter((c) => c.status !== "pending" && c.status !== "generating" && c.authorReview === void 0);
6087
+ const missing = project.chapters.filter((c) => c.status !== "pending" && c.status !== "generating" && c.status !== "error" && c.authorReview === void 0);
6046
6088
  if (missing.length === 0) {
6047
6089
  writeJson(res, 200, { count: 0 });
6048
6090
  return;
@@ -6324,6 +6366,12 @@ const Config = z.object({
6324
6366
  outputDir: z.string().default("D:\\ryan work\\harness\\novels\\归墟玉主"),
6325
6367
  provider: z.string().default("deepseek-official"),
6326
6368
  model: z.string().default("deepseek-v4-flash"),
6369
+ reasoningEffort: z.union([
6370
+ "off",
6371
+ "low",
6372
+ "high",
6373
+ "max"
6374
+ ]).default("off"),
6327
6375
  chapterChars: z.number().default(3500),
6328
6376
  maxTokens: z.number().default(12e3),
6329
6377
  reviewPassScore: z.number().default(70),
@@ -6337,6 +6385,7 @@ const DEFAULT_OUTLINE_PATH = "D:\\ryan work\\harness\\novels\\归墟玉主\\《
6337
6385
  const DEFAULT_OUTPUT_DIR = "D:\\ryan work\\harness\\novels\\归墟玉主";
6338
6386
  const DEFAULT_PROVIDER = "deepseek-official";
6339
6387
  const DEFAULT_MODEL = "deepseek-v4-flash";
6388
+ const DEFAULT_REASONING_EFFORT = "off";
6340
6389
  const DEFAULT_CHAPTER_CHARS = 3500;
6341
6390
  const DEFAULT_MAX_TOKENS = 12e3;
6342
6391
  const DEFAULT_REVIEW_PASS_SCORE = 70;
@@ -6354,6 +6403,7 @@ function resolveConfig(value) {
6354
6403
  outputDir: value?.outputDir ?? DEFAULT_OUTPUT_DIR,
6355
6404
  provider: value?.provider ?? DEFAULT_PROVIDER,
6356
6405
  model: value?.model ?? DEFAULT_MODEL,
6406
+ reasoningEffort: value?.reasoningEffort ?? DEFAULT_REASONING_EFFORT,
6357
6407
  chapterChars: value?.chapterChars ?? DEFAULT_CHAPTER_CHARS,
6358
6408
  maxTokens: value?.maxTokens ?? DEFAULT_MAX_TOKENS,
6359
6409
  reviewPassScore: value?.reviewPassScore ?? DEFAULT_REVIEW_PASS_SCORE,
@@ -6384,6 +6434,7 @@ function apply(ctx, config) {
6384
6434
  if (patch.outputDir !== void 0) next.outputDir = patch.outputDir;
6385
6435
  if (patch.provider !== void 0) next.provider = patch.provider;
6386
6436
  if (patch.model !== void 0) next.model = patch.model;
6437
+ if (patch.reasoningEffort !== void 0) next.reasoningEffort = patch.reasoningEffort;
6387
6438
  if (patch.chapterChars !== void 0) next.chapterChars = patch.chapterChars;
6388
6439
  if (patch.maxTokens !== void 0) next.maxTokens = patch.maxTokens;
6389
6440
  if (patch.reviewPassScore !== void 0) next.reviewPassScore = patch.reviewPassScore;