Files
lean-bench/textbook/llm.py
T
claude-bot caedf2e6f4 Add textbook formalization pipeline (extract -> LLM formalize -> elaboration check -> library assembly)
- extract_section.py: slice a book section out of a pdftotext -layout dump and
  list its labeled Theorem/Proposition/Lemma/Corollary/Definition items.
- formalize.py: LLM-formalize a section into Lean 4 candidate statements
  (preferring Mathlib vocabulary) plus a definitions manifest.
- check_statements.py: elaboration-only sanity check against a real
  Mathlib-backed Lean project (must run outside the Cowork sandbox, which has
  no Lean toolchain and can't run macOS binaries anyway).
- build_library.py: assemble solved lean-bench results into a reusable .lean
  file, reusing each winning proof verbatim.
- llm.py: standalone OpenAI-compatible chat client + .env loader, kept
  independent of lean_agent so this pipeline works without it installed.
- Source PDFs and raw/layout text dumps are gitignored (copyrighted material);
  only code and the final short Lean statement datasets are tracked.
2026-09-17 17:04:19 +00:00

47 lines
2.0 KiB
Python

"""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