ai-chatbot/lib/ai/runtime/memory-relevance.ts
Your Name 9ecd91376d
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
feat: strengthen agent runtime boundaries
2026-03-16 11:45:00 +08:00

135 lines
5.0 KiB
TypeScript

import "server-only";
import type { ConversationActionResolution } from "@/lib/ai/conversation-action-resolver";
import type { TaskBoundaryDecision } from "@/lib/ai/runtime/task-boundary-classifier";
export interface MemoryRelevancePolicy {
includePromptMemory: boolean;
includeImageWorkflowMemory: boolean;
includeStoryboardPreferenceMemory: boolean;
includeWorkflowState: boolean;
maxMemoryHints: number;
maxContextLines: number;
reason: string;
}
export function resolveMemoryRelevancePolicy(params: {
taskBoundaryDecision: TaskBoundaryDecision;
actionResolution: ConversationActionResolution;
hasActiveStoryboardTask: boolean;
hasPendingGridConfirmation: boolean;
hasCurrentImageInput: boolean;
hasCanvasSelection: boolean;
}) {
const { taskBoundaryDecision, actionResolution } = params;
switch (taskBoundaryDecision.taskType) {
case "greeting":
return {
includePromptMemory: false,
includeImageWorkflowMemory: false,
includeStoryboardPreferenceMemory: false,
includeWorkflowState: false,
maxMemoryHints: 0,
maxContextLines: 2,
reason: "greeting_turn_ignores_task_memory",
} satisfies MemoryRelevancePolicy;
case "chat":
return {
includePromptMemory: false,
includeImageWorkflowMemory: false,
includeStoryboardPreferenceMemory: false,
includeWorkflowState: false,
maxMemoryHints: 1,
maxContextLines: 3,
reason: "chat_turn_prefers_clean_context",
} satisfies MemoryRelevancePolicy;
case "prompt_only":
return {
includePromptMemory: false,
includeImageWorkflowMemory: false,
includeStoryboardPreferenceMemory: false,
includeWorkflowState: false,
maxMemoryHints: 2,
maxContextLines: 4,
reason: "prompt_only_turn_ignores_execution_memory",
} satisfies MemoryRelevancePolicy;
case "analysis_only":
return {
includePromptMemory: false,
includeImageWorkflowMemory: false,
includeStoryboardPreferenceMemory: false,
includeWorkflowState: false,
maxMemoryHints: 2,
maxContextLines: 4,
reason: "analysis_only_turn_ignores_workflow_memory",
} satisfies MemoryRelevancePolicy;
case "execution_video":
return {
includePromptMemory: false,
includeImageWorkflowMemory: false,
includeStoryboardPreferenceMemory: true,
includeWorkflowState:
params.hasActiveStoryboardTask || params.hasPendingGridConfirmation,
maxMemoryHints: 4,
maxContextLines: 6,
reason: "video_execution_prefers_storyboard_preferences",
} satisfies MemoryRelevancePolicy;
case "workflow_followup":
return {
includePromptMemory:
actionResolution.kind === "image_generation_followup" ||
actionResolution.kind === "prompt_carry_forward",
includeImageWorkflowMemory:
actionResolution.kind === "image_edit_followup" ||
actionResolution.kind === "image_generation_followup" ||
actionResolution.kind === "prompt_carry_forward",
includeStoryboardPreferenceMemory:
actionResolution.kind === "grid_confirmation_followup" ||
actionResolution.kind === "active_storyboard_followup",
includeWorkflowState: true,
maxMemoryHints: 6,
maxContextLines: 8,
reason: "workflow_followup_requires_workflow_memory",
} satisfies MemoryRelevancePolicy;
case "execution_image":
default:
return {
includePromptMemory:
actionResolution.usePromptCarryForward ||
taskBoundaryDecision.taskMode === "continue",
includeImageWorkflowMemory:
taskBoundaryDecision.taskMode === "continue" ||
(!params.hasCurrentImageInput && !params.hasCanvasSelection),
includeStoryboardPreferenceMemory: false,
includeWorkflowState:
taskBoundaryDecision.taskMode === "continue" &&
(actionResolution.kind === "image_edit_followup" ||
actionResolution.kind === "image_generation_followup" ||
actionResolution.kind === "prompt_carry_forward"),
maxMemoryHints: taskBoundaryDecision.taskMode === "continue" ? 5 : 3,
maxContextLines: taskBoundaryDecision.taskMode === "continue" ? 7 : 5,
reason:
taskBoundaryDecision.taskMode === "continue"
? "image_execution_continue_reuses_relevant_memory"
: "image_execution_new_task_limits_old_memory",
} satisfies MemoryRelevancePolicy;
}
}
export function applyMemoryHintLimit(hints: string[], maxHints: number) {
if (!Array.isArray(hints) || maxHints <= 0) return [] as string[];
return hints.slice(0, maxHints);
}
export function applyContextLineLimit(summary: string | undefined, maxLines: number) {
const normalized = String(summary || "").trim();
if (!normalized || maxLines <= 0) return "";
return normalized
.split("\n")
.map((line) => line.trim())
.filter(Boolean)
.slice(0, maxLines)
.join("\n");
}