feat: add config module and vision router stub

This commit is contained in:
pyr0ball 2026-03-25 11:08:03 -07:00
parent ae4624158e
commit e09622729c
5 changed files with 79 additions and 0 deletions

View file

@ -0,0 +1,3 @@
from .settings import require_env, load_env
__all__ = ["require_env", "load_env"]

View file

@ -0,0 +1,27 @@
"""Env validation and .env loader for CircuitForge products."""
from __future__ import annotations
import os
from pathlib import Path
def require_env(key: str) -> str:
"""Return env var value or raise EnvironmentError with clear message."""
value = os.environ.get(key)
if not value:
raise EnvironmentError(
f"Required environment variable {key!r} is not set. "
f"Check your .env file."
)
return value
def load_env(path: Path) -> None:
"""Load key=value pairs from a .env file into os.environ. Skips missing files."""
if not path.exists():
return
for line in path.read_text().splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, _, value = line.partition("=")
os.environ.setdefault(key.strip(), value.strip())

View file

@ -0,0 +1,3 @@
from .router import VisionRouter
__all__ = ["VisionRouter"]

View file

@ -0,0 +1,19 @@
"""
Vision model router stub until v0.2.
Supports: moondream2 (local) and Claude vision API (cloud).
"""
from __future__ import annotations
class VisionRouter:
"""Routes image analysis requests to local or cloud vision models."""
def analyze(self, image_bytes: bytes, prompt: str) -> str:
"""
Analyze image_bytes with the given prompt.
Raises NotImplementedError until vision backends are wired up.
"""
raise NotImplementedError(
"VisionRouter is not yet implemented. "
"Photo analysis requires a Paid tier or local vision model (v0.2+)."
)

27
tests/test_config.py Normal file
View file

@ -0,0 +1,27 @@
import os
import pytest
from circuitforge_core.config import require_env, load_env
def test_require_env_returns_value_when_set(monkeypatch):
monkeypatch.setenv("TEST_KEY", "hello")
assert require_env("TEST_KEY") == "hello"
def test_require_env_raises_when_missing(monkeypatch):
monkeypatch.delenv("TEST_KEY", raising=False)
with pytest.raises(EnvironmentError, match="TEST_KEY"):
require_env("TEST_KEY")
def test_load_env_sets_variables(tmp_path, monkeypatch):
env_file = tmp_path / ".env"
env_file.write_text("FOO=bar\nBAZ=qux\n")
monkeypatch.delenv("FOO", raising=False)
load_env(env_file)
assert os.environ.get("FOO") == "bar"
assert os.environ.get("BAZ") == "qux"
def test_load_env_skips_missing_file(tmp_path):
load_env(tmp_path / "nonexistent.env") # must not raise