@musnows/scriverse 0.5.2 → 0.5.3
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 -21
- package/README.en.md +8 -0
- package/README.md +8 -0
- package/dist/ai.js +950 -145
- package/dist/ai.js.map +1 -1
- package/dist/app.js +49 -6
- package/dist/app.js.map +1 -1
- package/dist/database.js +321 -0
- package/dist/database.js.map +1 -1
- package/dist/public/app.js +60 -11
- package/dist/public/display-labels.js +1 -0
- package/dist/public/index.html +13 -7
- package/dist/public/styles.css +17 -0
- package/dist/relationship-search.js +196 -0
- package/dist/relationship-search.js.map +1 -0
- package/dist/store.js +59 -8
- package/dist/store.js.map +1 -1
- package/dist/user-auth.js +23 -6
- package/dist/user-auth.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -2
package/dist/store.js
CHANGED
|
@@ -3371,8 +3371,13 @@ export class Store {
|
|
|
3371
3371
|
this.getWork(workId);
|
|
3372
3372
|
const reviewId = id("review");
|
|
3373
3373
|
const timestamp = now();
|
|
3374
|
-
this.db.run(`INSERT INTO review_items (id, work_id, item_type, severity, title, description, entity_refs_json, evidence_json,
|
|
3375
|
-
suggestion, status, resolution_note, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, reviewId, workId, input.itemType, input.severity ?? "medium", input.title, input.description ?? "", JSON.stringify(input.entityRefs ?? []), JSON.stringify(input.evidence ?? []), input.suggestion ?? "", input.status ?? "pending", input.resolutionNote ?? "", timestamp, timestamp);
|
|
3374
|
+
const result = this.db.run(`INSERT OR IGNORE INTO review_items (id, work_id, item_type, dedupe_key, severity, title, description, entity_refs_json, evidence_json,
|
|
3375
|
+
suggestion, status, resolution_note, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, reviewId, workId, input.itemType, input.dedupeKey ?? "", input.severity ?? "medium", input.title, input.description ?? "", JSON.stringify(input.entityRefs ?? []), JSON.stringify(input.evidence ?? []), input.suggestion ?? "", input.status ?? "pending", input.resolutionNote ?? "", timestamp, timestamp);
|
|
3376
|
+
if (result.changes === 0 && input.dedupeKey) {
|
|
3377
|
+
const existing = this.db.get("SELECT * FROM review_items WHERE work_id = ? AND item_type = ? AND dedupe_key = ?", workId, input.itemType, input.dedupeKey);
|
|
3378
|
+
if (existing)
|
|
3379
|
+
return this.mapReviewItem(existing);
|
|
3380
|
+
}
|
|
3376
3381
|
return this.getReviewItem(reviewId);
|
|
3377
3382
|
}
|
|
3378
3383
|
listReviewItems(workId, status) {
|
|
@@ -3707,9 +3712,13 @@ export class Store {
|
|
|
3707
3712
|
else if (scope.type === "settings") {
|
|
3708
3713
|
Object.assign(sourceVersions, this.relationshipSettingsSourceVersions(workId));
|
|
3709
3714
|
}
|
|
3710
|
-
this.db.run(`INSERT INTO analysis_tasks (id, work_id, task_type, scope_json, status, source_versions_json, created_at, updated_at, created_by_user_id)
|
|
3711
|
-
VALUES (?, ?, ?, ?, 'pending', ?, ?, ?, ?)`, taskId, workId, input.taskType, JSON.stringify(scope), JSON.stringify(sourceVersions), timestamp, timestamp, currentRequestActor()?.userId ?? null);
|
|
3712
|
-
this.audit(workId, "task.created", "analysis-task", taskId, {
|
|
3715
|
+
this.db.run(`INSERT INTO analysis_tasks (id, work_id, model_id, task_type, scope_json, status, source_versions_json, created_at, updated_at, created_by_user_id)
|
|
3716
|
+
VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, ?, ?)`, taskId, workId, input.modelId ?? null, input.taskType, JSON.stringify(scope), JSON.stringify(sourceVersions), timestamp, timestamp, currentRequestActor()?.userId ?? null);
|
|
3717
|
+
this.audit(workId, "task.created", "analysis-task", taskId, {
|
|
3718
|
+
taskType: input.taskType,
|
|
3719
|
+
scope,
|
|
3720
|
+
modelId: input.modelId ?? null
|
|
3721
|
+
});
|
|
3713
3722
|
this.notifyAnalysisTaskQueued(workId);
|
|
3714
3723
|
return this.getTask(taskId);
|
|
3715
3724
|
}
|
|
@@ -3722,8 +3731,11 @@ export class Store {
|
|
|
3722
3731
|
FROM analysis_tasks WHERE work_id = ?`, workId) ?? {};
|
|
3723
3732
|
const total = numberValue(statsRow, "total");
|
|
3724
3733
|
const page = paginationSql(pagination);
|
|
3725
|
-
const rows = this.db.all(`SELECT id, task_type, scope_json, status, progress, created_at, updated_at
|
|
3726
|
-
|
|
3734
|
+
const rows = this.db.all(`SELECT task.id, task.model_id, task.task_type, task.scope_json, task.status, task.progress, task.created_at, task.updated_at,
|
|
3735
|
+
model.display_name AS model_display_name, model.model_id AS model_api_id
|
|
3736
|
+
FROM analysis_tasks task
|
|
3737
|
+
LEFT JOIN models model ON model.id = task.model_id
|
|
3738
|
+
WHERE task.work_id = ? ORDER BY task.created_at DESC, task.id DESC${page.sql}`, workId, ...page.params);
|
|
3727
3739
|
const chapterSummaries = new Map(this.db.all(`SELECT chapter.id, chapter.title, volume.title AS volume_title
|
|
3728
3740
|
FROM chapters chapter JOIN volumes volume ON volume.id = chapter.volume_id
|
|
3729
3741
|
WHERE chapter.work_id = ?`, workId).map((row) => [
|
|
@@ -4474,9 +4486,15 @@ export class Store {
|
|
|
4474
4486
|
const targetSummary = analysisTarget.mode === "targeted-characters" && targetNames.length
|
|
4475
4487
|
? `重点分析 ${targetNames.join("、")} 与其他已建档人物之间的关系`
|
|
4476
4488
|
: "分析范围内已建档人物之间的长期关系";
|
|
4489
|
+
const sourceSelection = result.sourceSelection && typeof result.sourceSelection === "object" && !Array.isArray(result.sourceSelection)
|
|
4490
|
+
? result.sourceSelection
|
|
4491
|
+
: null;
|
|
4477
4492
|
summary = missingRelationshipIds.length > 0
|
|
4478
4493
|
? `${targetSummary}。任务结果记录 ${relationshipIds.length} 条关系,当前作品中保留 ${relationships.length} 条可展示关系,另有 ${missingRelationshipIds.length} 条已删除或合并。`
|
|
4479
4494
|
: `${targetSummary},共形成 ${relationships.length} 条可展示结果。`;
|
|
4495
|
+
if (sourceSelection) {
|
|
4496
|
+
summary += ` 来源筛选命中 ${Number(sourceSelection.exactSourceCount ?? 0)} 个精确来源,确认 ${Number(sourceSelection.confirmedSourceCount ?? 0)} 个疑似来源。`;
|
|
4497
|
+
}
|
|
4480
4498
|
const actionMetrics = [
|
|
4481
4499
|
["新建", result.createdCount],
|
|
4482
4500
|
["更新", result.updatedCount],
|
|
@@ -4487,7 +4505,14 @@ export class Store {
|
|
|
4487
4505
|
metric("任务记录", relationshipIds.length || relationships.length),
|
|
4488
4506
|
metric("当前可展示", relationships.length),
|
|
4489
4507
|
metric("已删除或合并", missingRelationshipIds.length),
|
|
4490
|
-
metric("跳过", Array.isArray(result.skipped) ? result.skipped.length : 0)
|
|
4508
|
+
metric("跳过", Array.isArray(result.skipped) ? result.skipped.length : 0),
|
|
4509
|
+
...(sourceSelection ? [
|
|
4510
|
+
metric("精确来源", Number(sourceSelection.exactSourceCount ?? 0)),
|
|
4511
|
+
metric("模糊候选", Number(sourceSelection.fuzzyCandidateCount ?? 0)),
|
|
4512
|
+
metric("确认来源", Number(sourceSelection.confirmedSourceCount ?? 0)),
|
|
4513
|
+
metric("排除", Number(sourceSelection.rejectedSourceCount ?? 0)),
|
|
4514
|
+
metric("不确定", Number(sourceSelection.uncertainSourceCount ?? 0))
|
|
4515
|
+
] : [])
|
|
4491
4516
|
];
|
|
4492
4517
|
storageTargets.unshift({
|
|
4493
4518
|
label: "人物关系",
|
|
@@ -4496,6 +4521,15 @@ export class Store {
|
|
|
4496
4521
|
count: relationshipIds.length || relationships.length,
|
|
4497
4522
|
note: `任务记录 ${relationshipIds.length || relationships.length} 条关系,当前可读取 ${relationships.length} 条;关系候选需由作者确认。`
|
|
4498
4523
|
});
|
|
4524
|
+
const variantReviewIds = sourceSelection && Array.isArray(sourceSelection.reviewIds) ? sourceSelection.reviewIds.map(String) : [];
|
|
4525
|
+
if (variantReviewIds.length > 0)
|
|
4526
|
+
storageTargets.unshift({
|
|
4527
|
+
label: "疑似人物名错字",
|
|
4528
|
+
entity: "审核中心",
|
|
4529
|
+
key: "reviews",
|
|
4530
|
+
count: variantReviewIds.length,
|
|
4531
|
+
note: "仅生成待处理审核项,不会修改正文或人物别名。"
|
|
4532
|
+
});
|
|
4499
4533
|
sections = [
|
|
4500
4534
|
{ title: "分析出的关系", totalCount: relationships.length, items: relationships.slice(0, 100), emptyMessage: "没有形成可展示的人物关系。" },
|
|
4501
4535
|
section("未写入候选", result.skipped, "没有候选被跳过。")
|
|
@@ -4599,6 +4633,7 @@ export class Store {
|
|
|
4599
4633
|
return {
|
|
4600
4634
|
id: requiredString(row, "id"),
|
|
4601
4635
|
workId,
|
|
4636
|
+
model: this.analysisTaskModel(row),
|
|
4602
4637
|
taskType,
|
|
4603
4638
|
scope,
|
|
4604
4639
|
scopeSummary: this.taskScopeSummary(workId, scope, characterNames),
|
|
@@ -4617,6 +4652,7 @@ export class Store {
|
|
|
4617
4652
|
const scope = json(requiredString(row, "scope_json"), {});
|
|
4618
4653
|
return {
|
|
4619
4654
|
id: requiredString(row, "id"),
|
|
4655
|
+
model: this.analysisTaskModel(row),
|
|
4620
4656
|
taskType: requiredString(row, "task_type"),
|
|
4621
4657
|
scopeSummary: this.taskScopeSummaryFromMaps(scope, chapterSummaries, volumeTitles, characterNames),
|
|
4622
4658
|
scopeSummaryWithoutCharacterNames: this.taskScopeSummaryFromMaps(scope, chapterSummaries, volumeTitles, new Map(), false),
|
|
@@ -4626,6 +4662,21 @@ export class Store {
|
|
|
4626
4662
|
updatedAt: requiredString(row, "updated_at")
|
|
4627
4663
|
};
|
|
4628
4664
|
}
|
|
4665
|
+
analysisTaskModel(row) {
|
|
4666
|
+
const modelId = optionalString(row, "model_id");
|
|
4667
|
+
if (!modelId)
|
|
4668
|
+
return null;
|
|
4669
|
+
const model = row.model_display_name !== undefined
|
|
4670
|
+
? row
|
|
4671
|
+
: this.db.get("SELECT display_name AS model_display_name, model_id AS model_api_id FROM models WHERE id = ?", modelId);
|
|
4672
|
+
if (!model)
|
|
4673
|
+
return { id: modelId, displayName: "模型已删除", modelId: "", deleted: true };
|
|
4674
|
+
return {
|
|
4675
|
+
id: modelId,
|
|
4676
|
+
displayName: requiredString(model, "model_display_name"),
|
|
4677
|
+
modelId: requiredString(model, "model_api_id")
|
|
4678
|
+
};
|
|
4679
|
+
}
|
|
4629
4680
|
taskScopeSummaryFromMaps(scope, chapterSummaries, volumeTitles, characterNames, includeCharacterNames = true) {
|
|
4630
4681
|
const targetedSuffix = this.taskTargetedSuffix(scope, characterNames, includeCharacterNames);
|
|
4631
4682
|
if (typeof scope.chapterId === "string")
|