-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfoundational.py
More file actions
128 lines (104 loc) · 4.39 KB
/
Copy pathfoundational.py
File metadata and controls
128 lines (104 loc) · 4.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#
# Foundational example: a voice agent that remembers callers across calls.
#
# MemorySyncMemoryService sits between the user context aggregator and the
# LLM: every LLMContextFrame is enriched with relevant long-term memories
# under a hard time budget, and new turns are persisted in the background.
# A slow or unreachable memory backend can never stall the voice reply.
#
# Run (choose any transport supported by the Pipecat runner):
#
# uv add pipecat-memorysync "pipecat-ai[deepgram,cartesia,openai,silero,runner,webrtc]"
# export MEMORYSYNC_API_KEY=ms_... # https://app.memorysync.io
# export DEEPGRAM_API_KEY=...
# export CARTESIA_API_KEY=...
# export OPENAI_API_KEY=...
# python foundational.py
#
# Then open http://localhost:7860/client, talk to the bot, tell it your
# name and a preference, hang up, and call again: it remembers.
#
import os
from dotenv import load_dotenv
from loguru import logger
from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.frames.frames import LLMRunFrame
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
from pipecat.pipeline.task import PipelineParams, PipelineTask
from pipecat.processors.aggregators.llm_context import LLMContext
from pipecat.processors.aggregators.llm_response_universal import (
LLMContextAggregatorPair,
LLMUserAggregatorParams,
)
from pipecat.runner.types import RunnerArguments
from pipecat.runner.utils import create_transport
from pipecat.services.cartesia.tts import CartesiaTTSService
from pipecat.services.deepgram.stt import DeepgramSTTService
from pipecat.services.openai.llm import OpenAILLMService
from pipecat.transports.base_transport import BaseTransport, TransportParams
from pipecat_memorysync import MemorySyncMemoryService
load_dotenv(override=True)
SYSTEM_INSTRUCTION = (
"You are a friendly voice assistant with long-term memory. "
"Relevant facts about the caller may appear as background memory context. "
"Use them naturally; never read them out verbatim. "
"Your answers are spoken aloud, so keep them short and conversational."
)
transport_params = {
"webrtc": lambda: TransportParams(audio_in_enabled=True, audio_out_enabled=True),
}
async def run_bot(transport: BaseTransport, runner_args: RunnerArguments):
logger.info("Starting bot")
stt = DeepgramSTTService(api_key=os.environ["DEEPGRAM_API_KEY"])
tts = CartesiaTTSService(
api_key=os.environ["CARTESIA_API_KEY"],
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121",
)
llm = OpenAILLMService(api_key=os.environ["OPENAI_API_KEY"])
# Long-term memory. In a real deployment, derive user_id from the
# caller's identity (phone number, account id, ...) so each caller
# gets their own memories.
memory = MemorySyncMemoryService(
api_key=os.environ["MEMORYSYNC_API_KEY"],
user_id=os.environ.get("MEMORYSYNC_USER_ID", "demo-caller"),
)
context = LLMContext([{"role": "system", "content": SYSTEM_INSTRUCTION}])
context_aggregator = LLMContextAggregatorPair(
context,
user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()),
)
pipeline = Pipeline(
[
transport.input(),
stt,
context_aggregator.user(),
memory, # enrich with memories + capture new turns, on budget
llm,
tts,
transport.output(),
context_aggregator.assistant(),
]
)
task = PipelineTask(
pipeline,
params=PipelineParams(enable_metrics=True, enable_usage_metrics=True),
idle_timeout_secs=runner_args.pipeline_idle_timeout_secs,
)
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
logger.info("Client connected")
await task.queue_frames([LLMRunFrame()])
@transport.event_handler("on_client_disconnected")
async def on_client_disconnected(transport, client):
logger.info("Client disconnected")
await task.cancel()
runner = PipelineRunner(handle_sigint=runner_args.handle_sigint)
await runner.run(task)
async def bot(runner_args: RunnerArguments):
"""Main bot entry point compatible with the Pipecat runner."""
transport = await create_transport(runner_args, transport_params)
await run_bot(transport, runner_args)
if __name__ == "__main__":
from pipecat.runner.run import main
main()