052591281d
- Complete agent system with planning and execution - Bridge API with FastAPI server - WhatsApp-style mobile dashboard - Enhanced web search with query improvement - Security guards and policy checks - Comprehensive documentation
118 lines
4.2 KiB
Python
118 lines
4.2 KiB
Python
"""FastAPI route definitions for Bridge V1.2 with UTF-8 support."""
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from fastapi import APIRouter, HTTPException, Request
|
|
|
|
import adapters as adapter_registry
|
|
from core.run_manager import RunManager
|
|
from models.schemas import CreateRunRequest, HealthResponse, RunResponse, RunStatus
|
|
from policy.guard import check as policy_check
|
|
|
|
def safe_print(text):
|
|
"""Print with UTF-8 encoding fallback for Windows CP1252."""
|
|
try:
|
|
print(text)
|
|
except UnicodeEncodeError:
|
|
sys.stdout.buffer.write(text.encode('utf-8', errors='replace'))
|
|
sys.stdout.buffer.write(b'\n')
|
|
|
|
RUNS_DIR = Path(__file__).resolve().parents[1] / "runs"
|
|
_manager = RunManager(RUNS_DIR)
|
|
|
|
router = APIRouter()
|
|
|
|
@router.get("/health", response_model=HealthResponse)
|
|
def health():
|
|
"""Returns server status and per-adapter mode (real/stub)."""
|
|
import os
|
|
from adapters import openai_adapter
|
|
from controller.decision_model import DECISION_MODE
|
|
|
|
print(f"[ENCODING DEBUG] sys.getdefaultencoding() = {sys.getdefaultencoding()}")
|
|
print(f"[ENCODING DEBUG] sys.getfilesystemencoding() = {sys.getfilesystemencoding()}")
|
|
print(f"[ENCODING DEBUG] LANG = {os.environ.get('LANG', 'NOT SET')}")
|
|
print(f"[ENCODING DEBUG] PYTHONIOENCODING = {os.environ.get('PYTHONIOENCODING', 'NOT SET')}")
|
|
|
|
return HealthResponse(
|
|
status="ok",
|
|
adapters=adapter_registry.available(),
|
|
openai_mode=openai_adapter.mode(),
|
|
decision_backend=DECISION_MODE,
|
|
ollama_model=os.environ.get("OLLAMA_MODEL", "qwen2.5:7b"),
|
|
)
|
|
|
|
|
|
@router.post("/runs", response_model=RunResponse, status_code=201)
|
|
def create_run(body: CreateRunRequest):
|
|
"""One-shot run: create -> instruct -> execute -> persist -> done|forced.
|
|
|
|
The response is the final completed state — status is never 'running'
|
|
when this endpoint returns.
|
|
|
|
FORCE AGENT ROUTING: All requests are forced to use 'agent' adapter.
|
|
No fallback, no alternative adapters, no direct LLM calls.
|
|
"""
|
|
safe_print("=== RUNS ENDPOINT CALLED ===")
|
|
safe_print("=== FORCE AGENT ROUTING ENABLED ===")
|
|
|
|
# Force agent adapter - ignore request body adapter
|
|
forced_adapter = "agent"
|
|
safe_print(f"=== USING ADAPTER: {forced_adapter} ===")
|
|
|
|
# DEBUG: Log encoding details
|
|
safe_print(f"[ENCODING DEBUG] Raw goal: {body.goal}")
|
|
safe_print(f"[ENCODING DEBUG] Repr goal: {repr(body.goal)}")
|
|
safe_print(f"[ENCODING DEBUG] Goal type: {type(body.goal)}")
|
|
safe_print(f"[ENCODING DEBUG] Goal bytes: {body.goal.encode('utf-8')}")
|
|
|
|
# DEBUG: Log adapter info
|
|
safe_print(f"[FORCE ROUTING] Requested adapter: {body.adapter}")
|
|
safe_print(f"[FORCE ROUTING] Forced adapter: {forced_adapter}")
|
|
safe_print(f"[FORCE ROUTING] No fallback, no alternatives - agent only")
|
|
|
|
adapter_mod = adapter_registry.get(forced_adapter)
|
|
if adapter_mod is None:
|
|
safe_print("=== RETURNING RESPONSE (ERROR) ===")
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail=f"Agent adapter not available - this should never happen",
|
|
)
|
|
|
|
try:
|
|
state = _manager.execute_run(
|
|
goal=body.goal,
|
|
adapter_name=forced_adapter,
|
|
adapter_fn=adapter_mod.execute, # type: ignore[attr-defined]
|
|
guard_fn=policy_check,
|
|
run_id=body.run_id,
|
|
)
|
|
except ValueError as exc:
|
|
safe_print("=== RETURNING RESPONSE (ERROR) ===")
|
|
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
|
|
|
safe_print("=== RETURNING RESPONSE (SUCCESS) ===")
|
|
return RunResponse(**state)
|
|
|
|
|
|
@router.get("/runs/{run_id}", response_model=RunResponse)
|
|
def get_run(run_id: str):
|
|
"""Retrieve persisted run state."""
|
|
state = _manager.get_run(run_id)
|
|
if state is None:
|
|
raise HTTPException(status_code=404, detail=f"run '{run_id}' not found")
|
|
return RunResponse(**state)
|
|
|
|
|
|
@router.post("/runs/{run_id}/cancel")
|
|
def cancel_run(run_id: str):
|
|
"""Cancel a running or pending run."""
|
|
state = _manager.get_run(run_id)
|
|
if state is None:
|
|
raise HTTPException(status_code=404, detail=f"run '{run_id}' not found")
|
|
|
|
_manager.cancel_run(run_id)
|
|
return {"message": f"run '{run_id}' cancellation requested"}
|