beacon-trial-finder / CLAUDE.md
KevinIsInCoding
feat: structured eligibility assessment with per-criterion verdicts (#14)
e15864e unverified
|
Raw
History Blame Contribute Delete
4.56 kB

A newer version of the Gradio SDK is available: 6.26.0

Upgrade

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, or config
  • Business rules, data transformations, or domain logic
  • Any knowledge of what a PatientProfile field 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:

  • BeaconState TypedDict 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_disease tool 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_disease resolves, call lookup_disease_profile(standardized_name) and use the returned dict exclusively.
  • Do not iterate over _ALL_DISEASES to 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