Replace httpx with aiohttp

This commit is contained in:
Mark Backman
2025-04-22 17:14:19 -04:00
parent 7358bc6428
commit cc9901a82f
2 changed files with 92 additions and 90 deletions

View File

@@ -6,6 +6,7 @@
import os import os
import aiohttp
from dotenv import load_dotenv from dotenv import load_dotenv
from loguru import logger from loguru import logger
@@ -31,78 +32,79 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection):
remote_smart_turn_url = os.getenv("REMOTE_SMART_TURN_URL") remote_smart_turn_url = os.getenv("REMOTE_SMART_TURN_URL")
transport = SmallWebRTCTransport( async with aiohttp.ClientSession() as session:
webrtc_connection=webrtc_connection, transport = SmallWebRTCTransport(
params=TransportParams( webrtc_connection=webrtc_connection,
audio_in_enabled=True, params=TransportParams(
audio_out_enabled=True, audio_in_enabled=True,
vad_enabled=True, audio_out_enabled=True,
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), vad_enabled=True,
vad_audio_passthrough=True, vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)),
turn_analyzer=SmartTurnAnalyzer(url=remote_smart_turn_url), vad_audio_passthrough=True,
), turn_analyzer=SmartTurnAnalyzer(url=remote_smart_turn_url, aiohttp_session=session),
) ),
)
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
tts = CartesiaTTSService( tts = CartesiaTTSService(
api_key=os.getenv("CARTESIA_API_KEY"), api_key=os.getenv("CARTESIA_API_KEY"),
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
) )
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"))
messages = [ messages = [
{ {
"role": "system", "role": "system",
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
}, },
]
context = OpenAILLMContext(messages)
context_aggregator = llm.create_context_aggregator(context)
pipeline = Pipeline(
[
transport.input(), # Transport user input
stt,
context_aggregator.user(), # User responses
llm, # LLM
tts, # TTS
transport.output(), # Transport bot output
context_aggregator.assistant(), # Assistant spoken responses
] ]
)
task = PipelineTask( context = OpenAILLMContext(messages)
pipeline, context_aggregator = llm.create_context_aggregator(context)
params=PipelineParams(
allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
report_only_initial_ttfb=True,
),
)
@transport.event_handler("on_client_connected") pipeline = Pipeline(
async def on_client_connected(transport, client): [
logger.info(f"Client connected") transport.input(), # Transport user input
# Kick off the conversation. stt,
messages.append({"role": "system", "content": "Please introduce yourself to the user."}) context_aggregator.user(), # User responses
await task.queue_frames([context_aggregator.user().get_context_frame()]) llm, # LLM
tts, # TTS
transport.output(), # Transport bot output
context_aggregator.assistant(), # Assistant spoken responses
]
)
@transport.event_handler("on_client_disconnected") task = PipelineTask(
async def on_client_disconnected(transport, client): pipeline,
logger.info(f"Client disconnected") params=PipelineParams(
allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
report_only_initial_ttfb=True,
),
)
@transport.event_handler("on_client_closed") @transport.event_handler("on_client_connected")
async def on_client_closed(transport, client): async def on_client_connected(transport, client):
logger.info(f"Client closed connection") logger.info(f"Client connected")
await task.cancel() # Kick off the conversation.
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
await task.queue_frames([context_aggregator.user().get_context_frame()])
runner = PipelineRunner(handle_sigint=False) @transport.event_handler("on_client_disconnected")
async def on_client_disconnected(transport, client):
logger.info(f"Client disconnected")
await runner.run(task) @transport.event_handler("on_client_closed")
async def on_client_closed(transport, client):
logger.info(f"Client closed connection")
await task.cancel()
runner = PipelineRunner(handle_sigint=False)
await runner.run(task)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -5,10 +5,11 @@
# #
import asyncio
import io import io
from typing import Dict from typing import Dict
import httpx import aiohttp
import numpy as np import numpy as np
from loguru import logger from loguru import logger
@@ -16,19 +17,15 @@ from pipecat.audio.turn.base_smart_turn import BaseSmartTurn, SmartTurnTimeoutEx
class SmartTurnAnalyzer(BaseSmartTurn): class SmartTurnAnalyzer(BaseSmartTurn):
def __init__(self, url: str, **kwargs): def __init__(self, url: str, aiohttp_session: aiohttp.ClientSession, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.remote_smart_turn_url = url self.remote_smart_turn_url = url
self._aiohttp_session = aiohttp_session
if not self.remote_smart_turn_url: if not self.remote_smart_turn_url:
logger.error("remote_smart_turn_url is not set.") logger.error("remote_smart_turn_url is not set.")
raise Exception("remote_smart_turn_url must be provided.") raise Exception("remote_smart_turn_url must be provided.")
self.client = httpx.AsyncClient(
headers={"Connection": "keep-alive"},
timeout=httpx.Timeout(self._params.stop_secs),
)
def _serialize_array(self, audio_array: np.ndarray) -> bytes: def _serialize_array(self, audio_array: np.ndarray) -> bytes:
logger.trace("Serializing NumPy array to bytes...") logger.trace("Serializing NumPy array to bytes...")
buffer = io.BytesIO() buffer = io.BytesIO()
@@ -43,33 +40,36 @@ class SmartTurnAnalyzer(BaseSmartTurn):
f"Sending {len(data_bytes)} bytes as raw body to {self.remote_smart_turn_url}..." f"Sending {len(data_bytes)} bytes as raw body to {self.remote_smart_turn_url}..."
) )
try: try:
response = await self.client.post( timeout = aiohttp.ClientTimeout(total=self._params.stop_secs)
self.remote_smart_turn_url,
content=data_bytes,
headers=headers,
)
logger.trace("\n--- Response ---") async with self._aiohttp_session.post(
logger.trace(f"Status Code: {response.status_code}") self.remote_smart_turn_url, data=data_bytes, headers=headers, timeout=timeout
) as response:
logger.trace("\n--- Response ---")
logger.trace(f"Status Code: {response.status}")
if response.is_success: if response.status == 200:
try: try:
json_data = response.json() json_data = await response.json()
logger.trace("Response JSON:") logger.trace("Response JSON:")
logger.trace(json_data) logger.trace(json_data)
return json_data return json_data
except httpx.DecodingError: except aiohttp.ContentTypeError:
logger.trace("Response Content (non-JSON):") # Non-JSON response
logger.trace(response.text) text = await response.text()
else: logger.trace("Response Content (non-JSON):")
logger.trace("Response Content (Error):") logger.trace(text)
logger.trace(response.text) raise Exception(f"Non-JSON response: {text}")
response.raise_for_status() else:
error_text = await response.text()
logger.trace("Response Content (Error):")
logger.trace(error_text)
response.raise_for_status()
except httpx.TimeoutException: except asyncio.TimeoutError:
logger.error(f"Request timed out after {self._params.stop_secs} seconds") logger.error(f"Request timed out after {self._params.stop_secs} seconds")
raise SmartTurnTimeoutException(f"Request exceeded {self._params.stop_secs} seconds.") raise SmartTurnTimeoutException(f"Request exceeded {self._params.stop_secs} seconds.")
except httpx.RequestError as e: except aiohttp.ClientError as e:
logger.error(f"Failed to send raw request to Daily Smart Turn: {e}") logger.error(f"Failed to send raw request to Daily Smart Turn: {e}")
raise Exception("Failed to send raw request to Daily Smart Turn.") raise Exception("Failed to send raw request to Daily Smart Turn.")