@waterwx/dsh-novel-forge 1.2.1-alpha → 1.3.0-alpha
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/README.md +4 -5
- package/lib/client.js +2470 -858
- package/lib/index.js +2155 -388
- package/lib/types/action-guard.d.ts +1 -1
- package/lib/types/assets.d.ts +7 -7
- package/lib/types/client/api.d.ts +31 -1
- package/lib/types/client/llmLive.d.ts +12 -14
- package/lib/types/client/panel/LiveFeedLog.d.ts +5 -0
- package/lib/types/client/panel/ModelManager.d.ts +2 -2
- package/lib/types/client/panel/PromptSlotsView.d.ts +11 -0
- package/lib/types/client/panel/TensionView.d.ts +14 -0
- package/lib/types/client/panel/TimelineView.d.ts +14 -0
- package/lib/types/engine.d.ts +40 -2
- package/lib/types/index.d.ts +4 -0
- package/lib/types/llm-live.d.ts +65 -34
- package/lib/types/llm-retry.d.ts +38 -0
- package/lib/types/market-radar-sources.d.ts +1 -1
- package/lib/types/prompt-slots.d.ts +57 -0
- package/lib/types/prompting.d.ts +2 -2
- package/lib/types/protocol.d.ts +283 -3
- package/lib/types/snapshots.d.ts +46 -0
- package/lib/types/stage-contract.d.ts +1 -1
- package/lib/types/tension.d.ts +65 -0
- package/lib/types/timeline.d.ts +37 -0
- package/package.json +3 -2
- package/src/action-guard.ts +1 -1
- package/src/assets.ts +300 -268
- package/src/assistant.ts +45 -29
- package/src/client/api.ts +76 -2
- package/src/client/llmLive.ts +26 -16
- package/src/client/panel/AssetsTab.tsx +2 -2
- package/src/client/panel/LiveFeedLog.tsx +204 -67
- package/src/client/panel/ModelManager.tsx +2 -2
- package/src/client/panel/NovelPanel.tsx +127 -13
- package/src/client/panel/PromptSlotsView.tsx +97 -0
- package/src/client/panel/SettingsView.tsx +35 -0
- package/src/client/panel/TensionView.tsx +196 -0
- package/src/client/panel/TimelineView.tsx +192 -0
- package/src/engine.ts +549 -108
- package/src/index.ts +12 -0
- package/src/llm-live.ts +260 -68
- package/src/llm-retry.ts +77 -0
- package/src/market-radar-sources.ts +173 -173
- package/src/prompt-slots.ts +125 -0
- package/src/prompting.ts +8 -8
- package/src/protocol.ts +314 -3
- package/src/routes.ts +325 -6
- package/src/run.ts +4 -0
- package/src/snapshots.ts +174 -0
- package/src/stage-contract.ts +189 -189
- package/src/tension.ts +240 -0
- package/src/timeline.ts +159 -0
package/lib/types/protocol.d.ts
CHANGED
|
@@ -23,6 +23,18 @@ export declare const NOVEL_API: {
|
|
|
23
23
|
readonly director: "/api/dsh-novel-forge/director";
|
|
24
24
|
readonly directorTodos: "/api/dsh-novel-forge/director/todos";
|
|
25
25
|
readonly llmLive: "/api/dsh-novel-forge/llm-live/stream";
|
|
26
|
+
/** 查看某次调用实际发送的 Prompt(按 sessionId)。 */
|
|
27
|
+
readonly llmPrompt: "/api/dsh-novel-forge/llm-live/prompt";
|
|
28
|
+
/** 清零本次运行的用量账本。 */
|
|
29
|
+
readonly usageReset: "/api/dsh-novel-forge/usage/reset";
|
|
30
|
+
/** 章节历史版本(快照)。 */
|
|
31
|
+
readonly snapshot: "/api/dsh-novel-forge/snapshot";
|
|
32
|
+
/** 张力曲线(GET 数据 / POST 设置)。 */
|
|
33
|
+
readonly tension: "/api/dsh-novel-forge/tension";
|
|
34
|
+
/** 提示词槽位(GET 列表 / POST 写入)。 */
|
|
35
|
+
readonly promptSlots: "/api/dsh-novel-forge/prompt-slots";
|
|
36
|
+
/** 故事时间线(GET 列表 / POST 操作)。 */
|
|
37
|
+
readonly timeline: "/api/dsh-novel-forge/timeline";
|
|
26
38
|
readonly marketRadar: "/api/dsh-novel-forge/market-radar";
|
|
27
39
|
readonly marketRadarScan: "/api/dsh-novel-forge/market-radar/scan";
|
|
28
40
|
readonly marketRadarApply: "/api/dsh-novel-forge/market-radar/apply";
|
|
@@ -289,6 +301,10 @@ export interface ChapterPlan {
|
|
|
289
301
|
review?: ReviewReport;
|
|
290
302
|
/** 作者复盘:钩子兑现/结尾钩子/剧情线推进/连续性/节奏趋势(生成后自动)。 */
|
|
291
303
|
authorReview?: AuthorReview;
|
|
304
|
+
/** 本章目标张力 0-100(规划或作者设定;用于张力曲线)。 */
|
|
305
|
+
tension?: number;
|
|
306
|
+
/** 本章实际张力 0-100(审稿时模型打分;缺省时回落到 review.tension)。 */
|
|
307
|
+
tensionActual?: number;
|
|
292
308
|
/**
|
|
293
309
|
* 待确认草稿:润色(去AI味)或整章重写的产物正文。生成时先存这里,
|
|
294
310
|
* 用户看过对比后点「采纳」才覆盖正文文件;点「放弃」则丢弃。刷新页面不丢失。
|
|
@@ -366,6 +382,8 @@ export interface ReviewReport {
|
|
|
366
382
|
word: string;
|
|
367
383
|
count: number;
|
|
368
384
|
}>;
|
|
385
|
+
/** 本章张力 0-100(审稿模型评分;用于张力曲线的"实际"值)。 */
|
|
386
|
+
tension?: number;
|
|
369
387
|
/** When the review ran. */
|
|
370
388
|
reviewedAt: string;
|
|
371
389
|
}
|
|
@@ -560,6 +578,118 @@ export interface StoryBible {
|
|
|
560
578
|
generatedAt?: string;
|
|
561
579
|
}
|
|
562
580
|
/** A planted/active/resolved foreshadowing thread. */
|
|
581
|
+
/**
|
|
582
|
+
* 张力曲线形状预设:
|
|
583
|
+
* `escalation` 递进爬升 / `suspense` 悬疑加压 / `wave` 波浪呼吸 /
|
|
584
|
+
* `frontLoad` 前紧后稳 / `flat` 平稳推进 / `custom` 自定义。
|
|
585
|
+
*/
|
|
586
|
+
export type TensionCurvePreset = 'escalation' | 'suspense' | 'wave' | 'frontLoad' | 'flat' | 'custom';
|
|
587
|
+
/** 张力曲线配置(随项目保存)。 */
|
|
588
|
+
export interface TensionCurveConfig {
|
|
589
|
+
preset: TensionCurvePreset;
|
|
590
|
+
/** preset=custom 时的逐章采样(0-100;缺省按章节数取默认值)。 */
|
|
591
|
+
custom?: number[];
|
|
592
|
+
}
|
|
593
|
+
/** 张力曲线的一条问题(实际 vs 目标 / 曲线形状)。 */
|
|
594
|
+
export interface TensionIssue {
|
|
595
|
+
severity: 'high' | 'medium' | 'low';
|
|
596
|
+
chapters: number[];
|
|
597
|
+
item: string;
|
|
598
|
+
suggestion: string;
|
|
599
|
+
}
|
|
600
|
+
/** 张力曲线数据点(面板绘图用)。 */
|
|
601
|
+
export interface TensionCurvePoint {
|
|
602
|
+
no: number;
|
|
603
|
+
title: string;
|
|
604
|
+
/** 目标张力。 */
|
|
605
|
+
target?: number;
|
|
606
|
+
/** 实际张力(审稿打分)。 */
|
|
607
|
+
actual?: number;
|
|
608
|
+
/** 参考曲线在此处的取值。 */
|
|
609
|
+
reference?: number;
|
|
610
|
+
}
|
|
611
|
+
/** GET /tension 响应。 */
|
|
612
|
+
export interface TensionResponse {
|
|
613
|
+
preset: TensionCurvePreset;
|
|
614
|
+
points: TensionCurvePoint[];
|
|
615
|
+
/** 规则核对发现的问题。 */
|
|
616
|
+
issues: TensionIssue[];
|
|
617
|
+
}
|
|
618
|
+
/** POST /tension 请求。 */
|
|
619
|
+
export interface TensionRequest {
|
|
620
|
+
op: 'preset' | 'set' | 'clear';
|
|
621
|
+
preset?: TensionCurvePreset;
|
|
622
|
+
chapterNo?: number;
|
|
623
|
+
/** op=set 时的目标张力(0-100)。 */
|
|
624
|
+
tension?: number;
|
|
625
|
+
}
|
|
626
|
+
/** 允许作者编辑的提示词槽位 id(白名单)。 */
|
|
627
|
+
export type PromptSlotId = 'styleExtra' | 'antiAiExtra' | 'dialogueExtra' | 'endingHookExtra';
|
|
628
|
+
/** 一个提示词槽位的当前内容。 */
|
|
629
|
+
export interface PromptSlotState {
|
|
630
|
+
id: PromptSlotId;
|
|
631
|
+
label: string;
|
|
632
|
+
hint: string;
|
|
633
|
+
placeholder: string;
|
|
634
|
+
maxChars: number;
|
|
635
|
+
value: string;
|
|
636
|
+
}
|
|
637
|
+
/** GET /prompt-slots 响应。 */
|
|
638
|
+
export interface PromptSlotsResponse {
|
|
639
|
+
slots: PromptSlotState[];
|
|
640
|
+
}
|
|
641
|
+
/** POST /prompt-slots 请求:设置或清空一个槽位。 */
|
|
642
|
+
export interface PromptSlotsRequest {
|
|
643
|
+
id: PromptSlotId;
|
|
644
|
+
/** 空串 = 清空该槽位(恢复默认)。 */
|
|
645
|
+
value: string;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* 故事时间线事件:从章节正文抽取的「故事内时间点 + 地点 + 参与角色 + 事件」。
|
|
649
|
+
* 用途:后续章节的时间连续性与地点衔接(比编年录更强调"顺序")。
|
|
650
|
+
*/
|
|
651
|
+
export interface TimelineEvent {
|
|
652
|
+
/** 稳定 id(时间线内唯一)。 */
|
|
653
|
+
id: string;
|
|
654
|
+
/** 所属章节号。 */
|
|
655
|
+
chapterNo: number;
|
|
656
|
+
/** 故事内时间点(如「第三日黄昏」「入宗三个月后」「同日夜」)。 */
|
|
657
|
+
time: string;
|
|
658
|
+
/** 相对顺序(同书内递增;无法判断时缺省)。用于检测时间倒流。 */
|
|
659
|
+
order?: number;
|
|
660
|
+
/** 地点。 */
|
|
661
|
+
place: string;
|
|
662
|
+
/** 参与角色(角色名)。 */
|
|
663
|
+
characters: string[];
|
|
664
|
+
/** 事件一句话。 */
|
|
665
|
+
event: string;
|
|
666
|
+
/** 来源:模型抽取 / 作者手改。 */
|
|
667
|
+
source: 'extracted' | 'manual';
|
|
668
|
+
createdAt: string;
|
|
669
|
+
}
|
|
670
|
+
/** 时间线矛盾(规则初筛 + AI 复核)。 */
|
|
671
|
+
export interface TimelineIssue {
|
|
672
|
+
severity: 'high' | 'medium' | 'low';
|
|
673
|
+
/** 定位到的章节(可多个)。 */
|
|
674
|
+
chapters: number[];
|
|
675
|
+
/** 问题描述。 */
|
|
676
|
+
item: string;
|
|
677
|
+
/** 建议修法。 */
|
|
678
|
+
suggestion: string;
|
|
679
|
+
}
|
|
680
|
+
/** GET /timeline 响应。 */
|
|
681
|
+
export interface TimelineResponse {
|
|
682
|
+
events: TimelineEvent[];
|
|
683
|
+
/** 规则初筛发现的问题(瞬时计算,不落盘)。 */
|
|
684
|
+
issues?: TimelineIssue[];
|
|
685
|
+
}
|
|
686
|
+
/** POST /timeline 请求:op=extract 抽单章 / check 全量检查 / update 手改 / remove 删除 / clear 清空。 */
|
|
687
|
+
export interface TimelineRequest {
|
|
688
|
+
op: 'extract' | 'check' | 'update' | 'remove' | 'clear';
|
|
689
|
+
chapterNo?: number;
|
|
690
|
+
event?: TimelineEvent;
|
|
691
|
+
id?: string;
|
|
692
|
+
}
|
|
563
693
|
export interface Foreshadow {
|
|
564
694
|
/** Stable id. */
|
|
565
695
|
id: string;
|
|
@@ -957,6 +1087,12 @@ export interface ProjectState {
|
|
|
957
1087
|
roles?: RoleRecord[];
|
|
958
1088
|
/** 人物志:角色当前状态聚合结果(从编年录刷新后存档,打开页面直接显示)。 */
|
|
959
1089
|
roleStatus?: RoleStatusCard[];
|
|
1090
|
+
/** 故事时间线(逐章抽取:时间点 / 地点 / 参与角色 / 事件)。 */
|
|
1091
|
+
timeline?: TimelineEvent[];
|
|
1092
|
+
/** 张力曲线配置(形状预设 + 自定义采样)。 */
|
|
1093
|
+
tensionCurve?: TensionCurveConfig;
|
|
1094
|
+
/** 作者自定义的提示词槽位(白名单见 prompt-slots.ts)。 */
|
|
1095
|
+
promptSlots?: Partial<Record<PromptSlotId, string>>;
|
|
960
1096
|
/** 本书参数(写作/审稿/推理/模型快切;未设字段回退全局 config)。 */
|
|
961
1097
|
bookSettings?: BookSettings;
|
|
962
1098
|
/** ISO timestamps. */
|
|
@@ -1099,6 +1235,13 @@ export interface NovelConfig {
|
|
|
1099
1235
|
reviewModel?: string;
|
|
1100
1236
|
/** 全书质检(审计)用模型(缺省用 model)。 */
|
|
1101
1237
|
auditModel?: string;
|
|
1238
|
+
/**
|
|
1239
|
+
* 备用模型:主模型调用失败(网络/限流/额度/服务不可用)时自动切换重试一次。
|
|
1240
|
+
* 留空表示不切换。批量连写时能显著减少"整批中断"。
|
|
1241
|
+
*/
|
|
1242
|
+
fallbackModel?: string;
|
|
1243
|
+
/** 是否在出章后自动抽取故事时间线(失败不阻断出章)。默认开。 */
|
|
1244
|
+
autoTimeline?: boolean;
|
|
1102
1245
|
/** LLM reasoning effort: off = no thinking; low/high/max = thinking intensity. */
|
|
1103
1246
|
reasoningEffort: 'off' | 'low' | 'high' | 'max';
|
|
1104
1247
|
/** 分析类任务(提炼/拆书/反推大纲等)的推理档位;默认 low,不受上面写作档位影响。 */
|
|
@@ -1177,6 +1320,119 @@ export interface StageInfo {
|
|
|
1177
1320
|
/** 为什么处于该阶段。 */
|
|
1178
1321
|
reason: string;
|
|
1179
1322
|
}
|
|
1323
|
+
/** 单次 LLM 调用的 token 用量(来自 dsh-llm 的 usage chunk)。 */
|
|
1324
|
+
export interface LlmTokenUsage {
|
|
1325
|
+
inputTokens: number;
|
|
1326
|
+
outputTokens: number;
|
|
1327
|
+
/** 供应商给出的总用量(缺失时由输入+输出推导)。 */
|
|
1328
|
+
totalTokens?: number;
|
|
1329
|
+
/** 思考(reasoning)通道用量。 */
|
|
1330
|
+
reasoningTokens?: number;
|
|
1331
|
+
/** 命中缓存的输入用量。 */
|
|
1332
|
+
cacheReadTokens?: number;
|
|
1333
|
+
}
|
|
1334
|
+
/** LLM 实况帧:宿主 → 浏览器的 SSE 事件(每次调用一组 session_* 事件)。 */
|
|
1335
|
+
export interface LlmLiveFrame {
|
|
1336
|
+
type: 'session_started' | 'output_delta' | 'reasoning_delta' | 'phase_changed' | 'session_completed';
|
|
1337
|
+
/** 宿主生成,形如 ll-<base36>-<n>。 */
|
|
1338
|
+
sessionId: string;
|
|
1339
|
+
/** 本次调用用途(如 正文生成/审稿/章节规划)。 */
|
|
1340
|
+
label?: string;
|
|
1341
|
+
/** 使用模型。 */
|
|
1342
|
+
model?: string;
|
|
1343
|
+
/** 事件时间(ISO)。 */
|
|
1344
|
+
at: string;
|
|
1345
|
+
/** session_started:交互上下文。 */
|
|
1346
|
+
context?: {
|
|
1347
|
+
taskId?: string;
|
|
1348
|
+
interactionId: string;
|
|
1349
|
+
};
|
|
1350
|
+
/** output_delta / reasoning_delta:本次增量文本。 */
|
|
1351
|
+
content?: string;
|
|
1352
|
+
/** session 累计字符数。 */
|
|
1353
|
+
totalChars?: number;
|
|
1354
|
+
/** reasoning 累计字符数。 */
|
|
1355
|
+
totalReasoningChars?: number;
|
|
1356
|
+
/** phase_changed:新阶段。 */
|
|
1357
|
+
phase?: 'requesting' | 'streaming' | 'completed' | 'failed';
|
|
1358
|
+
/** phase_changed:阶段描述。 */
|
|
1359
|
+
phaseMessage?: string;
|
|
1360
|
+
/** session_completed:最终预览(截断)。 */
|
|
1361
|
+
preview?: string;
|
|
1362
|
+
/** session_completed:失败信息。 */
|
|
1363
|
+
error?: string;
|
|
1364
|
+
/** session_completed:本次用量(供应商未上报时缺省)。 */
|
|
1365
|
+
usage?: LlmTokenUsage;
|
|
1366
|
+
/** session_completed:本次调用耗时(毫秒)。 */
|
|
1367
|
+
elapsedMs?: number;
|
|
1368
|
+
/** session_completed:首字耗时(毫秒;未拿到文本时缺省)。 */
|
|
1369
|
+
firstTokenMs?: number;
|
|
1370
|
+
/** session_started:是否已记录可查看的 Prompt(面板据此显示「查看 Prompt」)。 */
|
|
1371
|
+
hasPrompt?: boolean;
|
|
1372
|
+
/** session_started:Prompt 字符数(system + user)。 */
|
|
1373
|
+
promptChars?: number;
|
|
1374
|
+
}
|
|
1375
|
+
/** 按用途分组的用量。 */
|
|
1376
|
+
export interface LlmUsageBucket {
|
|
1377
|
+
label: string;
|
|
1378
|
+
calls: number;
|
|
1379
|
+
failed: number;
|
|
1380
|
+
inputTokens: number;
|
|
1381
|
+
outputTokens: number;
|
|
1382
|
+
reasoningTokens: number;
|
|
1383
|
+
elapsedMs: number;
|
|
1384
|
+
}
|
|
1385
|
+
/** 本次运行(进程内,重启归零)的 LLM 用量汇总。 */
|
|
1386
|
+
export interface LlmUsageSummary {
|
|
1387
|
+
calls: number;
|
|
1388
|
+
failed: number;
|
|
1389
|
+
inputTokens: number;
|
|
1390
|
+
outputTokens: number;
|
|
1391
|
+
reasoningTokens: number;
|
|
1392
|
+
totalTokens: number;
|
|
1393
|
+
elapsedMs: number;
|
|
1394
|
+
/** 供应商未上报 usage 的调用次数(用于说明数据为何不完整)。 */
|
|
1395
|
+
callsWithoutUsage: number;
|
|
1396
|
+
byLabel: LlmUsageBucket[];
|
|
1397
|
+
}
|
|
1398
|
+
/** 一次调用的 Prompt 记录(供面板查看「实际发送了什么」)。 */
|
|
1399
|
+
export interface LlmPromptRecord {
|
|
1400
|
+
sessionId: string;
|
|
1401
|
+
label?: string;
|
|
1402
|
+
model?: string;
|
|
1403
|
+
at: string;
|
|
1404
|
+
system?: string;
|
|
1405
|
+
user?: string;
|
|
1406
|
+
/** system + user 的字符数(记录被截断时仍给出原始长度)。 */
|
|
1407
|
+
chars: number;
|
|
1408
|
+
/** 记录本身是否被截断。 */
|
|
1409
|
+
truncated?: boolean;
|
|
1410
|
+
}
|
|
1411
|
+
/** 章节历史版本(快照):覆盖写入前自动留存,可回滚。 */
|
|
1412
|
+
export interface ChapterSnapshot {
|
|
1413
|
+
id: string;
|
|
1414
|
+
chapterNo: number;
|
|
1415
|
+
/** 快照文件(相对书目录)。 */
|
|
1416
|
+
file: string;
|
|
1417
|
+
/** 留存原因(生成覆盖 / 采纳草稿 / 修订 / 手工)。 */
|
|
1418
|
+
reason: string;
|
|
1419
|
+
chars: number;
|
|
1420
|
+
at: string;
|
|
1421
|
+
}
|
|
1422
|
+
/** GET /snapshot 响应。 */
|
|
1423
|
+
export interface SnapshotResponse {
|
|
1424
|
+
snapshots: ChapterSnapshot[];
|
|
1425
|
+
/** 快照占用字节数。 */
|
|
1426
|
+
bytes: number;
|
|
1427
|
+
}
|
|
1428
|
+
/** POST /snapshot 请求:op=create 手工存档 / restore 回滚 / remove 删除 / prune 只留最近 N 个。 */
|
|
1429
|
+
export interface SnapshotRequest {
|
|
1430
|
+
op: 'create' | 'restore' | 'remove' | 'prune';
|
|
1431
|
+
chapterNo?: number;
|
|
1432
|
+
id?: string;
|
|
1433
|
+
/** prune:每章保留数量。 */
|
|
1434
|
+
keep?: number;
|
|
1435
|
+
}
|
|
1180
1436
|
export interface StatusResponse {
|
|
1181
1437
|
config: NovelConfig;
|
|
1182
1438
|
/** The persisted project, when one exists in the output dir. */
|
|
@@ -1187,6 +1443,8 @@ export interface StatusResponse {
|
|
|
1187
1443
|
audit?: AuditStatus;
|
|
1188
1444
|
/** 宿主按项目真实状态算出的创作阶段(面板角标 / 外部自动化共用)。 */
|
|
1189
1445
|
stage?: StageInfo;
|
|
1446
|
+
/** 本次运行(进程内)的 LLM 用量汇总:调用次数 / 输入·输出·思考 token / 耗时。 */
|
|
1447
|
+
usage?: LlmUsageSummary;
|
|
1190
1448
|
/**
|
|
1191
1449
|
* 截断声明:本次响应中被裁掉的字段说明(如「编年录:共 320 条,仅返回最近 80 条」)。
|
|
1192
1450
|
* 消费方不得把缺失字段当作"不存在",需要完整数据时走专用接口。
|
|
@@ -1395,13 +1653,31 @@ export interface ChapterResponse {
|
|
|
1395
1653
|
markdown: string;
|
|
1396
1654
|
}
|
|
1397
1655
|
/** POST /export request/response. */
|
|
1656
|
+
/**
|
|
1657
|
+
* 导出范围:
|
|
1658
|
+
* - `book` 整本正文(txt / md)
|
|
1659
|
+
* - `settings` 项目设定(总纲 / 道藏 / 大世界 / 写作资产 / 卷首语)
|
|
1660
|
+
* - `plan` 规划(卷计划 + 章节计划,含每章目标/要点/钩子/硬事实)
|
|
1661
|
+
* - `characters` 角色(角色库 + 人物志当前状态)
|
|
1662
|
+
* - `review` 质检记录(逐章审稿 + 作者复盘 + 伏笔/剧情线状态)
|
|
1663
|
+
* - `project` 项目 JSON(完整状态,用于备份/迁移;仅 json 格式)
|
|
1664
|
+
*/
|
|
1665
|
+
export type ExportScope = 'book' | 'settings' | 'plan' | 'characters' | 'review' | 'project';
|
|
1666
|
+
/** 导出范围全集(宿主校验用;顺序即 UI 展示顺序)。 */
|
|
1667
|
+
export declare const EXPORT_SCOPE_VALUES: readonly ExportScope[];
|
|
1668
|
+
/** 导出范围的中文标签。 */
|
|
1669
|
+
export declare const EXPORT_SCOPE_LABELS: Record<ExportScope, string>;
|
|
1398
1670
|
export interface ExportRequest {
|
|
1399
|
-
format: 'txt' | 'md';
|
|
1671
|
+
format: 'txt' | 'md' | 'json';
|
|
1672
|
+
/** 缺省 `book`(兼容旧调用)。 */
|
|
1673
|
+
scope?: ExportScope;
|
|
1400
1674
|
}
|
|
1401
1675
|
export interface ExportResponse {
|
|
1402
1676
|
file: string;
|
|
1403
1677
|
chars: number;
|
|
1404
1678
|
chapters: number;
|
|
1679
|
+
scope: ExportScope;
|
|
1680
|
+
format: 'txt' | 'md' | 'json';
|
|
1405
1681
|
}
|
|
1406
1682
|
/** POST /config request: patch any subset of the runtime config. */
|
|
1407
1683
|
export interface ConfigPatch {
|
|
@@ -1412,6 +1688,10 @@ export interface ConfigPatch {
|
|
|
1412
1688
|
generateModel?: string;
|
|
1413
1689
|
reviewModel?: string;
|
|
1414
1690
|
auditModel?: string;
|
|
1691
|
+
/** 备用模型(主模型失败时自动切换重试一次;空串 = 关闭)。 */
|
|
1692
|
+
fallbackModel?: string;
|
|
1693
|
+
/** 出章后自动抽取故事时间线(默认开)。 */
|
|
1694
|
+
autoTimeline?: boolean;
|
|
1415
1695
|
reasoningEffort?: 'off' | 'low' | 'high' | 'max';
|
|
1416
1696
|
analysisReasoning?: 'off' | 'low' | 'high' | 'max';
|
|
1417
1697
|
chapterChars?: number;
|
|
@@ -1554,7 +1834,7 @@ export interface AntiAiRule {
|
|
|
1554
1834
|
/** 作用域(缺省 all)。 */
|
|
1555
1835
|
scope?: Array<'writing' | 'review' | 'polish' | 'all'>;
|
|
1556
1836
|
}
|
|
1557
|
-
/**
|
|
1837
|
+
/** 预置叙事风格模板(一键绑定无需样本文本)。 */
|
|
1558
1838
|
export interface StyleTemplateNarrativeRules {
|
|
1559
1839
|
progressionMode?: string;
|
|
1560
1840
|
sceneUnitPattern?: string[];
|
|
@@ -1622,7 +1902,7 @@ export interface StyleTemplate {
|
|
|
1622
1902
|
/** 该模板默认绑定的反 AI 规则 key(内置规则名)。 */
|
|
1623
1903
|
defaultAntiAiRuleKeys: string[];
|
|
1624
1904
|
}
|
|
1625
|
-
/**
|
|
1905
|
+
/** 起始风格画像:无样本文本也能快速绑定一套写法。 */
|
|
1626
1906
|
export interface StarterStyleProfile {
|
|
1627
1907
|
/** stable key(如 starter-power-up)。 */
|
|
1628
1908
|
key: string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 章节历史版本(快照):覆盖写入前自动留存正文,支持列表 / 回滚 / 清理。
|
|
3
|
+
*
|
|
4
|
+
* 为什么需要它:生成、按意见重写、去 AI 味润色、采纳草稿都会**覆盖** `第N章_*.md`,
|
|
5
|
+
* 一旦改差(尤其是自动修订把好稿改坏)就没有退路。这里在覆盖前把旧稿存到
|
|
6
|
+
* `<书目录>/snapshots/`,并维护一份 `snapshots/index.json` 索引。
|
|
7
|
+
*
|
|
8
|
+
* 纯 Node 文件操作,不依赖模型;路由层只做参数校验与响应。
|
|
9
|
+
*/
|
|
10
|
+
import type { ChapterPlan, ChapterSnapshot } from './protocol.ts';
|
|
11
|
+
/** 快照目录名(书目录下)。 */
|
|
12
|
+
export declare const SNAPSHOT_DIR = "snapshots";
|
|
13
|
+
/** 每章默认保留的快照数(超出后按时间淘汰最旧的)。 */
|
|
14
|
+
export declare const DEFAULT_SNAPSHOT_KEEP = 20;
|
|
15
|
+
/** 快照目录绝对路径。 */
|
|
16
|
+
export declare function snapshotDir(outputDir: string): string;
|
|
17
|
+
/** 读取索引(缺失/损坏时返回空数组)。 */
|
|
18
|
+
export declare function loadSnapshots(outputDir: string): ChapterSnapshot[];
|
|
19
|
+
/** 快照占用字节数(索引 + 正文)。 */
|
|
20
|
+
export declare function snapshotsBytes(outputDir: string): number;
|
|
21
|
+
/**
|
|
22
|
+
* 为一个章节留存快照(覆盖写入前调用)。
|
|
23
|
+
*
|
|
24
|
+
* @param outputDir 书目录。
|
|
25
|
+
* @param chapter 章节(需要已存在正文文件)。
|
|
26
|
+
* @param reason 留存原因(生成覆盖 / 采纳草稿 / 修订 / 润色 / 手工)。
|
|
27
|
+
* @param keep 每章保留数量(超出淘汰最旧)。
|
|
28
|
+
* @returns 新建的快照;无正文文件时返回 undefined(不产生空快照)。
|
|
29
|
+
*/
|
|
30
|
+
export declare function createSnapshot(outputDir: string, chapter: ChapterPlan, reason: string, keep?: number): ChapterSnapshot | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* 回滚到某个快照:把快照内容写回章节正文文件。
|
|
33
|
+
*
|
|
34
|
+
* 回滚前会**先给当前正文留一份快照**(reason=回滚前自动留存),所以回滚本身也可撤销。
|
|
35
|
+
*
|
|
36
|
+
* @param outputDir 书目录。
|
|
37
|
+
* @param chapter 目标章节。
|
|
38
|
+
* @param snapshotId 快照 id。
|
|
39
|
+
* @returns 回滚后的字符数。
|
|
40
|
+
* @throws 快照不存在或章节无正文文件时抛错。
|
|
41
|
+
*/
|
|
42
|
+
export declare function restoreSnapshot(outputDir: string, chapter: ChapterPlan, snapshotId: string): number;
|
|
43
|
+
/** 删除一个快照(连同文件)。 */
|
|
44
|
+
export declare function removeSnapshot(outputDir: string, snapshotId: string): boolean;
|
|
45
|
+
/** 按每章保留数量清理(连文件一起删)。 */
|
|
46
|
+
export declare function pruneSnapshots(outputDir: string, keep?: number): number;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 张力曲线:把「每章该有多紧」显式化,并在出章后核对实际走向。
|
|
3
|
+
*
|
|
4
|
+
* 为什么要有它:长篇最常见的两种崩法——**一直高能没有呼吸**(读者疲劳)与
|
|
5
|
+
* **长时间低位打转**(读者流失);这两种都不是单章问题,而是"曲线"问题,
|
|
6
|
+
* 单章审稿看不见。这里提供:
|
|
7
|
+
* 1) 参考曲线(本项目自行设计,仅作形状参照);
|
|
8
|
+
* 2) 把「本章目标张力」注入写作提示词;
|
|
9
|
+
* 3) 规则核对:实际 vs 目标偏差、连续同值、高潮后不回落、长期低位。
|
|
10
|
+
*
|
|
11
|
+
* 本模块只放纯逻辑(可离线单测);模型交互与落盘在 engine/routes。
|
|
12
|
+
*/
|
|
13
|
+
import type { ChapterPlan, ProjectState, TensionCurvePreset, TensionIssue } from './protocol.ts';
|
|
14
|
+
/** 单章张力的取值范围。 */
|
|
15
|
+
export declare const TENSION_MIN = 0;
|
|
16
|
+
export declare const TENSION_MAX = 100;
|
|
17
|
+
/** 参考曲线的中文标签(面板展示)。 */
|
|
18
|
+
export declare const TENSION_PRESET_LABELS: Record<TensionCurvePreset, string>;
|
|
19
|
+
/** 全部可选预设(UI 下拉用)。 */
|
|
20
|
+
export declare const TENSION_PRESETS: readonly TensionCurvePreset[];
|
|
21
|
+
/** 把参考形状按章节数重采样(最近邻,保持形状不引入平滑失真)。 */
|
|
22
|
+
export declare function referenceCurve(preset: TensionCurvePreset, chapterCount: number): number[];
|
|
23
|
+
/** 读一章的目标张力(未设置时返回 undefined)。 */
|
|
24
|
+
export declare function targetTension(chapter: ChapterPlan): number | undefined;
|
|
25
|
+
/** 读一章的实际张力(审稿时模型打分,未设置时返回 undefined)。 */
|
|
26
|
+
export declare function actualTension(chapter: ChapterPlan): number | undefined;
|
|
27
|
+
/** 收敛到合法区间并取整。 */
|
|
28
|
+
export declare function clampTension(value: number): number;
|
|
29
|
+
/**
|
|
30
|
+
* 渲染注入写作提示词的「本章张力」块。
|
|
31
|
+
*
|
|
32
|
+
* @param project 项目状态。
|
|
33
|
+
* @param chapterNo 本章章号。
|
|
34
|
+
* @returns 提示词片段;没有张力信息时返回空串。
|
|
35
|
+
*/
|
|
36
|
+
export declare function renderTensionBlock(project: ProjectState, chapterNo: number): string;
|
|
37
|
+
/**
|
|
38
|
+
* 规则核对张力曲线(不调模型)。
|
|
39
|
+
*
|
|
40
|
+
* 检查四类曲线级问题:
|
|
41
|
+
* 1) 实际与目标偏差过大(>25);
|
|
42
|
+
* 2) 连续多章几乎同值(≥4 章落差 <6,平台期无起伏);
|
|
43
|
+
* 3) 高点之后没有回落(连续 ≥3 章处在高位 ≥80);
|
|
44
|
+
* 4) 长期低位(连续 ≥5 章 <40,容易拖沓)。
|
|
45
|
+
*
|
|
46
|
+
* @param project 项目状态。
|
|
47
|
+
* @returns 问题清单(可能为空)。
|
|
48
|
+
*/
|
|
49
|
+
export declare function detectTensionIssues(project: ProjectState): TensionIssue[];
|
|
50
|
+
/**
|
|
51
|
+
* 组装面板需要的曲线数据(章节号 / 目标 / 实际 / 参考)。
|
|
52
|
+
*
|
|
53
|
+
* @param project 项目状态。
|
|
54
|
+
* @returns 逐章数据点与预设名。
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildCurveData(project: ProjectState): {
|
|
57
|
+
preset: TensionCurvePreset;
|
|
58
|
+
points: Array<{
|
|
59
|
+
no: number;
|
|
60
|
+
title: string;
|
|
61
|
+
target?: number;
|
|
62
|
+
actual?: number;
|
|
63
|
+
reference?: number;
|
|
64
|
+
}>;
|
|
65
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 故事时间线:把「章节正文」压成可排序的时间事件,用于后续章节的时间连续性。
|
|
3
|
+
*
|
|
4
|
+
* 与编年录的分工:编年录记「事实」(人物状态/资源/关系),时间线记「顺序」
|
|
5
|
+
* (什么时候、在哪里、谁在场、发生了什么),专门用来抓时间倒流、地点瞬移与
|
|
6
|
+
* 跨越章节的时序矛盾。
|
|
7
|
+
*
|
|
8
|
+
* 本模块只放纯逻辑(可离线单测):排序、注入渲染、规则初筛、事件归一化;
|
|
9
|
+
* 模型交互(抽取/复核)在 engine.ts,落盘与路由在 routes.ts。
|
|
10
|
+
*/
|
|
11
|
+
import type { ProjectState, TimelineEvent, TimelineIssue } from './protocol.ts';
|
|
12
|
+
/** 注入后续章节的时间线锚点条数(超过则显式声明省略,遵循截断显式化规则)。 */
|
|
13
|
+
export declare const TIMELINE_CONTEXT_LIMIT = 12;
|
|
14
|
+
/** 归一化事件(清洗字符串、补 id/时间戳)。 */
|
|
15
|
+
export declare function normalizeTimelineEvent(raw: Partial<TimelineEvent>, chapterNo: number, index: number, now?: string): TimelineEvent;
|
|
16
|
+
/** 时间线排序:先章节、再 order(缺省排在该章末尾)、最后 createdAt。 */
|
|
17
|
+
export declare function sortTimeline(events: readonly TimelineEvent[]): TimelineEvent[];
|
|
18
|
+
/**
|
|
19
|
+
* 渲染注入到「写作/规划」提示词的时间线锚点块。
|
|
20
|
+
*
|
|
21
|
+
* @param project 项目状态。
|
|
22
|
+
* @param beforeChapter 只注入该章之前的锚点(含该章本身不注入,避免剧透)。
|
|
23
|
+
* @returns 提示词片段(无事件时返回空串)。带显式截断声明。
|
|
24
|
+
*/
|
|
25
|
+
export declare function renderTimelineBlock(project: ProjectState, beforeChapter: number): string;
|
|
26
|
+
/**
|
|
27
|
+
* 规则初筛时间线矛盾(不调模型,瞬时计算)。
|
|
28
|
+
*
|
|
29
|
+
* 覆盖三类最容易犯又最容易用规则抓到的错:
|
|
30
|
+
* 1) 同一章内事件顺序号倒退;
|
|
31
|
+
* 2) 全书顺序号在章节推进后倒退(时间倒流);
|
|
32
|
+
* 3) 相邻两章之间地点瞬移(同一角色在未交代移动的情况下跨地点)。
|
|
33
|
+
*
|
|
34
|
+
* @param project 项目状态。
|
|
35
|
+
* @returns 问题清单(可能为空)。
|
|
36
|
+
*/
|
|
37
|
+
export declare function detectTimelineIssues(project: ProjectState): TimelineIssue[];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waterwx/dsh-novel-forge",
|
|
3
3
|
"description": "AI 编译纯文本小说工作台 for the dsh web GUI: import a novel outline (docx/text), plan volumes & chapters with LLM, generate chapter by chapter (3000-4000 chars each), auto AI review & book audit, and export the full book as Markdown/TXT into your chosen output folder.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.3.0-alpha",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "waterwx",
|
|
7
7
|
"repository": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"deepseek-harness",
|
|
19
19
|
"小说工坊",
|
|
20
20
|
"novel-forge",
|
|
21
|
-
"
|
|
21
|
+
"novel-writing-workbench",
|
|
22
22
|
"novel-writing",
|
|
23
23
|
"写作助手",
|
|
24
24
|
"小说创作",
|
|
@@ -113,6 +113,7 @@
|
|
|
113
113
|
"watch": "tsdown --watch",
|
|
114
114
|
"typecheck": "tsc --noEmit",
|
|
115
115
|
"check:styles": "node scripts/check-theme-sizes.mjs",
|
|
116
|
+
"check:third-party": "node scripts/check-third-party.mjs",
|
|
116
117
|
"release": "node scripts/release.mjs",
|
|
117
118
|
"release:dry": "node scripts/release.mjs --dry-run",
|
|
118
119
|
"test": "vitest run"
|