diff --git a/docs/latest/changelog/roadmap.md b/docs/latest/changelog/roadmap.md index 2fd3839f..1b04e45c 100644 --- a/docs/latest/changelog/roadmap.md +++ b/docs/latest/changelog/roadmap.md @@ -8,16 +8,19 @@ - 集成 LangFuse (观望) 添加用户日志与用户反馈模块,可以在 AgentView 中查看信息 - 集成 neo4j mcp (或者自己构建工具) - chat_model 的 call 需要异步 -- 考虑修改附件的处理逻辑,考虑使用文件系统,将附件解析后放到文件系统中,智能体按需读取,用户可以使用 @ 来引用附件,例如 @file:reports.md 相较于现在的处理逻辑感觉会更加自然一点。在此之前可能还要考虑文件系统的后端支持问题 +- 优化智能体文件系统: + - 使用 CompositeBackend 和 自定义后端系统,链接 minio 中的知识库文件。提供除检索以外的方法 + - 考虑修改附件的处理逻辑,考虑使用文件系统,将附件解析后放到文件系统中,智能体按需读取(当前是完全解析,放进上下文) + - 【后续】用户可以使用 @ 来引用附件,例如 @file:reports.md 相较于现在的处理逻辑感觉会更加自然一点。 - skills 如何实现还需要继续调研 - 增加 paddle-vl 以及 deepseek-ocr 的支持(deepseek-ocr 已支持) - 系统层面添加 apikey,在智能体、知识库调用中支持 apikey 以支持外部调用 -- 支持更多类型的文档源的导入功能 -- Tasker 新增删除任务的接口 +- 支持更多类型的文档源的导入功能(支持后端配置的白名单的 URL 导入) - 部分场景应该使用默认模型作为默认值而不是空值 - CommonRAG 添加更多检索类型,比如 BM25,关键词,grep 等 - 文件上传解析后,如何提示用户需要入库 - 检索测试中,添加问答 +- 丰富当前智能体的 Prompt,最好支持从 markdown 解析 ### Bugs @@ -47,6 +50,7 @@ - 优化后端的依赖,减少镜像体积 [#428](https://github.com/xerrors/Yuxi-Know/issues/428) - 优化 liaghtrag 的知识库调用结果,提供 content/graph/both 多个选项 - 优化数据库查询工具,可通过设计环境变量添加描述,让模型更好的调用 +- 优化任务组件,改用 postgresql 存储,并新增删除任务的接口 ### 修复 diff --git a/server/routers/task_router.py b/server/routers/task_router.py index 848af3f9..b482fce7 100644 --- a/server/routers/task_router.py +++ b/server/routers/task_router.py @@ -33,3 +33,12 @@ async def cancel_task(task_id: str, current_user: User = Depends(get_admin_user) if not success: raise HTTPException(status_code=400, detail="Task cannot be cancelled") return {"task_id": task_id, "status": "cancelled"} + + +@tasks.delete("/{task_id}") +async def delete_task(task_id: str, current_user: User = Depends(get_admin_user)): + """Delete a task by id.""" + success = await tasker.delete_task(task_id) + if not success: + raise HTTPException(status_code=404, detail="Task not found") + return {"task_id": task_id, "status": "deleted"} diff --git a/src/repositories/task_repository.py b/src/repositories/task_repository.py index 5f089f64..810e49f4 100644 --- a/src/repositories/task_repository.py +++ b/src/repositories/task_repository.py @@ -40,6 +40,12 @@ class TaskRepository: setattr(record, key, value) return record + async def delete(self, task_id: str) -> bool: + """Delete a task by id. Returns True if deleted, False if not found.""" + async with pg_manager.get_async_session_context() as session: + result = await session.execute(delete(TaskRecord).where(TaskRecord.id == task_id)) + return result.rowcount > 0 + async def delete_all(self) -> None: async with pg_manager.get_async_session_context() as session: await session.execute(delete(TaskRecord)) diff --git a/src/services/task_service.py b/src/services/task_service.py index d4741b6c..e8e54c4b 100644 --- a/src/services/task_service.py +++ b/src/services/task_service.py @@ -185,6 +185,16 @@ class Tasker: logger.info("Cancellation requested for task {}", task_id) return True + async def delete_task(self, task_id: str) -> bool: + """Delete a task by id. Returns True if deleted, False if not found.""" + async with self._lock: + if task_id not in self._tasks: + return False + del self._tasks[task_id] + await self._repo.delete(task_id) + logger.info("Deleted task {}", task_id) + return True + async def _worker_loop(self) -> None: while True: try: diff --git a/web/src/apis/tasker.js b/web/src/apis/tasker.js index 612d510b..32da76d3 100644 --- a/web/src/apis/tasker.js +++ b/web/src/apis/tasker.js @@ -1,4 +1,4 @@ -import { apiAdminGet, apiAdminPost } from './base' +import { apiAdminDelete, apiAdminGet, apiAdminPost } from './base' const BASE_URL = '/api/tasks' @@ -15,5 +15,9 @@ export const taskerApi = { cancelTask: async (taskId) => { return apiAdminPost(`${BASE_URL}/${taskId}/cancel`, {}) + }, + + deleteTask: async (taskId) => { + return apiAdminDelete(`${BASE_URL}/${taskId}`) } } diff --git a/web/src/components/TaskCenterDrawer.vue b/web/src/components/TaskCenterDrawer.vue index 121004cc..fa433c36 100644 --- a/web/src/components/TaskCenterDrawer.vue +++ b/web/src/components/TaskCenterDrawer.vue @@ -84,6 +84,15 @@ > 取消 + + 删除 + @@ -258,6 +267,19 @@ function handleCancel(taskId) { taskerStore.cancelTask(taskId) } +function handleDelete(taskId, taskName) { + Modal.confirm({ + title: '确认删除', + content: `确定要删除任务"${taskName}"吗?此操作不可恢复。`, + okText: '删除', + okType: 'danger', + cancelText: '取消', + onOk: () => { + taskerStore.deleteTask(taskId) + } + }) +} + function formatTime(value, mode = 'full') { if (!value) return '-' if (mode === 'short') { diff --git a/web/src/stores/tasker.js b/web/src/stores/tasker.js index e5eef511..e78e4447 100644 --- a/web/src/stores/tasker.js +++ b/web/src/stores/tasker.js @@ -124,6 +124,22 @@ export const useTaskerStore = defineStore('tasker', () => { } } + async function deleteTask(taskId) { + if (!taskId) return + try { + await taskerApi.deleteTask(taskId) + message.success('删除任务成功') + // 从本地列表中移除 + const index = tasks.value.findIndex((item) => item.id === taskId) + if (index >= 0) { + tasks.value.splice(index, 1) + } + } catch (error) { + console.error(`删除任务 ${taskId} 失败`, error) + message.error(error?.message || '删除任务失败') + } + } + function registerQueuedTask({ task_id, name, task_type, message: msg, payload } = {}) { if (!task_id) return const now = new Date().toISOString() @@ -192,6 +208,7 @@ export const useTaskerStore = defineStore('tasker', () => { loadTasks, refreshTask, cancelTask, + deleteTask, registerQueuedTask, startPolling, stopPolling,