""" architecture.py — Free distributed no-PC architecture profile The selected zero-cost architecture is deliberately split into control, execution, tools and memory layers: - iPhone/mobile browser: control surface only. - GitHub Actions: event-triggered Agent Kernel and lightweight execution. - Hugging Face Spaces: backend tools/API runtime, not a heavy persistent VM. - LiteLLM/OpenAI-compatible provider routing: free remote model pool. - GitHub repository: primary memory, code, task history and run reports. This keeps the system usable without Replit, without a local PC and without paid infrastructure while making the real limits explicit. """ from __future__ import annotations import os from dataclasses import dataclass, asdict from typing import Any @dataclass(frozen=True) class Capability: name: str status: str reason: str FREE_REMOTE_PLAN = { "name": "github_actions_hf_spaces_free_agent", "label": "GitHub Actions + HF Spaces Free Agent Kernel", "mode": "total_free_no_local_pc", "control_surface": "iPhone via GitHub Actions workflow_dispatch or web frontend", "primary_runtime": "GitHub Actions for agent runs; Hugging Face Spaces for backend tools/API", "tool_runtime": "Hugging Face Spaces Docker free tier", "memory_primary": "GitHub repository under .agent/ plus code/history", "memory_secondary": "SQLite/local backend memory when the Space is available", "principle": "Distributed backend-first agent kernel with safe-by-default GitHub memory, provider fallback and mobile review.", "model_priority": ["openrouter", "gemini", "groq", "huggingface", "openai_compatible"], "execution": "GitHub Actions for bounded jobs; backend allowlisted tools only; browser sandbox disabled by default", } def _env_enabled(name: str, default: bool = False) -> bool: raw = os.getenv(name) if raw is None: return default return raw.strip().lower() in {"1", "true", "yes", "on"} def get_architecture_profile() -> dict[str, Any]: """Return runtime capabilities for the free no-PC deployment plan.""" providers = { "openrouter": bool(os.getenv("OPENROUTER_API_KEY")), "gemini": bool(os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")), "groq": bool(os.getenv("GROQ_API_KEY")), "huggingface": bool(os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACE_API_KEY") or os.getenv("HUGGINGFACE_TOKEN")), "openai_compatible": bool(os.getenv("OPENAI_API_KEY")), } github_actions_configured = bool(os.getenv("GITHUB_ACTIONS") or os.getenv("GITHUB_REPOSITORY")) github_token_configured = bool(os.getenv("GITHUB_TOKEN") or os.getenv("GH_TOKEN")) capabilities = [ Capability("mobile_control", "enabled", "GitHub Actions workflow_dispatch and the frontend can be used from iPhone."), Capability("github_actions_agent_kernel", "enabled", "Bounded agent runs execute in GitHub Actions and persist reports under .agent/."), Capability("github_memory", "enabled", "State, task history and run reports are committed to the repository for durable zero-cost memory."), Capability("hf_spaces_tools", "enabled", "The FastAPI backend remains the lightweight tools/API runtime for the free Space."), Capability("litellm_routing", "enabled", "OpenAI-compatible provider order supports OpenRouter, Gemini, Groq, Hugging Face and optional OpenAI-compatible endpoints."), Capability("sqlite_memory", "secondary", "Backend SQLite/local memory remains useful but is not the primary no-PC source of truth."), Capability("qdrant", "optional", "Use only if an external free Qdrant endpoint is configured; not required for the baseline."), Capability("openhands", "external_optional", "Full OpenHands remains too heavy for the free baseline and should not run inside the free Space."), Capability("docker_sandbox", "disabled_on_free_space", "Nested Docker is intentionally disabled on the free plan."), Capability("browser_sandbox", "disabled_by_default", "Client-side code execution is not a real isolated workspace and is not part of the main path."), ] return { "plan": FREE_REMOTE_PLAN, "providers": providers, "capabilities": [asdict(c) for c in capabilities], "limits": { "requires_local_pc": False, "requires_paid_vm": False, "runs_heavy_local_models": False, "supports_nested_docker": False, "long_running_24_7_agent": False, "bounded_actions_runtime": True, }, "github_actions": { "workflow": ".github/workflows/agent-kernel.yml", "workflow_template": "docs/workflows/agent-kernel.yml", "activation_note": "Copy the template to .github/workflows/agent-kernel.yml with GitHub UI or a token that has workflow permission.", "script": "scripts/agent_kernel.py", "memory_dir": ".agent/", "configured_in_current_runtime": github_actions_configured, "token_available_in_current_runtime": github_token_configured, "trigger_modes": ["workflow_dispatch", "issue_label_agent-run"], }, "feature_flags": { "browser_sandbox_enabled": _env_enabled("VITE_ENABLE_BROWSER_SANDBOX", False), "browser_llm_enabled": _env_enabled("VITE_ENABLE_BROWSER_LLM", False), "qdrant_enabled": bool(os.getenv("QDRANT_URL")), }, }