File size: 5,275 Bytes
b218134
9cd569a
 
b218134
 
9cd569a
 
 
 
b218134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9cd569a
 
 
 
 
b218134
 
9cd569a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b218134
 
 
 
 
 
9cd569a
b218134
 
 
 
 
9cd569a
 
b218134
 
 
9cd569a
 
b218134
 
 
9cd569a
 
 
 
 
 
 
 
b218134
 
 
 
 
 
9cd569a
 
 
 
 
 
 
b218134
 
 
 
 
 
 
 
 
 
 
 
 
 
9cd569a
 
 
 
 
 
 
b218134
 
 
9cd569a
b218134
 
 
 
 
 
 
9cd569a
 
 
b218134
9cd569a
b218134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9cd569a
b218134
 
9cd569a
 
 
b218134
9cd569a
 
 
 
 
 
 
 
b218134
9cd569a
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import os
import asyncio
from fastapi import FastAPI, WebSocket
from dotenv import load_dotenv
from loguru import logger

from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import (
    LocalSmartTurnAnalyzerV3,
)
from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.audio.vad.vad_analyzer import VADParams
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 parse_telephony_websocket
from pipecat.serializers.exotel import ExotelFrameSerializer
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
from pipecat.transports.websocket.fastapi import (
    FastAPIWebsocketParams,
    FastAPIWebsocketTransport,
)
from pipecat.turns.user_stop import TurnAnalyzerUserTurnStopStrategy
from pipecat.turns.user_turn_strategies import UserTurnStrategies


# -------------------------------------------------------------------
# ENV
# -------------------------------------------------------------------

load_dotenv(override=True)

# -------------------------------------------------------------------
# FASTAPI APP
# -------------------------------------------------------------------

app = FastAPI()


@app.get("/health")
async def health():
    return {"status": "ok"}


# -------------------------------------------------------------------
# CORE BOT LOGIC (unchanged, just wrapped)
# -------------------------------------------------------------------

async def run_bot(transport: BaseTransport, handle_sigint: bool):
    llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"))
    stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
    tts = CartesiaTTSService(
        api_key=os.getenv("CARTESIA_API_KEY"),
        voice_id="07bc462a-c644-49f1-baf7-82d5599131be",
    )

    messages = [
        {
            "role": "system",
            "content": """<YOUR FULL SYSTEM PROMPT EXACTLY AS YOU WROTE IT>""",
        }
    ]

    context = LLMContext(messages)

    user_agg, assistant_agg = LLMContextAggregatorPair(
        context,
        user_params=LLMUserAggregatorParams(
            user_turn_strategies=UserTurnStrategies(
                stop=[
                    TurnAnalyzerUserTurnStopStrategy(
                        turn_analyzer=LocalSmartTurnAnalyzerV3()
                    )
                ]
            ),
            vad_analyzer=SileroVADAnalyzer(
                params=VADParams(stop_secs=0.2)
            ),
        ),
    )

    pipeline = Pipeline(
        [
            transport.input(),
            stt,
            user_agg,
            llm,
            tts,
            transport.output(),
            assistant_agg,
        ]
    )

    task = PipelineTask(
        pipeline,
        params=PipelineParams(
            audio_in_sample_rate=8000,
            audio_out_sample_rate=8000,
            enable_metrics=True,
            enable_usage_metrics=True,
        ),
    )

    @transport.event_handler("on_client_connected")
    async def on_connect(transport, client):
        messages.append(
            {
                "role": "system",
                "content": "హలో, నేను స్పందనా స్ఫూర్తి నుంచి ఐషా మాట్లాడుతున్నాను. నేను ప్రజ్వల్ గారితో మాట్లాడుతున్నానా?",
            }
        )
        await task.queue_frames([LLMRunFrame()])

    @transport.event_handler("on_client_disconnected")
    async def on_disconnect(transport, client):
        await task.cancel()

    runner = PipelineRunner(handle_sigint=handle_sigint)
    await runner.run(task)


async def bot(runner_args: RunnerArguments):
    transport_type, call_data = await parse_telephony_websocket(
        runner_args.websocket
    )

    logger.info(f"Transport detected: {transport_type}")

    serializer = ExotelFrameSerializer(
        stream_sid=call_data["stream_id"],
        call_sid=call_data["call_id"],
    )

    transport = FastAPIWebsocketTransport(
        websocket=runner_args.websocket,
        params=FastAPIWebsocketParams(
            audio_in_enabled=True,
            audio_out_enabled=True,
            add_wav_header=False,
            serializer=serializer,
        ),
    )

    await run_bot(transport, handle_sigint=False)


# -------------------------------------------------------------------
# WEBSOCKET ENDPOINT (/media)
# -------------------------------------------------------------------

@app.websocket("/media")
async def media_ws(websocket: WebSocket):
    await websocket.accept()

    runner_args = RunnerArguments(
        websocket=websocket,
        handle_sigint=False,
    )

    await bot(runner_args)