@musnows/scriverse 0.5.12 → 0.6.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/dist/ai-protocol.js +6 -1
- package/dist/ai-protocol.js.map +1 -1
- package/dist/ai.js +336 -21
- package/dist/ai.js.map +1 -1
- package/dist/app.js +14 -4
- package/dist/app.js.map +1 -1
- package/dist/database.js +17 -0
- package/dist/database.js.map +1 -1
- package/dist/public/ai-context-meter.js +32 -0
- package/dist/public/app.js +180 -38
- package/dist/public/index.html +7 -3
- package/dist/public/styles.css +45 -6
- package/dist/store.js +40 -7
- package/dist/store.js.map +1 -1
- package/dist/utils.js +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
package/dist/store.js
CHANGED
|
@@ -123,6 +123,10 @@ export const versionedEntityTypes = [
|
|
|
123
123
|
"chapter-outline",
|
|
124
124
|
"foreshadow"
|
|
125
125
|
];
|
|
126
|
+
export function defaultAiConversationTitle(prompt) {
|
|
127
|
+
const normalized = prompt.replace(/\s+/gu, " ").trim();
|
|
128
|
+
return Array.from(normalized).slice(0, 15).join("") || "新对话";
|
|
129
|
+
}
|
|
126
130
|
function isRecord(value) {
|
|
127
131
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
128
132
|
}
|
|
@@ -689,6 +693,9 @@ export class Store {
|
|
|
689
693
|
agentTools: json(String(row?.agent_tools_json ?? '["story_index","read_chapters","search_story_entities","grep","read_character_sections","search_drafts"]'), ["story_index", "read_chapters", "search_story_entities", "grep", "read_character_sections", "search_drafts"])
|
|
690
694
|
.map((tool) => tool === "query_story_knowledge" ? "search_story_entities" : tool)
|
|
691
695
|
.filter((tool, index, tools) => tools.indexOf(tool) === index),
|
|
696
|
+
titleGenerationModelId: row?.title_generation_model_id === null || row?.title_generation_model_id === undefined
|
|
697
|
+
? null
|
|
698
|
+
: String(row.title_generation_model_id),
|
|
692
699
|
updatedAt: String(row?.updated_at ?? "")
|
|
693
700
|
};
|
|
694
701
|
}
|
|
@@ -705,12 +712,15 @@ export class Store {
|
|
|
705
712
|
const nextBookSummaryContextPercent = input.bookSummaryContextPercent ?? Number(current.bookSummaryContextPercent);
|
|
706
713
|
const nextContextCompactThreshold = input.contextCompactThreshold ?? Number(current.contextCompactThreshold);
|
|
707
714
|
const nextAgentTools = input.agentTools ?? current.agentTools;
|
|
715
|
+
const nextTitleGenerationModelId = input.titleGenerationModelId === undefined
|
|
716
|
+
? (current.titleGenerationModelId ? String(current.titleGenerationModelId) : null)
|
|
717
|
+
: input.titleGenerationModelId?.trim() || null;
|
|
708
718
|
this.db.run(`INSERT INTO work_ai_settings (
|
|
709
719
|
work_id, system_prompt, auto_run_enabled, auto_run_concurrency, auto_run_batch_limit,
|
|
710
720
|
auto_run_daily_task_limit, auto_run_failure_threshold, auto_run_paused, auto_run_pause_reason,
|
|
711
721
|
auto_run_resume_at, auto_run_consecutive_failures, book_summary_context_percent,
|
|
712
|
-
context_compact_threshold, agent_tools_json, updated_at
|
|
713
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
722
|
+
context_compact_threshold, agent_tools_json, title_generation_model_id, updated_at
|
|
723
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
714
724
|
ON CONFLICT(work_id) DO UPDATE SET
|
|
715
725
|
system_prompt = excluded.system_prompt,
|
|
716
726
|
auto_run_enabled = excluded.auto_run_enabled,
|
|
@@ -725,7 +735,8 @@ export class Store {
|
|
|
725
735
|
book_summary_context_percent = excluded.book_summary_context_percent,
|
|
726
736
|
context_compact_threshold = excluded.context_compact_threshold,
|
|
727
737
|
agent_tools_json = excluded.agent_tools_json,
|
|
728
|
-
|
|
738
|
+
title_generation_model_id = excluded.title_generation_model_id,
|
|
739
|
+
updated_at = excluded.updated_at`, workId, nextPrompt, 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)), JSON.stringify(nextAgentTools), nextTitleGenerationModelId, timestamp);
|
|
729
740
|
this.audit(workId, "work.ai-settings.updated", "work-ai-settings", workId, {
|
|
730
741
|
systemPromptChanged: input.systemPrompt !== undefined,
|
|
731
742
|
autoRunEnabled: nextEnabled,
|
|
@@ -735,7 +746,8 @@ export class Store {
|
|
|
735
746
|
autoRunFailureThreshold: Math.min(10, Math.max(1, nextFailureThreshold)),
|
|
736
747
|
bookSummaryContextPercent: Math.min(90, Math.max(1, nextBookSummaryContextPercent)),
|
|
737
748
|
contextCompactThreshold: Math.min(90, Math.max(50, nextContextCompactThreshold)),
|
|
738
|
-
agentTools: nextAgentTools
|
|
749
|
+
agentTools: nextAgentTools,
|
|
750
|
+
titleGenerationModelId: nextTitleGenerationModelId
|
|
739
751
|
});
|
|
740
752
|
return this.getWorkAiSettings(workId);
|
|
741
753
|
}
|
|
@@ -4127,7 +4139,7 @@ export class Store {
|
|
|
4127
4139
|
throw notFound("AI 对话");
|
|
4128
4140
|
if (requiredString(conversation, "work_id") !== workId)
|
|
4129
4141
|
throw new AppError(400, "CONVERSATION_WORK_MISMATCH", "AI 对话不属于当前作品");
|
|
4130
|
-
const rows = this.db.all("SELECT id, role, content FROM ai_conversation_messages WHERE conversation_id = ? ORDER BY created_at, rowid", conversationId);
|
|
4142
|
+
const rows = this.db.all("SELECT id, role, content, metadata_json FROM ai_conversation_messages WHERE conversation_id = ? ORDER BY created_at, rowid", conversationId);
|
|
4131
4143
|
const compactedMessageCount = Math.min(rows.length, Math.max(0, numberValue(conversation, "compacted_message_count")));
|
|
4132
4144
|
return {
|
|
4133
4145
|
workId,
|
|
@@ -4140,10 +4152,23 @@ export class Store {
|
|
|
4140
4152
|
.map((message) => ({
|
|
4141
4153
|
id: requiredString(message, "id"),
|
|
4142
4154
|
role: requiredString(message, "role") === "assistant" ? "assistant" : "user",
|
|
4143
|
-
content: requiredString(message, "content")
|
|
4155
|
+
content: requiredString(message, "content"),
|
|
4156
|
+
metadata: json(requiredString(message, "metadata_json"), {})
|
|
4144
4157
|
}))
|
|
4145
4158
|
};
|
|
4146
4159
|
}
|
|
4160
|
+
getAiConversationTitleContext(conversationId, workId) {
|
|
4161
|
+
const conversation = this.db.get("SELECT title, work_id FROM ai_conversations WHERE id = ?", conversationId);
|
|
4162
|
+
if (!conversation)
|
|
4163
|
+
throw notFound("AI 对话");
|
|
4164
|
+
if (requiredString(conversation, "work_id") !== workId)
|
|
4165
|
+
throw new AppError(400, "CONVERSATION_WORK_MISMATCH", "AI 对话不属于当前作品");
|
|
4166
|
+
const messages = this.db.all("SELECT role, content FROM ai_conversation_messages WHERE conversation_id = ? ORDER BY created_at, rowid", conversationId).map((message) => ({
|
|
4167
|
+
role: requiredString(message, "role") === "assistant" ? "assistant" : "user",
|
|
4168
|
+
content: requiredString(message, "content")
|
|
4169
|
+
}));
|
|
4170
|
+
return { title: requiredString(conversation, "title"), messages };
|
|
4171
|
+
}
|
|
4147
4172
|
setAiConversationContextWarning(conversationId, pending) {
|
|
4148
4173
|
const conversation = this.db.get("SELECT id FROM ai_conversations WHERE id = ?", conversationId);
|
|
4149
4174
|
if (!conversation)
|
|
@@ -4157,6 +4182,14 @@ export class Store {
|
|
|
4157
4182
|
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);
|
|
4158
4183
|
return this.getAiConversation(conversationId);
|
|
4159
4184
|
}
|
|
4185
|
+
setAiConversationTitle(conversationId, title) {
|
|
4186
|
+
const conversation = this.db.get("SELECT id FROM ai_conversations WHERE id = ?", conversationId);
|
|
4187
|
+
if (!conversation)
|
|
4188
|
+
throw notFound("AI 对话");
|
|
4189
|
+
const normalizedTitle = title.replace(/\s+/gu, " ").trim().slice(0, 200) || "新对话";
|
|
4190
|
+
this.db.run("UPDATE ai_conversations SET title = ?, updated_at = ? WHERE id = ?", normalizedTitle, now(), conversationId);
|
|
4191
|
+
return this.getAiConversation(conversationId);
|
|
4192
|
+
}
|
|
4160
4193
|
addAiConversationMessage(conversationId, input) {
|
|
4161
4194
|
const conversation = this.db.get("SELECT * FROM ai_conversations WHERE id = ?", conversationId);
|
|
4162
4195
|
if (!conversation)
|
|
@@ -4170,7 +4203,7 @@ export class Store {
|
|
|
4170
4203
|
const messageId = id("message");
|
|
4171
4204
|
const timestamp = now();
|
|
4172
4205
|
const title = requiredString(conversation, "title") === "新对话" && input.role === "user"
|
|
4173
|
-
? input.content
|
|
4206
|
+
? defaultAiConversationTitle(input.content)
|
|
4174
4207
|
: requiredString(conversation, "title");
|
|
4175
4208
|
this.db.transaction(() => {
|
|
4176
4209
|
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);
|