docs: add CLAUDE.md with project guidelines and conventions, document new devii admin quota reset endpoints and CLI commands, update README with coverage CI and devii_admin_daily_usd config, add European date normalization to ProjectForm model

This commit is contained in:
2026-06-08 22:30:25 +00:00
parent 841c1c7417
commit 547e4eda8c
45 changed files with 1844 additions and 99 deletions
+28 -6
View File
@@ -67,10 +67,24 @@ class Settings:
daily_limit_usd: float
def _default_ai_key() -> str:
env_key = os.environ.get("DEVII_AI_KEY", "")
if env_key:
return env_key
try:
from devplacepy.database import internal_gateway_key
key = internal_gateway_key()
if key:
return key
except Exception: # noqa: BLE001 - no local DB / remote-only: fall back to a random key
pass
return str(uuid.uuid4())
def load_settings() -> Settings:
return Settings(
ai_url=os.environ.get("DEVII_AI_URL", DEFAULT_AI_URL),
ai_key=os.environ.get("DEVII_AI_KEY", str(uuid.uuid4())),
ai_key=_default_ai_key(),
ai_model=os.environ.get("DEVII_AI_MODEL", DEFAULT_AI_MODEL),
base_url=os.environ.get("DEVII_BASE_URL", DEFAULT_BASE_URL).rstrip("/"),
timeout_seconds=float(os.environ.get("DEVII_TIMEOUT", DEFAULT_TIMEOUT_SECONDS)),
@@ -119,6 +133,7 @@ FIELD_VERIFY_REQUIRED = "devii_verify_required"
FIELD_MAX_ITERATIONS = "devii_max_iterations"
FIELD_USER_DAILY_USD = "devii_user_daily_usd"
FIELD_GUEST_DAILY_USD = "devii_guest_daily_usd"
FIELD_ADMIN_DAILY_USD = "devii_admin_daily_usd"
FIELD_GUESTS_ENABLED = "devii_guests_enabled"
FIELD_PRICE_CACHE_HIT = "devii_price_cache_hit"
FIELD_PRICE_CACHE_MISS = "devii_price_cache_miss"
@@ -133,7 +148,17 @@ FIELD_RSEARCH_TIMEOUT = "devii_rsearch_timeout"
LESSONS_DB_PATH = os.environ.get("DEVII_LESSONS_DB", "devii_lessons.db")
def build_settings(config: dict, base_url: str, api_key: str, owner_kind: str = "guest") -> Settings:
def effective_daily_limit(config: dict, owner_kind: str, is_admin: bool = False) -> float:
if is_admin:
return float(config.get(FIELD_ADMIN_DAILY_USD, 0.0) or 0.0)
if owner_kind == "guest":
return float(config.get(FIELD_GUEST_DAILY_USD, 0.05) or 0.0)
return float(config.get(FIELD_USER_DAILY_USD, 1.0) or 0.0)
def build_settings(
config: dict, base_url: str, api_key: str, owner_kind: str = "guest", is_admin: bool = False
) -> Settings:
configured_key = config[FIELD_AI_KEY] or str(uuid.uuid4())
ai_key = api_key if (owner_kind == "user" and api_key) else configured_key
return Settings(
@@ -166,8 +191,5 @@ def build_settings(config: dict, base_url: str, api_key: str, owner_kind: str =
rsearch_enabled=bool(config.get(FIELD_RSEARCH_ENABLED, True)),
rsearch_url=(config.get(FIELD_RSEARCH_URL) or DEFAULT_RSEARCH_URL).rstrip("/"),
rsearch_timeout_seconds=float(config.get(FIELD_RSEARCH_TIMEOUT) or DEFAULT_RSEARCH_TIMEOUT_SECONDS),
daily_limit_usd=float(
(config.get(FIELD_GUEST_DAILY_USD, 0.05) if owner_kind == "guest"
else config.get(FIELD_USER_DAILY_USD, 1.0)) or 0.0
),
daily_limit_usd=effective_daily_limit(config, owner_kind, is_admin),
)
+2 -2
View File
@@ -17,7 +17,7 @@ from .tasks.store import TaskStore, memory_db
logger = logging.getLogger("devii.hub")
SettingsBuilder = Callable[[str, str, str], "tuple[Settings, Pricing]"]
SettingsBuilder = Callable[[str, str, str, bool], "tuple[Settings, Pricing]"]
class DeviiHub:
@@ -42,7 +42,7 @@ class DeviiHub:
session = self._sessions.get(key)
if session is not None:
return session
settings, pricing = self._build(api_key, base_url, owner_kind)
settings, pricing = self._build(api_key, base_url, owner_kind, is_admin)
llm = LLMClient(settings)
# Persistent, owner-isolated stores for signed-in users; ephemeral in-memory for guests.
owned_db = db if owner_kind == "user" else memory_db()
+19 -7
View File
@@ -62,6 +62,8 @@ class DeviiService(BaseService):
minimum=0, help="Rolling 24-hour spend cap for a signed-in user.", group="Limits"),
ConfigField(config.FIELD_GUEST_DAILY_USD, "Max USD per guest / 24h", type="float", default=0.05,
minimum=0, help="Rolling 24-hour spend cap for an anonymous guest.", group="Limits"),
ConfigField(config.FIELD_ADMIN_DAILY_USD, "Max USD per admin / 24h", type="float", default=0.0,
minimum=0, help="Rolling 24-hour spend cap for administrators. 0 = unlimited.", group="Limits"),
ConfigField(config.FIELD_GUESTS_ENABLED, "Allow guests", type="bool", default=True,
help="When off, only signed-in users may use Devii.", group="Limits"),
ConfigField(config.FIELD_PRICE_CACHE_HIT, "Price cache-hit / 1M", type="float", default=0.0028,
@@ -117,16 +119,13 @@ class DeviiService(BaseService):
output_per_m=float(cfg[config.FIELD_PRICE_OUTPUT]),
)
def _build_settings(self, api_key: str, base_url: str, owner_kind: str = "guest"):
def _build_settings(self, api_key: str, base_url: str, owner_kind: str = "guest", is_admin: bool = False):
cfg = self.effective_config()
settings = build_settings(cfg, base_url, api_key, owner_kind)
settings = build_settings(cfg, base_url, api_key, owner_kind, is_admin)
return settings, self._pricing(cfg)
def daily_limit_for(self, owner_kind: str) -> float:
cfg = self.get_config()
if owner_kind == "guest":
return float(cfg[config.FIELD_GUEST_DAILY_USD])
return float(cfg[config.FIELD_USER_DAILY_USD])
def daily_limit_for(self, owner_kind: str, is_admin: bool = False) -> float:
return config.effective_daily_limit(self.get_config(), owner_kind, is_admin)
def guests_enabled(self) -> bool:
return bool(self.get_config()[config.FIELD_GUESTS_ENABLED])
@@ -134,6 +133,15 @@ class DeviiService(BaseService):
def spent_24h(self, owner_kind: str, owner_id: str) -> float:
return self.hub().ledger.spent_24h(owner_kind, owner_id)
def reset_quota(self, owner_kind: str, owner_id: str) -> int:
return self.hub().ledger.reset(owner_kind, owner_id)
def reset_guest_quotas(self) -> int:
return self.hub().ledger.reset_owner_kind("guest")
def reset_all_quotas(self) -> int:
return self.hub().ledger.reset_all()
async def run_once(self) -> None:
if not self.is_enabled():
return
@@ -161,6 +169,10 @@ class DeviiService(BaseService):
{"label": "Spend 24h", "value": f"${spent:.4f}"},
{"label": "User cap 24h", "value": f"${float(cfg[config.FIELD_USER_DAILY_USD]):.2f}"},
{"label": "Guest cap 24h", "value": f"${float(cfg[config.FIELD_GUEST_DAILY_USD]):.2f}"},
{"label": "Admin cap 24h", "value": (
f"${float(cfg[config.FIELD_ADMIN_DAILY_USD]):.2f}"
if float(cfg[config.FIELD_ADMIN_DAILY_USD]) > 0 else "unlimited"
)},
{"label": "Guests", "value": "on" if cfg[config.FIELD_GUESTS_ENABLED] else "off"},
{"label": "Model", "value": cfg[config.FIELD_AI_MODEL]},
]
+24
View File
@@ -128,6 +128,30 @@ class UsageLedger:
cutoff = _iso(_now() - timedelta(hours=older_than_hours))
return int(get_table(LEDGER).delete(created_at={"<": cutoff}))
def reset(self, owner_kind: str, owner_id: str) -> int:
if LEDGER not in db.tables:
return 0
table = get_table(LEDGER)
count = table.count(owner_kind=owner_kind, owner_id=owner_id)
table.delete(owner_kind=owner_kind, owner_id=owner_id)
return int(count)
def reset_owner_kind(self, owner_kind: str) -> int:
if LEDGER not in db.tables:
return 0
table = get_table(LEDGER)
count = table.count(owner_kind=owner_kind)
table.delete(owner_kind=owner_kind)
return int(count)
def reset_all(self) -> int:
if LEDGER not in db.tables:
return 0
table = get_table(LEDGER)
count = table.count()
table.delete()
return int(count)
class TurnAudit:
def record(self, record: dict[str, Any]) -> None: