feat(api): 收敛对话与记忆流程边界,引入 LLM 网关与专用服务
- MemoryService 异步路径委托 MemoryIngestService / MemoryRetrievalService;富化派发经 MemoryEnrichmentScheduler - WebSocket pipeline 经 ChatTurnService 与显式 DTO 编排单轮对话;回忆录片段入队由 MemoirIngestScheduler 封装 - 新增 LlmGateway(LlmUseCase),各 agent、任务与适配器对齐 ports - 补充 memory 提示适配、runtime 类型、memory-retrieval 文档、ai-touchpoints 说明与扫描脚本及配套测试 Made-with: Cursor
This commit is contained in:
77
api/app/features/memoir/ingest_scheduler.py
Normal file
77
api/app/features/memoir/ingest_scheduler.py
Normal file
@@ -0,0 +1,77 @@
|
||||
"""Memoir ingest scheduling boundary.
|
||||
|
||||
The real batching logic still lives in ``BackgroundTaskRunner``. This adapter
|
||||
keeps conversation code from depending on that implementation directly.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal, Sequence
|
||||
|
||||
from app.features.memoir.background_runner import BackgroundTaskRunner
|
||||
|
||||
MemoirTrigger = Literal[
|
||||
"turn",
|
||||
"conversation_end",
|
||||
"manual_flush",
|
||||
"evaluation_replay",
|
||||
]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MemoirPhasePlan:
|
||||
"""A visible plan for submitting segments into the memoir pipeline."""
|
||||
|
||||
user_id: str
|
||||
segment_ids: tuple[str, ...]
|
||||
trigger: MemoirTrigger
|
||||
|
||||
|
||||
class MemoirIngestScheduler:
|
||||
"""Small facade over debounce batching and Phase2 flush dispatch."""
|
||||
|
||||
def __init__(self, runner: BackgroundTaskRunner | None = None) -> None:
|
||||
self._runner = runner or BackgroundTaskRunner()
|
||||
|
||||
@property
|
||||
def runner(self) -> BackgroundTaskRunner:
|
||||
"""Compatibility escape hatch for existing tests and eval utilities."""
|
||||
|
||||
return self._runner
|
||||
|
||||
async def queue_segment(
|
||||
self,
|
||||
user_id: str,
|
||||
segment_id: str,
|
||||
*,
|
||||
text_char_count: int = 0,
|
||||
trigger: MemoirTrigger = "turn",
|
||||
) -> MemoirPhasePlan:
|
||||
await self._runner.queue_message(
|
||||
user_id,
|
||||
segment_id,
|
||||
text_char_count=text_char_count,
|
||||
)
|
||||
return MemoirPhasePlan(
|
||||
user_id=user_id,
|
||||
segment_ids=(segment_id,),
|
||||
trigger=trigger,
|
||||
)
|
||||
|
||||
async def flush_pending(
|
||||
self,
|
||||
user_id: str,
|
||||
*,
|
||||
extra_segment_ids: Sequence[str] | None = None,
|
||||
trigger: MemoirTrigger = "manual_flush",
|
||||
) -> tuple[MemoirPhasePlan, str | None]:
|
||||
ids = tuple(str(x) for x in (extra_segment_ids or ()) if str(x).strip())
|
||||
task_id = await self._runner.flush_pending(
|
||||
user_id,
|
||||
extra_segment_ids=list(ids),
|
||||
)
|
||||
return MemoirPhasePlan(user_id=user_id, segment_ids=ids, trigger=trigger), task_id
|
||||
|
||||
|
||||
__all__ = ["MemoirIngestScheduler", "MemoirPhasePlan", "MemoirTrigger"]
|
||||
Reference in New Issue
Block a user