Files
nessi 792f85fc8b feat: add organization sessions mode for Devin self-serve usage tracking
Add `DEVIN_MODE` configuration supporting `enterprise` (default) and `organization_sessions` modes. Organization sessions mode uses `/v3/organizations/{org_id}/sessions` API with `ViewOrgSessions` permission, suitable for non-Enterprise organizations. Sums session ACUs consumed within configurable rolling window (1-365 days via `DEVIN_SESSION_WINDOW_DAYS`). Mode does not show quota, percentage, credit, or reset fields as these are unav
2026-09-18 12:04:13 +02:00

64 lines
1.9 KiB
Python

from functools import lru_cache
from typing import Literal
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
from pydantic import SecretStr, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file='.env', env_file_encoding='utf-8', extra='ignore', case_sensitive=False)
host: str = '0.0.0.0'
port: int = 8080
codex_enabled: bool = True
codex_command: str = 'codex'
codex_timeout: float = 8
devin_enabled: bool = True
devin_api_key: SecretStr = SecretStr('')
devin_api_url: str = 'https://api.devin.ai'
devin_mode: Literal['enterprise', 'organization_sessions'] = 'enterprise'
devin_org_id: str = ''
devin_user_id: str = ''
devin_session_window_days: int = 30
devin_timeout: float = 8
provider_cache_ttl: float = 8
refresh_interval: int = 10
timezone: str = 'Europe/Vienna'
demo_mode: bool = False
@field_validator('codex_timeout', 'devin_timeout', 'provider_cache_ttl')
@classmethod
def positive(cls, value: float) -> float:
if value <= 0:
raise ValueError('must be positive')
return value
@field_validator('refresh_interval')
@classmethod
def positive_interval(cls, value: int) -> int:
if value < 1:
raise ValueError('must be at least 1')
return value
@field_validator('devin_session_window_days')
@classmethod
def valid_session_window(cls, value: int) -> int:
if not 1 <= value <= 365:
raise ValueError('must be between 1 and 365')
return value
@field_validator('timezone')
@classmethod
def valid_timezone(cls, value: str) -> str:
try:
ZoneInfo(value)
except ZoneInfoNotFoundError as exc:
raise ValueError('invalid timezone') from exc
return value
@lru_cache
def get_settings() -> Settings:
return Settings()