"""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."""