Skip to content

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 ​

DimensionChoiceRationale
Web frameworkFastAPIpydantic type safety, automatic OpenAPI, ASGI with HTTP/2 + SSE support
Production serverhypercornHTTP/2 multiplexing solves Chromium's 6-socket-per-host limit
Fallback serveruvicornEnabled when FAMILIARS_HTTP_INSECURE=1; plain HTTP
CertificateSelf-signed workspace/.bridge.crtAuto-generated by src/bridge/certs.py
Listen address127.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) ​

python
# 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):

  1. Pin WORKSPACE_ROUTE environment variable to the repo root
  2. import src.bridge.app (triggers app.py module-level side effects, see §3.4)
  3. 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 filePrimary endpoint prefixResponsibility
agents.py/api/agentsAgent CRUD, listing, identity, persona evolution
chat.py/api/chatSSE chat stream /api/chat/stream, synchronous send
tree.py/api/treeDialogue tree CRUD, node operations
settings.py/api/settingsGlobal / per-agent settings
cron.py/api/cronCron job management
schedule.py/api/scheduleSchedule CRUD
todo.py/api/todoTo-do items
reminders.py/api/remindersReminders (SSE stream /api/reminders/stream)
skills.py/api/skillsSkill listing / invocation
monitor.py/api/monitorSocial platform monitoring
digital_human.py/api/digital_humanDigital human config / rendering
asr.py/api/asrSpeech recognition (faster-whisper)
auth.py/api/authFirst-launch registration / login
usage.py/api/usageUsage tracking
status.py/api/statusHealth check
reports.py/api/reportsReport generation / listing
gift.py/api/giftGifts / redemption codes
cg.py/api/cgCG animation workshop
agent_workshop.py/api/agent_workshopAgent workshop
skill_forge.py/api/skill_forgeSkill forging
skill_proposals.py/api/skill_proposalsSkill proposals
skill_workshop.py/api/skill_workshopSkill workshop
feishu.py/api/feishuFeishu integration
background.py/api/backgroundBackground tasks
user.py/api/userUser profile
user_profile.py/api/user/profileUser persona
agent_recipes.py/api/agent_recipesRecipe management
broker.py/api/brokerBroker layer (Hub / matching / ledger)
broker_workshop.py/api/broker_workshopBroker workshop (install / manage)
broker_app.py/api/broker_appBroker app (iframe UI)
broker_assets.py/api/broker_assetsBroker static assets
broker_dev.py/api/broker_devBroker dev tools
flow_templates.py/api/flow-templatesFlow template CRUD + custom field renderers
flows.py/api/flowsFlow instances (list / get / trace / cancel)
flow_dev.py/api/flow_devFlow dev tools
broadcast.py/api/broadcastBroadcast messages
memory_status.py/api/memory_statusMemory size / hit stats
memory.py/api/memoryMemory operations
onboarding.py/api/onboardingOnboarding wizard
phone_call.py/api/phone_callVoice calls
quick_ask.py/api/quick_askQuickAsk popup
quick_inputs.py/api/quick_inputsQuick inputs
draft.py/api/draftDrafts
feedback.py/api/feedbackFeedback (including proactive ratings)
demo.py/api/demoPublic demo
desktop_notify.py/api/desktop_notifyDesktop notifications
dev.py/api/devDev tools
recipe_dev.py/api/recipe_devRecipe dev (compile / dry-run / e2e)
responses.py/api/responsesApproval responses
setup.py/api/setupSetup wizard
tool_skills.py/api/tool_skillsTool skills
tools_catalog.py/api/tools_catalogTools catalog
tools_source.py/api/tools_sourceTool source code
vram.py/api/vramVRAM status
agent_activity.py/api/agent_activityAgent activity
agent_assets.py/api/agent_assetsAgent assets
guild.py/api/guildGuild system
learn.py/api/learnLearning module
marketplace_artifacts.py/api/marketplace_artifactsMarketplace artifacts
xun_da.py/api/xun_daXunDa module

Route registration (src/bridge/app.py):

python
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 ​

python
os.environ.setdefault("WORKSPACE_ROUTE", str(_Path(__file__).resolve().parents[2]))

Step 2: CUDA Main-Thread Pin ​

python
try:
    import torch as _torch
    if _torch.cuda.is_available():
        _torch.cuda.init()
        _ = _torch.zeros(1, device="cuda")
        _torch.cuda.synchronize()
except Exception:
    pass

Why: 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 ​

python
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 ​

python
app.add_middleware(CORSMiddleware, allow_origins=[...])

# BridgeTokenMiddleware: checks X-Bridge-Token or ?token=
# Exempt list: see _TOKEN_EXEMPT_PREFIXES

Step 6: on_event("startup") Hooks ​

In registration order:

  1. broker_workshop bootstrap — load installed broker apps
  2. anyio thread limiter bump — prevent asyncio worker pool saturation
  3. Pool watchdog — monitor + auto-scale
  4. 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) ​

python
# 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] = None

Provider functions:

FunctionReturnsDescription
get_auth()AuthManagerAuthentication manager
get_usage()UsageTrackerUsage tracker
get_daily()DailyToolsSchedule / todo / reminder tools
get_assistant_manager()AssistantManagerAgent management singleton
get_dialogue_manager(agent_id)DialogueManagerCached per agent
peek_dialogue_manager(agent_id)DialogueManager | NoneDoes not trigger creation
drop_dialogue_manager(agent_id)NoneRemove from cache
release_dialogue_manager(agent_id)boolRemove + release memory
get_comm_hub()CommHubCommunication hub
get_broker_hub()BrokerHubBroker layer (lazy-loaded)

Agent 0 (manager) special handling:

python
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 ​

python
# 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 token
  • tool_call — tool invocation started
  • tool_result — tool result
  • emit_event — custom event (audio / video / work log)
  • done — completion
  • error — 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 ​

python
# 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 ctx

Problem: 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 →

Familiars · 多 Agent AI 桌面应用