数据库 - 新增迁移 0003:timeline_events.memory_source_id 外键 → memory_sources,便于按 ingest 源做时间线幂等 后端 - 记忆 - 新增 ingest 后 LLM 富化(摘要/事实/时间线),可配置开关与最大字符数 - 新增证据包组装:合并 chunk、摘要、事实、时间线、故事等检索结果;支持空 query 时是否仍带 rolling 等开关 - repo/retriever/service/router/schemas/summarizer/timeline/extractor 等扩展;文档 memory-retrieval.md 更新 后端 - 对话 WS - 增加 PING/PONG;分段 ASR 日志与空音频处理;转写失败与「无助手回复」错误提示更明确 - 助手多段回复持久化使用统一分隔符,与分段逻辑一致 后端 - Agent - reply_limits:按 [SPLIT] 与段落拆段,并保证非空 fallback,供 WS 与 TTS 多段下发 后端 - 回忆录任务 - transcript ingest 记录 source_id;任务成功结?
72 lines
2.4 KiB
Python
72 lines
2.4 KiB
Python
"""Memory 策展与内部扩展 API。"""
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from pydantic import BaseModel, Field
|
|
|
|
from app.core.dependencies import get_current_user
|
|
from app.features.memory.deps import get_memory_service
|
|
from app.features.memory.service import MemoryService
|
|
from app.features.user.models import User
|
|
|
|
router = APIRouter(prefix="/api/memory", tags=["memory"])
|
|
|
|
|
|
class ExcludeBody(BaseModel):
|
|
reason: str = Field(default="", max_length=2000)
|
|
|
|
|
|
class RejectFactBody(BaseModel):
|
|
reason: str = Field(default="", max_length=2000)
|
|
|
|
|
|
@router.post("/chunks/{chunk_id}/exclude", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def exclude_chunk(
|
|
chunk_id: str,
|
|
body: ExcludeBody | None = None,
|
|
current_user: User = Depends(get_current_user),
|
|
memory: MemoryService = Depends(get_memory_service),
|
|
):
|
|
reason = (body.reason if body else "") or ""
|
|
ok = await memory.exclude_chunk(current_user.id, chunk_id, reason=reason)
|
|
if not ok:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND, detail="chunk 不存在"
|
|
)
|
|
|
|
|
|
@router.post("/chunks/{chunk_id}/restore", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def restore_chunk(
|
|
chunk_id: str,
|
|
current_user: User = Depends(get_current_user),
|
|
memory: MemoryService = Depends(get_memory_service),
|
|
):
|
|
ok = await memory.restore_chunk(current_user.id, chunk_id)
|
|
if not ok:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND, detail="chunk 不存在"
|
|
)
|
|
|
|
|
|
@router.post("/facts/{fact_id}/confirm", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def confirm_fact(
|
|
fact_id: str,
|
|
current_user: User = Depends(get_current_user),
|
|
memory: MemoryService = Depends(get_memory_service),
|
|
):
|
|
ok = await memory.confirm_fact(current_user.id, fact_id)
|
|
if not ok:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="fact 不存在")
|
|
|
|
|
|
@router.post("/facts/{fact_id}/reject", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def reject_fact(
|
|
fact_id: str,
|
|
body: RejectFactBody | None = None,
|
|
current_user: User = Depends(get_current_user),
|
|
memory: MemoryService = Depends(get_memory_service),
|
|
):
|
|
reason = (body.reason if body else "") or ""
|
|
ok = await memory.reject_fact(current_user.id, fact_id, reason=reason)
|
|
if not ok:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="fact 不存在")
|