Sixteen mutating routes now refuse a caller who cannot present the token the server started with; queries stay open, because a question is not a write. The environment variable is ELY_EYE_AUTH_TOKEN, following the repo's env_prefix convention — the shorter name I reached for first returned 403 where the smoke expected 401, and the server printed the reason. Words from a stranger are quarantined on the way in: an HTTP upload is third_party by definition, and a sentence carrying an instruction-shaped phrase is quarantined whoever wrote it. A quarantined atom stays findable and stays out of the compiled context until a second, live, unquarantined source agrees with its rarest words. Corroboration is computed at read time because it is a property of the corpus now, not of the moment a memory was written; it is term-level, and the docstring says so. forget now erases the source row and the compiled contexts beside the atoms, so a forgotten memory leaves nothing that can still speak. The semantic cache key carries a store revision, which fixes a real staleness: a context compiled before new evidence arrived kept its old quarantine decision forever. bench.audit --product still pointed at evals.py, which the p1-6 split moved into backend/src/ely_eye/proofs/. It failed fast, but the anti-self-justification rule had quietly stopped auditing the product; it now walks the package, and a test pins that the package exists and holds no re-roll. Gates: pytest 306 passed; ruff clean; mypy cold cache 24 errors in 9 files, the recorded baseline, net zero; pyright 0 errors on the 23 backend files and the 2 bench files; go build/vet/test and gofmt clean; npm run build 221.39 kB with eslint exit 0; bench/filesize_check clean; bench.audit --product clean over 31 harness files; scripts/mcp_write_smoke.sh PASS with the gate live across two servers and the MCP child.
95 lines
2.8 KiB
Python
95 lines
2.8 KiB
Python
from __future__ import annotations
|
|
|
|
from functools import lru_cache
|
|
from pathlib import Path
|
|
from typing import Literal
|
|
|
|
from pydantic import Field
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
model_config = SettingsConfigDict(env_prefix="ELY_EYE_", env_file=".env", extra="ignore")
|
|
|
|
home: Path = Field(default_factory=lambda: Path.cwd() / ".ely_eye")
|
|
default_holder: str = "local"
|
|
auth_token: str | None = None
|
|
allowed_origins: list[str] = Field(
|
|
default_factory=lambda: ["http://localhost:5173", "http://127.0.0.1:5173"]
|
|
)
|
|
model_id: str = "Qwen/Qwen3.5-9B"
|
|
model_dir: Path | None = None
|
|
kv_cache_dtype: str = "auto"
|
|
planner_model_id: str = "Qwen/Qwen3.5-0.8B"
|
|
embedding_model_id: str = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
|
|
embedding_model_dir: Path | None = None
|
|
clip_model_dir: Path | None = None
|
|
vlm_model_dir: Path | None = None
|
|
vlm_max_new_tokens: int = 96
|
|
asr_model_dir: Path | None = None
|
|
adapter_dir: Path | None = None
|
|
runtime_backend: Literal["sglang", "transformers"] = "transformers"
|
|
sglang_base_url: str = "http://127.0.0.1:8000/v1"
|
|
sglang_api_key: str = "ely-eye-local"
|
|
ui_dir: Path | None = None
|
|
wsl_distro: str = "Ubuntu"
|
|
dense_embeddings_enabled: bool = False
|
|
live_context_tokens: int = 262_144
|
|
extreme_context_tokens: int = 1_010_000
|
|
library_target_tokens: int = 100_000_000
|
|
upload_max_bytes: int = 20 * 1024 * 1024 * 1024
|
|
video_sample_seconds: float = 5.0
|
|
max_text_file_bytes: int = 10 * 1024 * 1024
|
|
retrieval_top_k: int = 24
|
|
max_new_tokens: int = 512
|
|
temperature: float = 0.1
|
|
train_max_length: int = 8192
|
|
train_gradient_accumulation_steps: int = 8
|
|
train_learning_rate: float = 2e-4
|
|
|
|
@property
|
|
def data_dir(self) -> Path:
|
|
return self.home / "data"
|
|
|
|
@property
|
|
def object_dir(self) -> Path:
|
|
return self.home / "objects"
|
|
|
|
@property
|
|
def cartridge_dir(self) -> Path:
|
|
return self.home / "cartridges"
|
|
|
|
@property
|
|
def cache_dir(self) -> Path:
|
|
return self.home / "cache"
|
|
|
|
@property
|
|
def training_dir(self) -> Path:
|
|
return self.home / "training"
|
|
|
|
@property
|
|
def adapters_dir(self) -> Path:
|
|
return self.home / "adapters"
|
|
|
|
@property
|
|
def db_path(self) -> Path:
|
|
return self.data_dir / "ely_eye.sqlite"
|
|
|
|
def ensure_dirs(self) -> None:
|
|
for path in (
|
|
self.data_dir,
|
|
self.object_dir,
|
|
self.cartridge_dir,
|
|
self.cache_dir,
|
|
self.training_dir,
|
|
self.adapters_dir,
|
|
):
|
|
path.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def get_settings() -> Settings:
|
|
settings = Settings()
|
|
settings.ensure_dirs()
|
|
return settings
|