"""Minimal OpenAI-compatible chat client, standalone (no third-party deps, no import of lean_agent) so this pipeline works regardless of whether lean-agent is pip-installed. Mirrors lean_agent.core.HTTPProvider on purpose; kept separate deliberately.""" import json import os import urllib.request def load_dotenv(path): """Load KEY=VALUE lines from a .env file into os.environ (does not overwrite vars already set). Silently does nothing if the file does not exist.""" if not path.exists(): return for line in path.read_text(encoding='utf-8').splitlines(): line = line.strip() if not line or line.startswith('#') or '=' not in line: continue key, _, value = line.partition('=') key = key.strip() value = value.strip().strip('"').strip("'") os.environ.setdefault(key, value) class ChatClient: """OpenAI-compatible /chat/completions caller.""" def __init__(self, model, base_url, api_key_env='DASHSCOPE_API_KEY', timeout=300): self.model = model self.base_url = base_url.rstrip('/') self.api_key_env = api_key_env self.timeout = timeout def complete(self, messages, temperature=0.2): key = os.environ.get(self.api_key_env) if not key: raise RuntimeError(f'Missing API key in environment variable {self.api_key_env}') headers = {"Content-Type": "application/json", "Authorization": "Bearer " + key} payload = {"model": self.model, "messages": messages, "temperature": temperature} request = urllib.request.Request(self.base_url + '/chat/completions', data=json.dumps(payload).encode(), headers=headers) with urllib.request.urlopen(request, timeout=self.timeout) as response: data = json.load(response) content = data["choices"][0]["message"]["content"] if not isinstance(content, str): raise ValueError("Provider returned non-text content") return content