Initial commit: Agentic OS troubleshooting platform
Self-hosted, Docker-based agentic troubleshooting platform: FastAPI backend + LangGraph agent, Next.js UI, tiered LLM routing (local Ollama -> Gemini -> DeepSeek -> OpenRouter), MCP server manager, encrypted device credentials, RBAC, audit log, project-memory + Obsidian integrations, and editable troubleshooting decision rules tuned for the GeneseasX vessel stack. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
115
backend/app/config.py
Normal file
115
backend/app/config.py
Normal file
@@ -0,0 +1,115 @@
|
||||
"""Application configuration, loaded from environment variables."""
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import lru_cache
|
||||
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
model_config = SettingsConfigDict(env_file=".env", extra="ignore", case_sensitive=False)
|
||||
|
||||
# Core security
|
||||
secret_key: str = "dev-insecure-secret-change-me"
|
||||
credential_encryption_key: str = ""
|
||||
access_token_expire_minutes: int = 60 * 12
|
||||
algorithm: str = "HS256"
|
||||
|
||||
first_admin_email: str = "admin@agentic.local"
|
||||
first_admin_password: str = "changeme"
|
||||
|
||||
# Database / cache
|
||||
database_url: str = "postgresql+asyncpg://agentic:agentic@postgres:5432/agentic_os"
|
||||
redis_url: str = "redis://redis:6379/0"
|
||||
|
||||
# CORS: comma-separated allowed browser origins (no wildcard with credentials).
|
||||
cors_allow_origins: str = "http://localhost:3000"
|
||||
|
||||
# LLM routing
|
||||
litellm_base_url: str = "http://litellm:4000"
|
||||
litellm_master_key: str = "sk-litellm-master-change-me"
|
||||
ollama_base_url: str = "http://host.docker.internal:11434"
|
||||
local_triage_model: str = "qwen2.5:7b-instruct"
|
||||
cloud_reasoning_model: str = "openrouter/deepseek/deepseek-chat"
|
||||
|
||||
# Tiered LLM routing (local → gemini → deepseek → openrouter)
|
||||
llm_local_model: str = "qwen2.5:7b-instruct"
|
||||
llm_economy_gemini: str = "gemini-2.5-flash"
|
||||
llm_economy_deepseek: str = "deepseek-chat"
|
||||
llm_economy_openrouter: str = "deepseek/deepseek-chat"
|
||||
llm_premium_gemini: str = "gemini-2.5-pro"
|
||||
llm_premium_deepseek: str = "deepseek-reasoner"
|
||||
llm_premium_openrouter: str = "anthropic/claude-3.5-sonnet"
|
||||
llm_auto_escalate: bool = True
|
||||
llm_max_tier: str = "premium"
|
||||
llm_cloud_provider_order: str = "gemini,deepseek,openrouter"
|
||||
|
||||
# Cloud API keys
|
||||
gemini_api_key: str = ""
|
||||
openrouter_api_key: str = ""
|
||||
openrouter_provisioning_key: str = ""
|
||||
deepseek_api_key: str = ""
|
||||
|
||||
# Balance polling
|
||||
balance_poll_interval_seconds: int = 900
|
||||
min_balance_usd: float = 1.0
|
||||
|
||||
# Gitea / MCP repos
|
||||
gitea_base_url: str = "http://10.77.30.250:3000"
|
||||
gitea_username: str = "nearxos"
|
||||
gitea_token: str = ""
|
||||
mcp_repos: str = "ssh-generic-mcp,fortigate-mcp,fortiswitch-mcp,pfsense-mcp,proxmox-mcp,asterisk-mcp"
|
||||
|
||||
# Obsidian
|
||||
obsidian_vault_repo: str = "homelab-vault"
|
||||
obsidian_tasks_folder: str = "agentic-os-tasks"
|
||||
obsidian_auto_push: bool = True
|
||||
|
||||
# Project memory MCP
|
||||
memory_mcp_url: str = "http://10.77.30.184:8787/mcp"
|
||||
memory_mcp_token: str = ""
|
||||
memory_task_project_id: str = "agentic-os-task"
|
||||
memory_search_projects: str = "agentic-os-task,agentic-os,global"
|
||||
obsidian_search_paths: str = "agentic-os-tasks,Meta/Agent-Memory"
|
||||
|
||||
# Troubleshooting decision rules (YAML, editable via Admin → Rules)
|
||||
troubleshooting_rules_path: str = "/app/rules/troubleshooting.yaml"
|
||||
|
||||
# Runtime paths (inside container)
|
||||
runtime_dir: str = "/runtime"
|
||||
|
||||
# MCP self-development: agent patches MCP source on tool failure / missing capability
|
||||
mcp_development_enabled: bool = True
|
||||
mcp_dev_auto_push: bool = False # when false, patch applies + reload; git push needs approval
|
||||
mcp_dev_model_tier: str = "premium"
|
||||
|
||||
# Worker: max tasks executed in parallel (MCP sessions are shared — use 1 for same-vessel safety)
|
||||
worker_max_concurrent_tasks: int = 1
|
||||
|
||||
@property
|
||||
def cors_origin_list(self) -> list[str]:
|
||||
return [o.strip() for o in self.cors_allow_origins.split(",") if o.strip()]
|
||||
|
||||
@property
|
||||
def mcp_repo_list(self) -> list[str]:
|
||||
return [r.strip() for r in self.mcp_repos.split(",") if r.strip()]
|
||||
|
||||
def gitea_clone_url(self, repo: str) -> str:
|
||||
"""Authenticated clone URL for a Gitea repo (preserves http/https from base URL)."""
|
||||
base = self.gitea_base_url.rstrip("/")
|
||||
if self.gitea_token:
|
||||
from urllib.parse import urlparse
|
||||
|
||||
parsed = urlparse(base if "://" in base else f"http://{base}")
|
||||
scheme = parsed.scheme or "http"
|
||||
host = parsed.netloc or parsed.path
|
||||
return f"{scheme}://{self.gitea_username}:{self.gitea_token}@{host}/{self.gitea_username}/{repo}.git"
|
||||
return f"{base}/{self.gitea_username}/{repo}.git"
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_settings() -> Settings:
|
||||
return Settings()
|
||||
|
||||
|
||||
settings = get_settings()
|
||||
Reference in New Issue
Block a user