@musnows/scriverse 0.6.7 → 0.6.9
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-protocol.js +5 -1
- package/dist/ai-protocol.js.map +1 -1
- package/dist/ai.js +461 -49
- package/dist/ai.js.map +1 -1
- package/dist/app.js +35 -7
- package/dist/app.js.map +1 -1
- package/dist/database.js +174 -2
- package/dist/database.js.map +1 -1
- package/dist/hybrid-search.js +2 -1
- package/dist/hybrid-search.js.map +1 -1
- package/dist/public/app.js +302 -89
- package/dist/public/display-labels.js +2 -1
- package/dist/public/global-search.d.ts +3 -1
- package/dist/public/global-search.js +22 -0
- package/dist/public/index.html +7 -4
- package/dist/public/model-config.d.ts +3 -0
- package/dist/public/model-config.js +8 -0
- package/dist/public/styles.css +44 -2
- package/dist/release-update.js +170 -0
- package/dist/release-update.js.map +1 -0
- package/dist/server-runtime.js +5 -1
- package/dist/server-runtime.js.map +1 -1
- package/dist/store.js +99 -18
- package/dist/store.js.map +1 -1
- package/dist/user-auth.js +6 -2
- package/dist/user-auth.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/store.js
CHANGED
|
@@ -16,7 +16,8 @@ export const WORK_AGENT_TOOL_IDS = [
|
|
|
16
16
|
"grep",
|
|
17
17
|
"search_story_entities",
|
|
18
18
|
"read_character_sections",
|
|
19
|
-
"search_drafts"
|
|
19
|
+
"search_drafts",
|
|
20
|
+
"image"
|
|
20
21
|
];
|
|
21
22
|
const DEFAULT_WORK_AGENT_TOOLS = [...WORK_AGENT_TOOL_IDS];
|
|
22
23
|
export function normalizeWorkAgentTools(value) {
|
|
@@ -681,13 +682,20 @@ export class Store {
|
|
|
681
682
|
const row = this.db.get("SELECT * FROM platform_ai_settings WHERE id = 1");
|
|
682
683
|
return {
|
|
683
684
|
systemPrompt: String(row?.system_prompt ?? ""),
|
|
685
|
+
imageToolModelId: row?.image_tool_model_id === null || row?.image_tool_model_id === undefined
|
|
686
|
+
? null
|
|
687
|
+
: String(row.image_tool_model_id),
|
|
684
688
|
updatedAt: String(row?.updated_at ?? "")
|
|
685
689
|
};
|
|
686
690
|
}
|
|
687
691
|
updatePlatformAiSettings(input) {
|
|
688
692
|
const timestamp = now();
|
|
689
|
-
|
|
690
|
-
|
|
693
|
+
const current = this.getPlatformAiSettings();
|
|
694
|
+
this.db.run(`INSERT INTO platform_ai_settings (id, system_prompt, image_tool_model_id, updated_at) VALUES (1, ?, ?, ?)
|
|
695
|
+
ON CONFLICT(id) DO UPDATE SET system_prompt = excluded.system_prompt,
|
|
696
|
+
image_tool_model_id = excluded.image_tool_model_id, updated_at = excluded.updated_at`, input.systemPrompt ?? String(current.systemPrompt), input.imageToolModelId === undefined
|
|
697
|
+
? (current.imageToolModelId === null ? null : String(current.imageToolModelId))
|
|
698
|
+
: input.imageToolModelId, timestamp);
|
|
691
699
|
return this.getPlatformAiSettings();
|
|
692
700
|
}
|
|
693
701
|
getPlatformUiSettings() {
|
|
@@ -754,6 +762,9 @@ export class Store {
|
|
|
754
762
|
agentToolCallLimit: Math.min(48, Math.max(5, Number(row?.agent_tool_call_limit ?? 12) || 12)),
|
|
755
763
|
agentToolCallGlobalMultiplier: Math.min(6, Math.max(1, Number(row?.agent_tool_call_global_multiplier ?? 3) || 3)),
|
|
756
764
|
agentTools: normalizeWorkAgentTools(row?.agent_tools_json),
|
|
765
|
+
imageToolModelId: row?.image_tool_model_id === null || row?.image_tool_model_id === undefined
|
|
766
|
+
? null
|
|
767
|
+
: String(row.image_tool_model_id),
|
|
757
768
|
alwaysIncludeSettingInfo: Number(row?.always_include_setting_info ?? 0) === 1,
|
|
758
769
|
titleGenerationModelId: row?.title_generation_model_id === null || row?.title_generation_model_id === undefined
|
|
759
770
|
? null
|
|
@@ -779,6 +790,9 @@ export class Store {
|
|
|
779
790
|
const nextAgentToolCallLimit = input.agentToolCallLimit ?? Number(current.agentToolCallLimit);
|
|
780
791
|
const nextAgentToolCallGlobalMultiplier = input.agentToolCallGlobalMultiplier ?? Number(current.agentToolCallGlobalMultiplier);
|
|
781
792
|
const nextAgentTools = normalizeWorkAgentTools(input.agentTools ?? current.agentTools);
|
|
793
|
+
const nextImageToolModelId = input.imageToolModelId === undefined
|
|
794
|
+
? (current.imageToolModelId ? String(current.imageToolModelId) : null)
|
|
795
|
+
: input.imageToolModelId?.trim() || null;
|
|
782
796
|
const nextAlwaysIncludeSettingInfo = input.alwaysIncludeSettingInfo ?? Boolean(current.alwaysIncludeSettingInfo);
|
|
783
797
|
const nextTitleGenerationModelId = input.titleGenerationModelId === undefined
|
|
784
798
|
? (current.titleGenerationModelId ? String(current.titleGenerationModelId) : null)
|
|
@@ -788,8 +802,8 @@ export class Store {
|
|
|
788
802
|
auto_run_daily_task_limit, auto_run_failure_threshold, auto_run_paused, auto_run_pause_reason,
|
|
789
803
|
auto_run_resume_at, auto_run_consecutive_failures, book_summary_context_percent,
|
|
790
804
|
context_compact_threshold, agent_tool_call_limit, agent_tool_call_global_multiplier,
|
|
791
|
-
agent_tools_json, title_generation_model_id, always_include_setting_info, updated_at
|
|
792
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
805
|
+
agent_tools_json, title_generation_model_id, image_tool_model_id, always_include_setting_info, updated_at
|
|
806
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
793
807
|
ON CONFLICT(work_id) DO UPDATE SET
|
|
794
808
|
system_prompt = excluded.system_prompt,
|
|
795
809
|
daily_token_quota = excluded.daily_token_quota,
|
|
@@ -808,8 +822,9 @@ export class Store {
|
|
|
808
822
|
agent_tool_call_global_multiplier = excluded.agent_tool_call_global_multiplier,
|
|
809
823
|
agent_tools_json = excluded.agent_tools_json,
|
|
810
824
|
title_generation_model_id = excluded.title_generation_model_id,
|
|
825
|
+
image_tool_model_id = excluded.image_tool_model_id,
|
|
811
826
|
always_include_setting_info = excluded.always_include_setting_info,
|
|
812
|
-
updated_at = excluded.updated_at`, workId, nextPrompt, nextDailyTokenQuota, nextEnabled ? 1 : 0, Math.min(8, Math.max(1, nextConcurrency)), Math.min(200, Math.max(1, nextBatchLimit)), Math.min(10_000, Math.max(0, nextDailyTaskLimit)), Math.min(10, Math.max(1, nextFailureThreshold)), current.autoRunPaused ? 1 : 0, String(current.autoRunPauseReason ?? ""), current.autoRunResumeAt === null ? null : String(current.autoRunResumeAt), Math.max(0, Number(current.autoRunConsecutiveFailures) || 0), Math.min(90, Math.max(1, nextBookSummaryContextPercent)), Math.min(90, Math.max(50, nextContextCompactThreshold)), Math.min(48, Math.max(5, nextAgentToolCallLimit)), Math.min(6, Math.max(1, nextAgentToolCallGlobalMultiplier)), JSON.stringify(nextAgentTools), nextTitleGenerationModelId, nextAlwaysIncludeSettingInfo ? 1 : 0, timestamp);
|
|
827
|
+
updated_at = excluded.updated_at`, workId, nextPrompt, nextDailyTokenQuota, nextEnabled ? 1 : 0, Math.min(8, Math.max(1, nextConcurrency)), Math.min(200, Math.max(1, nextBatchLimit)), Math.min(10_000, Math.max(0, nextDailyTaskLimit)), Math.min(10, Math.max(1, nextFailureThreshold)), current.autoRunPaused ? 1 : 0, String(current.autoRunPauseReason ?? ""), current.autoRunResumeAt === null ? null : String(current.autoRunResumeAt), Math.max(0, Number(current.autoRunConsecutiveFailures) || 0), Math.min(90, Math.max(1, nextBookSummaryContextPercent)), Math.min(90, Math.max(50, nextContextCompactThreshold)), Math.min(48, Math.max(5, nextAgentToolCallLimit)), Math.min(6, Math.max(1, nextAgentToolCallGlobalMultiplier)), JSON.stringify(nextAgentTools), nextTitleGenerationModelId, nextImageToolModelId, nextAlwaysIncludeSettingInfo ? 1 : 0, timestamp);
|
|
813
828
|
this.audit(workId, "work.ai-settings.updated", "work-ai-settings", workId, {
|
|
814
829
|
systemPromptChanged: input.systemPrompt !== undefined,
|
|
815
830
|
dailyTokenQuota: nextDailyTokenQuota,
|
|
@@ -823,6 +838,7 @@ export class Store {
|
|
|
823
838
|
agentToolCallLimit: Math.min(48, Math.max(5, nextAgentToolCallLimit)),
|
|
824
839
|
agentToolCallGlobalMultiplier: Math.min(6, Math.max(1, nextAgentToolCallGlobalMultiplier)),
|
|
825
840
|
agentTools: nextAgentTools,
|
|
841
|
+
imageToolModelId: nextImageToolModelId,
|
|
826
842
|
alwaysIncludeSettingInfo: nextAlwaysIncludeSettingInfo,
|
|
827
843
|
titleGenerationModelId: nextTitleGenerationModelId
|
|
828
844
|
});
|
|
@@ -3353,9 +3369,11 @@ export class Store {
|
|
|
3353
3369
|
JOIN characters character ON character.id = search.character_id`;
|
|
3354
3370
|
const rows = [...normalized].length <= 2
|
|
3355
3371
|
? this.db.all(`${columns} JOIN character_profile_section_short_terms term ON term.search_id = search.id
|
|
3356
|
-
WHERE search.work_id = ? AND
|
|
3372
|
+
WHERE search.work_id = ? AND character.merged_into_character_id IS NULL AND term.term = ?
|
|
3373
|
+
ORDER BY character.name, section.sort_order LIMIT ?`, workId, normalized, limit)
|
|
3357
3374
|
: this.db.all(`${columns} JOIN character_profile_section_search_fts fts ON fts.rowid = search.id
|
|
3358
|
-
WHERE search.work_id = ? AND
|
|
3375
|
+
WHERE search.work_id = ? AND character.merged_into_character_id IS NULL
|
|
3376
|
+
AND character_profile_section_search_fts MATCH ?
|
|
3359
3377
|
ORDER BY bm25(character_profile_section_search_fts), character.name, section.sort_order LIMIT ?`, workId, `"${normalized.replaceAll('"', '""')}"`, limit);
|
|
3360
3378
|
return rows.map((row) => ({ ...this.mapCharacterProfileSection(row), characterName: requiredString(row, "character_name") }));
|
|
3361
3379
|
}
|
|
@@ -3425,6 +3443,16 @@ export class Store {
|
|
|
3425
3443
|
throw notFound("附件");
|
|
3426
3444
|
return this.mapAttachment(row);
|
|
3427
3445
|
}
|
|
3446
|
+
getSettingAttachment(workId, attachmentId) {
|
|
3447
|
+
const row = this.db.get(`SELECT attachment.*
|
|
3448
|
+
FROM attachments attachment
|
|
3449
|
+
JOIN attachment_references reference ON reference.attachment_id = attachment.id
|
|
3450
|
+
WHERE attachment.id = ? AND attachment.work_id = ? AND reference.work_id = ? AND reference.entity_type = 'setting'
|
|
3451
|
+
LIMIT 1`, attachmentId, workId, workId);
|
|
3452
|
+
if (!row)
|
|
3453
|
+
throw new AppError(404, "SETTING_IMAGE_ATTACHMENT_NOT_FOUND", "图片附件不存在或未被设定正文引用");
|
|
3454
|
+
return this.mapAttachment(row);
|
|
3455
|
+
}
|
|
3428
3456
|
attachmentModules(attachmentId) {
|
|
3429
3457
|
this.getAttachment(attachmentId);
|
|
3430
3458
|
const modules = new Set();
|
|
@@ -4377,7 +4405,10 @@ export class Store {
|
|
|
4377
4405
|
const conversationId = id("conversation");
|
|
4378
4406
|
const timestamp = now();
|
|
4379
4407
|
const agentTools = normalizeWorkAgentTools(this.getWorkAiSettings(workId).agentTools);
|
|
4380
|
-
this.db.
|
|
4408
|
+
this.db.transaction(() => {
|
|
4409
|
+
this.db.run("INSERT INTO ai_conversations (id, work_id, task_type, title, agent_tools_json, created_at, updated_at, created_by_user_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", conversationId, workId, taskType, title.trim() || "新对话", JSON.stringify(agentTools), timestamp, timestamp, currentRequestActor()?.userId ?? null);
|
|
4410
|
+
this.syncAiHistorySearchShortTermsForSource("conversation", conversationId);
|
|
4411
|
+
});
|
|
4381
4412
|
return this.getAiConversation(conversationId);
|
|
4382
4413
|
}
|
|
4383
4414
|
listAiConversations(workId) {
|
|
@@ -4418,14 +4449,29 @@ export class Store {
|
|
|
4418
4449
|
const messages = this.db.all("SELECT * FROM ai_conversation_messages WHERE conversation_id = ? ORDER BY created_at, rowid", conversationId).map((message) => this.mapAiConversationMessage(message));
|
|
4419
4450
|
return { ...this.mapAiConversation(row), messageCount: messages.length, messages };
|
|
4420
4451
|
}
|
|
4421
|
-
getAiConversationPage(conversationId, pagination) {
|
|
4452
|
+
getAiConversationPage(conversationId, pagination, focusMessageId) {
|
|
4422
4453
|
const row = this.db.get("SELECT * FROM ai_conversations WHERE id = ?", conversationId);
|
|
4423
4454
|
if (!row)
|
|
4424
4455
|
throw notFound("AI 对话");
|
|
4425
4456
|
const countRow = this.db.get("SELECT COUNT(*) AS count FROM ai_conversation_messages WHERE conversation_id = ?", conversationId);
|
|
4426
|
-
|
|
4457
|
+
let effectivePagination = pagination;
|
|
4458
|
+
if (focusMessageId) {
|
|
4459
|
+
const focused = this.db.get("SELECT rowid, created_at FROM ai_conversation_messages WHERE conversation_id = ? AND id = ?", conversationId, focusMessageId);
|
|
4460
|
+
if (focused) {
|
|
4461
|
+
const newerCount = this.db.get(`SELECT COUNT(*) AS count FROM ai_conversation_messages
|
|
4462
|
+
WHERE conversation_id = ?
|
|
4463
|
+
AND (created_at > ? OR (created_at = ? AND rowid > ?))`, conversationId, String(focused.created_at ?? ""), String(focused.created_at ?? ""), Number(focused.rowid ?? 0));
|
|
4464
|
+
const focusPage = Math.floor(Number(newerCount?.count ?? 0) / pagination.limit) + 1;
|
|
4465
|
+
effectivePagination = {
|
|
4466
|
+
...pagination,
|
|
4467
|
+
page: focusPage,
|
|
4468
|
+
offset: (focusPage - 1) * pagination.limit
|
|
4469
|
+
};
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
const page = paginationSql(effectivePagination);
|
|
4427
4473
|
const rows = this.db.all(`SELECT * FROM ai_conversation_messages WHERE conversation_id = ? ORDER BY created_at DESC, rowid DESC${page.sql}`, conversationId, ...page.params);
|
|
4428
|
-
const messagesPage = paginated(rows.map((message) => this.mapAiConversationMessage(message)),
|
|
4474
|
+
const messagesPage = paginated(rows.map((message) => this.mapAiConversationMessage(message)), effectivePagination);
|
|
4429
4475
|
messagesPage.items.reverse();
|
|
4430
4476
|
return {
|
|
4431
4477
|
...this.mapAiConversation(row),
|
|
@@ -4529,7 +4575,10 @@ export class Store {
|
|
|
4529
4575
|
const conversation = this.db.get("SELECT id FROM ai_conversations WHERE id = ?", conversationId);
|
|
4530
4576
|
if (!conversation)
|
|
4531
4577
|
throw notFound("AI 对话");
|
|
4532
|
-
this.db.
|
|
4578
|
+
this.db.transaction(() => {
|
|
4579
|
+
this.db.run("UPDATE ai_conversations SET compacted_summary = ?, compacted_message_count = ?, context_warning_at = NULL, updated_at = ? WHERE id = ?", summary, Math.max(0, compactedMessageCount), now(), conversationId);
|
|
4580
|
+
this.syncAiHistorySearchShortTermsForSource("conversation", conversationId);
|
|
4581
|
+
});
|
|
4533
4582
|
return this.getAiConversation(conversationId);
|
|
4534
4583
|
}
|
|
4535
4584
|
setAiConversationTitle(conversationId, title) {
|
|
@@ -4537,7 +4586,10 @@ export class Store {
|
|
|
4537
4586
|
if (!conversation)
|
|
4538
4587
|
throw notFound("AI 对话");
|
|
4539
4588
|
const normalizedTitle = title.replace(/\s+/gu, " ").trim().slice(0, 200) || "新对话";
|
|
4540
|
-
this.db.
|
|
4589
|
+
this.db.transaction(() => {
|
|
4590
|
+
this.db.run("UPDATE ai_conversations SET title = ?, updated_at = ? WHERE id = ?", normalizedTitle, now(), conversationId);
|
|
4591
|
+
this.syncAiHistorySearchShortTermsForSource("conversation", conversationId);
|
|
4592
|
+
});
|
|
4541
4593
|
return this.getAiConversation(conversationId);
|
|
4542
4594
|
}
|
|
4543
4595
|
setAiConversationRoleplayCharacter(conversationId, characterId) {
|
|
@@ -4640,14 +4692,19 @@ export class Store {
|
|
|
4640
4692
|
}
|
|
4641
4693
|
const messageId = id("message");
|
|
4642
4694
|
const timestamp = now();
|
|
4643
|
-
const
|
|
4695
|
+
const previousTitle = requiredString(conversation, "title");
|
|
4696
|
+
const title = previousTitle === "新对话" && input.role === "user"
|
|
4644
4697
|
? defaultAiConversationTitle(input.content)
|
|
4645
|
-
:
|
|
4698
|
+
: previousTitle;
|
|
4646
4699
|
this.db.transaction(() => {
|
|
4647
4700
|
this.db.run("INSERT INTO ai_conversation_messages (id, conversation_id, role, content, citations_json, metadata_json, request_id, created_at, created_by_user_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(conversation_id, request_id) WHERE request_id IS NOT NULL DO NOTHING", messageId, conversationId, input.role, input.content, JSON.stringify(input.citations ?? []), JSON.stringify(input.metadata ?? {}), requestId, timestamp, currentRequestActor()?.userId ?? null);
|
|
4648
4701
|
const inserted = this.db.get("SELECT id FROM ai_conversation_messages WHERE id = ?", messageId);
|
|
4649
|
-
if (inserted)
|
|
4702
|
+
if (inserted) {
|
|
4650
4703
|
this.db.run("UPDATE ai_conversations SET title = ?, updated_at = ? WHERE id = ?", title, timestamp, conversationId);
|
|
4704
|
+
if (title !== previousTitle)
|
|
4705
|
+
this.syncAiHistorySearchShortTermsForSource("conversation", conversationId);
|
|
4706
|
+
this.syncAiHistorySearchShortTermsForSource("message", messageId);
|
|
4707
|
+
}
|
|
4651
4708
|
});
|
|
4652
4709
|
const message = requestId
|
|
4653
4710
|
? this.db.get("SELECT * FROM ai_conversation_messages WHERE conversation_id = ? AND request_id = ?", conversationId, requestId)
|
|
@@ -4681,9 +4738,33 @@ export class Store {
|
|
|
4681
4738
|
for (const message of messages.slice(0, targetIndex + 1)) {
|
|
4682
4739
|
this.db.run("INSERT INTO ai_conversation_messages (id, conversation_id, role, content, citations_json, metadata_json, request_id, created_at, created_by_user_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", id("message"), forkId, requiredString(message, "role"), requiredString(message, "content"), requiredString(message, "citations_json"), requiredString(message, "metadata_json"), optionalString(message, "request_id"), requiredString(message, "created_at"), currentRequestActor()?.userId ?? null);
|
|
4683
4740
|
}
|
|
4741
|
+
this.syncAiHistorySearchShortTermsForConversation(forkId);
|
|
4684
4742
|
});
|
|
4685
4743
|
return this.getAiConversation(forkId);
|
|
4686
4744
|
}
|
|
4745
|
+
syncAiHistorySearchShortTermsForSource(sourceType, sourceId) {
|
|
4746
|
+
const row = this.db.get("SELECT id, title, content, search_content FROM ai_history_search WHERE source_type = ? AND source_id = ?", sourceType, sourceId);
|
|
4747
|
+
if (!row)
|
|
4748
|
+
return;
|
|
4749
|
+
const searchId = numberValue(row, "id");
|
|
4750
|
+
const searchContent = sourceType === "message"
|
|
4751
|
+
? normalizeDocumentSearchText(String(row.content ?? ""))
|
|
4752
|
+
: normalizeDocumentSearchText(`${String(row.title ?? "")}\n${String(row.content ?? "")}`);
|
|
4753
|
+
if (String(row.search_content ?? "") !== searchContent) {
|
|
4754
|
+
this.db.run("UPDATE ai_history_search SET search_content = ? WHERE id = ?", searchContent, searchId);
|
|
4755
|
+
}
|
|
4756
|
+
this.db.run("DELETE FROM ai_history_search_short_terms WHERE search_id = ?", searchId);
|
|
4757
|
+
for (const term of documentShortSearchTerms(searchContent)) {
|
|
4758
|
+
this.db.run("INSERT INTO ai_history_search_short_terms (search_id, term) VALUES (?, ?)", searchId, term);
|
|
4759
|
+
}
|
|
4760
|
+
}
|
|
4761
|
+
syncAiHistorySearchShortTermsForConversation(conversationId) {
|
|
4762
|
+
const rows = this.db.all("SELECT source_type, source_id FROM ai_history_search WHERE conversation_id = ?", conversationId);
|
|
4763
|
+
for (const row of rows) {
|
|
4764
|
+
const sourceType = row.source_type === "message" ? "message" : "conversation";
|
|
4765
|
+
this.syncAiHistorySearchShortTermsForSource(sourceType, String(row.source_id));
|
|
4766
|
+
}
|
|
4767
|
+
}
|
|
4687
4768
|
mapAiConversation(row) {
|
|
4688
4769
|
const roleplayCharacterId = optionalString(row, "roleplay_character_id");
|
|
4689
4770
|
const roleplayCharacter = roleplayCharacterId
|
|
@@ -6059,7 +6140,7 @@ export class Store {
|
|
|
6059
6140
|
SELECT character.id, character.name, character.aliases_json, character.species, character.is_dead,
|
|
6060
6141
|
COALESCE(path.path, character.species) AS race_path
|
|
6061
6142
|
FROM characters character LEFT JOIN character_race_paths path ON path.character_id = character.id
|
|
6062
|
-
WHERE character.work_id = ? AND (
|
|
6143
|
+
WHERE character.work_id = ? AND character.merged_into_character_id IS NULL AND (
|
|
6063
6144
|
character.name LIKE ? ESCAPE '\\' OR character.aliases_json LIKE ? ESCAPE '\\' OR character.species LIKE ? ESCAPE '\\'
|
|
6064
6145
|
OR EXISTS (SELECT 1 FROM character_race_lineage lineage WHERE lineage.character_id = character.id AND lineage.name LIKE ? ESCAPE '\\')
|
|
6065
6146
|
) LIMIT 50`, workId, workId, pattern, pattern, pattern, pattern);
|