From bf88935b8b266e4b26ff6318b685b283655c2e88 Mon Sep 17 00:00:00 2001 From: npmrun <1549469775@qq.com> Date: Sun, 9 Aug 2026 14:06:26 +0800 Subject: [PATCH] feat(agent-documents): add delete in drawer + inject memory into system prompt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AgentDocumentDrawer: delete button in list cards and detail view header with confirm dialog, auto-refresh list after delete - Drawer now re-fetches on every open (not just first time) - agent-document service: add getMemoryContext(userId) — queries all memory-type docs, wraps in tag - chat-engine: inject memory context into systemPrompt for logged-in users Co-authored-by: CodeFree --- app/components/agent/AgentDocumentDrawer.vue | 89 +++++++++++++++++++++++++++- server/service/agent-document/index.ts | 28 +++++++++ server/service/agent/chat-engine.ts | 19 +++++- 3 files changed, 134 insertions(+), 2 deletions(-) diff --git a/app/components/agent/AgentDocumentDrawer.vue b/app/components/agent/AgentDocumentDrawer.vue index 80bdd4d..05cb2e9 100644 --- a/app/components/agent/AgentDocumentDrawer.vue +++ b/app/components/agent/AgentDocumentDrawer.vue @@ -86,6 +86,27 @@ function closeDetail() { selectedDoc.value = null } +const deleting = ref(false) + +async function deleteDocument(doc: AgentDocumentItem | DocDetail) { + if (!confirm(`确定删除「${doc.title}」吗?`)) return + deleting.value = true + try { + await request<{ code: number }>(`/api/agent-documents/${doc.id}`, { + method: 'DELETE', + }) + $toast.success('已删除') + documents.value = documents.value.filter((d) => d.id !== doc.id) + if (selectedDoc.value?.id === doc.id) { + selectedDoc.value = null + } + } catch { + $toast.error('删除失败') + } finally { + deleting.value = false + } +} + function onFilterChange(type: 'memory' | 'document' | '') { typeFilter.value = type fetchDocuments() @@ -100,7 +121,7 @@ function onSearchInput() { } watch(() => props.show, (val) => { - if (val && documents.value.length === 0) { + if (val) { fetchDocuments() } }) @@ -177,6 +198,13 @@ watch(() => props.show, (val) => { {{ item.type === 'document' ? '文档' : '记忆' }} {{ new Date(item.updatedAt).toLocaleDateString('zh-CN') }} +

{{ item.title }}

{{ item.summary }}

@@ -192,6 +220,14 @@ watch(() => props.show, (val) => { 返回 + @@ -304,6 +340,57 @@ watch(() => props.show, (val) => { color: var(--color-ink, #141413); } +.delete-btn { + display: flex; + align-items: center; + gap: 4px; + padding: 6px 10px; + border: none; + background: none; + border-radius: 8px; + cursor: pointer; + color: var(--color-muted, #6c6a64); + font-size: 13px; + transition: all 0.15s; + margin-left: auto; +} + +.delete-btn:hover:not(:disabled) { + background: #fde8e8; + color: #c0392b; +} + +.delete-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.card-delete-btn { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border: none; + background: none; + border-radius: 6px; + cursor: pointer; + color: var(--color-muted, #b8b2a6); + font-size: 14px; + transition: all 0.15s; + margin-left: auto; +} + +.card-delete-btn:hover:not(:disabled) { + background: #fde8e8; + color: #c0392b; +} + +.card-delete-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + .drawer-filters { display: flex; align-items: center; diff --git a/server/service/agent-document/index.ts b/server/service/agent-document/index.ts index 77ad11a..83119a3 100644 --- a/server/service/agent-document/index.ts +++ b/server/service/agent-document/index.ts @@ -207,5 +207,33 @@ export async function deleteDocument( return result.length > 0; } +// ============ Memory Context ============ + +export async function getMemoryContext( + userId: number, +): Promise { + const rows = await dbGlobal + .select({ + title: agentDocuments.title, + content: agentDocuments.content, + }) + .from(agentDocuments) + .where( + and( + eq(agentDocuments.userId, userId), + eq(agentDocuments.type, "memory"), + ), + ) + .orderBy(desc(agentDocuments.updatedAt)); + + if (rows.length === 0) return null; + + const parts = rows.map( + (r) => `## ${r.title}\n${r.content}`, + ); + + return `\n${parts.join("\n\n")}\n`; +} + // ============ Tags Helper ============ export { deserializeTags, serializeTags }; diff --git a/server/service/agent/chat-engine.ts b/server/service/agent/chat-engine.ts index b67c1d7..8297fb2 100644 --- a/server/service/agent/chat-engine.ts +++ b/server/service/agent/chat-engine.ts @@ -14,6 +14,7 @@ import { updateSession, } from "#server/service/agent/session"; import { getAgentToolsForChatByAgentId, getAgentToolsBySlugs, executeAgentTool } from "#server/service/agent-tool"; +import { getMemoryContext } from "#server/service/agent-document"; import { checkRateLimit, incrementRateLimit } from "#server/service/agent/rate-limit"; import { resolveModelForUser, resolveModelAny, toLanguageModel } from "#server/service/llm/model-resolver"; import { generateSessionTitle } from "./title"; @@ -102,6 +103,12 @@ function buildModelMessages( }); } else { assistantContent.push({ + type: "tool-call", + toolCallId: p.toolCallId, + toolName: p.toolName, + input: p.args ?? {}, + }); + assistantContent.push({ type: "tool-approval-request", approvalId: p.approvalId, toolCallId: p.toolCallId, @@ -238,7 +245,17 @@ export async function executeChat(params: ChatEngineParams): Promise