#!/usr/bin/env python3 """ llm.py — LLM interaction layer for The Chaos engine. Provides the low-level call_llm function and environment variable setup for provider-specific auth. """ from __future__ import annotations import os from state import append_llm_log def set_llm_env(model: str, api_key: str | None, api_base: str | None) -> None: """Set provider-specific env vars for litellm.""" prefix = model.split("/")[0].upper() key = api_key or "sk-placeholder" os.environ[f"{prefix}_API_KEY"] = key if api_base: os.environ[f"{prefix}_API_BASE"] = api_base def call_llm( messages: list[dict], *, model: str, temperature: float, timeout: int, max_tokens: int, label: str = "", on_debug: callable = None, ) -> str | None: """Make a single LLM call. Returns content text or None on error.""" try: import litellm except ImportError: if on_debug: on_debug("llm_error", {"label": label, "error": "litellm not installed"}) return None try: response = litellm.completion( model=model, messages=messages, temperature=temperature, stream=False, timeout=timeout, max_tokens=max_tokens, ) content = getattr(response.choices[0].message, 'content', None) or "" reasoning = getattr(response.choices[0].message, 'reasoning_content', None) or "" if reasoning and reasoning not in content: append_llm_log(f"\n--- {label} [reasoning] ---\n{reasoning}") text = content or reasoning append_llm_log(f"\n--- {label} ---\n{text}") return text except Exception as e: err_msg = f"{type(e).__name__}: {e}" append_llm_log(f"\n--- LLM ERROR ({label}) ---\n{err_msg}") if on_debug: on_debug("llm_error", {"label": label, "error": err_msg}) return None