@musnows/scriverse 0.7.4 → 0.7.6

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 CHANGED
@@ -50,6 +50,7 @@ const AUTO_RUN_MAX_ATTEMPTS = 3;
50
50
  const AUTO_RUN_RETRY_DELAYS_MS = [5_000, 30_000];
51
51
  const AI_INTERACTIVE_TIMEOUT_MS = 60_000;
52
52
  const AI_LONG_RUNNING_TIMEOUT_MS = 300_000;
53
+ const FORCE_CONVERSATION_COMPACTION_USAGE_PERCENT = 95;
53
54
  const analysisTaskTypes = new Set(ANALYSIS_TASK_TYPES);
54
55
  const interactiveStreamErrorCodes = new Set([
55
56
  "AI_STREAM_IDLE_TIMEOUT",
@@ -1950,6 +1951,23 @@ export class AiManager {
1950
1951
  runningCount: this.store.countRunningTasks(workId)
1951
1952
  };
1952
1953
  }
1954
+ deleteWork(workId, expectedVersionNo) {
1955
+ const taskIds = this.store.deleteWork(workId, expectedVersionNo);
1956
+ const autoRunTimer = this.autoRunTimers.get(workId);
1957
+ if (autoRunTimer)
1958
+ clearTimeout(autoRunTimer);
1959
+ this.autoRunTimers.delete(workId);
1960
+ this.autoRunStarting.delete(workId);
1961
+ const relationshipIndexTimer = this.relationshipIndexSyncTimers.get(workId);
1962
+ if (relationshipIndexTimer)
1963
+ clearTimeout(relationshipIndexTimer);
1964
+ this.relationshipIndexSyncTimers.delete(workId);
1965
+ for (const taskId of taskIds) {
1966
+ this.taskControllers.get(taskId)?.abort(new Error("作品已移入回收站"));
1967
+ }
1968
+ logger.info("ai.work_tasks.expired", { workId, taskCount: taskIds.length });
1969
+ return taskIds;
1970
+ }
1953
1971
  dispose() {
1954
1972
  logger.info("ai.manager.disposing", { scheduledWorks: this.autoRunTimers.size, activeTasks: this.taskControllers.size });
1955
1973
  if (this.autoRunStartupTimer)
@@ -3621,6 +3639,7 @@ export class AiManager {
3621
3639
  leftTokens: remainingTokens
3622
3640
  },
3623
3641
  compactThreshold: threshold,
3642
+ compactableMessageCount,
3624
3643
  compactRecommended: compactableMessageCount > 0 && conversationUsagePercent >= threshold,
3625
3644
  contextWarningPending: conversation?.warningPending ?? false,
3626
3645
  compactedMessageCount: conversation?.compactedMessageCount ?? 0,
@@ -3658,14 +3677,18 @@ export class AiManager {
3658
3677
  }
3659
3678
  inspectConversationContext(input) {
3660
3679
  const usage = this.getContextUsage({ ...input, taskType: "chat" });
3661
- const conversation = this.store.getAiConversationContext(input.conversationId, input.workId);
3680
+ const usagePercent = Number(usage.usagePercent) || 0;
3681
+ const compactableMessageCount = Number(usage.compactableMessageCount) || 0;
3682
+ if (usagePercent >= FORCE_CONVERSATION_COMPACTION_USAGE_PERCENT) {
3683
+ if (compactableMessageCount <= 0) {
3684
+ throw new AppError(409, "AI_CONTEXT_COMPACTION_UNAVAILABLE", "当前请求已占满模型上下文,但没有可压缩的较早对话;请缩短问题、减少引用或新开对话");
3685
+ }
3686
+ return { action: "compact", usage };
3687
+ }
3662
3688
  if (!usage.compactRecommended) {
3663
3689
  return { action: "ready", usage: { ...usage, contextWarningPending: false } };
3664
3690
  }
3665
- if (!conversation.warningPending) {
3666
- return { action: "warn", usage: { ...usage, contextWarningPending: true } };
3667
- }
3668
- return { action: "compact", usage };
3691
+ return { action: "warn", usage: { ...usage, contextWarningPending: true } };
3669
3692
  }
3670
3693
  async prepareConversationContext(input, options = {}) {
3671
3694
  const inspection = this.inspectConversationContext(input);
@@ -3675,13 +3698,32 @@ export class AiManager {
3675
3698
  this.store.setAiConversationContextWarning(input.conversationId, false);
3676
3699
  return inspection;
3677
3700
  }
3678
- if (inspection.action === "warn" && !options.skipWarning) {
3701
+ if (inspection.action === "warn" && !options.ignoreWarning) {
3679
3702
  this.store.setAiConversationContextWarning(input.conversationId, true);
3680
3703
  return inspection;
3681
3704
  }
3705
+ if (inspection.action === "warn") {
3706
+ this.store.setAiConversationContextWarning(input.conversationId, false);
3707
+ return {
3708
+ action: "ready",
3709
+ reason: "warning_ignored",
3710
+ usage: { ...inspection.usage, contextWarningPending: false }
3711
+ };
3712
+ }
3682
3713
  const compaction = await this.compactConversation(input);
3714
+ if (compaction.changed !== true) {
3715
+ throw new AppError(409, "AI_CONTEXT_COMPACTION_UNAVAILABLE", "当前请求已占满模型上下文,但没有可压缩的较早对话;请缩短问题、减少引用或新开对话");
3716
+ }
3683
3717
  const compactedUsage = this.getContextUsage({ ...input, taskType: "chat" });
3684
- return { action: "compacted", usage: compactedUsage, compaction };
3718
+ if ((Number(compactedUsage.usagePercent) || 0) >= FORCE_CONVERSATION_COMPACTION_USAGE_PERCENT) {
3719
+ throw new AppError(413, "AI_CONTEXT_STILL_OVER_LIMIT", "自动压缩后当前请求仍占满模型上下文;请缩短问题、减少引用或新开对话");
3720
+ }
3721
+ return {
3722
+ action: "compacted",
3723
+ reason: "forced_usage_threshold",
3724
+ usage: compactedUsage,
3725
+ compaction
3726
+ };
3685
3727
  }
3686
3728
  /** 解析本轮消息中的自动角色提及;不使用会话累计排除集,也不改写累计注入状态。 */
3687
3729
  resolveInstructionMentions(input) {