"""LLM service for AI-powered analysis using OpenAI-compatible APIs (StepFun, ZhipuAI).""" import logging from openai import AsyncOpenAI logger = logging.getLogger(__name__) async def get_llm_client(settings: dict, use_fallback: bool = False) -> tuple[AsyncOpenAI, str]: """Create an OpenAI-compatible client based on settings. Returns (client, model_name) tuple. """ if use_fallback: base_url = settings.get("ai_fallback_api_base_url", "") api_key = settings.get("ai_fallback_api_key", "") model = settings.get("ai_fallback_model_name", "codegeex-4") else: base_url = settings.get("ai_api_base_url", "") api_key = settings.get("ai_api_key", "") model = settings.get("ai_model_name", "step-2-16k") client = AsyncOpenAI(base_url=base_url, api_key=api_key, timeout=30.0) return client, model async def chat_completion( messages: list[dict], settings: dict, temperature: float | None = None, max_tokens: int | None = None, ) -> str: """Send a chat completion request with automatic fallback.""" temp = temperature or float(settings.get("ai_temperature", "0.7")) tokens = max_tokens or int(settings.get("ai_max_tokens", "2000")) # Try primary try: client, model = await get_llm_client(settings, use_fallback=False) response = await client.chat.completions.create( model=model, messages=messages, temperature=temp, max_tokens=tokens, ) return response.choices[0].message.content or "" except Exception as e: logger.warning(f"Primary LLM failed: {e}") # Try fallback if enabled if settings.get("ai_fallback_enabled") == "true": try: client, model = await get_llm_client(settings, use_fallback=True) response = await client.chat.completions.create( model=model, messages=messages, temperature=temp, max_tokens=tokens, ) return response.choices[0].message.content or "" except Exception as e2: logger.error(f"Fallback LLM also failed: {e2}") raise Exception(f"All LLM providers failed. Primary: {e}, Fallback: {e2}") else: raise async def test_connection(settings: dict) -> dict: """Test connection to the configured LLM provider.""" results = {"primary": {"status": "unknown"}, "fallback": {"status": "unknown"}} # Test primary try: client, model = await get_llm_client(settings, use_fallback=False) response = await client.chat.completions.create( model=model, messages=[{"role": "user", "content": "你好,请回复'连接成功'"}], max_tokens=20, temperature=0, ) reply = response.choices[0].message.content or "" results["primary"] = { "status": "success", "model": model, "reply": reply[:100], } except Exception as e: results["primary"] = {"status": "error", "error": str(e)[:200]} # Test fallback if settings.get("ai_fallback_enabled") == "true": try: client, model = await get_llm_client(settings, use_fallback=True) response = await client.chat.completions.create( model=model, messages=[{"role": "user", "content": "你好,请回复'连接成功'"}], max_tokens=20, temperature=0, ) reply = response.choices[0].message.content or "" results["fallback"] = { "status": "success", "model": model, "reply": reply[:100], } except Exception as e: results["fallback"] = {"status": "error", "error": str(e)[:200]} return results