@waterwx/dsh-novel-forge 1.8.0 → 1.9.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/client.js +1439 -816
- package/lib/client.js.map +1 -1
- package/lib/index.js +519 -121
- package/lib/index.js.map +1 -1
- package/lib/types/client/api.d.ts +13 -1
- package/lib/types/client/locales.d.ts +16 -0
- package/lib/types/client/panel/AuthorHome.d.ts +11 -1
- package/lib/types/client/panel/ModelManager.d.ts +14 -0
- package/lib/types/client/panel/ModelPicker.d.ts +11 -0
- package/lib/types/client/panel/ProgressConsole.d.ts +17 -0
- package/lib/types/client/panel/ReasoningSection.d.ts +10 -0
- package/lib/types/client/panel/SettingsView.d.ts +1 -1
- package/lib/types/engine.d.ts +16 -1
- package/lib/types/protocol.d.ts +147 -0
- package/package.json +1 -1
- package/src/client/api.ts +41 -0
- package/src/client/locales.ts +32 -0
- package/src/client/panel/AdaptModeView.tsx +3 -5
- package/src/client/panel/AuthorHome.tsx +19 -5
- package/src/client/panel/ModelManager.tsx +205 -0
- package/src/client/panel/NovelPanel.tsx +26 -63
- package/src/client/panel/ProgressConsole.tsx +64 -0
- package/src/client/panel/ProgressHomeView.tsx +1 -1
- package/src/client/panel/ReasoningSection.tsx +31 -0
- package/src/client/panel/SettingsView.tsx +56 -49
- package/src/engine.ts +221 -0
- package/src/index.ts +2 -2
- package/src/protocol.ts +160 -0
- package/src/routes.ts +132 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* fetch, same origin; generation/rewrite/polish ride NDJSON streams read
|
|
4
4
|
* incrementally.
|
|
5
5
|
*/
|
|
6
|
-
import { type AssetsPatch, type AssetsResponse, type BibleResponse, type ChapterResponse, type ConfigPatch, type ExportResponse, type ForeshadowRequest, type ForeshadowResponse, type JobFrame, type LoadOutlineResponse, type NovelConfig, type PlanResponse, type ReviewReport, type StatusResponse, type StyleEngineRequest, type StyleAsset, type VolumesResponse } from '../protocol.ts';
|
|
6
|
+
import { type AssetsPatch, type AssetsResponse, type AddModelRequest, type AddModelResponse, type LlmModelsResponse, type LlmProvidersResponse, type LlmVendorsResponse, type RemoveProviderRequest, type RemoveProviderResponse, type BibleResponse, type ChapterResponse, type ConfigPatch, type ExportResponse, type ForeshadowRequest, type ForeshadowResponse, type JobFrame, type LoadOutlineResponse, type LlmTestResponse, type NovelConfig, type PlanResponse, type ReviewReport, type StatusResponse, type StyleEngineRequest, type StyleAsset, type VolumesResponse } from '../protocol.ts';
|
|
7
7
|
/** Error carrying the route's JSON error message. */
|
|
8
8
|
export declare class NovelApiError extends Error {
|
|
9
9
|
constructor(message: string);
|
|
@@ -99,6 +99,18 @@ export declare class NovelApi {
|
|
|
99
99
|
}>;
|
|
100
100
|
/** 角色库:AI 提炼 / 采纳 / 更新 / 删除。 */
|
|
101
101
|
imageTest(req: import('../protocol.ts').ImageTestRequest): Promise<import('../protocol.ts').ImageTestResponse>;
|
|
102
|
+
/** 对选中的提供商/模型发一次最小真实调用,验证连通性。 */
|
|
103
|
+
llmTest(provider: string, model: string): Promise<LlmTestResponse>;
|
|
104
|
+
/** 添加模型:厂商直填 key,或自定义 OpenAI 兼容路由(写 DSH 凭据 + 注册路由)。 */
|
|
105
|
+
addModel(req: AddModelRequest): Promise<AddModelResponse>;
|
|
106
|
+
/** 运行时厂商目录(DSH pi-ai 可配置提供方 + 内置适配器)。 */
|
|
107
|
+
llmVendors(): Promise<LlmVendorsResponse>;
|
|
108
|
+
/** 查询某个 provider 当前可用的模型(添加成功后可即时刷新)。 */
|
|
109
|
+
llmModels(provider: string): Promise<LlmModelsResponse>;
|
|
110
|
+
/** 已注册的提供方路由列表(提供方管理卡片)。 */
|
|
111
|
+
llmProviders(): Promise<LlmProvidersResponse>;
|
|
112
|
+
/** 移除一个提供方(unset key + 移除 llm-pi-ai 路由)。 */
|
|
113
|
+
removeProvider(req: RemoveProviderRequest): Promise<RemoveProviderResponse>;
|
|
102
114
|
roles(req: import('../protocol.ts').RolesRequest): Promise<import('../protocol.ts').RolesResponse>;
|
|
103
115
|
/** 漫剧角色库:从分镜提名 / 建卡 / 更新 / 删除 / 形象锚点 / 精修提示词。 */
|
|
104
116
|
mangaRoles(req: import('../protocol.ts').MangaRolesRequest): Promise<import('../protocol.ts').MangaRolesResponse>;
|
|
@@ -142,7 +142,23 @@ export declare const zh: {
|
|
|
142
142
|
readonly 'settings.provider': "模型提供商";
|
|
143
143
|
readonly 'settings.model': "模型";
|
|
144
144
|
readonly 'settings.modelCustom': "自定义…";
|
|
145
|
+
readonly 'settings.customProvider': "自定义提供商…";
|
|
146
|
+
readonly 'settings.edit': "编辑";
|
|
147
|
+
readonly 'settings.delete': "删除";
|
|
145
148
|
readonly 'settings.modelCustomPlaceholder': "输入任意模型 id";
|
|
149
|
+
readonly 'settings.savedModels': "我的模型";
|
|
150
|
+
readonly 'settings.modelCards': "模型卡片(内置 + 已添加)";
|
|
151
|
+
readonly 'settings.savedModelName': "名称";
|
|
152
|
+
readonly 'settings.addModel': "添加模型";
|
|
153
|
+
readonly 'settings.catalogRefresh': "刷新厂商";
|
|
154
|
+
readonly 'settings.refreshModels': "刷新模型";
|
|
155
|
+
readonly 'settings.savedModelsHint': "手动添加常用模型(provider + 模型 id),点击即可切换当前模型;厂商模式会写入 DSH 凭据并注册路由。";
|
|
156
|
+
readonly 'settings.testConnection': "测试连通";
|
|
157
|
+
readonly 'settings.testRunning': "测试中";
|
|
158
|
+
readonly 'settings.testOk': "连通";
|
|
159
|
+
readonly 'settings.testFail': "失败";
|
|
160
|
+
readonly 'settings.testHint': "真实发送一次最小调用(约几十 token),验证 Key / 端点 / 模型可用。";
|
|
161
|
+
readonly 'settings.reasoningSection': "推理强度";
|
|
146
162
|
readonly 'settings.reasoningEffort': "写作类任务思考强度 (reasoningEffort)";
|
|
147
163
|
readonly 'settings.reasoningHint': "章节生成/润色/重写/审稿等写作任务使用;关掉更省更快。";
|
|
148
164
|
readonly 'settings.analysisReasoning': "分析类任务思考强度(提炼/拆书/反推大纲)";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { NovelApi } from '../api.ts';
|
|
2
2
|
import type { BookshelfSnapshot } from '../../protocol.ts';
|
|
3
|
-
|
|
3
|
+
import { type ProgressLine } from './ProgressConsole.tsx';
|
|
4
|
+
export declare function AuthorHome({ api, shelf, onOpenBook, onReadBook, onAddBook, onImportBook, onOpenSettings, onTheme, onBackground, onOpacity, adaptEnabled, progress, busy, busyLabel, liveBar, onClearProgress }: {
|
|
4
5
|
api: NovelApi;
|
|
5
6
|
shelf: BookshelfSnapshot;
|
|
6
7
|
onOpenBook: (id: string) => void;
|
|
@@ -17,4 +18,13 @@ export declare function AuthorHome({ api, shelf, onOpenBook, onReadBook, onAddBo
|
|
|
17
18
|
onOpacity?: (n: number) => void;
|
|
18
19
|
/** 是否启用改编模式(默认 false=隐藏入口)。 */
|
|
19
20
|
adaptEnabled?: boolean;
|
|
21
|
+
/** AI 进度实时状态(与书内共享同一份)。 */
|
|
22
|
+
progress?: ProgressLine[];
|
|
23
|
+
busy?: boolean;
|
|
24
|
+
busyLabel?: string;
|
|
25
|
+
liveBar?: {
|
|
26
|
+
text: string;
|
|
27
|
+
ratio?: number;
|
|
28
|
+
} | null;
|
|
29
|
+
onClearProgress?: () => void;
|
|
20
30
|
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { NovelApi } from '../api.ts';
|
|
2
|
+
import type { SavedModel } from '../../protocol.ts';
|
|
3
|
+
/** 目录不可用时回退的内置预设(历史行为)。 */
|
|
4
|
+
export declare const FALLBACK_MODEL_PRESETS: readonly ["deepseek-v4-flash", "deepseek-v4-pro", "deepseek-chat", "deepseek-reasoner"];
|
|
5
|
+
export interface ModelManagerProps {
|
|
6
|
+
api: NovelApi;
|
|
7
|
+
provider: string;
|
|
8
|
+
model: string;
|
|
9
|
+
savedModels: SavedModel[];
|
|
10
|
+
onProvider: (provider: string) => void;
|
|
11
|
+
onModel: (model: string) => void;
|
|
12
|
+
onSavedModels: (models: SavedModel[]) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare function ModelManager({ api, provider, model, onProvider, onModel }: ModelManagerProps): JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { NovelApi } from '../api.ts';
|
|
2
|
+
/** 目录不可用时回退的内置预设(历史行为)。 */
|
|
3
|
+
export declare const FALLBACK_MODEL_PRESETS: readonly ["deepseek-v4-flash", "deepseek-v4-pro", "deepseek-chat", "deepseek-reasoner"];
|
|
4
|
+
export interface ModelPickerProps {
|
|
5
|
+
api: NovelApi;
|
|
6
|
+
provider: string;
|
|
7
|
+
model: string;
|
|
8
|
+
onProvider: (provider: string) => void;
|
|
9
|
+
onModel: (model: string) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function ModelPicker({ api, provider, model, onProvider, onModel }: ModelPickerProps): JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface ProgressLine {
|
|
2
|
+
id: number;
|
|
3
|
+
text: string;
|
|
4
|
+
kind: 'info' | 'done' | 'error';
|
|
5
|
+
live?: boolean;
|
|
6
|
+
ratio?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function ProgressConsole({ progress, busy, busyLabel, liveBar, onClear }: {
|
|
9
|
+
progress: ProgressLine[];
|
|
10
|
+
busy: boolean;
|
|
11
|
+
busyLabel: string;
|
|
12
|
+
liveBar: {
|
|
13
|
+
text: string;
|
|
14
|
+
ratio?: number;
|
|
15
|
+
} | null;
|
|
16
|
+
onClear: () => void;
|
|
17
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { NovelConfig } from '../../protocol.ts';
|
|
2
|
+
export interface ReasoningSectionProps {
|
|
3
|
+
reasoningEffort: NovelConfig['reasoningEffort'];
|
|
4
|
+
analysisReasoning: NovelConfig['analysisReasoning'];
|
|
5
|
+
onChange: (patch: {
|
|
6
|
+
reasoningEffort?: NovelConfig['reasoningEffort'];
|
|
7
|
+
analysisReasoning?: NovelConfig['analysisReasoning'];
|
|
8
|
+
}) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function ReasoningSection({ reasoningEffort, analysisReasoning, onChange }: ReasoningSectionProps): JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { NovelApi } from '../api.ts';
|
|
2
|
-
type SettingsTab = 'model' | 'writing' | 'image' | 'files' | 'appearance'
|
|
2
|
+
type SettingsTab = 'model' | 'writing' | 'image' | 'files' | 'appearance';
|
|
3
3
|
type ThemeMode = 'system' | 'light' | 'dark';
|
|
4
4
|
type ThemeName = 'liquid' | 'neumorph' | 'macos' | 'clay';
|
|
5
5
|
type ThemeDensity = 'comfort' | 'compact' | 'spacious';
|
package/lib/types/engine.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export declare const COMPLIANCE_REDLINES: ReadonlyArray<string>;
|
|
14
14
|
import type { Context } from '@deepseek-ai/cordis';
|
|
15
|
-
import type { AdaptationDimension, AdaptAnalyzeResponse, AdaptationMapping, AdaptProposeResponse, AuditIssue, AuthorReview, BreakdownResponse, ChapterPlan, Foreshadow, NovelConfig, OutlineCandidate, Plotline, PlotlineHealthReport, PlotlinePlan, ProjectState, ReviewReport, RoleRecord, RoleStatusCard, SceneCard, StoryBible, StoryboardSkeleton, StoryboardTable, StoryboardPrompt, MangaRoleCandidate, Volume, WorldState } from './protocol.ts';
|
|
15
|
+
import type { AdaptationDimension, AdaptAnalyzeResponse, AdaptationMapping, AdaptProposeResponse, AuditIssue, AuthorReview, BreakdownResponse, ChapterPlan, Foreshadow, NovelConfig, OutlineCandidate, Plotline, PlotlineHealthReport, PlotlinePlan, ProjectState, ReviewReport, RoleRecord, RoleStatusCard, SceneCard, StoryBible, StoryboardSkeleton, StoryboardTable, StoryboardPrompt, AddModelRequest, AddModelResponse, LlmModelsResponse, LlmVendorsResponse, LlmProvidersResponse, RemoveProviderRequest, RemoveProviderResponse, LlmTestResponse, MangaRoleCandidate, Volume, WorldState } 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 */
|
|
@@ -147,6 +147,21 @@ export declare function testImageEndpoint(baseURL: string, apiKey: string, model
|
|
|
147
147
|
message?: string;
|
|
148
148
|
modelFound?: boolean;
|
|
149
149
|
}>;
|
|
150
|
+
/** 对选中的提供商/模型发一次最小真实调用(maxTokens=16),验证 Key / 端点 / 模型可用。 */
|
|
151
|
+
export declare function testLlmModel(ctx: Context, provider: string, model: string): Promise<LlmTestResponse>;
|
|
152
|
+
/** 运行时厂商目录:DSH pi-ai 可配置提供方 + 内置适配器,作为「添加模型」下拉。 */
|
|
153
|
+
export declare function listLlmVendors(ctx: Context): Promise<LlmVendorsResponse>;
|
|
154
|
+
/** 查询某个 provider 当前可用模型(添加成功后可即时刷新下拉)。 */
|
|
155
|
+
export declare function listLlmModels(ctx: Context, provider: string): Promise<LlmModelsResponse>;
|
|
156
|
+
/** 当前已注册的提供方路由列表(提供方管理卡片)。 */
|
|
157
|
+
export declare function listLlmProviders(ctx: Context): Promise<LlmProvidersResponse>;
|
|
158
|
+
/** 移除一个提供方:unset 凭据 ref + 移除 llm-pi-ai providers 路由。 */
|
|
159
|
+
export declare function removeLlmProvider(ctx: Context, req: RemoveProviderRequest): Promise<RemoveProviderResponse>;
|
|
160
|
+
/**
|
|
161
|
+
* 添加模型(DSH 同款体验):厂商直填 API Key,或自定义 OpenAI 兼容路由。
|
|
162
|
+
* 写入 DSH 凭据 refs,并(必要时)注册/更新 llm-pi-ai provider 路由。
|
|
163
|
+
*/
|
|
164
|
+
export declare function registerLlmModel(ctx: Context, req: AddModelRequest): Promise<AddModelResponse>;
|
|
150
165
|
/** 小说角色库:生成角色定妆图并写回角色卡(imageUrl)。 */
|
|
151
166
|
export declare function generateRoleReferenceImage(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, name: string, style?: string, modelId?: string): Promise<string>;
|
|
152
167
|
/** 漫剧角色卡:生成定妆图并写回漫剧卡(imageUrl)。 */
|
package/lib/types/protocol.d.ts
CHANGED
|
@@ -55,6 +55,18 @@ export declare const NOVEL_API: {
|
|
|
55
55
|
readonly storyboardPrompts: "/api/dsh-novel-forge/storyboard/prompts";
|
|
56
56
|
/** 生图接口连通性测试(设置页每个模型条目用)。 */
|
|
57
57
|
readonly imageTest: "/api/dsh-novel-forge/image-test";
|
|
58
|
+
/** LLM 模型连通性测试:真实最小调用,验证 Key / 端点 / 模型可用。 */
|
|
59
|
+
readonly llmTest: "/api/dsh-novel-forge/llm-test";
|
|
60
|
+
/** 添加模型:厂商直填 key 或自定义路由,写进 DSH 凭据与 llm-pi-ai 路由。 */
|
|
61
|
+
readonly addModel: "/api/dsh-novel-forge/llm-add";
|
|
62
|
+
/** 运行时厂商目录(DSH pi-ai 可配置提供方 + 内置适配器)。 */
|
|
63
|
+
readonly llmVendors: "/api/dsh-novel-forge/llm-vendors";
|
|
64
|
+
/** 查询某个 provider 当前可用的模型(添加成功后可即时刷新)。 */
|
|
65
|
+
readonly llmModels: "/api/dsh-novel-forge/llm-models";
|
|
66
|
+
/** 已注册的提供方路由列表(提供方管理)。 */
|
|
67
|
+
readonly llmProviders: "/api/dsh-novel-forge/llm-providers";
|
|
68
|
+
/** 移除一个提供方。 */
|
|
69
|
+
readonly llmRemove: "/api/dsh-novel-forge/llm-remove";
|
|
58
70
|
/** 重置项目(可选携带新大纲):清空设定/卷/章节/伏笔/资产/事实库。 */
|
|
59
71
|
readonly reset: "/api/dsh-novel-forge/reset";
|
|
60
72
|
/** 全书一致性质检:LLM 扫描已生成章节,输出矛盾问题清单。 */
|
|
@@ -995,6 +1007,21 @@ export interface ImageTestResponse {
|
|
|
995
1007
|
/** 模型 id 是否出现在接口的模型列表(探测成功时才有)。 */
|
|
996
1008
|
modelFound?: boolean;
|
|
997
1009
|
}
|
|
1010
|
+
/** POST /llm-test 请求:对选中的提供商/模型发一次最小真实调用。 */
|
|
1011
|
+
export interface LlmTestRequest {
|
|
1012
|
+
provider: string;
|
|
1013
|
+
model: string;
|
|
1014
|
+
}
|
|
1015
|
+
/** POST /llm-test 响应。 */
|
|
1016
|
+
export interface LlmTestResponse {
|
|
1017
|
+
ok: boolean;
|
|
1018
|
+
/** 连通延迟(毫秒)。 */
|
|
1019
|
+
ms?: number;
|
|
1020
|
+
/** 失败原因(已映射为人话)。 */
|
|
1021
|
+
message?: string;
|
|
1022
|
+
/** 稳定错误码(LlmError code),便于排查。 */
|
|
1023
|
+
code?: string;
|
|
1024
|
+
}
|
|
998
1025
|
/** POST /roles 响应。 */
|
|
999
1026
|
export interface RolesResponse {
|
|
1000
1027
|
roles: RoleRecord[];
|
|
@@ -1158,6 +1185,122 @@ export interface ImageModelConfig {
|
|
|
1158
1185
|
enabled: boolean;
|
|
1159
1186
|
}
|
|
1160
1187
|
/** Runtime config surface exposed to the panel (subset of plugin Config). */
|
|
1188
|
+
/** 手动添加的模型库条目(只存插件内,不改 DSH 全局)。 */
|
|
1189
|
+
export interface SavedModel {
|
|
1190
|
+
/** 唯一 id(前端生成的短 id)。 */
|
|
1191
|
+
id: string;
|
|
1192
|
+
/** 展示名(可为空,回退到 model)。 */
|
|
1193
|
+
name: string;
|
|
1194
|
+
/** DSH 提供商路由(如 zai-coding-cn)。 */
|
|
1195
|
+
provider: string;
|
|
1196
|
+
/** 模型 id(如 glm-5.3-flash)。 */
|
|
1197
|
+
model: string;
|
|
1198
|
+
}
|
|
1199
|
+
/** DSH 模型添加:厂商预设(选择厂商,直接填 API Key)。 */
|
|
1200
|
+
export interface LlmVendor {
|
|
1201
|
+
id: string;
|
|
1202
|
+
name: string;
|
|
1203
|
+
/** 该厂商对应的 provider 路由。 */
|
|
1204
|
+
route: string;
|
|
1205
|
+
/** DSH 凭据引用名(写入 .credentials.yaml 的 refs)。 */
|
|
1206
|
+
apiKeyEnv: string;
|
|
1207
|
+
/** 默认模型 id。 */
|
|
1208
|
+
defaultModel: string;
|
|
1209
|
+
/** 建议可选模型 id 列表(下拉 data-list 用)。 */
|
|
1210
|
+
models: string[];
|
|
1211
|
+
/** 内置适配器(如 deepseek-official),无需注册 pi-ai 路由。 */
|
|
1212
|
+
builtin?: boolean;
|
|
1213
|
+
}
|
|
1214
|
+
/** 预置的常见厂商(id=provider 路由;添加模型下拉兜底用,其余厂商由运行时目录动态补充)。 */
|
|
1215
|
+
export declare const LLM_VENDORS: LlmVendor[];
|
|
1216
|
+
/** 运行时厂商目录的一项(添加到模型下拉;由 DSH 的 pi-ai 可配置提供方动态生成)。 */
|
|
1217
|
+
export interface LlmVendorOption {
|
|
1218
|
+
/** provider 路由 id(也是厂商下拉的值)。 */
|
|
1219
|
+
id: string;
|
|
1220
|
+
/** 展示名。 */
|
|
1221
|
+
name: string;
|
|
1222
|
+
/** 建议模型 id(可为空,用户手填)。 */
|
|
1223
|
+
models: string[];
|
|
1224
|
+
/** 已知 DSH 凭据引用名;为空时 host 生成 `PI_AI_<ID>_API_KEY`。 */
|
|
1225
|
+
apiKeyEnv?: string;
|
|
1226
|
+
/** 内置适配器(只写凭据,不注册 pi-ai 路由)。 */
|
|
1227
|
+
builtin?: boolean;
|
|
1228
|
+
}
|
|
1229
|
+
/** GET /llm-vendors 响应。 */
|
|
1230
|
+
export interface LlmVendorsResponse {
|
|
1231
|
+
vendors: LlmVendorOption[];
|
|
1232
|
+
}
|
|
1233
|
+
/** /llm-models 里的一条模型。 */
|
|
1234
|
+
export interface LlmModelOption {
|
|
1235
|
+
id: string;
|
|
1236
|
+
name: string;
|
|
1237
|
+
}
|
|
1238
|
+
/** GET /llm-models?provider=x 响应。 */
|
|
1239
|
+
export interface LlmModelsResponse {
|
|
1240
|
+
models: LlmModelOption[];
|
|
1241
|
+
}
|
|
1242
|
+
/** GET /llm-providers 响应:当前已注册的提供方路由。 */
|
|
1243
|
+
export interface LlmProvidersResponse {
|
|
1244
|
+
providers: {
|
|
1245
|
+
id: string;
|
|
1246
|
+
name: string;
|
|
1247
|
+
}[];
|
|
1248
|
+
}
|
|
1249
|
+
/** POST /llm-remove 请求:移除一个提供方(unset key + 移除 llm-pi-ai 路由)。 */
|
|
1250
|
+
export interface RemoveProviderRequest {
|
|
1251
|
+
provider: string;
|
|
1252
|
+
/** 该提供方对应的 DSH 凭据引用名(用于 unset)。 */
|
|
1253
|
+
apiKeyEnv?: string;
|
|
1254
|
+
}
|
|
1255
|
+
/** POST /llm-remove 响应。 */
|
|
1256
|
+
export interface RemoveProviderResponse {
|
|
1257
|
+
ok: boolean;
|
|
1258
|
+
message?: string;
|
|
1259
|
+
}
|
|
1260
|
+
/** GET /llm-providers 响应:当前已注册的提供方路由。 */
|
|
1261
|
+
export interface LlmProvidersResponse {
|
|
1262
|
+
providers: {
|
|
1263
|
+
id: string;
|
|
1264
|
+
name: string;
|
|
1265
|
+
}[];
|
|
1266
|
+
}
|
|
1267
|
+
/** POST /llm-remove 请求:移除一个提供方(unset key + 移除 llm-pi-ai 路由)。 */
|
|
1268
|
+
export interface RemoveProviderRequest {
|
|
1269
|
+
provider: string;
|
|
1270
|
+
/** 该提供方对应的 DSH 凭据引用名(用于 unset)。 */
|
|
1271
|
+
apiKeyEnv?: string;
|
|
1272
|
+
}
|
|
1273
|
+
/** POST /llm-remove 响应。 */
|
|
1274
|
+
export interface RemoveProviderResponse {
|
|
1275
|
+
ok: boolean;
|
|
1276
|
+
message?: string;
|
|
1277
|
+
}
|
|
1278
|
+
/** POST /llm-add 请求:添加一个模型(厂商直填 key,或自定义路由)。 */
|
|
1279
|
+
export interface AddModelRequest {
|
|
1280
|
+
mode: 'vendor' | 'custom';
|
|
1281
|
+
/** 厂商模式用:provider 路由 id(来自 /llm-vendors)。 */
|
|
1282
|
+
vendor?: string;
|
|
1283
|
+
/** 厂商已知的 DSH 凭据引用名(可选;为空时 host 生成)。 */
|
|
1284
|
+
apiKeyEnv?: string;
|
|
1285
|
+
/** 自定义模式用:provider 路由 id。 */
|
|
1286
|
+
provider?: string;
|
|
1287
|
+
/** 模型 id。 */
|
|
1288
|
+
model: string;
|
|
1289
|
+
/** API Key。 */
|
|
1290
|
+
apiKey: string;
|
|
1291
|
+
/** 展示名(可选)。 */
|
|
1292
|
+
name?: string;
|
|
1293
|
+
/** 自定义模式用:OpenAI 兼容 base URL。 */
|
|
1294
|
+
baseURL?: string;
|
|
1295
|
+
}
|
|
1296
|
+
/** POST /llm-add 响应。 */
|
|
1297
|
+
export interface AddModelResponse {
|
|
1298
|
+
ok: boolean;
|
|
1299
|
+
saved: SavedModel;
|
|
1300
|
+
provider: string;
|
|
1301
|
+
message?: string;
|
|
1302
|
+
}
|
|
1303
|
+
/** 手动添加的模型库条目(只存插件内,不改 DSH 全局)。 */
|
|
1161
1304
|
export interface NovelConfig {
|
|
1162
1305
|
/** Absolute path of the default docx outline to load. */
|
|
1163
1306
|
outlinePath: string;
|
|
@@ -1201,6 +1344,8 @@ export interface NovelConfig {
|
|
|
1201
1344
|
themeOpacity?: number;
|
|
1202
1345
|
/** 是否启用改编模式(默认关闭;发布后开启)。 */
|
|
1203
1346
|
enableAdaptMode?: boolean;
|
|
1347
|
+
/** 手动添加的模型库(「我的模型」条目;只存插件内)。 */
|
|
1348
|
+
savedModels?: SavedModel[];
|
|
1204
1349
|
}
|
|
1205
1350
|
/** GET /status response. */
|
|
1206
1351
|
export interface StatusResponse {
|
|
@@ -1447,6 +1592,8 @@ export interface ConfigPatch {
|
|
|
1447
1592
|
enableAdaptMode?: boolean;
|
|
1448
1593
|
/** 生图模型库(完整替换保存)。 */
|
|
1449
1594
|
imageModels?: ImageModelConfig[];
|
|
1595
|
+
/** 手动添加的模型库(完整替换保存)。 */
|
|
1596
|
+
savedModels?: SavedModel[];
|
|
1450
1597
|
}
|
|
1451
1598
|
/** One assistant conversation message (persisted per project). */
|
|
1452
1599
|
export interface AssistantMessage {
|
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 chapters with LLM, generate chapter by chapter (3000-4000 chars each), and save every chapter as Markdown into your chosen output folder.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.9.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
package/src/client/api.ts
CHANGED
|
@@ -8,6 +8,13 @@ import {
|
|
|
8
8
|
NOVEL_API,
|
|
9
9
|
type AssetsPatch,
|
|
10
10
|
type AssetsResponse,
|
|
11
|
+
type AddModelRequest,
|
|
12
|
+
type AddModelResponse,
|
|
13
|
+
type LlmModelsResponse,
|
|
14
|
+
type LlmProvidersResponse,
|
|
15
|
+
type LlmVendorsResponse,
|
|
16
|
+
type RemoveProviderRequest,
|
|
17
|
+
type RemoveProviderResponse,
|
|
11
18
|
type BibleResponse,
|
|
12
19
|
type ChapterResponse,
|
|
13
20
|
type ConfigPatch,
|
|
@@ -16,6 +23,7 @@ import {
|
|
|
16
23
|
type ForeshadowResponse,
|
|
17
24
|
type JobFrame,
|
|
18
25
|
type LoadOutlineResponse,
|
|
26
|
+
type LlmTestResponse,
|
|
19
27
|
type NovelConfig,
|
|
20
28
|
type PlanResponse,
|
|
21
29
|
type ReviewReport,
|
|
@@ -232,6 +240,39 @@ export class NovelApi {
|
|
|
232
240
|
return postJson<import('../protocol.ts').ImageTestResponse>(NOVEL_API.imageTest, req)
|
|
233
241
|
}
|
|
234
242
|
|
|
243
|
+
/** 对选中的提供商/模型发一次最小真实调用,验证连通性。 */
|
|
244
|
+
async llmTest(provider: string, model: string): Promise<LlmTestResponse> {
|
|
245
|
+
return postJson<LlmTestResponse>(NOVEL_API.llmTest, { provider, model })
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** 添加模型:厂商直填 key,或自定义 OpenAI 兼容路由(写 DSH 凭据 + 注册路由)。 */
|
|
249
|
+
async addModel(req: AddModelRequest): Promise<AddModelResponse> {
|
|
250
|
+
return postJson<AddModelResponse>(NOVEL_API.addModel, req)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** 运行时厂商目录(DSH pi-ai 可配置提供方 + 内置适配器)。 */
|
|
254
|
+
async llmVendors(): Promise<LlmVendorsResponse> {
|
|
255
|
+
const response = await fetch(NOVEL_API.llmVendors)
|
|
256
|
+
return readJson<LlmVendorsResponse>(response)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** 查询某个 provider 当前可用的模型(添加成功后可即时刷新)。 */
|
|
260
|
+
async llmModels(provider: string): Promise<LlmModelsResponse> {
|
|
261
|
+
const response = await fetch(NOVEL_API.llmModels + '?provider=' + encodeURIComponent(provider))
|
|
262
|
+
return readJson<LlmModelsResponse>(response)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** 已注册的提供方路由列表(提供方管理卡片)。 */
|
|
266
|
+
async llmProviders(): Promise<LlmProvidersResponse> {
|
|
267
|
+
const response = await fetch(NOVEL_API.llmProviders)
|
|
268
|
+
return readJson<LlmProvidersResponse>(response)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** 移除一个提供方(unset key + 移除 llm-pi-ai 路由)。 */
|
|
272
|
+
async removeProvider(req: RemoveProviderRequest): Promise<RemoveProviderResponse> {
|
|
273
|
+
return postJson<RemoveProviderResponse>(NOVEL_API.llmRemove, req)
|
|
274
|
+
}
|
|
275
|
+
|
|
235
276
|
async roles(req: import('../protocol.ts').RolesRequest): Promise<import('../protocol.ts').RolesResponse> {
|
|
236
277
|
return postJson<import('../protocol.ts').RolesResponse>(NOVEL_API.roles, req)
|
|
237
278
|
}
|
package/src/client/locales.ts
CHANGED
|
@@ -143,7 +143,23 @@ export const zh = {
|
|
|
143
143
|
'settings.provider': '模型提供商',
|
|
144
144
|
'settings.model': '模型',
|
|
145
145
|
'settings.modelCustom': '自定义…',
|
|
146
|
+
'settings.customProvider': '自定义提供商…',
|
|
147
|
+
'settings.edit': '编辑',
|
|
148
|
+
'settings.delete': '删除',
|
|
146
149
|
'settings.modelCustomPlaceholder': '输入任意模型 id',
|
|
150
|
+
'settings.savedModels': '我的模型',
|
|
151
|
+
'settings.modelCards': '模型卡片(内置 + 已添加)',
|
|
152
|
+
'settings.savedModelName': '名称',
|
|
153
|
+
'settings.addModel': '添加模型',
|
|
154
|
+
'settings.catalogRefresh': '刷新厂商',
|
|
155
|
+
'settings.refreshModels': '刷新模型',
|
|
156
|
+
'settings.savedModelsHint': '手动添加常用模型(provider + 模型 id),点击即可切换当前模型;厂商模式会写入 DSH 凭据并注册路由。',
|
|
157
|
+
'settings.testConnection': '测试连通',
|
|
158
|
+
'settings.testRunning': '测试中',
|
|
159
|
+
'settings.testOk': '连通',
|
|
160
|
+
'settings.testFail': '失败',
|
|
161
|
+
'settings.testHint': '真实发送一次最小调用(约几十 token),验证 Key / 端点 / 模型可用。',
|
|
162
|
+
'settings.reasoningSection': '推理强度',
|
|
147
163
|
'settings.reasoningEffort': '写作类任务思考强度 (reasoningEffort)',
|
|
148
164
|
'settings.reasoningHint': '章节生成/润色/重写/审稿等写作任务使用;关掉更省更快。',
|
|
149
165
|
'settings.analysisReasoning': '分析类任务思考强度(提炼/拆书/反推大纲)',
|
|
@@ -295,7 +311,23 @@ export const en: Record<string, string> = {
|
|
|
295
311
|
'settings.provider': 'Provider',
|
|
296
312
|
'settings.model': 'Model',
|
|
297
313
|
'settings.modelCustom': 'Custom…',
|
|
314
|
+
'settings.customProvider': 'Custom provider…',
|
|
315
|
+
'settings.edit': 'Edit',
|
|
316
|
+
'settings.delete': 'Delete',
|
|
298
317
|
'settings.modelCustomPlaceholder': 'Type any model id',
|
|
318
|
+
'settings.savedModels': 'My Models',
|
|
319
|
+
'settings.modelCards': 'Model cards (built-in + added)',
|
|
320
|
+
'settings.savedModelName': 'Name',
|
|
321
|
+
'settings.addModel': 'Add model',
|
|
322
|
+
'settings.catalogRefresh': 'Refresh vendors',
|
|
323
|
+
'settings.refreshModels': 'Refresh models',
|
|
324
|
+
'settings.savedModelsHint': 'Manually add a model (provider + model id); click to switch. Vendor mode writes the key into DSH credentials and registers the route.',
|
|
325
|
+
'settings.testConnection': 'Test',
|
|
326
|
+
'settings.testRunning': 'Testing',
|
|
327
|
+
'settings.testOk': 'OK',
|
|
328
|
+
'settings.testFail': 'Failed',
|
|
329
|
+
'settings.testHint': 'Sends one minimal real call (~dozens of tokens) to verify key / endpoint / model.',
|
|
330
|
+
'settings.reasoningSection': 'Reasoning',
|
|
299
331
|
'settings.reasoningEffort': 'Writing-task reasoning effort',
|
|
300
332
|
'settings.analysisReasoning': 'Analysis-task reasoning (extract / breakdown / reverse outline)',
|
|
301
333
|
'settings.analysisReasoningHint': 'One-off analysis tasks (roles/scenes/bible extract, breakdown, reverse outline) default to low; not affected by the writing-level switch.',
|
|
@@ -95,11 +95,9 @@ export function AdaptModeView({ api }: { api: NovelApi }) {
|
|
|
95
95
|
|
|
96
96
|
return (
|
|
97
97
|
<div className={css.authorPageBody}>
|
|
98
|
-
<div className={css.
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
<span className={css.meta}>上传全文 → 分析可改范围 → 勾选/填新值 → 生成映射表 → 术语替换执行。</span>
|
|
102
|
-
</div>
|
|
98
|
+
<div className={css.card + ' ' + css.settingsCard} style={{ gap: 'var(--nf-space-6)' }}>
|
|
99
|
+
<h2 className={css.panelTitle} style={{ margin: 0 }}>🎬 改编模式</h2>
|
|
100
|
+
<span className={css.meta}>上传全文 → 分析可改范围 → 勾选/填新值 → 生成映射表 → 术语替换执行。</span>
|
|
103
101
|
</div>
|
|
104
102
|
|
|
105
103
|
<div className={css.adaptInputCard}>
|
|
@@ -9,10 +9,14 @@ import type { BookshelfSnapshot } from '../../protocol.ts'
|
|
|
9
9
|
import { ShelfView } from './ShelfView.tsx'
|
|
10
10
|
import { AdaptModeView } from './AdaptModeView.tsx'
|
|
11
11
|
import { AuthorAssetsView } from './AuthorAssetsView.tsx'
|
|
12
|
-
import {
|
|
12
|
+
import { ProgressConsole, type ProgressLine } from './ProgressConsole.tsx'
|
|
13
13
|
import { SettingsView } from './SettingsView.tsx'
|
|
14
14
|
import css from './panel.module.css'
|
|
15
15
|
|
|
16
|
+
/** 构建时注入的插件版本(tsdown define 替换为字符串字面量)。 */
|
|
17
|
+
declare const __NOVEL_FORGE_VERSION__: string | undefined
|
|
18
|
+
const PLUGIN_VERSION: string = typeof __NOVEL_FORGE_VERSION__ !== 'undefined' ? __NOVEL_FORGE_VERSION__ : '0.0.0'
|
|
19
|
+
|
|
16
20
|
type AuthorNav = 'shelf' | 'adapt' | 'assets' | 'library' | 'progress' | 'settings';
|
|
17
21
|
|
|
18
22
|
const NAV_ITEMS: Array<{ id: Exclude<AuthorNav, 'settings'>; label: string; icon: JSX.Element; hint: string }> = [
|
|
@@ -26,7 +30,7 @@ const NAV_ITEMS: Array<{ id: Exclude<AuthorNav, 'settings'>; label: string; icon
|
|
|
26
30
|
function Placeholder({ title, hint }: { title: string; hint: string }) {
|
|
27
31
|
return (
|
|
28
32
|
<div className={css.authorPageBody}>
|
|
29
|
-
<div className={css.
|
|
33
|
+
<div className={css.card + ' ' + css.settingsCard} style={{ gap: 'var(--nf-space-6)' }}><h2 className={css.panelTitle} style={{ margin: 0 }}>{title}</h2><span className={css.meta}>{hint}</span></div>
|
|
30
34
|
<div className={css.shelfEmpty} style={{ minHeight: 160 }}>
|
|
31
35
|
<span className={css.shelfEmptyIcon}><Info size={30} /></span>
|
|
32
36
|
<span className={css.shelfEmptyTitle}>此入口为占位</span>
|
|
@@ -36,7 +40,7 @@ function Placeholder({ title, hint }: { title: string; hint: string }) {
|
|
|
36
40
|
);
|
|
37
41
|
}
|
|
38
42
|
|
|
39
|
-
export function AuthorHome({ api, shelf, onOpenBook, onReadBook, onAddBook, onImportBook, onOpenSettings, onTheme, onBackground, onOpacity, adaptEnabled }: {
|
|
43
|
+
export function AuthorHome({ api, shelf, onOpenBook, onReadBook, onAddBook, onImportBook, onOpenSettings, onTheme, onBackground, onOpacity, adaptEnabled, progress, busy, busyLabel, liveBar, onClearProgress }: {
|
|
40
44
|
api: NovelApi; shelf: BookshelfSnapshot;
|
|
41
45
|
onOpenBook: (id: string) => void; onReadBook: (id: string) => void; onAddBook: () => void; onImportBook: () => void;
|
|
42
46
|
/** 兼容旧入口:首页设置现为独立设置页,此回调保留但不再使用。 */
|
|
@@ -49,6 +53,12 @@ export function AuthorHome({ api, shelf, onOpenBook, onReadBook, onAddBook, onIm
|
|
|
49
53
|
onOpacity?: (n: number) => void;
|
|
50
54
|
/** 是否启用改编模式(默认 false=隐藏入口)。 */
|
|
51
55
|
adaptEnabled?: boolean;
|
|
56
|
+
/** AI 进度实时状态(与书内共享同一份)。 */
|
|
57
|
+
progress?: ProgressLine[];
|
|
58
|
+
busy?: boolean;
|
|
59
|
+
busyLabel?: string;
|
|
60
|
+
liveBar?: { text: string; ratio?: number } | null;
|
|
61
|
+
onClearProgress?: () => void;
|
|
52
62
|
}) {
|
|
53
63
|
const [nav, setNav] = useState<AuthorNav>('shelf');
|
|
54
64
|
|
|
@@ -77,7 +87,7 @@ export function AuthorHome({ api, shelf, onOpenBook, onReadBook, onAddBook, onIm
|
|
|
77
87
|
<span className={css.navTabLabel}>设置</span>
|
|
78
88
|
</button>
|
|
79
89
|
<div className={css.navAbout}>
|
|
80
|
-
<div className={css.navAboutRow}><span>ℹ️
|
|
90
|
+
<div className={css.navAboutRow}><span>ℹ️ v{PLUGIN_VERSION} · 改编/资产库</span></div>
|
|
81
91
|
</div>
|
|
82
92
|
</nav>
|
|
83
93
|
<div className={css.authorContent}>
|
|
@@ -85,7 +95,11 @@ export function AuthorHome({ api, shelf, onOpenBook, onReadBook, onAddBook, onIm
|
|
|
85
95
|
{nav === 'adapt' && <AdaptModeView api={api} />}
|
|
86
96
|
{nav === 'assets' && <AuthorAssetsView api={api} />}
|
|
87
97
|
{nav === 'library' && <Placeholder title="🎨 全局写作资产库" hint="内置题材基底库 / 反AI规则库 / 风格模板 / 推进模式(跨书)" />}
|
|
88
|
-
{nav === 'progress' &&
|
|
98
|
+
{nav === 'progress' && (
|
|
99
|
+
<div className={css.authorPageBody}>
|
|
100
|
+
<ProgressConsole progress={progress ?? []} busy={busy ?? false} busyLabel={busyLabel ?? ''} liveBar={liveBar ?? null} onClear={() => onClearProgress?.()} />
|
|
101
|
+
</div>
|
|
102
|
+
)}
|
|
89
103
|
{nav === 'settings' && <SettingsView api={api} onTheme={onTheme} onBackground={onBackground} onOpacity={onOpacity} />}
|
|
90
104
|
</div>
|
|
91
105
|
</div>
|