Spaces:
Sleeping
A newer version of the Gradio SDK is available: 6.26.0
Beacon β Architecture Guide for Claude Code
Layer Responsibilities
app.py β UI wiring only
app.py owns Gradio state and event wiring. Nothing else.
Allowed:
gr.State,gr.Chatbot,gr.Textbox, and other Gradio components- Event handlers that call into
agents.*and yield streaming updates - Language switching, layout, and display formatting
Not allowed:
- Direct calls to
trials_api,prompts,tools,models, orconfig - Business rules, data transformations, or domain logic
- Any knowledge of what a
PatientProfilefield means
If you find yourself importing a non-agent module into app.py, that logic belongs in an agent or a business-logic module instead.
clinical_trials_guru.py β business wiring only
clinical_trials_guru.py owns orchestration and re-exports. It is the single public facade for CLI callers and external scripts.
Allowed:
BeaconStateTypedDict and LangGraph graph construction (_build_graph)- CLI entry point (
guru_main) - Re-exporting symbols from internal modules so callers import one place
Not allowed:
- UI logic, Gradio imports, or display formatting
- New business logic that belongs in
agents/,models.py,tools.py, etc. - Duplicating functions that already live in
agents/
When you add a new public symbol (function, class, constant), expose it here so the rest of the codebase has one stable import surface.
agents/ β orchestration with streaming support
Each agent module (intake.py, research.py) provides two variants:
- A blocking function (
run_*) for CLI / LangGraph nodes - A streaming generator (
stream_*) for the Gradio web UI
Keep agents free of Gradio types. They yield plain strings and return domain objects (PatientProfile, str).
Design Principle: Load Only Necessary Context
Never inject all disease benchmarks into context when only one disease is relevant.
Rule
Context for a disease-specific benchmark set must not be loaded until the disease is identified. Once the patient's disease is known (via the identify_disease tool), load only that disease's profile and pass it to the LLM. Do not pre-load or concatenate profiles for other diseases.
Rationale
- Each disease JSON carries benchmark definitions, ranges, and guidance text. Loading all 8+ profiles wastes tokens and dilutes the system prompt.
- The
identify_diseasetool call is the earliest reliable signal of disease identity. Everything after that point can be scoped to one profile. - This keeps context proportional to the task and reduces the risk of cross-disease confusion in the LLM.
How it works today
prompts.py eagerly reads all disease files into _ALL_DISEASES at module import, but lookup_disease_profile(standardized_name) returns only one profile β the matched disease. The intake agent calls this function after the identify_disease tool fires and injects only the returned benchmarks into the conversation.
How to stay compliant
- After
identify_diseaseresolves, calllookup_disease_profile(standardized_name)and use the returned dict exclusively. - Do not iterate over
_ALL_DISEASESto build a multi-disease context block. - If you need to add a new disease, add its JSON under
data/diseases/β do not hardcode benchmark lists in prompts or agents. - For features that genuinely need cross-disease comparison (e.g., a disease selector UI), load lazily: fetch each profile only when selected, not all at startup.
Module Map
| File | Layer | Imports allowed from |
|---|---|---|
app.py |
UI | agents.*, translations, models (type hints only) |
clinical_trials_guru.py |
Business facade | Everything |
agents/intake.py |
Agent | models, prompts, tools, config, llm, translations |
agents/research.py |
Agent | models, prompts, tools, config, llm, trials_api, translations |
models.py |
Domain model | stdlib, requests |
prompts.py |
Prompt builder | stdlib, data/diseases/*.json |
tools.py |
Tool schema loader | stdlib, data/tools/*.json |
trials_api.py |
External API | stdlib, models, config |
config.py |
Constants | stdlib |
translations.py |
i18n | stdlib |
Entry Points
| Entry | File | UI | Flow |
|---|---|---|---|
| Web | app.py |
Gradio | intake_greeting β stream_intake_turn β stream_research_agent |
| CLI | main.py β clinical_trials_guru.py |
Rich console | LangGraph: run_intake_agent β run_research_agent |