feat: add config module and vision router stub
This commit is contained in:
parent
ae4624158e
commit
e09622729c
5 changed files with 79 additions and 0 deletions
3
circuitforge_core/config/__init__.py
Normal file
3
circuitforge_core/config/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from .settings import require_env, load_env
|
||||
|
||||
__all__ = ["require_env", "load_env"]
|
||||
27
circuitforge_core/config/settings.py
Normal file
27
circuitforge_core/config/settings.py
Normal 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())
|
||||
3
circuitforge_core/vision/__init__.py
Normal file
3
circuitforge_core/vision/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from .router import VisionRouter
|
||||
|
||||
__all__ = ["VisionRouter"]
|
||||
19
circuitforge_core/vision/router.py
Normal file
19
circuitforge_core/vision/router.py
Normal 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
27
tests/test_config.py
Normal 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
|
||||
Loading…
Reference in a new issue