""" Base LLM client interface. [AC-AISVC-02, AC-AISVC-06] Abstract interface for LLM providers. Design reference: design.md Section 8.1 - LLMClient interface - generate(prompt, params) -> text - stream_generate(prompt, params) -> iterator[delta] """ from abc import ABC, abstractmethod from collections.abc import AsyncGenerator from dataclasses import dataclass, field from typing import Any @dataclass class LLMConfig: """ Configuration for LLM client. [AC-AISVC-02] Supports configurable model parameters. """ model: str = "gpt-4o-mini" max_tokens: int = 2048 temperature: float = 0.7 top_p: float = 1.0 timeout_seconds: int = 30 max_retries: int = 3 extra_params: dict[str, Any] = field(default_factory=dict) @dataclass class ToolCall: """ Represents a function call from the LLM. Used in Function Calling mode. """ id: str name: str arguments: dict[str, Any] def to_dict(self) -> dict[str, Any]: import json return { "id": self.id, "type": "function", "function": { "name": self.name, "arguments": json.dumps(self.arguments, ensure_ascii=False), } } @dataclass class LLMResponse: """ Response from LLM generation. [AC-AISVC-02] Contains generated content and metadata. """ content: str | None = None model: str = "" usage: dict[str, int] = field(default_factory=dict) finish_reason: str = "stop" tool_calls: list[ToolCall] = field(default_factory=list) metadata: dict[str, Any] = field(default_factory=dict) @property def has_tool_calls(self) -> bool: """Check if response contains tool calls.""" return len(self.tool_calls) > 0 @dataclass class LLMStreamChunk: """ Streaming chunk from LLM. [AC-AISVC-06, AC-AISVC-07] Incremental output for SSE streaming. """ delta: str model: str finish_reason: str | None = None tool_calls_delta: list[dict[str, Any]] = field(default_factory=list) metadata: dict[str, Any] = field(default_factory=dict) @dataclass class ToolDefinition: """ Tool definition for Function Calling. Compatible with OpenAI/DeepSeek function calling format. """ name: str description: str parameters: dict[str, Any] type: str = "function" def to_openai_format(self) -> dict[str, Any]: """Convert to OpenAI tools format.""" return { "type": self.type, "function": { "name": self.name, "description": self.description, "parameters": self.parameters, } } class LLMClient(ABC): """ Abstract base class for LLM clients. [AC-AISVC-02, AC-AISVC-06] Provides unified interface for different LLM providers. Design reference: design.md Section 8.2 - Plugin points - OpenAICompatibleClient / LocalModelClient can be swapped """ @abstractmethod async def generate( self, messages: list[dict[str, Any]], config: LLMConfig | None = None, tools: list[ToolDefinition] | None = None, tool_choice: str | dict[str, Any] | None = None, **kwargs: Any, ) -> LLMResponse: """ Generate a non-streaming response. [AC-AISVC-02] Returns complete response for ChatResponse. Args: messages: List of chat messages with 'role' and 'content'. config: Optional LLM configuration overrides. tools: Optional list of tools for function calling. tool_choice: Tool choice strategy ("auto", "none", or specific tool). **kwargs: Additional provider-specific parameters. Returns: LLMResponse with generated content, tool_calls, and metadata. Raises: LLMException: If generation fails. """ pass @abstractmethod async def stream_generate( self, messages: list[dict[str, Any]], config: LLMConfig | None = None, tools: list[ToolDefinition] | None = None, tool_choice: str | dict[str, Any] | None = None, **kwargs: Any, ) -> AsyncGenerator[LLMStreamChunk, None]: """ Generate a streaming response. [AC-AISVC-06, AC-AISVC-07] Yields incremental chunks for SSE. Args: messages: List of chat messages with 'role' and 'content'. config: Optional LLM configuration overrides. tools: Optional list of tools for function calling. tool_choice: Tool choice strategy ("auto", "none", or specific tool). **kwargs: Additional provider-specific parameters. Yields: LLMStreamChunk with incremental content. Raises: LLMException: If generation fails. """ pass @abstractmethod async def close(self) -> None: """Close the client and release resources.""" pass