feat: integrate OCI agent streaming and web voice client
This commit is contained in:
@@ -34,8 +34,13 @@ Architecture:
|
||||
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from contextlib import aclosing
|
||||
|
||||
import asyncio
|
||||
|
||||
@@ -83,8 +88,16 @@ from voice_assistant.audio_gateway.app.protocol import (
|
||||
STTPartial,
|
||||
TurnEnded,
|
||||
TurnStarted,
|
||||
AgentDelta,
|
||||
)
|
||||
|
||||
from voice_assistant.voice_pipeline import (
|
||||
PipelineTextDelta,
|
||||
PipelineAgentResponse,
|
||||
PipelineAudioChunk,
|
||||
)
|
||||
|
||||
|
||||
from voice_assistant.audio_gateway.app.session import SessionState
|
||||
|
||||
# logger configuration
|
||||
@@ -100,6 +113,18 @@ provider_catalog = load_provider_catalog()
|
||||
|
||||
MAX_AUDIO_CHUNK_BYTES = 64 * 1024 # 64 KB
|
||||
|
||||
WEB_DIR = Path(__file__).resolve().parents[1] / "web"
|
||||
|
||||
app.mount(
|
||||
"/web",
|
||||
StaticFiles(directory=WEB_DIR),
|
||||
name="web",
|
||||
)
|
||||
|
||||
|
||||
@app.get("/ui", include_in_schema=False)
|
||||
async def web_client() -> FileResponse:
|
||||
return FileResponse(WEB_DIR / "index.html")
|
||||
|
||||
|
||||
def create_turn_manager() -> TurnManager:
|
||||
@@ -176,27 +201,63 @@ async def forward_stt_events(
|
||||
),
|
||||
)
|
||||
|
||||
result = await pipeline.process_transcript(
|
||||
session_id=session_id,
|
||||
turn_id=turn_id,
|
||||
text=event.text,
|
||||
)
|
||||
if callable(getattr(pipeline.agent, "stream", None)):
|
||||
async with aclosing(
|
||||
pipeline.stream_turn_response(
|
||||
session_id=session_id,
|
||||
turn_id=turn_id,
|
||||
text=event.text,
|
||||
)
|
||||
) as response_events:
|
||||
async for response_event in response_events:
|
||||
if not pipeline.is_active_turn(turn_id):
|
||||
break
|
||||
|
||||
await send_json(
|
||||
websocket,
|
||||
AgentResponse(
|
||||
if isinstance(response_event, PipelineTextDelta):
|
||||
await send_json(
|
||||
websocket,
|
||||
AgentDelta(
|
||||
session_id=session_id,
|
||||
turn_id=turn_id,
|
||||
text=response_event.text,
|
||||
),
|
||||
)
|
||||
|
||||
elif isinstance(response_event, PipelineAgentResponse):
|
||||
await send_json(
|
||||
websocket,
|
||||
AgentResponse(
|
||||
session_id=session_id,
|
||||
turn_id=turn_id,
|
||||
text=response_event.text,
|
||||
),
|
||||
)
|
||||
|
||||
elif isinstance(response_event, PipelineAudioChunk):
|
||||
await websocket.send_bytes(response_event.audio)
|
||||
|
||||
else:
|
||||
result = await pipeline.process_transcript(
|
||||
session_id=session_id,
|
||||
turn_id=turn_id,
|
||||
text=result.response_text,
|
||||
),
|
||||
)
|
||||
text=event.text,
|
||||
)
|
||||
|
||||
async for audio_chunk in pipeline.synthesize_response(
|
||||
result.response_text,
|
||||
):
|
||||
await websocket.send_bytes(audio_chunk)
|
||||
await send_json(
|
||||
websocket,
|
||||
AgentResponse(
|
||||
session_id=session_id,
|
||||
turn_id=turn_id,
|
||||
text=result.response_text,
|
||||
),
|
||||
)
|
||||
|
||||
pipeline.on_tts_complete(turn_id)
|
||||
async for audio_chunk in pipeline.synthesize_response(
|
||||
result.response_text,
|
||||
):
|
||||
await websocket.send_bytes(audio_chunk)
|
||||
|
||||
pipeline.on_tts_complete(turn_id)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user