@waterwx/dsh-novel-forge 2.3.0 → 2.5.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/LICENSE +661 -202
- package/README.md +20 -4
- package/lib/client.js +1373 -704
- package/lib/index.js +498 -28
- package/lib/types/assets.d.ts +3 -3
- package/lib/types/client/api.d.ts +18 -0
- package/lib/types/client/panel/NameForgeView.d.ts +11 -0
- package/lib/types/client/panel/StyleView.d.ts +4 -0
- package/lib/types/engine.d.ts +33 -1
- package/lib/types/name-forge.d.ts +110 -0
- package/lib/types/prompts/nameforge.d.ts +16 -0
- package/lib/types/protocol.d.ts +108 -0
- package/lib/types/style-fingerprint.d.ts +3 -2
- package/lib/types/style-profile.d.ts +62 -0
- package/package.json +2 -2
|
@@ -292,8 +292,26 @@ export declare class NovelApi {
|
|
|
292
292
|
promptSlots(): Promise<import('../protocol.ts').PromptSlotsResponse>;
|
|
293
293
|
/** 写入一个提示词槽位(空串 = 清空)。 */
|
|
294
294
|
promptSlotSet(id: import('../protocol.ts').PromptSlotId, value: string): Promise<void>;
|
|
295
|
+
/** 取名器:本书名字库。 */
|
|
296
|
+
nameForgeBank(): Promise<import('../protocol.ts').NameForgeBankResponse>;
|
|
297
|
+
/** 取名器:生成一批候选(不落盘)。 */
|
|
298
|
+
nameForgeGenerate(category: import('../protocol.ts').NameCategory, count: number, hint: string): Promise<import('../protocol.ts').NameForgeGenerateResponse>;
|
|
299
|
+
/** 取名器:采纳候选入名字库。 */
|
|
300
|
+
nameForgeAdopt(category: import('../protocol.ts').NameCategory, candidates: import('../protocol.ts').NameForgeCandidate[]): Promise<import('../protocol.ts').NameForgeMutateResponse>;
|
|
301
|
+
/** 取名器:从名字库移除一条。 */
|
|
302
|
+
nameForgeDiscard(id: string): Promise<import('../protocol.ts').NameForgeMutateResponse>;
|
|
295
303
|
/** 故事时间线:列表(含规则初筛问题)。 */
|
|
296
304
|
timeline(): Promise<import('../protocol.ts').TimelineResponse>;
|
|
305
|
+
/** 风格目标卡:当前卡 + 生效说明 + 可蒸馏章数。 */
|
|
306
|
+
styleProfile(): Promise<import('../protocol.ts').StyleProfileResponse>;
|
|
307
|
+
/** 蒸馏风格目标卡(archived = 本书已过审章节中位数;sample = 参考样文本)。 */
|
|
308
|
+
styleProfileDistill(action: 'distill-archived' | 'distill-sample', text?: string): Promise<{
|
|
309
|
+
profile: import('../protocol.ts').StyleProfile | null;
|
|
310
|
+
}>;
|
|
311
|
+
/** 清除风格目标卡(回落绑定资产的样本文本)。 */
|
|
312
|
+
styleProfileClear(): Promise<{
|
|
313
|
+
profile: null;
|
|
314
|
+
}>;
|
|
297
315
|
/** 抽取某一章的时间线事件。 */
|
|
298
316
|
timelineExtract(chapterNo: number): Promise<{
|
|
299
317
|
events: import('../protocol.ts').TimelineEvent[];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 取名器:按类别批量生成专有名字,逐条采纳入本书名字库。
|
|
3
|
+
*
|
|
4
|
+
* 边界写在界面上:候选不落盘,采纳后才注入写作提示词(本书层「已定名资产」块);
|
|
5
|
+
* 库有容量上限(每类 60 / 全书 200),满了会明确报错而不是静默丢弃。
|
|
6
|
+
*/
|
|
7
|
+
import type { NovelApi } from '../api.ts';
|
|
8
|
+
export interface NameForgeViewProps {
|
|
9
|
+
api: NovelApi;
|
|
10
|
+
}
|
|
11
|
+
export default function NameForgeView({ api }: NameForgeViewProps): JSX.Element;
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
* 若各画各的,作者会看到两个不一致的结论。
|
|
10
10
|
* 2. **数值即时算、不落盘** —— 数据源就是章节文件本身,存一份快照只会带来陈旧问题。
|
|
11
11
|
*
|
|
12
|
+
* 页面分两截,各管一把尺子:
|
|
13
|
+
* - **风格目标卡**(上):生成端的锚点,作者显式蒸馏后落盘;审稿不读它;
|
|
14
|
+
* - **漂移曲线**(下):审稿端的自适应尺子,实时算。两者刻意分开,见 style-profile.ts 文件头。
|
|
15
|
+
*
|
|
12
16
|
* 只画四项长度/比率指标:句长起伏与意象密度是诊断用的,画成曲线反而难读。
|
|
13
17
|
*/
|
|
14
18
|
import type { NovelApi } from '../api.ts';
|
package/lib/types/engine.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
12
12
|
import { type StyleFingerprint } from './style-fingerprint.ts';
|
|
13
13
|
import { fullTextBudget } from './model-capability.ts';
|
|
14
14
|
import { applyModelCapability } from './model-request.ts';
|
|
15
|
-
import type { AdaptationDimension, AdaptAnalyzeResponse, AdaptationMapping, AdaptationRules, AdaptProposeResponse, AuditIssue, AuthorReview, ExportScope, TimelineEvent, TimelineIssue, BreakdownResponse, ChapterPlan, Foreshadow, NovelConfig, OutlineCandidate, Plotline, PlotlineHealthReport, PlotlinePlan, ProjectState, ReviewReport, RoleRecord, RoleStatusCard, StoryBible, AddModelRequest, AddModelResponse, LlmModelsResponse, LlmVendorsResponse, LlmProvidersResponse, RemoveProviderRequest, RemoveProviderResponse, LlmTestResponse, Volume, WorldState, AdaptMaterializeRequest, AdaptMaterializeResponse, AdaptMaterializeSaveRequest, AdaptMaterializeSaveResponse, MarketRadarRequest, MarketRadarResult, MarketRadarSignal, MarketRadarBriefRequest, ProductionFoundation, MarketCreativeBrief, BookAnalysisRequest, BookAnalysisResult, IdeaInspirationRequest, IdeaInspirationResult, DirectorRequest, DirectorAdvice } from './protocol.ts';
|
|
15
|
+
import type { AdaptationDimension, AdaptAnalyzeResponse, AdaptationMapping, AdaptationRules, AdaptProposeResponse, AuditIssue, AuthorReview, ExportScope, TimelineEvent, TimelineIssue, BreakdownResponse, ChapterPlan, Foreshadow, NovelConfig, OutlineCandidate, Plotline, PlotlineHealthReport, PlotlinePlan, ProjectState, ReviewReport, RoleRecord, RoleStatusCard, StoryBible, AddModelRequest, AddModelResponse, LlmModelsResponse, LlmVendorsResponse, LlmProvidersResponse, RemoveProviderRequest, RemoveProviderResponse, LlmTestResponse, Volume, WorldState, AdaptMaterializeRequest, AdaptMaterializeResponse, AdaptMaterializeSaveRequest, AdaptMaterializeSaveResponse, MarketRadarRequest, MarketRadarResult, MarketRadarSignal, MarketRadarBriefRequest, ProductionFoundation, MarketCreativeBrief, BookAnalysisRequest, BookAnalysisResult, IdeaInspirationRequest, IdeaInspirationResult, NameCategory, NameForgeCandidate, StyleProfile, DirectorRequest, DirectorAdvice } from './protocol.ts';
|
|
16
16
|
/** Project state file name inside the output dir. */
|
|
17
17
|
export declare const PROJECT_FILE = "novel-project.json";
|
|
18
18
|
/** Chapter output file name, e.g. 第001章_开篇.md */
|
|
@@ -112,6 +112,27 @@ export interface StyleBaseline {
|
|
|
112
112
|
* @returns 基线;既无已过审章节也无带样本文本的写法资产时返回 undefined。
|
|
113
113
|
*/
|
|
114
114
|
export declare function styleBaseline(outputDir: string | undefined, project: ProjectState): StyleBaseline | undefined;
|
|
115
|
+
/**
|
|
116
|
+
* 从本书**已过审章节**等距抽样算风格指纹(中位数)。
|
|
117
|
+
*
|
|
118
|
+
* 审稿尺子(styleBaseline)与蒸馏卡(distillStyleProfile)共用这一份采样——
|
|
119
|
+
* 两条路必须同源,否则「审稿说没偏离、生成目标却是另一个方向」。
|
|
120
|
+
* @returns 抽样指纹与参与章数;不足 {@link STYLE_BASELINE_MIN_CHAPTERS} 章时返回 undefined。
|
|
121
|
+
*/
|
|
122
|
+
export declare function sampleArchivedFingerprint(outputDir: string | undefined, project: ProjectState): {
|
|
123
|
+
fingerprint: StyleFingerprint;
|
|
124
|
+
count: number;
|
|
125
|
+
} | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* 蒸馏一张风格目标卡(作者显式动作,落 project.styleProfile)。
|
|
128
|
+
*
|
|
129
|
+
* archived:本书已过审章节的中位数(把「我现在实际写出来的样子」固化成目标);
|
|
130
|
+
* sample:作者粘贴的参考样文本(开书定调)。
|
|
131
|
+
*
|
|
132
|
+
* 与审稿尺子刻意分工:见 style-profile.ts 文件头「棘轮效应」说明。
|
|
133
|
+
* @throws 数据不足时抛错(路由转 400)。
|
|
134
|
+
*/
|
|
135
|
+
export declare function distillStyleProfile(outputDir: string | undefined, project: ProjectState, source: 'archived' | 'sample', sampleText?: string): StyleProfile;
|
|
115
136
|
/** Run the AI review on one chapter. */
|
|
116
137
|
export declare function reviewChapter(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, chapterNo: number): Promise<ReviewReport>;
|
|
117
138
|
/**
|
|
@@ -437,6 +458,17 @@ export declare function runMarketCreativeBrief(ctx: Context, config: NovelConfig
|
|
|
437
458
|
export declare function runBookAnalysis(ctx: Context, config: NovelConfig, req: BookAnalysisRequest): Promise<BookAnalysisResult>;
|
|
438
459
|
/** 创意灵感:一句话/题材 → 多方向开书灵感。 */
|
|
439
460
|
export declare function runIdeaInspiration(ctx: Context, config: NovelConfig, req: IdeaInspirationRequest): Promise<IdeaInspirationResult>;
|
|
461
|
+
/**
|
|
462
|
+
* 取名器:按类别批量生成专有名字(题材感知 + 世界观避重 + 库内避重)。
|
|
463
|
+
*
|
|
464
|
+
* 候选**不落盘**——作者在面板逐条采纳后才进名字库(走 nameForgeRoute 的
|
|
465
|
+
* adopt 动作)。生成失败(含模型输出无法解析)抛错,由路由转 500。
|
|
466
|
+
*/
|
|
467
|
+
export declare function runNameForge(ctx: Context, config: NovelConfig, project: ProjectState, req: {
|
|
468
|
+
category: NameCategory;
|
|
469
|
+
count?: number;
|
|
470
|
+
hint?: string;
|
|
471
|
+
}): Promise<NameForgeCandidate[]>;
|
|
440
472
|
/** 雷达→灵感:基于市场信号/生产底座/创意简报,生成贴合市场的开书灵感。 */
|
|
441
473
|
export declare function runMarketIdeaInspiration(ctx: Context, config: NovelConfig, req: {
|
|
442
474
|
signals?: MarketRadarSignal[];
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 取名器:按类别批量生成中文网文专有名字,入每书「名字库」,采纳后注入写作提示词。
|
|
3
|
+
*
|
|
4
|
+
* 设计边界(与提示词槽位同一家风):
|
|
5
|
+
* * 取名是**辅助资产**:候选由模型生成,作者逐条采纳才进库;未采纳的候选
|
|
6
|
+
* 不落盘、不注入——作者保有最终决定权;
|
|
7
|
+
* * 库有容量上限(每类 60 / 全书 200 条),超限拒绝写入并要求先清理,防止
|
|
8
|
+
* 「生成很爽、库变垃圾场」;
|
|
9
|
+
* * 注入写作提示词的名字块只列**已采纳**名字,且声明「新角色/地点优先
|
|
10
|
+
* 使用下表;表外创新名须与下表气质一致」——不锁死创作,只防跑偏。
|
|
11
|
+
*/
|
|
12
|
+
import type { NameCategory, NameForgeCandidate, NameForgeEntry, ProjectState } from './protocol.ts';
|
|
13
|
+
/** 一个取名类别。 */
|
|
14
|
+
export interface NameCategorySpec {
|
|
15
|
+
id: NameCategory;
|
|
16
|
+
/** 面板显示名。 */
|
|
17
|
+
label: string;
|
|
18
|
+
/** 面板上的一句话说明。 */
|
|
19
|
+
hint: string;
|
|
20
|
+
/** 送模型的类别定义(Prompt 内使用)。 */
|
|
21
|
+
brief: string;
|
|
22
|
+
/** 示例(占位符)。 */
|
|
23
|
+
placeholder: string;
|
|
24
|
+
/** 单次生成上限。 */
|
|
25
|
+
maxCount: number;
|
|
26
|
+
}
|
|
27
|
+
/** 取名类别表(顺序即面板顺序)。 */
|
|
28
|
+
export declare const NAME_CATEGORIES: readonly NameCategorySpec[];
|
|
29
|
+
/** 取一个类别规格;未知 id 抛错(路由层转 400)。 */
|
|
30
|
+
export declare function categorySpec(id: string): NameCategorySpec;
|
|
31
|
+
/**
|
|
32
|
+
* 名字库容量纪律:单类 ≤ 60、全书 ≤ 200。
|
|
33
|
+
*
|
|
34
|
+
* 全书上限必须**小于**「类数 × 单类上限」(5×60=300)否则永远先被单类规则
|
|
35
|
+
* 挡住、成为死代码——200 逼作者在囤积前清理,也足够一部 600 章的书用。
|
|
36
|
+
*/
|
|
37
|
+
export declare const NAME_BANK_MAX_PER_CATEGORY = 60;
|
|
38
|
+
export declare const NAME_BANK_MAX_TOTAL = 200;
|
|
39
|
+
/** 归一化一条候选/入库条目(裁剪 + 截断;去重由调用方处理)。 */
|
|
40
|
+
export declare function normalizeNameEntry(raw: {
|
|
41
|
+
name?: unknown;
|
|
42
|
+
note?: unknown;
|
|
43
|
+
}): {
|
|
44
|
+
name: string;
|
|
45
|
+
note: string;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* 解析取名 LLM 输出为候选列表。
|
|
49
|
+
*
|
|
50
|
+
* 宽容度分级:标准 JSON → 去围栏 → 截取首尾花括号;仍失败返回空数组(由
|
|
51
|
+
* 调用方决定重试或报错),绝不把模型废话当名字。
|
|
52
|
+
*
|
|
53
|
+
* @param text 模型原始输出。
|
|
54
|
+
* @param category 目标类别(用于校验字段完整性)。
|
|
55
|
+
* @param count 期望条数(上限)。
|
|
56
|
+
* @returns 候选列表(已去重、去空、截断)。
|
|
57
|
+
*/
|
|
58
|
+
export declare function parseNameCandidates(text: string, count: number): NameForgeCandidate[];
|
|
59
|
+
/**
|
|
60
|
+
* 构造取名请求的 user 提示词。
|
|
61
|
+
*
|
|
62
|
+
* 书籍语境三件套:题材基调、世界设定(境界/区域/势力既有名)、已入库名字
|
|
63
|
+
* (避重)——三者都可缺省,缺省时明确告诉模型「无既有设定,自由发挥」。
|
|
64
|
+
*
|
|
65
|
+
* @param category 类别规格。
|
|
66
|
+
* @param count 期望条数。
|
|
67
|
+
* @param hint 作者补充要求(可空)。
|
|
68
|
+
* @param context 书籍语境。
|
|
69
|
+
*/
|
|
70
|
+
export declare function buildNameForgeUserPrompt(category: NameCategorySpec, count: number, hint: string, context: {
|
|
71
|
+
genre?: string;
|
|
72
|
+
worldNames?: string[];
|
|
73
|
+
bankNames?: string[];
|
|
74
|
+
}): string;
|
|
75
|
+
/** 从项目状态收集取名语境(题材 + 世界既有名 + 已入库名字)。 */
|
|
76
|
+
export declare function collectNameForgeContext(project: ProjectState): {
|
|
77
|
+
genre: string;
|
|
78
|
+
worldNames: string[];
|
|
79
|
+
bankNames: string[];
|
|
80
|
+
};
|
|
81
|
+
/** 读取并规整名字库(按类别分组返回前先做一致性校验;非法条目丢弃)。 */
|
|
82
|
+
export declare function readNameBank(project: ProjectState): NameForgeEntry[];
|
|
83
|
+
/**
|
|
84
|
+
* 写入名字库(采纳候选 / 弃用单条 / 清空)。
|
|
85
|
+
*
|
|
86
|
+
* 容量纪律:单类 ≤ 60、总数 ≤ 200;超限抛错由路由转 400,**不做静默截断**——
|
|
87
|
+
* 让作者明确知道库满了,而不是疑惑「我刚取的名字去哪了」。
|
|
88
|
+
*/
|
|
89
|
+
export declare function mutateNameBank(project: ProjectState, op: {
|
|
90
|
+
kind: 'adopt';
|
|
91
|
+
category: NameCategory;
|
|
92
|
+
candidates: Array<{
|
|
93
|
+
name: string;
|
|
94
|
+
note?: string;
|
|
95
|
+
}>;
|
|
96
|
+
} | {
|
|
97
|
+
kind: 'discard';
|
|
98
|
+
id: string;
|
|
99
|
+
} | {
|
|
100
|
+
kind: 'clear';
|
|
101
|
+
}): NameForgeEntry[];
|
|
102
|
+
/**
|
|
103
|
+
* 把已采纳名字渲染成写作提示词块(本书层注入)。
|
|
104
|
+
*
|
|
105
|
+
* 声明边界:优先使用、不锁死——「表外创新名须与下表气质一致」。
|
|
106
|
+
*
|
|
107
|
+
* @param project 项目状态。
|
|
108
|
+
* @returns 提示词片段;无库时返回空串。
|
|
109
|
+
*/
|
|
110
|
+
export declare function renderNameBank(project: ProjectState): string;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 取名域:按类别批量生成中文网文专有名字(人名 / 地名 / 功法 / 势力 / 宝物)。
|
|
3
|
+
*
|
|
4
|
+
* 本文件同时是提示词注册表的域模块之一(见 catalog.ts)。
|
|
5
|
+
*
|
|
6
|
+
* 设计边界:
|
|
7
|
+
* * 取名是**辅助资产**,产出进「名字库」供作者采纳;采纳后的名字才注入
|
|
8
|
+
* 写作提示词(书名库渲染在 prompts/writing.ts 的本书层);
|
|
9
|
+
* * 提示词只描述「怎么取名」,不写死任何名字——所有候选来自模型按本书
|
|
10
|
+
* 语境(题材 / 世界设定 / 已有名字避重)的实时生成。
|
|
11
|
+
*/
|
|
12
|
+
import type { PromptEntry } from './catalog.ts';
|
|
13
|
+
/** 取名系统提示词:类别感知 + 避重 + 结构化 JSON 输出。 */
|
|
14
|
+
export declare function nameForgeSystemPrompt(): string;
|
|
15
|
+
/** 本域提示词登记(注册表消费)。 */
|
|
16
|
+
export declare const entries: PromptEntry[];
|
package/lib/types/protocol.d.ts
CHANGED
|
@@ -34,8 +34,12 @@ export declare const NOVEL_API: {
|
|
|
34
34
|
/** 张力曲线(GET 数据 / POST 设置)。 */
|
|
35
35
|
readonly tension: "/api/dsh-novel-forge/tension";
|
|
36
36
|
readonly styleHistory: "/api/dsh-novel-forge/style-history";
|
|
37
|
+
/** 风格目标卡:GET 当前卡;POST {action:'distill-archived'|'distill-sample'|'clear'}。 */
|
|
38
|
+
readonly styleProfile: "/api/dsh-novel-forge/style-profile";
|
|
37
39
|
/** 提示词槽位(GET 列表 / POST 写入)。 */
|
|
38
40
|
readonly promptSlots: "/api/dsh-novel-forge/prompt-slots";
|
|
41
|
+
/** 取名器:POST {action:'generate'} 生成候选;GET 名字库;POST {action:'adopt'|'discard'|'clear'} 维护。 */
|
|
42
|
+
readonly nameForge: "/api/dsh-novel-forge/name-forge";
|
|
39
43
|
/** 故事时间线(GET 列表 / POST 操作)。 */
|
|
40
44
|
readonly timeline: "/api/dsh-novel-forge/timeline";
|
|
41
45
|
readonly marketRadar: "/api/dsh-novel-forge/market-radar";
|
|
@@ -699,6 +703,57 @@ export interface PromptSlotsRequest {
|
|
|
699
703
|
/** 空串 = 清空该槽位(恢复默认)。 */
|
|
700
704
|
value: string;
|
|
701
705
|
}
|
|
706
|
+
/** 取名类别。 */
|
|
707
|
+
export type NameCategory = 'person' | 'place' | 'technique' | 'faction' | 'artifact';
|
|
708
|
+
/** 名字库里的一条(采纳后才注入写作提示词)。 */
|
|
709
|
+
export interface NameForgeEntry {
|
|
710
|
+
/** 稳定 id(`类别:名字`)。 */
|
|
711
|
+
id: string;
|
|
712
|
+
category: NameCategory;
|
|
713
|
+
name: string;
|
|
714
|
+
/** 命名理由/意象(20 字内为宜)。 */
|
|
715
|
+
note: string;
|
|
716
|
+
/** 是否生效(false = 已弃用,不注入)。 */
|
|
717
|
+
adopted: boolean;
|
|
718
|
+
createdAt: string;
|
|
719
|
+
}
|
|
720
|
+
/** 一次取名生成的候选(未落盘,作者采纳后才入库)。 */
|
|
721
|
+
export interface NameForgeCandidate {
|
|
722
|
+
name: string;
|
|
723
|
+
note: string;
|
|
724
|
+
}
|
|
725
|
+
/** POST /name-forge {action:'generate'} 请求。 */
|
|
726
|
+
export interface NameForgeGenerateRequest {
|
|
727
|
+
action: 'generate';
|
|
728
|
+
category: NameCategory;
|
|
729
|
+
/** 生成条数(1-20,默认 10)。 */
|
|
730
|
+
count?: number;
|
|
731
|
+
/** 作者补充要求(可空)。 */
|
|
732
|
+
hint?: string;
|
|
733
|
+
bookId?: string;
|
|
734
|
+
}
|
|
735
|
+
/** POST /name-forge 库维护请求。 */
|
|
736
|
+
export interface NameForgeMutateRequest {
|
|
737
|
+
action: 'adopt' | 'discard' | 'clear';
|
|
738
|
+
category?: NameCategory;
|
|
739
|
+
/** adopt 时的候选列表。 */
|
|
740
|
+
candidates?: NameForgeCandidate[];
|
|
741
|
+
/** discard 时的条目 id。 */
|
|
742
|
+
id?: string;
|
|
743
|
+
bookId?: string;
|
|
744
|
+
}
|
|
745
|
+
/** POST /name-forge {action:'generate'} 响应。 */
|
|
746
|
+
export interface NameForgeGenerateResponse {
|
|
747
|
+
candidates: NameForgeCandidate[];
|
|
748
|
+
}
|
|
749
|
+
/** GET /name-forge 响应:本书名字库(按类别分组由面板做)。 */
|
|
750
|
+
export interface NameForgeBankResponse {
|
|
751
|
+
entries: NameForgeEntry[];
|
|
752
|
+
}
|
|
753
|
+
/** POST /name-forge 库维护响应。 */
|
|
754
|
+
export interface NameForgeMutateResponse {
|
|
755
|
+
entries: NameForgeEntry[];
|
|
756
|
+
}
|
|
702
757
|
/**
|
|
703
758
|
* 故事时间线事件:从章节正文抽取的「故事内时间点 + 地点 + 参与角色 + 事件」。
|
|
704
759
|
* 用途:后续章节的时间连续性与地点衔接(比编年录更强调"顺序")。
|
|
@@ -1190,6 +1245,10 @@ export interface ProjectState {
|
|
|
1190
1245
|
tensionCurve?: TensionCurveConfig;
|
|
1191
1246
|
/** 作者自定义的提示词槽位(白名单见 prompt-slots.ts)。 */
|
|
1192
1247
|
promptSlots?: Partial<Record<PromptSlotId, string>>;
|
|
1248
|
+
/** 取名器名字库(按类别收录的已采纳专有名字,注入写作提示词)。 */
|
|
1249
|
+
nameBank?: NameForgeEntry[];
|
|
1250
|
+
/** 风格目标卡(显式蒸馏的生成端锚点;审稿尺子不读它,实时算)。 */
|
|
1251
|
+
styleProfile?: StyleProfile;
|
|
1193
1252
|
/** 本书参数(写作/审稿/推理/模型快切;未设字段回退全局 config)。 */
|
|
1194
1253
|
bookSettings?: BookSettings;
|
|
1195
1254
|
/** ISO timestamps. */
|
|
@@ -2092,6 +2151,55 @@ export interface StyleHistoryPoint {
|
|
|
2092
2151
|
significant: string[];
|
|
2093
2152
|
}
|
|
2094
2153
|
/** 风格漂移曲线:逐章指标 + 参照基线。 */
|
|
2154
|
+
/**
|
|
2155
|
+
* 风格指纹数据(字段与 style-fingerprint.ts 的 StyleFingerprint 结构一致,
|
|
2156
|
+
* 这里复述一份是为了让协议文件不依赖宿主模块——shared vocabulary 自包含)。
|
|
2157
|
+
*/
|
|
2158
|
+
export interface StyleFingerprintData {
|
|
2159
|
+
chars: number;
|
|
2160
|
+
sentences: number;
|
|
2161
|
+
avgSentenceLength: number;
|
|
2162
|
+
sentenceLengthCv: number;
|
|
2163
|
+
shortSentenceRatio: number;
|
|
2164
|
+
dialogueRatio: number;
|
|
2165
|
+
avgParagraphLength: number;
|
|
2166
|
+
imageryPer1000: number;
|
|
2167
|
+
}
|
|
2168
|
+
/** 风格目标卡的蒸馏来源。 */
|
|
2169
|
+
export type StyleProfileSource = 'archived' | 'sample';
|
|
2170
|
+
/**
|
|
2171
|
+
* 风格目标卡:作者**显式蒸馏**的产物(不是派生视图,所以落盘)。
|
|
2172
|
+
*
|
|
2173
|
+
* 与审稿端尺子的分工:审稿用「本书已过审章节中位数」实时算(自适应尺子);
|
|
2174
|
+
* 生成端的目标由本卡提供(外部锚点)——两把尺子刻意分开,否则生成目标会跟着
|
|
2175
|
+
* 本书漂移一起走,越写越偏而且互相认证(棘轮效应)。本卡只在作者点「蒸馏」时变。
|
|
2176
|
+
*/
|
|
2177
|
+
export interface StyleProfile {
|
|
2178
|
+
/** 蒸馏来源。 */
|
|
2179
|
+
source: StyleProfileSource;
|
|
2180
|
+
/** 人类可读来源说明,如「本书已过审 12 章的中位数」。 */
|
|
2181
|
+
label: string;
|
|
2182
|
+
fingerprint: StyleFingerprintData;
|
|
2183
|
+
/** archived 来源的采样章数。 */
|
|
2184
|
+
sampleChapters?: number;
|
|
2185
|
+
updatedAt: string;
|
|
2186
|
+
}
|
|
2187
|
+
/** GET /style-profile 响应。 */
|
|
2188
|
+
export interface StyleProfileResponse {
|
|
2189
|
+
/** 当前生效的目标卡;从未蒸馏过为 null(回落资产样本文本)。 */
|
|
2190
|
+
profile: StyleProfile | null;
|
|
2191
|
+
/** 可供蒸馏的已过审章数(<3 时不可用 archived 蒸馏)。 */
|
|
2192
|
+
approvedChapters: number;
|
|
2193
|
+
/** 当前实际生效的目标说明(卡优先,否则回落样本文本或「无」)。 */
|
|
2194
|
+
effectiveLabel: string;
|
|
2195
|
+
}
|
|
2196
|
+
/** POST /style-profile 请求。 */
|
|
2197
|
+
export interface StyleProfileRequest {
|
|
2198
|
+
action: 'distill-archived' | 'distill-sample' | 'clear';
|
|
2199
|
+
/** distill-sample 时的样本文本(≥200 字)。 */
|
|
2200
|
+
text?: string;
|
|
2201
|
+
bookId?: string;
|
|
2202
|
+
}
|
|
2095
2203
|
export interface StyleHistoryResponse {
|
|
2096
2204
|
/** 基线说明,如「本书已过审 12 章的中位数」 */
|
|
2097
2205
|
baselineLabel: string;
|
|
@@ -61,10 +61,11 @@ export declare function compareStyleFingerprint(reference: StyleFingerprint, act
|
|
|
61
61
|
export declare function mergeFingerprints(items: readonly StyleFingerprint[]): StyleFingerprint;
|
|
62
62
|
/**
|
|
63
63
|
* 渲染**生成端**的风格目标(区间形式)。
|
|
64
|
-
* @param reference -
|
|
64
|
+
* @param reference - 参照指纹(写法样本或蒸馏卡)。
|
|
65
|
+
* @param sourceLabel - 参照物的人类可读说明(缺省「绑定的写法样本」)。
|
|
65
66
|
* @returns 可注入写作 prompt 的文本;样本过短时返回空串。
|
|
66
67
|
*/
|
|
67
|
-
export declare function renderFingerprintTargets(reference: StyleFingerprint): string;
|
|
68
|
+
export declare function renderFingerprintTargets(reference: StyleFingerprint, sourceLabel?: string): string;
|
|
68
69
|
/**
|
|
69
70
|
* 渲染**审稿端**的风格比对(事实锚点)。
|
|
70
71
|
*
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 风格目标卡:把「这本书的写法」蒸馏成一份**作者显式确认**的确定性指标卡,
|
|
3
|
+
* 作为生成端的风格锚点。
|
|
4
|
+
*
|
|
5
|
+
* ## 为什么生成端要一张卡,而审稿端实时算
|
|
6
|
+
*
|
|
7
|
+
* 审稿端的尺子是「本书已过审章节的中位数」(styleBaseline,engine.ts)——自适应:
|
|
8
|
+
* 书写到哪,尺子量到哪。生成端如果也用同一把尺子,会出现**棘轮效应**:某一章
|
|
9
|
+
* 对话占比偏高 → 进了中位数 → 下一章的目标跟着抬高 → 越写越偏且「生成符合目标、
|
|
10
|
+
* 审稿也符合尺子」互相认证,没有任何一方报警。
|
|
11
|
+
*
|
|
12
|
+
* 所以两边刻意分开:
|
|
13
|
+
* * 审稿 = 自适应尺子(实时算,不落盘);
|
|
14
|
+
* * 生成 = 外部锚点(本卡;只在作者点「蒸馏」时更新,是**决定**不是派生)。
|
|
15
|
+
*
|
|
16
|
+
* ## 卡的来源
|
|
17
|
+
*
|
|
18
|
+
* 1. `archived` —— 本书已过审章节等距抽样(≤12 章)的中位数:把「我现在实际
|
|
19
|
+
* 写出来的样子」固化成目标,适合写了一定时量后收紧;
|
|
20
|
+
* 2. `sample` —— 作者粘贴的参考样文本:开书阶段就想定调;
|
|
21
|
+
* 3. 无卡 —— 回落绑定写法资产的样本文本(旧行为,保持兼容)。
|
|
22
|
+
*/
|
|
23
|
+
import type { StyleProfile, StyleProfileSource, StyleFingerprintData } from './protocol.ts';
|
|
24
|
+
/** 生成端实际生效的风格目标(判别联合:卡直给指纹,样本由调用方现算)。 */
|
|
25
|
+
export type EffectiveStyleTarget = {
|
|
26
|
+
kind: 'profile';
|
|
27
|
+
fingerprint: StyleFingerprintData;
|
|
28
|
+
label: string;
|
|
29
|
+
} | {
|
|
30
|
+
kind: 'sample';
|
|
31
|
+
sampleText: string;
|
|
32
|
+
label: string;
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'none';
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* 解析生成端生效的风格目标。
|
|
38
|
+
*
|
|
39
|
+
* 优先级:显式卡 > 绑定资产的样本文本 > 无。样本文本取「第一个有 sourceText
|
|
40
|
+
* 的资产」,指纹由调用方现算——本模块不依赖 style-fingerprint,保持纯数据。
|
|
41
|
+
*
|
|
42
|
+
* @param project 项目状态(只需 styleProfile 与 assets.styleAssets[].sourceText)。
|
|
43
|
+
* @returns 生效目标。
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveStyleTarget(project: {
|
|
46
|
+
styleProfile?: StyleProfile;
|
|
47
|
+
assets?: {
|
|
48
|
+
styleAssets?: Array<{
|
|
49
|
+
sourceText?: string;
|
|
50
|
+
}>;
|
|
51
|
+
};
|
|
52
|
+
}): EffectiveStyleTarget;
|
|
53
|
+
/** 用指纹数据构造一张目标卡。 */
|
|
54
|
+
export declare function makeStyleProfile(source: StyleProfileSource, label: string, fingerprint: StyleFingerprintData, sampleChapters?: number): StyleProfile;
|
|
55
|
+
/** 目标卡的中文来源名(面板用)。 */
|
|
56
|
+
export declare function styleProfileSourceLabel(source: StyleProfileSource): string;
|
|
57
|
+
/**
|
|
58
|
+
* 校验一次 distilled-sample 请求的样本文本。
|
|
59
|
+
* @returns 归一化后的文本。
|
|
60
|
+
* @throws 文本过短时抛错(路由转 400)。
|
|
61
|
+
*/
|
|
62
|
+
export declare function normalizeSampleText(text: string | undefined): string;
|
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": "2.
|
|
4
|
+
"version": "2.5.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "waterwx",
|
|
7
7
|
"repository": {
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"cordis.patch.yml",
|
|
104
104
|
"README.md"
|
|
105
105
|
],
|
|
106
|
-
"license": "
|
|
106
|
+
"license": "AGPL-3.0-only",
|
|
107
107
|
"scripts": {
|
|
108
108
|
"postinstall": "node scripts/patch-schemastery.mjs",
|
|
109
109
|
"build": "node scripts/clean-types.mjs && tsc -p tsconfig.build.json && tsdown",
|