Add conversation history management and API endpoints
- Introduce new database models for conversation sessions, messages, and artifacts to support conversation history tracking. - Implement API routes for listing conversations and retrieving detailed conversation data, enhancing user interaction with historical records. - Add a conversation recorder service to persist conversation messages in real-time without disrupting ongoing calls. - Update the frontend to display conversation history, including filtering and sorting options, improving user experience. - Enhance the pipeline to integrate conversation history recording seamlessly during interactions.
This commit is contained in:
@@ -17,6 +17,7 @@ from models import AssistantConfig
|
||||
from openai import AsyncOpenAI
|
||||
from PIL import Image
|
||||
from services.brains import build_brain
|
||||
from services.conversation_history import ConversationRecorder
|
||||
from services.pipecat.service_factory import (
|
||||
create_realtime_service,
|
||||
create_stt,
|
||||
@@ -298,6 +299,20 @@ class RealtimeTextInputProcessor(FrameProcessor):
|
||||
)
|
||||
|
||||
|
||||
class ConversationHistoryProcessor(FrameProcessor):
|
||||
"""从最终客户端事件旁路保存历史,不改变 Pipecat 的上下文与帧语义。"""
|
||||
|
||||
def __init__(self, recorder: ConversationRecorder | None):
|
||||
super().__init__()
|
||||
self._recorder = recorder
|
||||
|
||||
async def process_frame(self, frame, direction: FrameDirection):
|
||||
await super().process_frame(frame, direction)
|
||||
await self.push_frame(frame, direction)
|
||||
if self._recorder and isinstance(frame, OutputTransportMessageUrgentFrame):
|
||||
await self._recorder.record_transport_message(frame.message)
|
||||
|
||||
|
||||
class PassthroughLLMAssistantAggregator(LLMAssistantAggregator):
|
||||
"""聚合 LLM 回复进上下文,同时继续把回复帧交给下游 TTS。"""
|
||||
|
||||
@@ -363,6 +378,8 @@ async def run_pipeline(
|
||||
cfg: AssistantConfig,
|
||||
*,
|
||||
vision_enabled: bool = False,
|
||||
assistant_id: str | None = None,
|
||||
channel: str = "webrtc",
|
||||
) -> None:
|
||||
"""在给定 transport 上构建并运行管线,直到连接结束。
|
||||
|
||||
@@ -388,7 +405,12 @@ async def run_pipeline(
|
||||
if cfg.runtimeMode == "realtime":
|
||||
if vision_enabled:
|
||||
logger.warning("Realtime 模式暂未接入视频帧工具,本次仅启用语音通话")
|
||||
await run_realtime_pipeline(transport, cfg)
|
||||
await run_realtime_pipeline(
|
||||
transport,
|
||||
cfg,
|
||||
assistant_id=assistant_id,
|
||||
channel=channel,
|
||||
)
|
||||
return
|
||||
|
||||
stt = create_stt(cfg)
|
||||
@@ -677,6 +699,12 @@ async def run_pipeline(
|
||||
reason = str(call_end_state["reason"] or "completed")
|
||||
await queue_call_end(reason)
|
||||
|
||||
recorder = await ConversationRecorder.start(
|
||||
assistant_id=assistant_id,
|
||||
assistant_name=cfg.name,
|
||||
channel=channel,
|
||||
runtime_mode=cfg.runtimeMode,
|
||||
)
|
||||
pipeline = Pipeline(
|
||||
[
|
||||
transport.input(),
|
||||
@@ -691,6 +719,7 @@ async def run_pipeline(
|
||||
assistant_aggregator,
|
||||
tts,
|
||||
EndCallAfterSpeech(),
|
||||
ConversationHistoryProcessor(recorder),
|
||||
transport.output(),
|
||||
]
|
||||
)
|
||||
@@ -950,21 +979,42 @@ async def run_pipeline(
|
||||
await worker.queue_frame(EndFrame())
|
||||
|
||||
runner = WorkerRunner(handle_sigint=False)
|
||||
await runner.add_workers(worker)
|
||||
await runner.run()
|
||||
run_status = "completed"
|
||||
try:
|
||||
await runner.add_workers(worker)
|
||||
await runner.run()
|
||||
except Exception:
|
||||
run_status = "failed"
|
||||
raise
|
||||
finally:
|
||||
if recorder:
|
||||
await recorder.finish(status=run_status)
|
||||
logger.info("管线已结束")
|
||||
|
||||
|
||||
async def run_realtime_pipeline(transport, cfg: AssistantConfig) -> None:
|
||||
async def run_realtime_pipeline(
|
||||
transport,
|
||||
cfg: AssistantConfig,
|
||||
*,
|
||||
assistant_id: str | None = None,
|
||||
channel: str = "webrtc",
|
||||
) -> None:
|
||||
"""Run a speech-to-speech model that owns ASR, reasoning, and synthesis."""
|
||||
realtime = create_realtime_service(cfg)
|
||||
text_input = RealtimeTextInputProcessor()
|
||||
|
||||
recorder = await ConversationRecorder.start(
|
||||
assistant_id=assistant_id,
|
||||
assistant_name=cfg.name,
|
||||
channel=channel,
|
||||
runtime_mode=cfg.runtimeMode,
|
||||
)
|
||||
pipeline = Pipeline(
|
||||
[
|
||||
transport.input(),
|
||||
text_input,
|
||||
realtime,
|
||||
ConversationHistoryProcessor(recorder),
|
||||
transport.output(),
|
||||
]
|
||||
)
|
||||
@@ -1017,6 +1067,14 @@ async def run_realtime_pipeline(transport, cfg: AssistantConfig) -> None:
|
||||
await worker.queue_frame(EndFrame())
|
||||
|
||||
runner = WorkerRunner(handle_sigint=False)
|
||||
await runner.add_workers(worker)
|
||||
await runner.run()
|
||||
run_status = "completed"
|
||||
try:
|
||||
await runner.add_workers(worker)
|
||||
await runner.run()
|
||||
except Exception:
|
||||
run_status = "failed"
|
||||
raise
|
||||
finally:
|
||||
if recorder:
|
||||
await recorder.finish(status=run_status)
|
||||
logger.info("Realtime 管线已结束")
|
||||
|
||||
Reference in New Issue
Block a user