Spaces:
Running
Running
| import os | |
| import json | |
| import logging | |
| from openai import AsyncOpenAI | |
| logger = logging.getLogger(__name__) | |
| class LLMClient: | |
| def __init__(self, config: dict): | |
| self.client = AsyncOpenAI( | |
| api_key=config.get("api_key") or os.getenv("OPENAI_API_KEY"), | |
| base_url=config.get("base_url", "https://api.deepseek.com"), | |
| ) | |
| self.model = config.get("model", "deepseek-chat") | |
| self.temperature = config.get("temperature", 0.5) | |
| self.max_tokens = config.get("max_tokens", 4096) | |
| def from_settings(cls, provider: str = "default", settings: dict | None = None) -> "LLMClient": | |
| if settings is None: | |
| from .config import load_settings | |
| settings = load_settings() | |
| provider_config = settings["providers"][provider] | |
| return cls(provider_config) | |
| async def chat( | |
| self, | |
| messages: list[dict], | |
| tools: list[dict] | None = None, | |
| tool_choice: str = "auto", | |
| temperature: float | None = None, | |
| ) -> dict: | |
| kwargs = { | |
| "model": self.model, | |
| "messages": messages, | |
| "temperature": temperature or self.temperature, | |
| "max_tokens": self.max_tokens, | |
| } | |
| if tools: | |
| kwargs["tools"] = tools | |
| kwargs["tool_choice"] = tool_choice | |
| response = await self.client.chat.completions.create(**kwargs) | |
| choice = response.choices[0] | |
| result = { | |
| "content": choice.message.content or "", | |
| "tool_calls": [], | |
| "usage": { | |
| "prompt_tokens": response.usage.prompt_tokens if response.usage else 0, | |
| "completion_tokens": response.usage.completion_tokens if response.usage else 0, | |
| }, | |
| } | |
| if choice.message.tool_calls: | |
| for tc in choice.message.tool_calls: | |
| arguments = self._safe_parse_arguments(tc.function.arguments) | |
| result["tool_calls"].append({ | |
| "id": tc.id, | |
| "function": tc.function.name, | |
| "arguments": arguments, | |
| }) | |
| logger.debug( | |
| f"LLM call: model={self.model}, " | |
| f"tokens={result['usage']['prompt_tokens']}+{result['usage']['completion_tokens']}" | |
| ) | |
| return result | |
| def _safe_parse_arguments(self, raw: str) -> dict: | |
| try: | |
| return json.loads(raw) | |
| except json.JSONDecodeError: | |
| import re | |
| match = re.search(r'\{.*\}', raw, re.DOTALL) | |
| if match: | |
| try: | |
| return json.loads(match.group()) | |
| except json.JSONDecodeError: | |
| pass | |
| logger.warning(f"Failed to parse tool arguments: {raw[:200]}") | |
| return {"_raw": raw} | |