# retoor from __future__ import annotations import os import uuid from dataclasses import dataclass from devplacepy.config import ( INTERNAL_GATEWAY_URL, INTERNAL_MODEL, DEVII_TASKS_DB, DEVII_LESSONS_DB, ) DEFAULT_AI_URL = INTERNAL_GATEWAY_URL DEFAULT_AI_MODEL = INTERNAL_MODEL DEFAULT_BASE_URL = "http://127.0.0.1:10500" CONTEXT_WINDOW_TOKENS = 1_048_576 MAX_OUTPUT_TOKENS = 384_000 SYSTEM_RESERVE_TOKENS = 64_000 CHARS_PER_TOKEN = 3 CONTEXT_INPUT_BUDGET_TOKENS = ( CONTEXT_WINDOW_TOKENS - MAX_OUTPUT_TOKENS - SYSTEM_RESERVE_TOKENS ) MIN_TIMEOUT_SECONDS = 300.0 DEFAULT_TIMEOUT_SECONDS = 300.0 DEFAULT_MAX_RESPONSE_CHARS = 200_000 DEFAULT_MAX_TOOL_ITERATIONS = 40 DEFAULT_DELEGATE_MAX_ITERATIONS = 25 DEFAULT_CONTEXT_COMPACT_THRESHOLD = CONTEXT_INPUT_BUDGET_TOKENS * CHARS_PER_TOKEN DEFAULT_CONTEXT_KEEP_TAIL = 12 DEFAULT_RECALL_TOP_K = 3 DEFAULT_FETCH_MAX_CHARS = 200_000 DEFAULT_FETCH_TIMEOUT_SECONDS = 300.0 DEFAULT_FETCH_MAX_BYTES = 8_000_000 DEFAULT_RSEARCH_URL = "https://rsearch.app.molodetz.nl" DEFAULT_RSEARCH_TIMEOUT_SECONDS = 300.0 DEFAULT_EMAIL_TIMEOUT_SECONDS = 30.0 @dataclass(frozen=True) class Settings: ai_url: str ai_key: str ai_model: str base_url: str timeout_seconds: float max_response_chars: int max_tool_iterations: int log_level: str log_path: str tasks_db_path: str scheduler_tick_seconds: float lessons_db_path: str delegate_max_iterations: int context_compact_threshold: int context_keep_tail: int recall_top_k: int plan_required: bool verify_required: bool platform_api_key: str login_email: str login_password: str fetch_max_chars: int fetch_timeout_seconds: float fetch_max_bytes: int fetch_allow_private: bool allow_eval: bool browser_control: bool rsearch_enabled: bool rsearch_url: str rsearch_timeout_seconds: float email_enabled: bool email_timeout_seconds: float 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=_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)), max_response_chars=int( os.environ.get("DEVII_MAX_RESPONSE", DEFAULT_MAX_RESPONSE_CHARS) ), max_tool_iterations=int( os.environ.get("DEVII_MAX_TOOL_ITERATIONS", DEFAULT_MAX_TOOL_ITERATIONS) ), log_level=os.environ.get("DEVII_LOG_LEVEL", "WARNING").upper(), log_path=os.environ.get("DEVII_LOG_PATH", "devii.log"), tasks_db_path=os.environ.get("DEVII_TASKS_DB", str(DEVII_TASKS_DB)), scheduler_tick_seconds=float(os.environ.get("DEVII_SCHEDULER_TICK", "1.0")), lessons_db_path=os.environ.get("DEVII_LESSONS_DB", str(DEVII_LESSONS_DB)), delegate_max_iterations=int( os.environ.get( "DEVII_DELEGATE_MAX_ITERATIONS", DEFAULT_DELEGATE_MAX_ITERATIONS ) ), context_compact_threshold=int( os.environ.get( "DEVII_CONTEXT_COMPACT_THRESHOLD", DEFAULT_CONTEXT_COMPACT_THRESHOLD ) ), context_keep_tail=int( os.environ.get("DEVII_CONTEXT_KEEP_TAIL", DEFAULT_CONTEXT_KEEP_TAIL) ), recall_top_k=int(os.environ.get("DEVII_RECALL_TOP_K", DEFAULT_RECALL_TOP_K)), plan_required=os.environ.get("DEVII_PLAN_REQUIRED", "1").lower() not in ("0", "false", "no"), verify_required=os.environ.get("DEVII_VERIFY_REQUIRED", "1").lower() not in ("0", "false", "no"), platform_api_key=os.environ.get("DEVII_PLATFORM_API_KEY", ""), login_email=os.environ.get("DEVII_LOGIN_EMAIL", ""), login_password=os.environ.get("DEVII_LOGIN_PASSWORD", ""), fetch_max_chars=int( os.environ.get("DEVII_FETCH_MAX_CHARS", DEFAULT_FETCH_MAX_CHARS) ), fetch_timeout_seconds=float( os.environ.get("DEVII_FETCH_TIMEOUT", DEFAULT_FETCH_TIMEOUT_SECONDS) ), fetch_max_bytes=int( os.environ.get("DEVII_FETCH_MAX_BYTES", DEFAULT_FETCH_MAX_BYTES) ), fetch_allow_private=os.environ.get("DEVII_FETCH_ALLOW_PRIVATE", "0").lower() in ("1", "true", "yes", "on"), allow_eval=os.environ.get("DEVII_ALLOW_EVAL", "1").lower() in ("1", "true", "yes", "on"), browser_control=os.environ.get("DEVII_BROWSER_CONTROL", "1").lower() in ("1", "true", "yes", "on"), rsearch_enabled=os.environ.get("DEVII_RSEARCH_ENABLED", "1").lower() in ("1", "true", "yes", "on"), rsearch_url=os.environ.get("DEVII_RSEARCH_URL", DEFAULT_RSEARCH_URL).rstrip( "/" ), rsearch_timeout_seconds=float( os.environ.get("DEVII_RSEARCH_TIMEOUT", DEFAULT_RSEARCH_TIMEOUT_SECONDS) ), email_enabled=os.environ.get("DEVII_EMAIL_ENABLED", "1").lower() in ("1", "true", "yes", "on"), email_timeout_seconds=float( os.environ.get("DEVII_EMAIL_TIMEOUT", DEFAULT_EMAIL_TIMEOUT_SECONDS) ), daily_limit_usd=0.0, ) FIELD_AI_URL = "devii_ai_url" FIELD_AI_MODEL = "devii_ai_model" FIELD_AI_KEY = "devii_ai_key" FIELD_PLAN_REQUIRED = "devii_plan_required" 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" FIELD_PRICE_OUTPUT = "devii_price_output" FIELD_ALLOW_EVAL = "devii_allow_eval" FIELD_INTERACTIONS_DEFAULT = "devii_interactions_default" FIELD_BROWSER_CONTROL = "devii_browser_control" FIELD_TIMEOUT = "devii_timeout" FIELD_FETCH_TIMEOUT = "devii_fetch_timeout" FIELD_RSEARCH_ENABLED = "devii_rsearch_enabled" FIELD_RSEARCH_URL = "devii_rsearch_url" FIELD_RSEARCH_TIMEOUT = "devii_rsearch_timeout" FIELD_EMAIL_ENABLED = "devii_email_enabled" FIELD_EMAIL_TIMEOUT = "devii_email_timeout" LESSONS_DB_PATH = os.environ.get("DEVII_LESSONS_DB", str(DEVII_LESSONS_DB)) 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( ai_url=config[FIELD_AI_URL] or DEFAULT_AI_URL, ai_key=ai_key, ai_model=config[FIELD_AI_MODEL] or DEFAULT_AI_MODEL, base_url=(base_url or DEFAULT_BASE_URL).rstrip("/"), timeout_seconds=float(config.get(FIELD_TIMEOUT) or DEFAULT_TIMEOUT_SECONDS), max_response_chars=DEFAULT_MAX_RESPONSE_CHARS, max_tool_iterations=int(config[FIELD_MAX_ITERATIONS]), log_level="INFO", log_path="devii.log", tasks_db_path="", scheduler_tick_seconds=1.0, lessons_db_path=LESSONS_DB_PATH, delegate_max_iterations=DEFAULT_DELEGATE_MAX_ITERATIONS, context_compact_threshold=DEFAULT_CONTEXT_COMPACT_THRESHOLD, context_keep_tail=DEFAULT_CONTEXT_KEEP_TAIL, recall_top_k=DEFAULT_RECALL_TOP_K, plan_required=bool(config[FIELD_PLAN_REQUIRED]), verify_required=bool(config[FIELD_VERIFY_REQUIRED]), platform_api_key=api_key, login_email="", login_password="", fetch_max_chars=DEFAULT_FETCH_MAX_CHARS, fetch_timeout_seconds=float( config.get(FIELD_FETCH_TIMEOUT) or DEFAULT_FETCH_TIMEOUT_SECONDS ), fetch_max_bytes=DEFAULT_FETCH_MAX_BYTES, fetch_allow_private=False, allow_eval=bool(config.get(FIELD_ALLOW_EVAL, True)), browser_control=bool(config.get(FIELD_BROWSER_CONTROL, True)), 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 ), email_enabled=bool(config.get(FIELD_EMAIL_ENABLED, True)), email_timeout_seconds=float( config.get(FIELD_EMAIL_TIMEOUT) or DEFAULT_EMAIL_TIMEOUT_SECONDS ), daily_limit_usd=effective_daily_limit(config, owner_kind, is_admin), )