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,17 +1,32 @@
|
||||
"""Port interfaces for engine-side integration boundaries."""
|
||||
|
||||
from core.ports.backend import (
|
||||
AssistantConfigProvider,
|
||||
BackendGateway,
|
||||
HistoryWriter,
|
||||
KnowledgeSearcher,
|
||||
ToolResourceResolver,
|
||||
from core.ports.asr import ASRBufferControl, ASRInterimControl, ASRPort, ASRServiceSpec
|
||||
from core.ports.control_plane import (
|
||||
AssistantRuntimeConfigProvider,
|
||||
ControlPlaneGateway,
|
||||
ConversationHistoryStore,
|
||||
KnowledgeRetriever,
|
||||
ToolCatalog,
|
||||
)
|
||||
from core.ports.llm import LLMCancellable, LLMPort, LLMRuntimeConfigurable, LLMServiceSpec
|
||||
from core.ports.service_factory import RealtimeServiceFactory
|
||||
from core.ports.tts import TTSPort, TTSServiceSpec
|
||||
|
||||
__all__ = [
|
||||
"AssistantConfigProvider",
|
||||
"BackendGateway",
|
||||
"HistoryWriter",
|
||||
"KnowledgeSearcher",
|
||||
"ToolResourceResolver",
|
||||
"ASRPort",
|
||||
"ASRServiceSpec",
|
||||
"ASRInterimControl",
|
||||
"ASRBufferControl",
|
||||
"AssistantRuntimeConfigProvider",
|
||||
"ControlPlaneGateway",
|
||||
"ConversationHistoryStore",
|
||||
"KnowledgeRetriever",
|
||||
"ToolCatalog",
|
||||
"LLMCancellable",
|
||||
"LLMPort",
|
||||
"LLMRuntimeConfigurable",
|
||||
"LLMServiceSpec",
|
||||
"RealtimeServiceFactory",
|
||||
"TTSPort",
|
||||
"TTSServiceSpec",
|
||||
]
|
||||
|
||||
64
engine/core/ports/asr.py
Normal file
64
engine/core/ports/asr.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""ASR extension port contracts."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import AsyncIterator, Awaitable, Callable, Optional, Protocol
|
||||
|
||||
from services.base import ASRResult
|
||||
|
||||
TranscriptCallback = Callable[[str, bool], Awaitable[None]]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ASRServiceSpec:
|
||||
"""Resolved runtime configuration for ASR service creation."""
|
||||
|
||||
provider: str
|
||||
sample_rate: int
|
||||
language: str = "auto"
|
||||
api_key: Optional[str] = None
|
||||
api_url: Optional[str] = None
|
||||
model: Optional[str] = None
|
||||
interim_interval_ms: int = 500
|
||||
min_audio_for_interim_ms: int = 300
|
||||
on_transcript: Optional[TranscriptCallback] = None
|
||||
|
||||
|
||||
class ASRPort(Protocol):
|
||||
"""Port for speech recognition providers."""
|
||||
|
||||
async def connect(self) -> None:
|
||||
"""Establish connection to ASR provider."""
|
||||
|
||||
async def disconnect(self) -> None:
|
||||
"""Release ASR resources."""
|
||||
|
||||
async def send_audio(self, audio: bytes) -> None:
|
||||
"""Push one PCM audio chunk for recognition."""
|
||||
|
||||
async def receive_transcripts(self) -> AsyncIterator[ASRResult]:
|
||||
"""Stream partial/final recognition results."""
|
||||
|
||||
|
||||
class ASRInterimControl(Protocol):
|
||||
"""Optional extension for explicit interim transcription control."""
|
||||
|
||||
async def start_interim_transcription(self) -> None:
|
||||
"""Start interim transcription loop if supported."""
|
||||
|
||||
async def stop_interim_transcription(self) -> None:
|
||||
"""Stop interim transcription loop if supported."""
|
||||
|
||||
|
||||
class ASRBufferControl(Protocol):
|
||||
"""Optional extension for explicit ASR buffer lifecycle control."""
|
||||
|
||||
def clear_buffer(self) -> None:
|
||||
"""Clear provider-side ASR buffer."""
|
||||
|
||||
async def get_final_transcription(self) -> str:
|
||||
"""Return final transcription for the current utterance."""
|
||||
|
||||
def get_and_clear_text(self) -> str:
|
||||
"""Return buffered text and clear internal state."""
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Backend integration ports.
|
||||
"""Control-plane integration ports.
|
||||
|
||||
These interfaces define the boundary between engine runtime logic and
|
||||
backend-side capabilities (config lookup, history persistence, retrieval,
|
||||
control-plane capabilities (config lookup, history persistence, retrieval,
|
||||
and tool resource discovery).
|
||||
"""
|
||||
|
||||
@@ -10,14 +10,14 @@ from __future__ import annotations
|
||||
from typing import Any, Dict, List, Optional, Protocol
|
||||
|
||||
|
||||
class AssistantConfigProvider(Protocol):
|
||||
class AssistantRuntimeConfigProvider(Protocol):
|
||||
"""Port for loading trusted assistant runtime configuration."""
|
||||
|
||||
async def fetch_assistant_config(self, assistant_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Fetch assistant configuration payload."""
|
||||
|
||||
|
||||
class HistoryWriter(Protocol):
|
||||
class ConversationHistoryStore(Protocol):
|
||||
"""Port for persisting call and transcript history."""
|
||||
|
||||
async def create_call_record(
|
||||
@@ -27,7 +27,7 @@ class HistoryWriter(Protocol):
|
||||
assistant_id: Optional[str],
|
||||
source: str = "debug",
|
||||
) -> Optional[str]:
|
||||
"""Create a call record and return backend call ID."""
|
||||
"""Create a call record and return control-plane call ID."""
|
||||
|
||||
async def add_transcript(
|
||||
self,
|
||||
@@ -53,7 +53,7 @@ class HistoryWriter(Protocol):
|
||||
"""Finalize a call record."""
|
||||
|
||||
|
||||
class KnowledgeSearcher(Protocol):
|
||||
class KnowledgeRetriever(Protocol):
|
||||
"""Port for RAG / knowledge retrieval operations."""
|
||||
|
||||
async def search_knowledge_context(
|
||||
@@ -66,19 +66,18 @@ class KnowledgeSearcher(Protocol):
|
||||
"""Search a knowledge source and return ranked snippets."""
|
||||
|
||||
|
||||
class ToolResourceResolver(Protocol):
|
||||
class ToolCatalog(Protocol):
|
||||
"""Port for resolving tool metadata/configuration."""
|
||||
|
||||
async def fetch_tool_resource(self, tool_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Fetch tool resource configuration."""
|
||||
|
||||
|
||||
class BackendGateway(
|
||||
AssistantConfigProvider,
|
||||
HistoryWriter,
|
||||
KnowledgeSearcher,
|
||||
ToolResourceResolver,
|
||||
class ControlPlaneGateway(
|
||||
AssistantRuntimeConfigProvider,
|
||||
ConversationHistoryStore,
|
||||
KnowledgeRetriever,
|
||||
ToolCatalog,
|
||||
Protocol,
|
||||
):
|
||||
"""Composite backend gateway interface used by engine services."""
|
||||
|
||||
"""Composite control-plane gateway used by engine services."""
|
||||
67
engine/core/ports/llm.py
Normal file
67
engine/core/ports/llm.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""LLM extension port contracts."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, AsyncIterator, Awaitable, Callable, Dict, List, Optional, Protocol
|
||||
|
||||
from services.base import LLMMessage, LLMStreamEvent
|
||||
|
||||
KnowledgeRetrieverFn = Callable[..., Awaitable[List[Dict[str, Any]]]]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LLMServiceSpec:
|
||||
"""Resolved runtime configuration for LLM service creation."""
|
||||
|
||||
provider: str
|
||||
model: str
|
||||
api_key: Optional[str] = None
|
||||
base_url: Optional[str] = None
|
||||
system_prompt: Optional[str] = None
|
||||
temperature: float = 0.7
|
||||
knowledge_config: Dict[str, Any] = field(default_factory=dict)
|
||||
knowledge_searcher: Optional[KnowledgeRetrieverFn] = None
|
||||
|
||||
|
||||
class LLMPort(Protocol):
|
||||
"""Port for LLM providers."""
|
||||
|
||||
async def connect(self) -> None:
|
||||
"""Establish connection to LLM provider."""
|
||||
|
||||
async def disconnect(self) -> None:
|
||||
"""Release LLM resources."""
|
||||
|
||||
async def generate(
|
||||
self,
|
||||
messages: List[LLMMessage],
|
||||
temperature: float = 0.7,
|
||||
max_tokens: Optional[int] = None,
|
||||
) -> str:
|
||||
"""Generate a complete assistant response."""
|
||||
|
||||
async def generate_stream(
|
||||
self,
|
||||
messages: List[LLMMessage],
|
||||
temperature: float = 0.7,
|
||||
max_tokens: Optional[int] = None,
|
||||
) -> AsyncIterator[LLMStreamEvent]:
|
||||
"""Generate streaming assistant response events."""
|
||||
|
||||
|
||||
class LLMCancellable(Protocol):
|
||||
"""Optional extension for interrupting in-flight LLM generation."""
|
||||
|
||||
def cancel(self) -> None:
|
||||
"""Cancel an in-flight generation request."""
|
||||
|
||||
|
||||
class LLMRuntimeConfigurable(Protocol):
|
||||
"""Optional extension for runtime config updates."""
|
||||
|
||||
def set_knowledge_config(self, config: Optional[Dict[str, Any]]) -> None:
|
||||
"""Apply runtime knowledge retrieval settings."""
|
||||
|
||||
def set_tool_schemas(self, schemas: Optional[List[Dict[str, Any]]]) -> None:
|
||||
"""Apply runtime tool schemas used for tool calling."""
|
||||
22
engine/core/ports/service_factory.py
Normal file
22
engine/core/ports/service_factory.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""Factory port for creating runtime ASR/LLM/TTS services."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol
|
||||
|
||||
from core.ports.asr import ASRPort, ASRServiceSpec
|
||||
from core.ports.llm import LLMPort, LLMServiceSpec
|
||||
from core.ports.tts import TTSPort, TTSServiceSpec
|
||||
|
||||
|
||||
class RealtimeServiceFactory(Protocol):
|
||||
"""Port for provider-specific service construction."""
|
||||
|
||||
def create_llm_service(self, spec: LLMServiceSpec) -> LLMPort:
|
||||
"""Create an LLM service instance from a resolved spec."""
|
||||
|
||||
def create_tts_service(self, spec: TTSServiceSpec) -> TTSPort:
|
||||
"""Create a TTS service instance from a resolved spec."""
|
||||
|
||||
def create_asr_service(self, spec: ASRServiceSpec) -> ASRPort:
|
||||
"""Create an ASR service instance from a resolved spec."""
|
||||
41
engine/core/ports/tts.py
Normal file
41
engine/core/ports/tts.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""TTS extension port contracts."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import AsyncIterator, Optional, Protocol
|
||||
|
||||
from services.base import TTSChunk
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TTSServiceSpec:
|
||||
"""Resolved runtime configuration for TTS service creation."""
|
||||
|
||||
provider: str
|
||||
voice: str
|
||||
sample_rate: int
|
||||
speed: float = 1.0
|
||||
api_key: Optional[str] = None
|
||||
api_url: Optional[str] = None
|
||||
model: Optional[str] = None
|
||||
mode: str = "commit"
|
||||
|
||||
|
||||
class TTSPort(Protocol):
|
||||
"""Port for speech synthesis providers."""
|
||||
|
||||
async def connect(self) -> None:
|
||||
"""Establish connection to TTS provider."""
|
||||
|
||||
async def disconnect(self) -> None:
|
||||
"""Release TTS resources."""
|
||||
|
||||
async def synthesize(self, text: str) -> bytes:
|
||||
"""Synthesize complete PCM payload for text."""
|
||||
|
||||
async def synthesize_stream(self, text: str) -> AsyncIterator[TTSChunk]:
|
||||
"""Stream synthesized PCM chunks for text."""
|
||||
|
||||
async def cancel(self) -> None:
|
||||
"""Cancel an in-flight synthesis request."""
|
||||
Reference in New Issue
Block a user