Files
FusionAGI/fusionagi/api/dependencies.py
defiQUG c052b07662
Some checks failed
Tests / test (3.10) (push) Has been cancelled
Tests / test (3.11) (push) Has been cancelled
Tests / test (3.12) (push) Has been cancelled
Tests / lint (push) Has been cancelled
Tests / docker (push) Has been cancelled
Initial commit: add .gitignore and README
2026-02-09 21:51:42 -08:00

184 lines
6.2 KiB
Python

"""API dependencies: orchestrator, session store, guardrails."""
import os
from dataclasses import dataclass
from typing import Any
from fusionagi import Orchestrator, EventBus, StateManager
from fusionagi.agents import WitnessAgent
from fusionagi.agents.heads import create_all_content_heads
from fusionagi.adapters.base import LLMAdapter
from fusionagi.adapters.native_adapter import NativeAdapter
from fusionagi.schemas.head import HeadId
from fusionagi.governance import SafetyPipeline, AuditLog
def _get_reasoning_provider() -> Any:
"""Return reasoning provider based on SUPER_BIG_BRAIN_ENABLED env."""
if os.environ.get("SUPER_BIG_BRAIN_ENABLED", "false").lower() in ("true", "1", "yes"):
from fusionagi.core.super_big_brain import SuperBigBrainReasoningProvider
from fusionagi.memory import SemanticGraphMemory
return SuperBigBrainReasoningProvider(semantic_graph=SemanticGraphMemory())
return None
# App state populated by lifespan or lazy init
_app_state: dict[str, Any] = {}
_default_adapter: Any = None
def set_default_adapter(adapter: Any) -> None:
global _default_adapter
_default_adapter = adapter
def default_orchestrator(adapter: LLMAdapter | None = None) -> tuple[Orchestrator, Any]:
"""Create default Orchestrator with Dvādaśa heads and Witness registered.
When adapter is None, uses native reasoning throughout: heads use NativeReasoningProvider,
Witness uses NativeAdapter for synthesis. No external LLM calls.
"""
bus = EventBus()
state = StateManager()
orch = Orchestrator(event_bus=bus, state_manager=state)
# Heads: use native or Super Big Brain reasoning when no adapter
reasoning_provider = _get_reasoning_provider()
heads = create_all_content_heads(
adapter=adapter,
reasoning_provider=reasoning_provider,
use_native_reasoning=reasoning_provider is None,
)
for hid, agent in heads.items():
orch.register_agent(hid.value, agent)
# Witness: use NativeAdapter when no adapter for native synthesis
witness_adapter = adapter if adapter is not None else NativeAdapter()
orch.register_agent(HeadId.WITNESS.value, WitnessAgent(adapter=witness_adapter))
return orch, bus
class SessionStore:
"""In-memory session store for API sessions."""
def __init__(self) -> None:
self._sessions: dict[str, dict[str, Any]] = {}
def create(self, session_id: str, user_id: str | None = None) -> dict[str, Any]:
sess = {"session_id": session_id, "user_id": user_id, "history": []}
self._sessions[session_id] = sess
return sess
def get(self, session_id: str) -> dict[str, Any] | None:
return self._sessions.get(session_id)
def append_history(self, session_id: str, entry: dict[str, Any]) -> None:
sess = self._sessions.get(session_id)
if sess:
sess.setdefault("history", []).append(entry)
def get_orchestrator() -> Any:
return _app_state.get("orchestrator")
def get_event_bus() -> Any:
return _app_state.get("event_bus")
def get_session_store() -> SessionStore | None:
return _app_state.get("session_store")
def get_safety_pipeline() -> Any:
return _app_state.get("safety_pipeline")
def get_telemetry_tracer() -> Any:
return _app_state.get("telemetry_tracer")
def set_app_state(orchestrator: Any, event_bus: Any, session_store: SessionStore) -> None:
_app_state["orchestrator"] = orchestrator
_app_state["event_bus"] = event_bus
_app_state["session_store"] = session_store
if "safety_pipeline" not in _app_state:
_app_state["safety_pipeline"] = SafetyPipeline(audit_log=AuditLog())
try:
from fusionagi.telemetry import TelemetryTracer, set_tracer
tracer = TelemetryTracer()
tracer.subscribe(event_bus)
set_tracer(tracer)
_app_state["telemetry_tracer"] = tracer
except Exception:
pass
def ensure_initialized(adapter: Any = None) -> None:
"""Lazy init: ensure orchestrator and store exist (for TestClient)."""
if _app_state.get("orchestrator") is not None:
return
adj = adapter if adapter is not None else _default_adapter
orch, bus = default_orchestrator(adj)
set_app_state(orch, bus, SessionStore())
@dataclass
class OpenAIBridgeConfig:
"""Configuration for OpenAI-compatible API bridge."""
model_id: str
auth_enabled: bool
api_key: str | None
timeout_per_head: float
@classmethod
def from_env(cls) -> "OpenAIBridgeConfig":
"""Load config from environment variables."""
auth = os.environ.get("OPENAI_BRIDGE_AUTH", "disabled").lower()
auth_enabled = auth not in ("disabled", "false", "0", "no")
return cls(
model_id=os.environ.get("OPENAI_BRIDGE_MODEL_ID", "fusionagi-dvadasa"),
auth_enabled=auth_enabled,
api_key=os.environ.get("OPENAI_BRIDGE_API_KEY") if auth_enabled else None,
timeout_per_head=float(os.environ.get("OPENAI_BRIDGE_TIMEOUT_PER_HEAD", "60")),
)
def get_openai_bridge_config() -> OpenAIBridgeConfig:
"""Return OpenAI bridge config from app state or env."""
cfg = _app_state.get("openai_bridge_config")
if cfg is not None:
return cfg
return OpenAIBridgeConfig.from_env()
def verify_openai_bridge_auth(authorization: str | None) -> None:
"""
Verify OpenAI bridge auth. Raises HTTPException(401) if auth enabled and invalid.
Call from route dependencies.
"""
try:
from fastapi import HTTPException
cfg = get_openai_bridge_config()
if not cfg.auth_enabled:
return
if not cfg.api_key:
return # Auth enabled but no key configured: allow (misconfig)
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(
status_code=401,
detail={"error": {"message": "Missing or invalid Authorization header", "type": "authentication_error"}},
)
token = authorization[7:].strip()
if token != cfg.api_key:
raise HTTPException(
status_code=401,
detail={"error": {"message": "Invalid API key", "type": "authentication_error"}},
)
except HTTPException:
raise
except Exception:
pass