02 · Bridge Core Layer
Second installment of the architecture deep-dive: FastAPI bridge route organization, startup side-effect ordering, SSE event pump, DI container, and concurrency control. Corresponds to
ARCHITECTURE.md§3.
§3.1 Technology Choices
| Dimension | Choice | Rationale |
|---|---|---|
| Web framework | FastAPI | pydantic type safety, automatic OpenAPI, ASGI with HTTP/2 + SSE support |
| Production server | hypercorn | HTTP/2 multiplexing solves Chromium's 6-socket-per-host limit |
| Fallback server | uvicorn | Enabled when FAMILIARS_HTTP_INSECURE=1; plain HTTP |
| Certificate | Self-signed workspace/.bridge.crt | Auto-generated by src/bridge/certs.py |
| Listen address | 127.0.0.1:18711 (default) | Not exposed externally; the port file workspace/.bridge.port is the actual signal |
§3.2 Entry Point & Startup (src/bridge/run.py)
# Top of src/bridge/run.py
_REPO_ROOT = str(Path(__file__).resolve().parents[2])
os.environ.setdefault("WORKSPACE_ROUTE", _REPO_ROOT)Startup sequence (python -m src.bridge.run):
- Pin
WORKSPACE_ROUTEenvironment variable to the repo root import src.bridge.app(triggers app.py module-level side effects, see §3.4)- hypercorn.serve starts the event loop + worker thread pool
Daemon mode (FAMILIARS_DAEMON=1):
- pidfile lock at
workspace/.bridge.pid - stdout/stderr redirected to
workspace/.bridge.log(rotating) - SIGTERM / SIGINT → graceful shutdown of APScheduler
§3.3 Route Organization
There are 60 route files under src/bridge/routes/ (as of the current version). Each file exports an APIRouter:
| Route file | Primary endpoint prefix | Responsibility |
|---|---|---|
agents.py | /api/agents | Agent CRUD, listing, identity, persona evolution |
chat.py | /api/chat | SSE chat stream /api/chat/stream, synchronous send |
tree.py | /api/tree | Dialogue tree CRUD, node operations |
settings.py | /api/settings | Global / per-agent settings |
cron.py | /api/cron | Cron job management |
schedule.py | /api/schedule | Schedule CRUD |
todo.py | /api/todo | To-do items |
reminders.py | /api/reminders | Reminders (SSE stream /api/reminders/stream) |
skills.py | /api/skills | Skill listing / invocation |
monitor.py | /api/monitor | Social platform monitoring |
digital_human.py | /api/digital_human | Digital human config / rendering |
asr.py | /api/asr | Speech recognition (faster-whisper) |
auth.py | /api/auth | First-launch registration / login |
usage.py | /api/usage | Usage tracking |
status.py | /api/status | Health check |
reports.py | /api/reports | Report generation / listing |
gift.py | /api/gift | Gifts / redemption codes |
cg.py | /api/cg | CG animation workshop |
agent_workshop.py | /api/agent_workshop | Agent workshop |
skill_forge.py | /api/skill_forge | Skill forging |
skill_proposals.py | /api/skill_proposals | Skill proposals |
skill_workshop.py | /api/skill_workshop | Skill workshop |
feishu.py | /api/feishu | Feishu integration |
background.py | /api/background | Background tasks |
user.py | /api/user | User profile |
user_profile.py | /api/user/profile | User persona |
agent_recipes.py | /api/agent_recipes | Recipe management |
broker.py | /api/broker | Broker layer (Hub / matching / ledger) |
broker_workshop.py | /api/broker_workshop | Broker workshop (install / manage) |
broker_app.py | /api/broker_app | Broker app (iframe UI) |
broker_assets.py | /api/broker_assets | Broker static assets |
broker_dev.py | /api/broker_dev | Broker dev tools |
flow_templates.py | /api/flow-templates | Flow template CRUD + custom field renderers |
flows.py | /api/flows | Flow instances (list / get / trace / cancel) |
flow_dev.py | /api/flow_dev | Flow dev tools |
broadcast.py | /api/broadcast | Broadcast messages |
memory_status.py | /api/memory_status | Memory size / hit stats |
memory.py | /api/memory | Memory operations |
onboarding.py | /api/onboarding | Onboarding wizard |
phone_call.py | /api/phone_call | Voice calls |
quick_ask.py | /api/quick_ask | QuickAsk popup |
quick_inputs.py | /api/quick_inputs | Quick inputs |
draft.py | /api/draft | Drafts |
feedback.py | /api/feedback | Feedback (including proactive ratings) |
demo.py | /api/demo | Public demo |
desktop_notify.py | /api/desktop_notify | Desktop notifications |
dev.py | /api/dev | Dev tools |
recipe_dev.py | /api/recipe_dev | Recipe dev (compile / dry-run / e2e) |
responses.py | /api/responses | Approval responses |
setup.py | /api/setup | Setup wizard |
tool_skills.py | /api/tool_skills | Tool skills |
tools_catalog.py | /api/tools_catalog | Tools catalog |
tools_source.py | /api/tools_source | Tool source code |
vram.py | /api/vram | VRAM status |
agent_activity.py | /api/agent_activity | Agent activity |
agent_assets.py | /api/agent_assets | Agent assets |
guild.py | /api/guild | Guild system |
learn.py | /api/learn | Learning module |
marketplace_artifacts.py | /api/marketplace_artifacts | Marketplace artifacts |
xun_da.py | /api/xun_da | XunDa module |
Route registration (src/bridge/app.py):
from src.bridge.routes import (
agent_activity, agent_recipes, agent_workshop, agents, agent_assets,
asr, auth, background, broadcast, broker, broker_app, broker_assets,
broker_dev, broker_workshop, cg, chat, cron, demo, desktop_notify,
dev, digital_human, draft, feedback, feishu, flow_dev, flow_templates,
flows, gift, guild, learn, marketplace_artifacts, memory, memory_status,
monitor, onboarding, phone_call, quick_ask, quick_inputs, recipe_dev,
reminders, reports, responses, schedule, settings, setup, skill_forge,
skill_proposals, skill_workshop, skills, status, todo, tool_skills,
tools_catalog, tools_source, tree, usage, user, user_profile, vram, xun_da,
)§3.4 Startup Side-Effect Ordering (Critical!)
src/bridge/app.py executes module-level (at import time) side effects in a strict order:
Step 1: Pin WORKSPACE_ROUTE
os.environ.setdefault("WORKSPACE_ROUTE", str(_Path(__file__).resolve().parents[2]))Step 2: CUDA Main-Thread Pin
try:
import torch as _torch
if _torch.cuda.is_available():
_torch.cuda.init()
_ = _torch.zeros(1, device="cuda")
_torch.cuda.synchronize()
except Exception:
passWhy: Windows WDDM does not allow first-touch CUDA from a worker thread. If torch.zeros runs on an asyncio worker thread, the entire process crashes with 0xC0000005 (access violation).
Step 3: Path Alignment
from src.base.paths_align import align_all_paths as _align_all_paths
_align_summary = _align_all_paths()Why: When DialogueManager._load_existing_dialogues reads dialogue files, the absolute paths stored in them must be correct. If the user moved the repo directory, align_all_paths automatically rewrites dialogue.json + turn_*/plan.json + step_*.json.
Step 4: Import + Mount Routes
All src.bridge.routes.* modules are imported and mounted onto the FastAPI app here.
Step 5: CORS + Token Middleware
app.add_middleware(CORSMiddleware, allow_origins=[...])
# BridgeTokenMiddleware: checks X-Bridge-Token or ?token=
# Exempt list: see _TOKEN_EXEMPT_PREFIXESStep 6: on_event("startup") Hooks
In registration order:
- broker_workshop bootstrap — load installed broker apps
- anyio thread limiter bump — prevent asyncio worker pool saturation
- Pool watchdog — monitor + auto-scale
- Browser lifecycle — Chrome CDP connection management
Step 7: Module-Level Finalization
- Kill orphan GPU processes
- Prewarm embedder / rembg / rewriter / most-recent agent's digital human
- autorun check + drift detection
- Rehydrate monitors / cron / skill scheduler
§3.5 DI Container (src/bridge/deps.py)
# Global singleton caches
_dm_cache: dict[int, object] = {} # agent_id → DialogueManager
_daily: Optional[DailyTools] = None
_manager: Optional[object] = None # AssistantManager singleton
_comm_hub: Optional[CommHub] = None
_broker_hub: Optional[object] = NoneProvider functions:
| Function | Returns | Description |
|---|---|---|
get_auth() | AuthManager | Authentication manager |
get_usage() | UsageTracker | Usage tracker |
get_daily() | DailyTools | Schedule / todo / reminder tools |
get_assistant_manager() | AssistantManager | Agent management singleton |
get_dialogue_manager(agent_id) | DialogueManager | Cached per agent |
peek_dialogue_manager(agent_id) | DialogueManager | None | Does not trigger creation |
drop_dialogue_manager(agent_id) | None | Remove from cache |
release_dialogue_manager(agent_id) | bool | Remove + release memory |
get_comm_hub() | CommHub | Communication hub |
get_broker_hub() | BrokerHub | Broker layer (lazy-loaded) |
Agent 0 (manager) special handling:
if agent_id == 0:
agent_path = os.path.join(workspace, "workspace", "manager")
mem_id = "manager"
else:
agent_path = resolve_agent_dir(agent_id)
mem_id = f"agent_{agent_id}"§3.6 SSE Event Pump
/api/chat/stream
# chat.py endpoint
@router.post("/api/chat/stream")
async def chat_stream(request: ChatRequest):
async def event_generator():
async for event in conversation_session.stream(...):
yield f"data: {json.dumps(event)}\n\n"
return StreamingResponse(event_generator(), media_type="text/event-stream")SSE event types:
token— streaming tokentool_call— tool invocation startedtool_result— tool resultemit_event— custom event (audio / video / work log)done— completionerror— error
Concurrency Control
- One async task per dialogue
- HTTP/2 multiplexing → client can run multiple streams in parallel
- Events are pushed to the renderer via asyncio.Queue
§3.7 Request Context Isolation
# src/bridge/app.py
def _fresh_request_context() -> "contextvars.Context":
ctx = contextvars.copy_context()
def _reset_and_stamp():
for var in (trace_id_var, agent_id_var, dialogue_id_var,
owner_label_var, _current_agent, _current_dialogue,
_current_intense, _current_turbo):
var.set(None)
bind_trace(trace_id=new_trace_id())
bind_actor(agent_id=0, dialogue_id=0)
ctx.run(_reset_and_stamp)
return ctxProblem: FastAPI's BaseHTTPMiddleware runs on the request coroutine; Starlette reuses worker threads without resetting contextvars between them. The previous request's agent_id / trace_id / recipe_copilot state can leak into the next request.
Solution: copy_context() snapshots the current context → resets all observability / security variables → stamps fresh values → the handler executes inside ctx.run(...).
Next: 03 - Agent Pipeline →
