Refactor backend integration and service architecture
- Removed the backend client compatibility wrapper and associated methods to streamline backend integration. - Updated session management to utilize control plane gateways and runtime configuration providers. - Adjusted TTS service implementations to remove the EdgeTTS service and simplify service dependencies. - Enhanced documentation to reflect changes in backend integration and service architecture. - Updated configuration files to remove deprecated TTS provider options and clarify available settings.
This commit is contained in:
@@ -1,87 +0,0 @@
|
||||
"""Compatibility wrappers around backend adapter implementations."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from app.backend_adapters import build_backend_adapter_from_settings
|
||||
|
||||
|
||||
def _adapter():
|
||||
return build_backend_adapter_from_settings()
|
||||
|
||||
|
||||
async def fetch_assistant_config(assistant_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Fetch assistant config payload from backend adapter."""
|
||||
return await _adapter().fetch_assistant_config(assistant_id)
|
||||
|
||||
|
||||
async def create_history_call_record(
|
||||
*,
|
||||
user_id: int,
|
||||
assistant_id: Optional[str],
|
||||
source: str = "debug",
|
||||
) -> Optional[str]:
|
||||
"""Create a call record via backend history API and return call_id."""
|
||||
return await _adapter().create_call_record(
|
||||
user_id=user_id,
|
||||
assistant_id=assistant_id,
|
||||
source=source,
|
||||
)
|
||||
|
||||
|
||||
async def add_history_transcript(
|
||||
*,
|
||||
call_id: str,
|
||||
turn_index: int,
|
||||
speaker: str,
|
||||
content: str,
|
||||
start_ms: int,
|
||||
end_ms: int,
|
||||
confidence: Optional[float] = None,
|
||||
duration_ms: Optional[int] = None,
|
||||
) -> bool:
|
||||
"""Append a transcript segment to backend history."""
|
||||
return await _adapter().add_transcript(
|
||||
call_id=call_id,
|
||||
turn_index=turn_index,
|
||||
speaker=speaker,
|
||||
content=content,
|
||||
start_ms=start_ms,
|
||||
end_ms=end_ms,
|
||||
confidence=confidence,
|
||||
duration_ms=duration_ms,
|
||||
)
|
||||
|
||||
|
||||
async def finalize_history_call_record(
|
||||
*,
|
||||
call_id: str,
|
||||
status: str,
|
||||
duration_seconds: int,
|
||||
) -> bool:
|
||||
"""Finalize a call record with status and duration."""
|
||||
return await _adapter().finalize_call_record(
|
||||
call_id=call_id,
|
||||
status=status,
|
||||
duration_seconds=duration_seconds,
|
||||
)
|
||||
|
||||
|
||||
async def search_knowledge_context(
|
||||
*,
|
||||
kb_id: str,
|
||||
query: str,
|
||||
n_results: int = 5,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Search backend knowledge base and return retrieval results."""
|
||||
return await _adapter().search_knowledge_context(
|
||||
kb_id=kb_id,
|
||||
query=query,
|
||||
n_results=n_results,
|
||||
)
|
||||
|
||||
|
||||
async def fetch_tool_resource(tool_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Fetch tool resource configuration from backend API."""
|
||||
return await _adapter().fetch_tool_resource(tool_id)
|
||||
@@ -71,7 +71,7 @@ class Settings(BaseSettings):
|
||||
# TTS Configuration
|
||||
tts_provider: str = Field(
|
||||
default="openai_compatible",
|
||||
description="TTS provider (edge, openai_compatible, siliconflow, dashscope)"
|
||||
description="TTS provider (openai_compatible, siliconflow, dashscope)"
|
||||
)
|
||||
tts_api_url: Optional[str] = Field(default=None, description="TTS provider API URL")
|
||||
tts_model: Optional[str] = Field(default=None, description="TTS model name")
|
||||
|
||||
@@ -76,7 +76,7 @@ app.add_middleware(
|
||||
|
||||
# Active sessions storage
|
||||
active_sessions: Dict[str, Session] = {}
|
||||
backend_gateway = build_backend_adapter_from_settings()
|
||||
control_plane_gateway = build_backend_adapter_from_settings()
|
||||
|
||||
# Configure logging
|
||||
logger.remove()
|
||||
@@ -187,7 +187,7 @@ async def websocket_endpoint(websocket: WebSocket):
|
||||
session = Session(
|
||||
session_id,
|
||||
transport,
|
||||
backend_gateway=backend_gateway,
|
||||
control_plane_gateway=control_plane_gateway,
|
||||
assistant_id=assistant_id,
|
||||
)
|
||||
active_sessions[session_id] = session
|
||||
@@ -272,7 +272,7 @@ async def webrtc_endpoint(websocket: WebSocket):
|
||||
session = Session(
|
||||
session_id,
|
||||
transport,
|
||||
backend_gateway=backend_gateway,
|
||||
control_plane_gateway=control_plane_gateway,
|
||||
assistant_id=assistant_id,
|
||||
)
|
||||
active_sessions[session_id] = session
|
||||
|
||||
112
engine/app/service_factory.py
Normal file
112
engine/app/service_factory.py
Normal file
@@ -0,0 +1,112 @@
|
||||
"""Default runtime service factory implementing core extension ports."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from core.ports import (
|
||||
ASRPort,
|
||||
ASRServiceSpec,
|
||||
LLMPort,
|
||||
LLMServiceSpec,
|
||||
RealtimeServiceFactory,
|
||||
TTSPort,
|
||||
TTSServiceSpec,
|
||||
)
|
||||
from services.asr import BufferedASRService
|
||||
from services.dashscope_tts import DashScopeTTSService
|
||||
from services.llm import MockLLMService, OpenAILLMService
|
||||
from services.openai_compatible_asr import OpenAICompatibleASRService
|
||||
from services.openai_compatible_tts import OpenAICompatibleTTSService
|
||||
from services.tts import MockTTSService
|
||||
|
||||
_OPENAI_COMPATIBLE_PROVIDERS = {"openai_compatible", "openai-compatible", "siliconflow"}
|
||||
_SUPPORTED_LLM_PROVIDERS = {"openai", *_OPENAI_COMPATIBLE_PROVIDERS}
|
||||
|
||||
|
||||
class DefaultRealtimeServiceFactory(RealtimeServiceFactory):
|
||||
"""Build concrete runtime services from normalized specs."""
|
||||
|
||||
_DEFAULT_DASHSCOPE_TTS_REALTIME_URL = "wss://dashscope.aliyuncs.com/api-ws/v1/realtime"
|
||||
_DEFAULT_DASHSCOPE_TTS_MODEL = "qwen3-tts-flash-realtime"
|
||||
_DEFAULT_OPENAI_COMPATIBLE_TTS_MODEL = "FunAudioLLM/CosyVoice2-0.5B"
|
||||
_DEFAULT_OPENAI_COMPATIBLE_ASR_MODEL = "FunAudioLLM/SenseVoiceSmall"
|
||||
|
||||
@staticmethod
|
||||
def _normalize_provider(provider: Any) -> str:
|
||||
return str(provider or "").strip().lower()
|
||||
|
||||
@staticmethod
|
||||
def _resolve_dashscope_mode(raw_mode: Any) -> str:
|
||||
mode = str(raw_mode or "commit").strip().lower()
|
||||
if mode in {"commit", "server_commit"}:
|
||||
return mode
|
||||
return "commit"
|
||||
|
||||
def create_llm_service(self, spec: LLMServiceSpec) -> LLMPort:
|
||||
provider = self._normalize_provider(spec.provider)
|
||||
if provider in _SUPPORTED_LLM_PROVIDERS and spec.api_key:
|
||||
return OpenAILLMService(
|
||||
api_key=spec.api_key,
|
||||
base_url=spec.base_url,
|
||||
model=spec.model,
|
||||
system_prompt=spec.system_prompt,
|
||||
knowledge_config=spec.knowledge_config,
|
||||
knowledge_searcher=spec.knowledge_searcher,
|
||||
)
|
||||
|
||||
logger.warning(
|
||||
"LLM provider unsupported or API key missing (provider={}); using mock LLM",
|
||||
provider or "-",
|
||||
)
|
||||
return MockLLMService()
|
||||
|
||||
def create_tts_service(self, spec: TTSServiceSpec) -> TTSPort:
|
||||
provider = self._normalize_provider(spec.provider)
|
||||
|
||||
if provider == "dashscope" and spec.api_key:
|
||||
return DashScopeTTSService(
|
||||
api_key=spec.api_key,
|
||||
api_url=spec.api_url or self._DEFAULT_DASHSCOPE_TTS_REALTIME_URL,
|
||||
voice=spec.voice,
|
||||
model=spec.model or self._DEFAULT_DASHSCOPE_TTS_MODEL,
|
||||
mode=self._resolve_dashscope_mode(spec.mode),
|
||||
sample_rate=spec.sample_rate,
|
||||
speed=spec.speed,
|
||||
)
|
||||
|
||||
if provider in _OPENAI_COMPATIBLE_PROVIDERS and spec.api_key:
|
||||
return OpenAICompatibleTTSService(
|
||||
api_key=spec.api_key,
|
||||
api_url=spec.api_url,
|
||||
voice=spec.voice,
|
||||
model=spec.model or self._DEFAULT_OPENAI_COMPATIBLE_TTS_MODEL,
|
||||
sample_rate=spec.sample_rate,
|
||||
speed=spec.speed,
|
||||
)
|
||||
|
||||
logger.warning(
|
||||
"TTS provider unsupported or API key missing (provider={}); using mock TTS",
|
||||
provider or "-",
|
||||
)
|
||||
return MockTTSService(sample_rate=spec.sample_rate)
|
||||
|
||||
def create_asr_service(self, spec: ASRServiceSpec) -> ASRPort:
|
||||
provider = self._normalize_provider(spec.provider)
|
||||
|
||||
if provider in _OPENAI_COMPATIBLE_PROVIDERS and spec.api_key:
|
||||
return OpenAICompatibleASRService(
|
||||
api_key=spec.api_key,
|
||||
api_url=spec.api_url,
|
||||
model=spec.model or self._DEFAULT_OPENAI_COMPATIBLE_ASR_MODEL,
|
||||
sample_rate=spec.sample_rate,
|
||||
language=spec.language,
|
||||
interim_interval_ms=spec.interim_interval_ms,
|
||||
min_audio_for_interim_ms=spec.min_audio_for_interim_ms,
|
||||
on_transcript=spec.on_transcript,
|
||||
)
|
||||
|
||||
logger.info("Using buffered ASR service (provider={})", provider or "-")
|
||||
return BufferedASRService(sample_rate=spec.sample_rate, language=spec.language)
|
||||
Reference in New Issue
Block a user