Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copy to .env and fill in real values. .env is gitignored.
# Read by /env.py at import; exposed as ENV["openrouter_key"] / ENV.openrouter_key.

# OpenRouter API key - paid LLM routes. https://openrouter.ai/settings/keys
OPENROUTER_API_KEY=

# Ollama API key - any non-empty string for local. Cloud-hosted Ollama tags
# (like gemma4:31b-cloud) need a real key from https://ollama.com/settings/keys
OLLAMA_API_KEY=ollama
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,7 @@ website/.cache-loader/
/bench_*.py
/bonsai_*.py
/test_reasoning.py

# Secrets. .env holds API keys + local overrides. Never commit it.
# .env.example is the tracked template - copy to .env and fill in values.
/.env
31 changes: 31 additions & 0 deletions env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Repo-wide env loader. Reads /.env, exposes typed ENV.

Callers do ``ENV["openrouter_key"]`` or ``ENV.openrouter_key``.
Shell env wins over .env file. Missing .env is silent (CI / prod use
real env vars). Typos raise KeyError from __getitem__.
"""
from __future__ import annotations

import os
from dataclasses import dataclass
from pathlib import Path

_DOTENV = Path(__file__).resolve().parent / ".env"
if _DOTENV.exists():
for _line in _DOTENV.read_text().splitlines():
_line = _line.strip()
if _line and not _line.startswith("#") and "=" in _line:
_k, _, _v = _line.partition("=")
os.environ.setdefault(_k.strip(), _v.strip().strip('"').strip("'"))


@dataclass(frozen=True)
class _Env:
openrouter_key: str = os.environ.get("OPENROUTER_API_KEY", "")
ollama_key: str = os.environ.get("OLLAMA_API_KEY", "ollama")

def __getitem__(self, k: str) -> str:
return vars(self)[k]


ENV = _Env()
6 changes: 3 additions & 3 deletions tools/autoresearch/config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"providers": {
"local_ollama": {
"base_url": "http://localhost:11434",
"api_key": "REPLACE_WITH_OLLAMA_KEY_OR_LEAVE_EMPTY",
"env_key": "ollama_key",
"is_local": true,
"litellm_prefix": "ollama_chat",
"models": {
Expand All @@ -21,7 +21,7 @@
},
"openrouter": {
"base_url": "https://openrouter.ai/api/v1",
"api_key": "REPLACE_WITH_OPENROUTER_KEY_sk-or-v1-...",
"env_key": "openrouter_key",
"is_local": false,
"litellm_prefix": "openrouter",
"models": {
Expand All @@ -45,5 +45,5 @@
"noise_tolerance": 1.02,
"full_bench_repeats": 3,
"attempts_per_hypothesis": 3,
"notes": "Copy to config.json and fill in api_key fields. config.json is gitignored. To swap models, edit active_model. Max LLM calls per (algo, model) = 6 hypotheses × attempts_per_hypothesis."
"notes": "Copy to config.json. API keys come from /.env (never embed them here). See /.env.example + env.py for the typed ENV loader. To swap models, edit active_model."
}
14 changes: 8 additions & 6 deletions tools/autoresearch/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
from __future__ import annotations

import json
import os
from pathlib import Path

from env import ENV

CONFIG_PATH = Path(__file__).resolve().parent / "config.json"


Expand Down Expand Up @@ -58,11 +59,12 @@ def resolve_providers(
base_url = p.get("base_url", "")
if not base_url:
continue
api_key = (
p.get("api_key", "")
or os.environ.get(p.get("api_key_env", ""), "")
or "ollama"
)
# Each provider config declares `env_key` pointing at an ENV field
# (see env.py). Typed: typos raise KeyError from ENV.__getitem__.
# Missing `env_key` falls back to "ollama" (works for local Ollama
# which accepts any non-empty API key).
env_field = p.get("env_key", "")
api_key = str(ENV[env_field]) if env_field else "ollama"
is_local = p.get("is_local", "localhost" in base_url or "127.0.0.1" in base_url)
prefix = p.get("litellm_prefix") or ("ollama_chat" if is_local else "")
available = p.get("models", {})
Expand Down
2 changes: 1 addition & 1 deletion tools/finetune/training_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"providers": {
"openrouter": {
"base_url": "https://openrouter.ai/api/v1",
"api_key": "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",
"env_key": "openrouter_key",
"is_local": false,
"litellm_prefix": "openrouter",
"models": {
Expand Down
Loading