@musnows/scriverse 0.9.2 → 0.9.4
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/dist/ai.js +34 -11
- package/dist/ai.js.map +1 -1
- package/dist/app.js +31 -8
- package/dist/app.js.map +1 -1
- package/dist/public/ai-context-meter.js +8 -3
- package/dist/public/ai-stream-protocol.js +9 -0
- package/dist/public/app.js +15 -34
- package/dist/public/index.html +4 -4
- package/dist/public/styles.css +7 -5
- package/dist/relationship-search.js +30 -1
- package/dist/relationship-search.js.map +1 -1
- package/dist/store.js +5 -6
- package/dist/store.js.map +1 -1
- package/dist/utils.js +0 -8
- package/dist/utils.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/ai.js
CHANGED
|
@@ -20,7 +20,7 @@ import { defaultAiConversationTitle, normalizeCharacterName } from "./store.js";
|
|
|
20
20
|
import { composeRoleplayCurrentUserTurn, formatRoleplayScenePinText, roleplayUserTurnTitleSource } from "./roleplay-turn.js";
|
|
21
21
|
import { canReadWorkModule } from "./work-permissions.js";
|
|
22
22
|
import { buildWritingCalendar, buildWritingMonthCalendar, formatServerLocalClock, resolveServerTimeZone } from "./writing-progress-time.js";
|
|
23
|
-
import { RELATIONSHIP_SEARCH_POLICY_VERSION, RelationshipApproximateMatchLimitError, findApproximateNameMatchesChunked, ftsPhrase, isRelationshipPhoneticReference, normalizeRelationshipSearchText, relationshipCharacterTokenText, relationshipCharacterTokens, relationshipPinyinSearchTokens, relationshipPinyinTokenText, relationshipPinyinTokens } from "./relationship-search.js";
|
|
23
|
+
import { RELATIONSHIP_SEARCH_POLICY_VERSION, RelationshipApproximateMatchLimitError, findApproximateNameMatchesChunked, ftsPhrase, isRelationshipPhoneticReference, normalizeRelationshipSearchText, relationshipCharacterTokenText, relationshipCharacterTokens, relationshipPinyinFtsQuery, relationshipPinyinSearchTokens, relationshipPinyinSequenceMatches, relationshipPinyinTokenText, relationshipPinyinTokens } from "./relationship-search.js";
|
|
24
24
|
import { clamp, id, json, maskSecret, now } from "./utils.js";
|
|
25
25
|
import { z } from "zod";
|
|
26
26
|
export function aiErrorForLog(error) {
|
|
@@ -344,6 +344,19 @@ const AGENT_ENTITY_CATEGORY_MODULES = {
|
|
|
344
344
|
outline: "outlines",
|
|
345
345
|
foreshadow: "outlines"
|
|
346
346
|
};
|
|
347
|
+
const AGENT_ENTITY_CATEGORY_SEARCH_TYPES = {
|
|
348
|
+
setting: ["setting"],
|
|
349
|
+
character: ["character"],
|
|
350
|
+
race: ["race"],
|
|
351
|
+
organization: ["organization"],
|
|
352
|
+
timeline: ["timeline-track", "timeline-event"],
|
|
353
|
+
relationship: ["relationship"],
|
|
354
|
+
outline: ["chapter-outline"],
|
|
355
|
+
foreshadow: ["foreshadow"]
|
|
356
|
+
};
|
|
357
|
+
function agentEntitySearchTypes(categories) {
|
|
358
|
+
return [...new Set([...categories].flatMap((category) => AGENT_ENTITY_CATEGORY_SEARCH_TYPES[category]))];
|
|
359
|
+
}
|
|
347
360
|
function traceRecord(value) {
|
|
348
361
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
349
362
|
}
|
|
@@ -536,6 +549,7 @@ const grepArguments = z.object({
|
|
|
536
549
|
const searchStoryEntitiesArguments = z.object({
|
|
537
550
|
query: z.string().trim().min(1).max(MAXIMUM_WORK_SEARCH_QUERY_LENGTH),
|
|
538
551
|
categories: z.array(z.enum(["setting", "character", "race", "organization", "timeline", "relationship", "outline", "foreshadow"])).max(8).default([]),
|
|
552
|
+
includePhonetic: z.boolean().default(false),
|
|
539
553
|
limit: z.number().int().min(1).max(30).default(30),
|
|
540
554
|
cursor: agentToolCursor
|
|
541
555
|
}).strict();
|
|
@@ -626,8 +640,8 @@ const AGENT_TOOL_DEFINITIONS = {
|
|
|
626
640
|
type: "function",
|
|
627
641
|
function: {
|
|
628
642
|
name: "search_story_entities",
|
|
629
|
-
description: "
|
|
630
|
-
parameters: { type: "object", properties: { query: { type: "string", minLength: 1, maxLength: MAXIMUM_WORK_SEARCH_QUERY_LENGTH }, categories: { type: "array", items: { type: "string", enum: ["setting", "character", "race", "organization", "timeline", "relationship", "outline", "foreshadow"] }, maxItems: 8 }, limit: { type: "integer", minimum: 1, maximum: 30, default: 30 }, cursor: agentToolCursorParameter }, required: ["query"], additionalProperties: false }
|
|
643
|
+
description: "按短关键词在结构化作品实体中进行元数据与精确全文检索:设定、人物(含 Markdown 档案章节)、种族、组织、时间线、关系、大纲和伏笔。默认不查询拼音索引;只有中文实体可能存在同音字或错别字且精确检索无结果时,才设置 includePhonetic=true。拼音索引极其缓慢,必须谨慎使用。人物结果包含权威 gender 字段:male 表示男/雄性,female 表示女/雌性,none 表示无性别,unknown 表示未知;gender=unknown 时禁止根据正文或常识自行推断。人物、种族、组织结果还分别包含权威布尔状态 isDead、isExtinct、isDissolved;只有值为 true 才能判定该角色已死亡、该种族已灭绝或该组织已解散,字段为 false 时必须视为仍存活、未灭绝或未解散,禁止根据正文情节自行改判。时间线事件结果返回 trackId、timeSort、chapterIds、chapterStoryOrders 与 orderEligible;只有 orderEligible=true 的事件才可参与同轨道时间比较。不是语义问答;请传入实体名、别名、标题或短关键词,不要传入自然语言整句。结果按综合相关度排序;人物结果含 sectionId 时可再调用 read_character_sections 精读。无匹配时先改用更短关键词,再按需谨慎启用拼音索引,或改用 story_index / grep。",
|
|
644
|
+
parameters: { type: "object", properties: { query: { type: "string", minLength: 1, maxLength: MAXIMUM_WORK_SEARCH_QUERY_LENGTH }, categories: { type: "array", items: { type: "string", enum: ["setting", "character", "race", "organization", "timeline", "relationship", "outline", "foreshadow"] }, maxItems: 8 }, includePhonetic: { type: "boolean", default: false, description: "是否启用极其缓慢的拼音索引。默认关闭;仅在同音字或错别字检索确有必要时谨慎开启。" }, limit: { type: "integer", minimum: 1, maximum: 30, default: 30 }, cursor: agentToolCursorParameter }, required: ["query"], additionalProperties: false }
|
|
631
645
|
}
|
|
632
646
|
},
|
|
633
647
|
read_character_sections: {
|
|
@@ -2112,7 +2126,7 @@ export class AiManager {
|
|
|
2112
2126
|
? this.hybridAgentHistoryMatches(workId, normalizedQuery, channelLimit, options.conversationOwnerUserId)
|
|
2113
2127
|
: [])
|
|
2114
2128
|
];
|
|
2115
|
-
const phoneticCandidates = [
|
|
2129
|
+
const phoneticCandidates = options.includePhonetic === false ? [] : [
|
|
2116
2130
|
...(requestedTypes.has("chapter")
|
|
2117
2131
|
? this.hybridChapterMatches(workId, normalizedQuery, "phonetic", channelLimit, chapterLineRangeFallbackState)
|
|
2118
2132
|
: []),
|
|
@@ -2170,10 +2184,12 @@ export class AiManager {
|
|
|
2170
2184
|
}
|
|
2171
2185
|
hybridChapterMatches(workId, query, matchKind, limit, fallbackState) {
|
|
2172
2186
|
let rows;
|
|
2187
|
+
let phoneticVerificationSyllables = null;
|
|
2173
2188
|
if (matchKind === "phonetic") {
|
|
2174
|
-
const
|
|
2175
|
-
if (
|
|
2189
|
+
const pinyinQuery = relationshipPinyinFtsQuery(query);
|
|
2190
|
+
if (!pinyinQuery)
|
|
2176
2191
|
return [];
|
|
2192
|
+
phoneticVerificationSyllables = pinyinQuery.verificationSyllables;
|
|
2177
2193
|
rows = this.store.db.all(`SELECT paragraph.id AS paragraph_id, paragraph.chapter_id, paragraph.paragraph_order,
|
|
2178
2194
|
paragraph.content AS paragraph_content, line_range.chapter_version AS range_chapter_version,
|
|
2179
2195
|
line_range.start_line, line_range.end_line, chapter.version_no AS chapter_version,
|
|
@@ -2186,7 +2202,7 @@ export class AiManager {
|
|
|
2186
2202
|
WHERE paragraph.work_id = ? AND chapter.deleted_at IS NULL
|
|
2187
2203
|
AND chapter_paragraph_pinyin_fts MATCH ?
|
|
2188
2204
|
ORDER BY bm25(chapter_paragraph_pinyin_fts), volume.sort_order, chapter.sort_order, paragraph.paragraph_order
|
|
2189
|
-
LIMIT ?`, workId,
|
|
2205
|
+
LIMIT ?`, workId, pinyinQuery.expression, limit);
|
|
2190
2206
|
}
|
|
2191
2207
|
else if ([...query].length < 3) {
|
|
2192
2208
|
rows = this.store.db.all(`SELECT paragraph.id AS paragraph_id, paragraph.chapter_id, paragraph.paragraph_order,
|
|
@@ -2217,6 +2233,9 @@ export class AiManager {
|
|
|
2217
2233
|
ORDER BY bm25(chapter_paragraph_search_fts), volume.sort_order, chapter.sort_order, paragraph.paragraph_order
|
|
2218
2234
|
LIMIT ?`, workId, `"${query.replaceAll('"', '""')}"`, limit);
|
|
2219
2235
|
}
|
|
2236
|
+
if (phoneticVerificationSyllables) {
|
|
2237
|
+
rows = rows.filter((row) => relationshipPinyinSequenceMatches(String(row.paragraph_content ?? ""), phoneticVerificationSyllables));
|
|
2238
|
+
}
|
|
2220
2239
|
const seen = new Set();
|
|
2221
2240
|
return rows.flatMap((row) => {
|
|
2222
2241
|
const chapterId = String(row.chapter_id ?? "");
|
|
@@ -6667,11 +6686,15 @@ export class AiManager {
|
|
|
6667
6686
|
};
|
|
6668
6687
|
}
|
|
6669
6688
|
if (name === "search_story_entities") {
|
|
6670
|
-
const { query, categories: categoryList, limit, cursor } = args;
|
|
6689
|
+
const { query, categories: categoryList, includePhonetic, limit, cursor } = args;
|
|
6671
6690
|
const readableCategories = this.readableAgentEntityCategories(permissions);
|
|
6672
6691
|
const categories = new Set(categoryList.filter((category) => readableCategories.has(category)));
|
|
6673
6692
|
const requestedCategories = categoryList.length > 0 ? categories : readableCategories;
|
|
6674
|
-
const combined = (await this.searchWork(workId, query, {
|
|
6693
|
+
const combined = (await this.searchWork(workId, query, {
|
|
6694
|
+
limit: 100,
|
|
6695
|
+
allowedTypes: agentEntitySearchTypes(requestedCategories),
|
|
6696
|
+
includePhonetic
|
|
6697
|
+
})).flatMap((item) => {
|
|
6675
6698
|
const sourceType = String(item.type);
|
|
6676
6699
|
const type = sourceType === "timeline-track" || sourceType === "timeline-event"
|
|
6677
6700
|
? "timeline"
|
|
@@ -6706,7 +6729,7 @@ export class AiManager {
|
|
|
6706
6729
|
ok: true,
|
|
6707
6730
|
data: {
|
|
6708
6731
|
query,
|
|
6709
|
-
matchMode: "hybrid_exact_phonetic",
|
|
6732
|
+
matchMode: includePhonetic ? "hybrid_exact_phonetic" : "hybrid_exact",
|
|
6710
6733
|
...(requestedCategories.has("timeline")
|
|
6711
6734
|
? { storyOrdering: entityStoryOrdering }
|
|
6712
6735
|
: {}),
|
|
@@ -6721,7 +6744,7 @@ export class AiManager {
|
|
|
6721
6744
|
id: toolCall.id,
|
|
6722
6745
|
name,
|
|
6723
6746
|
calledAt,
|
|
6724
|
-
arguments: { query, categories: [...requestedCategories], limit, ...(cursor > 0 ? { cursor } : {}) },
|
|
6747
|
+
arguments: { query, categories: [...requestedCategories], includePhonetic, limit, ...(cursor > 0 ? { cursor } : {}) },
|
|
6725
6748
|
status: "completed",
|
|
6726
6749
|
result
|
|
6727
6750
|
};
|