KevinIsInCoding commited on
Commit
56d41c3
·
1 Parent(s): de5e581

FEAT: Add more information about Patient intake agent

Browse files
Files changed (4) hide show
  1. README.md +0 -64
  2. clinical_trials_guru.py +0 -114
  3. pyproject.toml +2 -2
  4. uv.lock +44 -44
README.md CHANGED
@@ -1,4 +1,3 @@
1
- <<<<<<< HEAD
2
  ---
3
  title: Beacon Trial Finder
4
  emoji: 📚
@@ -13,15 +12,12 @@ license: mit
13
  short_description: help patients with rare disease to find clinical trials
14
  ---
15
 
16
- =======
17
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
18
  # Beacon — Rare Disease Clinical Trial Finder
19
 
20
  Beacon is a conversational AI assistant that helps patients with rare diseases find relevant recruiting clinical trials near them. It conducts a warm intake interview, geocodes the patient's location, queries [ClinicalTrials.gov](https://clinicaltrials.gov) in real time, and produces a ranked report of the closest matching trials.
21
 
22
  ## How it works
23
 
24
- <<<<<<< HEAD
25
  1. **Intake agent** (Claude Sonnet) — interviews the patient conversationally to collect disease, age, symptom onset date, formal diagnosis date, location, preferred trial phases, and whether they are interested in Expanded Access Programs (EAP / compassionate use).
26
  2. **Research agent** (Claude Opus) — searches ClinicalTrials.gov via the official v2 API for clinical trials and/or EAP listings, retrying with synonyms or wider radii if results are sparse, then outputs a ranked report with eligibility notes and next steps.
27
  3. **LangGraph** orchestrates the two-node pipeline (intake → research).
@@ -40,12 +36,6 @@ Beacon is a conversational AI assistant that helps patients with rare diseases f
40
 
41
  EAP (also called compassionate use) allows patients who do not qualify for or cannot access a clinical trial to receive an investigational drug or device outside of a formal trial. The treatment is not yet FDA-approved; a physician must submit the EAP request to the drug sponsor and obtain FDA authorization. Beacon can search for available EAP listings alongside clinical trials.
42
 
43
- =======
44
- 1. **Intake agent** (Claude Sonnet) — interviews the patient conversationally to collect disease, age, symptom onset, location, and optional benchmark scores.
45
- 2. **Research agent** (Claude Opus) — searches ClinicalTrials.gov via the official v2 API, retrying with synonyms or wider radii if results are sparse, then outputs a ranked trial report with eligibility notes and next steps.
46
- 3. **LangGraph** orchestrates the two-node pipeline (intake → research).
47
-
48
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
49
  ## Project setup
50
 
51
  ### Prerequisites
@@ -83,7 +73,6 @@ cp .env.example .env
83
  Open `.env` and replace the placeholder values:
84
 
85
  ```env
86
- <<<<<<< HEAD
87
  ANTHROPIC_API_KEY=your_anthropic_api_key_here
88
  ```
89
 
@@ -108,43 +97,11 @@ uv run gradio app.py
108
  | Environment variable | Default | Description |
109
  |----------------------|-------------|-------------------|
110
  | `ANTHROPIC_API_KEY` | *(required)*| Anthropic API key |
111
- =======
112
- # ⚠️ Replace with your actual API keys — never commit real keys to version control
113
- OPENAI_API_KEY=your_openai_api_key_here
114
- ANTHROPIC_API_KEY=your_anthropic_api_key_here
115
- ```
116
-
117
- - Get your Anthropic key at <https://console.anthropic.com>
118
- - Get your OpenAI key at <https://platform.openai.com/api-keys>
119
-
120
- ### 4. Run
121
-
122
- ```bash
123
- uv run python main.py
124
- ```
125
-
126
- Or if using a plain virtualenv:
127
-
128
- ```bash
129
- python main.py
130
- ```
131
-
132
- ## Configuration
133
-
134
- | Environment variable | Default | Description |
135
- |----------------------|-------------|--------------------------------------------------|
136
- | `ANTHROPIC_API_KEY` | *(required)*| Anthropic API key |
137
- | `OPENAI_API_KEY` | *(optional)*| OpenAI API key (only needed for OpenAI provider) |
138
- | `LLM_PROVIDER` | `anthropic` | LLM backend: `anthropic` or `openai` |
139
-
140
- To switch to the OpenAI backend, set `LLM_PROVIDER=openai` in `.env`.
141
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
142
 
143
  ## Project structure
144
 
145
  ```
146
  beacon/
147
- <<<<<<< HEAD
148
  ├── app.py # Gradio web UI entry point
149
  ├── main.py # Terminal entry point
150
  ├── clinical_trials_guru.py # Intake + research agents, LangGraph pipeline
@@ -154,27 +111,6 @@ beacon/
154
  └── .env # Local secrets — not committed
155
  ```
156
  =======
157
- ├── main.py # Entry point
158
- ├── clinical_trials_guru.py # Intake + research agents, LangGraph pipeline
159
- ├── llm.py # LLM provider abstraction (Anthropic / OpenAI)
160
- ├── pyproject.toml
161
- ├── .env # Local secrets — not committed
162
- └── .gitignore
163
- ```
164
-
165
- ## Dependencies
166
-
167
- | Package | Purpose |
168
- |------------------|--------------------------------------|
169
- | `anthropic` | Claude API client |
170
- | `openai` | OpenAI API client |
171
- | `langgraph` | Agent pipeline orchestration |
172
- | `rich` | Terminal UI (panels, markdown, etc.) |
173
- | `python-dotenv` | `.env` file loading |
174
- | `httpx` | HTTP client for ClinicalTrials.gov |
175
- <<<<<<< HEAD
176
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
177
- =======
178
  =======
179
  ---
180
  title: Beacon Trial Finder
 
 
1
  ---
2
  title: Beacon Trial Finder
3
  emoji: 📚
 
12
  short_description: help patients with rare disease to find clinical trials
13
  ---
14
 
 
 
15
  # Beacon — Rare Disease Clinical Trial Finder
16
 
17
  Beacon is a conversational AI assistant that helps patients with rare diseases find relevant recruiting clinical trials near them. It conducts a warm intake interview, geocodes the patient's location, queries [ClinicalTrials.gov](https://clinicaltrials.gov) in real time, and produces a ranked report of the closest matching trials.
18
 
19
  ## How it works
20
 
 
21
  1. **Intake agent** (Claude Sonnet) — interviews the patient conversationally to collect disease, age, symptom onset date, formal diagnosis date, location, preferred trial phases, and whether they are interested in Expanded Access Programs (EAP / compassionate use).
22
  2. **Research agent** (Claude Opus) — searches ClinicalTrials.gov via the official v2 API for clinical trials and/or EAP listings, retrying with synonyms or wider radii if results are sparse, then outputs a ranked report with eligibility notes and next steps.
23
  3. **LangGraph** orchestrates the two-node pipeline (intake → research).
 
36
 
37
  EAP (also called compassionate use) allows patients who do not qualify for or cannot access a clinical trial to receive an investigational drug or device outside of a formal trial. The treatment is not yet FDA-approved; a physician must submit the EAP request to the drug sponsor and obtain FDA authorization. Beacon can search for available EAP listings alongside clinical trials.
38
 
 
 
 
 
 
 
39
  ## Project setup
40
 
41
  ### Prerequisites
 
73
  Open `.env` and replace the placeholder values:
74
 
75
  ```env
 
76
  ANTHROPIC_API_KEY=your_anthropic_api_key_here
77
  ```
78
 
 
97
  | Environment variable | Default | Description |
98
  |----------------------|-------------|-------------------|
99
  | `ANTHROPIC_API_KEY` | *(required)*| Anthropic API key |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
  ## Project structure
102
 
103
  ```
104
  beacon/
 
105
  ├── app.py # Gradio web UI entry point
106
  ├── main.py # Terminal entry point
107
  ├── clinical_trials_guru.py # Intake + research agents, LangGraph pipeline
 
111
  └── .env # Local secrets — not committed
112
  ```
113
  =======
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  =======
115
  ---
116
  title: Beacon Trial Finder
clinical_trials_guru.py CHANGED
@@ -41,13 +41,10 @@ SUBMIT_PROFILE_TOOL: anthropic.types.ToolParam = {
41
  "type": "integer",
42
  "description": "Months since first symptom onset",
43
  },
44
- <<<<<<< HEAD
45
  "diagnosis_months": {
46
  "type": "integer",
47
  "description": "Months since formal/official diagnosis",
48
  },
49
- =======
50
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
51
  "benchmarks": {
52
  "type": "object",
53
  "description": "Disease-specific scores, e.g. {\"ALSFRS-R\": \"38\"}",
@@ -65,7 +62,6 @@ SUBMIT_PROFILE_TOOL: anthropic.types.ToolParam = {
65
  "phases": {
66
  "type": "array",
67
  "items": {"type": "string", "enum": ["0", "1", "2", "3", "4"]},
68
- <<<<<<< HEAD
69
  "description": "Desired trial phases (0=Early Phase 1, 1=Phase 1, 2=Phase 2, 3=Phase 3, 4=Phase 4). Empty = all phases.",
70
  },
71
  "include_eap": {
@@ -74,30 +70,17 @@ SUBMIT_PROFILE_TOOL: anthropic.types.ToolParam = {
74
  },
75
  },
76
  "required": ["disease", "age", "onset_months", "diagnosis_months", "zip_code"],
77
- =======
78
- "description": "Desired trial phases. Empty = all phases.",
79
- },
80
- },
81
- "required": ["disease", "age", "onset_months", "zip_code"],
82
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
83
  },
84
  }
85
 
86
  SEARCH_TRIALS_TOOL: anthropic.types.ToolParam = {
87
  "name": "search_clinical_trials",
88
  "description": (
89
- <<<<<<< HEAD
90
  "Search ClinicalTrials.gov for studies within a geographic radius. "
91
  "Results are pre-ranked by distance from the patient's location. "
92
  "Call multiple times with different parameters (synonyms, broader radius, "
93
  "different phases) if initial results are sparse. "
94
  "Use study_type='EXPANDED_ACCESS' to search for Expanded Access Programs (EAP / compassionate use)."
95
- =======
96
- "Search ClinicalTrials.gov for recruiting trials within a geographic radius. "
97
- "Results are pre-ranked by distance from the patient's location. "
98
- "Call multiple times with different parameters (synonyms, broader radius, "
99
- "different phases) if initial results are sparse."
100
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
101
  ),
102
  "input_schema": {
103
  "type": "object",
@@ -112,16 +95,12 @@ SEARCH_TRIALS_TOOL: anthropic.types.ToolParam = {
112
  "phases": {
113
  "type": "array",
114
  "items": {"type": "string"},
115
- <<<<<<< HEAD
116
  "description": "Phase numbers to filter ['1','2','3']. Empty = all. Ignored for EAP.",
117
  },
118
  "study_type": {
119
  "type": "string",
120
  "enum": ["INTERVENTIONAL", "EXPANDED_ACCESS"],
121
  "description": "INTERVENTIONAL (default) for clinical trials; EXPANDED_ACCESS for EAP/compassionate use.",
122
- =======
123
- "description": "Phase numbers to filter ['1','2','3']. Empty = all.",
124
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
125
  },
126
  "max_results": {"type": "integer", "description": "Max trials to return (default 20)"},
127
  },
@@ -139,15 +118,11 @@ REQUIRED:
139
  • Disease/condition (standardize: "Lou Gehrig's" → "Amyotrophic Lateral Sclerosis")
140
  • Patient age
141
  • Months since first symptom onset (convert dates/years as needed)
142
- <<<<<<< HEAD
143
  • Months since formal/official diagnosis (convert dates/years as needed; may differ from onset)
144
- =======
145
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
146
  • ZIP/postal code and country for geographic search
147
 
148
  OPTIONAL (ask based on disease):
149
  • Disease-specific benchmark scores:
150
- <<<<<<< HEAD
151
  ALS → ALSFRS-R (0-48) + FVC % predicted (0-100%) + ALS subtype;
152
  FVC (Forced Vital Capacity) measures how much air a person can forcibly exhale —
153
  it reflects respiratory muscle strength. In ALS it is expressed as a percentage
@@ -185,15 +160,6 @@ OPTIONAL (ask based on disease):
185
  - Or both; or all phases (default if no preference)
186
 
187
  Ask naturally. You may infer disease synonyms and convert dates to months, but never infer or skip the ZIP/postal code — always ask the patient for it directly. Once you have every required field confirmed by the patient, call submit_profile.\
188
- =======
189
- ALS → ALSFRS-R (0-48); MS → EDSS (0-10); Parkinson's → MDS-UPDRS III;
190
- Huntington's → TFC (0-13) + CAG repeats; SMA → HFMS + SMA type;
191
- Duchenne/Pompe → 6-Minute Walk Test; Friedreich's → SARA score
192
- • Preferred search radius in miles (default 100)
193
- • Trial phases of interest (1 / 2 / 3 / 4 / early)
194
-
195
- Ask naturally. Infer what you can. Once you have the required fields, call submit_profile.\
196
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
197
  """
198
 
199
  RESEARCH_SYSTEM = """\
@@ -203,7 +169,6 @@ Results are already ranked by geographic distance from the patient.
203
 
204
  Workflow:
205
  1. Search for the patient's disease. Use both the full medical name and common abbreviation.
206
- <<<<<<< HEAD
207
  - If the patient wants clinical trials, search with study_type="INTERVENTIONAL".
208
  - If the patient wants Expanded Access Programs (EAP), also search with study_type="EXPANDED_ACCESS".
209
  - If the patient wants both, run separate searches for each study_type.
@@ -223,25 +188,10 @@ Workflow:
223
  **Qualification criteria:** [Key inclusion AND exclusion criteria relevant to this patient,
224
  including age range, functional score thresholds, FVC cutoffs,
225
  and any red flags. Be specific — use exact numbers from the data.]
226
- =======
227
- 2. If fewer than 3 results are found, retry with: a wider radius, a disease synonym,
228
- or fewer phase filters.
229
- 3. Produce a final report listing the top 5 trials ranked by site proximity.
230
- For EACH trial use exactly this format (repeat the block per trial):
231
-
232
- 📍 **[Closest hospital name]** — [City, State] ([X] mi)
233
- **Trial:** [Full trial title] ([Phase])
234
- **Sponsor:** [Lead sponsor]
235
- **Summary:** [2–3 sentence plain-language description of what the trial is testing
236
- and why it may matter for this patient]
237
- **Eligibility notes:** [Key inclusion/exclusion criteria relevant to this patient,
238
- including any red flags]
239
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
240
  **Link:** https://clinicaltrials.gov/study/[NCT_ID]
241
 
242
  ---
243
 
244
- <<<<<<< HEAD
245
  4. After the results add a short "Next steps" section (bullet points).
246
  For EAP results, note that patients typically need a physician to submit the EAP request.
247
 
@@ -249,9 +199,6 @@ IMPORTANT: Only report trials returned by the search_clinical_trials tool. Do NO
249
  list, or recommend any hospitals, centers, or trials that were not in the tool results —
250
  even well-known institutions. If no results are found, say so clearly and suggest the patient
251
  ask their neurologist or contact the ALS Association for a referral.
252
- =======
253
- 4. After the trial list add a short "Next steps" section (bullet points).
254
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
255
 
256
  Be accurate. Do not fabricate details. If data is missing, say so.\
257
  """
@@ -263,10 +210,7 @@ class PatientProfile:
263
  disease: str
264
  age: int
265
  onset_months: int
266
- <<<<<<< HEAD
267
  diagnosis_months: int = 0
268
- =======
269
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
270
  benchmarks: dict[str, str] = field(default_factory=dict)
271
  zip_code: str = ""
272
  country_code: str = "US"
@@ -274,20 +218,14 @@ class PatientProfile:
274
  lon: float = 0.0
275
  radius_miles: int = 100
276
  phases: list[str] = field(default_factory=list)
277
- <<<<<<< HEAD
278
  include_eap: bool = False
279
- =======
280
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
281
 
282
  def summary(self) -> str:
283
  lines = [
284
  f"Disease: {self.disease}",
285
  f"Age: {self.age}",
286
  f"Symptom onset: {self.onset_months} months ago",
287
- <<<<<<< HEAD
288
  f"Formal diagnosis: {self.diagnosis_months} months ago",
289
- =======
290
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
291
  ]
292
  if self.benchmarks:
293
  lines.append("Benchmarks: " + ", ".join(f"{k}={v}" for k, v in self.benchmarks.items()))
@@ -299,13 +237,10 @@ class PatientProfile:
299
  if self.phases:
300
  labels = ["Early Phase 1" if p == "0" else f"Phase {p}" for p in self.phases]
301
  lines.append(f"Phases: {', '.join(labels)}")
302
- <<<<<<< HEAD
303
  interests = ["Clinical trials"]
304
  if self.include_eap:
305
  interests.append("Expanded Access Programs (EAP)")
306
  lines.append(f"Study type interest: {', '.join(interests)}")
307
- =======
308
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
309
  return "\n".join(lines)
310
 
311
 
@@ -343,7 +278,6 @@ def search_trials_api(
343
  lon: float,
344
  radius_miles: int = 100,
345
  phases: list[str] | None = None,
346
- <<<<<<< HEAD
347
  study_type: str = "INTERVENTIONAL",
348
  max_results: int = 20,
349
  ) -> list[dict]:
@@ -351,18 +285,10 @@ def search_trials_api(
351
  params: dict[str, str | int] = {
352
  "query.cond": condition,
353
  "filter.overallStatus": "AVAILABLE" if is_eap else "RECRUITING",
354
- =======
355
- max_results: int = 20,
356
- ) -> list[dict]:
357
- params: dict[str, str | int] = {
358
- "query.cond": condition,
359
- "filter.overallStatus": "RECRUITING",
360
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
361
  "filter.geo": f"distance({lat},{lon},{radius_miles}mi)",
362
  "pageSize": max_results,
363
  "format": "json",
364
  }
365
- <<<<<<< HEAD
366
  # aggFilters accepts only one value; studyType and phase can't be combined.
367
  # RECRUITING status already excludes EAPs, so studyType:int is only needed
368
  # when no phase filter is applied.
@@ -372,10 +298,6 @@ def search_trials_api(
372
  params["aggFilters"] = "phase:" + " ".join(phases)
373
  else:
374
  params["aggFilters"] = "studyType:int"
375
- =======
376
- if phases:
377
- params["aggFilters"] = "phase:" + " ".join(phases)
378
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
379
  for attempt in range(3):
380
  try:
381
  resp = httpx.get(CTGOV_BASE, params=params, timeout=30)
@@ -400,7 +322,6 @@ def _flatten_and_rank(studies: list[dict], patient_lat: float, patient_lon: floa
400
  sponsor_mod = proto.get("sponsorCollaboratorsModule", {})
401
  design_mod = proto.get("designModule", {})
402
 
403
- <<<<<<< HEAD
404
  # Central (overall) contacts
405
  central_contacts = contacts_mod.get("centralContacts", [])
406
  central_phone = next((c.get("phone", "") for c in central_contacts if c.get("phone")), "")
@@ -414,14 +335,10 @@ def _flatten_and_rank(studies: list[dict], patient_lat: float, patient_lon: floa
414
  )
415
 
416
  sites_with_dist: list[tuple[float, dict]] = []
417
- =======
418
- sites_with_dist: list[tuple[float, str]] = []
419
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
420
  for loc in contacts_mod.get("locations", []):
421
  geo = loc.get("geoPoint", {})
422
  if geo.get("lat") and geo.get("lon"):
423
  d = haversine_miles(patient_lat, patient_lon, geo["lat"], geo["lon"])
424
- <<<<<<< HEAD
425
  loc_contacts = loc.get("contacts", [])
426
  loc_phone = next((c.get("phone", "") for c in loc_contacts if c.get("phone")), "")
427
  loc_email = next((c.get("email", "") for c in loc_contacts if c.get("email")), "")
@@ -439,15 +356,6 @@ def _flatten_and_rank(studies: list[dict], patient_lat: float, patient_lon: floa
439
  "phone": loc_phone or central_phone,
440
  "email": loc_email or central_email,
441
  }))
442
- =======
443
- label = (
444
- f"{loc.get('facility', '').strip()} — "
445
- f"{loc.get('city', '')}, "
446
- f"{loc.get('state', loc.get('country', ''))} "
447
- f"({d:.0f} mi)"
448
- )
449
- sites_with_dist.append((d, label))
450
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
451
  sites_with_dist.sort(key=lambda x: x[0])
452
 
453
  closest_dist = sites_with_dist[0][0] if sites_with_dist else None
@@ -456,22 +364,15 @@ def _flatten_and_rank(studies: list[dict], patient_lat: float, patient_lon: floa
456
  "title": id_mod.get("briefTitle", ""),
457
  "phase": ", ".join(design_mod.get("phases", [])) or "N/A",
458
  "sponsor": sponsor_mod.get("leadSponsor", {}).get("name", ""),
459
- <<<<<<< HEAD
460
  "principal_investigator": pi,
461
  "contact_phone": central_phone,
462
  "contact_email": central_email,
463
- =======
464
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
465
  "summary": desc_mod.get("briefSummary", "")[:500],
466
  "eligibility": elig_mod.get("eligibilityCriteria", "")[:1000],
467
  "min_age": elig_mod.get("minimumAge", ""),
468
  "max_age": elig_mod.get("maximumAge", ""),
469
  "closest_site_miles": round(closest_dist, 1) if closest_dist is not None else None,
470
- <<<<<<< HEAD
471
  "nearest_sites": [info for _, info in sites_with_dist[:5]],
472
- =======
473
- "nearest_sites": [label for _, label in sites_with_dist[:5]],
474
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
475
  })
476
 
477
  result.sort(key=lambda x: x["closest_site_miles"] if x["closest_site_miles"] is not None else float("inf"))
@@ -524,10 +425,7 @@ def run_intake_agent(client: anthropic.Anthropic) -> PatientProfile:
524
  disease=data["disease"],
525
  age=data["age"],
526
  onset_months=data["onset_months"],
527
- <<<<<<< HEAD
528
  diagnosis_months=data.get("diagnosis_months", 0),
529
- =======
530
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
531
  benchmarks=data.get("benchmarks") or {},
532
  zip_code=data["zip_code"],
533
  country_code=data.get("country_code", "US"),
@@ -535,10 +433,7 @@ def run_intake_agent(client: anthropic.Anthropic) -> PatientProfile:
535
  lon=lon,
536
  radius_miles=data.get("radius_miles", 100),
537
  phases=data.get("phases") or [],
538
- <<<<<<< HEAD
539
  include_eap=data.get("include_eap", False),
540
- =======
541
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
542
  )
543
 
544
  messages.append({"role": "assistant", "content": response.content})
@@ -583,17 +478,11 @@ def run_research_agent(client: anthropic.Anthropic, profile: PatientProfile) ->
583
  args = block.input
584
  radius = args.get("radius_miles", profile.radius_miles)
585
  phases = args.get("phases") or None
586
- <<<<<<< HEAD
587
  study_type = args.get("study_type", "INTERVENTIONAL")
588
  status_msg = (
589
  f"[cyan]Searching:[/cyan] '[bold]{args['condition']}[/bold]' | "
590
  f"radius=[bold]{radius}[/bold] mi | "
591
  f"type=[bold]{study_type}[/bold] | "
592
- =======
593
- status_msg = (
594
- f"[cyan]Searching:[/cyan] '[bold]{args['condition']}[/bold]' | "
595
- f"radius=[bold]{radius}[/bold] mi | "
596
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
597
  f"phases=[bold]{phases or 'all'}[/bold]"
598
  )
599
  try:
@@ -604,10 +493,7 @@ def run_research_agent(client: anthropic.Anthropic, profile: PatientProfile) ->
604
  lon=args["lon"],
605
  radius_miles=radius,
606
  phases=phases,
607
- <<<<<<< HEAD
608
  study_type=study_type,
609
- =======
610
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
611
  max_results=args.get("max_results", 20),
612
  )
613
  ranked = _flatten_and_rank(studies, profile.lat, profile.lon)
 
41
  "type": "integer",
42
  "description": "Months since first symptom onset",
43
  },
 
44
  "diagnosis_months": {
45
  "type": "integer",
46
  "description": "Months since formal/official diagnosis",
47
  },
 
 
48
  "benchmarks": {
49
  "type": "object",
50
  "description": "Disease-specific scores, e.g. {\"ALSFRS-R\": \"38\"}",
 
62
  "phases": {
63
  "type": "array",
64
  "items": {"type": "string", "enum": ["0", "1", "2", "3", "4"]},
 
65
  "description": "Desired trial phases (0=Early Phase 1, 1=Phase 1, 2=Phase 2, 3=Phase 3, 4=Phase 4). Empty = all phases.",
66
  },
67
  "include_eap": {
 
70
  },
71
  },
72
  "required": ["disease", "age", "onset_months", "diagnosis_months", "zip_code"],
 
 
 
 
 
 
73
  },
74
  }
75
 
76
  SEARCH_TRIALS_TOOL: anthropic.types.ToolParam = {
77
  "name": "search_clinical_trials",
78
  "description": (
 
79
  "Search ClinicalTrials.gov for studies within a geographic radius. "
80
  "Results are pre-ranked by distance from the patient's location. "
81
  "Call multiple times with different parameters (synonyms, broader radius, "
82
  "different phases) if initial results are sparse. "
83
  "Use study_type='EXPANDED_ACCESS' to search for Expanded Access Programs (EAP / compassionate use)."
 
 
 
 
 
 
84
  ),
85
  "input_schema": {
86
  "type": "object",
 
95
  "phases": {
96
  "type": "array",
97
  "items": {"type": "string"},
 
98
  "description": "Phase numbers to filter ['1','2','3']. Empty = all. Ignored for EAP.",
99
  },
100
  "study_type": {
101
  "type": "string",
102
  "enum": ["INTERVENTIONAL", "EXPANDED_ACCESS"],
103
  "description": "INTERVENTIONAL (default) for clinical trials; EXPANDED_ACCESS for EAP/compassionate use.",
 
 
 
104
  },
105
  "max_results": {"type": "integer", "description": "Max trials to return (default 20)"},
106
  },
 
118
  • Disease/condition (standardize: "Lou Gehrig's" → "Amyotrophic Lateral Sclerosis")
119
  • Patient age
120
  • Months since first symptom onset (convert dates/years as needed)
 
121
  • Months since formal/official diagnosis (convert dates/years as needed; may differ from onset)
 
 
122
  • ZIP/postal code and country for geographic search
123
 
124
  OPTIONAL (ask based on disease):
125
  • Disease-specific benchmark scores:
 
126
  ALS → ALSFRS-R (0-48) + FVC % predicted (0-100%) + ALS subtype;
127
  FVC (Forced Vital Capacity) measures how much air a person can forcibly exhale —
128
  it reflects respiratory muscle strength. In ALS it is expressed as a percentage
 
160
  - Or both; or all phases (default if no preference)
161
 
162
  Ask naturally. You may infer disease synonyms and convert dates to months, but never infer or skip the ZIP/postal code — always ask the patient for it directly. Once you have every required field confirmed by the patient, call submit_profile.\
 
 
 
 
 
 
 
 
 
163
  """
164
 
165
  RESEARCH_SYSTEM = """\
 
169
 
170
  Workflow:
171
  1. Search for the patient's disease. Use both the full medical name and common abbreviation.
 
172
  - If the patient wants clinical trials, search with study_type="INTERVENTIONAL".
173
  - If the patient wants Expanded Access Programs (EAP), also search with study_type="EXPANDED_ACCESS".
174
  - If the patient wants both, run separate searches for each study_type.
 
188
  **Qualification criteria:** [Key inclusion AND exclusion criteria relevant to this patient,
189
  including age range, functional score thresholds, FVC cutoffs,
190
  and any red flags. Be specific — use exact numbers from the data.]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  **Link:** https://clinicaltrials.gov/study/[NCT_ID]
192
 
193
  ---
194
 
 
195
  4. After the results add a short "Next steps" section (bullet points).
196
  For EAP results, note that patients typically need a physician to submit the EAP request.
197
 
 
199
  list, or recommend any hospitals, centers, or trials that were not in the tool results —
200
  even well-known institutions. If no results are found, say so clearly and suggest the patient
201
  ask their neurologist or contact the ALS Association for a referral.
 
 
 
202
 
203
  Be accurate. Do not fabricate details. If data is missing, say so.\
204
  """
 
210
  disease: str
211
  age: int
212
  onset_months: int
 
213
  diagnosis_months: int = 0
 
 
214
  benchmarks: dict[str, str] = field(default_factory=dict)
215
  zip_code: str = ""
216
  country_code: str = "US"
 
218
  lon: float = 0.0
219
  radius_miles: int = 100
220
  phases: list[str] = field(default_factory=list)
 
221
  include_eap: bool = False
 
 
222
 
223
  def summary(self) -> str:
224
  lines = [
225
  f"Disease: {self.disease}",
226
  f"Age: {self.age}",
227
  f"Symptom onset: {self.onset_months} months ago",
 
228
  f"Formal diagnosis: {self.diagnosis_months} months ago",
 
 
229
  ]
230
  if self.benchmarks:
231
  lines.append("Benchmarks: " + ", ".join(f"{k}={v}" for k, v in self.benchmarks.items()))
 
237
  if self.phases:
238
  labels = ["Early Phase 1" if p == "0" else f"Phase {p}" for p in self.phases]
239
  lines.append(f"Phases: {', '.join(labels)}")
 
240
  interests = ["Clinical trials"]
241
  if self.include_eap:
242
  interests.append("Expanded Access Programs (EAP)")
243
  lines.append(f"Study type interest: {', '.join(interests)}")
 
 
244
  return "\n".join(lines)
245
 
246
 
 
278
  lon: float,
279
  radius_miles: int = 100,
280
  phases: list[str] | None = None,
 
281
  study_type: str = "INTERVENTIONAL",
282
  max_results: int = 20,
283
  ) -> list[dict]:
 
285
  params: dict[str, str | int] = {
286
  "query.cond": condition,
287
  "filter.overallStatus": "AVAILABLE" if is_eap else "RECRUITING",
 
 
 
 
 
 
 
288
  "filter.geo": f"distance({lat},{lon},{radius_miles}mi)",
289
  "pageSize": max_results,
290
  "format": "json",
291
  }
 
292
  # aggFilters accepts only one value; studyType and phase can't be combined.
293
  # RECRUITING status already excludes EAPs, so studyType:int is only needed
294
  # when no phase filter is applied.
 
298
  params["aggFilters"] = "phase:" + " ".join(phases)
299
  else:
300
  params["aggFilters"] = "studyType:int"
 
 
 
 
301
  for attempt in range(3):
302
  try:
303
  resp = httpx.get(CTGOV_BASE, params=params, timeout=30)
 
322
  sponsor_mod = proto.get("sponsorCollaboratorsModule", {})
323
  design_mod = proto.get("designModule", {})
324
 
 
325
  # Central (overall) contacts
326
  central_contacts = contacts_mod.get("centralContacts", [])
327
  central_phone = next((c.get("phone", "") for c in central_contacts if c.get("phone")), "")
 
335
  )
336
 
337
  sites_with_dist: list[tuple[float, dict]] = []
 
 
 
338
  for loc in contacts_mod.get("locations", []):
339
  geo = loc.get("geoPoint", {})
340
  if geo.get("lat") and geo.get("lon"):
341
  d = haversine_miles(patient_lat, patient_lon, geo["lat"], geo["lon"])
 
342
  loc_contacts = loc.get("contacts", [])
343
  loc_phone = next((c.get("phone", "") for c in loc_contacts if c.get("phone")), "")
344
  loc_email = next((c.get("email", "") for c in loc_contacts if c.get("email")), "")
 
356
  "phone": loc_phone or central_phone,
357
  "email": loc_email or central_email,
358
  }))
 
 
 
 
 
 
 
 
 
359
  sites_with_dist.sort(key=lambda x: x[0])
360
 
361
  closest_dist = sites_with_dist[0][0] if sites_with_dist else None
 
364
  "title": id_mod.get("briefTitle", ""),
365
  "phase": ", ".join(design_mod.get("phases", [])) or "N/A",
366
  "sponsor": sponsor_mod.get("leadSponsor", {}).get("name", ""),
 
367
  "principal_investigator": pi,
368
  "contact_phone": central_phone,
369
  "contact_email": central_email,
 
 
370
  "summary": desc_mod.get("briefSummary", "")[:500],
371
  "eligibility": elig_mod.get("eligibilityCriteria", "")[:1000],
372
  "min_age": elig_mod.get("minimumAge", ""),
373
  "max_age": elig_mod.get("maximumAge", ""),
374
  "closest_site_miles": round(closest_dist, 1) if closest_dist is not None else None,
 
375
  "nearest_sites": [info for _, info in sites_with_dist[:5]],
 
 
 
376
  })
377
 
378
  result.sort(key=lambda x: x["closest_site_miles"] if x["closest_site_miles"] is not None else float("inf"))
 
425
  disease=data["disease"],
426
  age=data["age"],
427
  onset_months=data["onset_months"],
 
428
  diagnosis_months=data.get("diagnosis_months", 0),
 
 
429
  benchmarks=data.get("benchmarks") or {},
430
  zip_code=data["zip_code"],
431
  country_code=data.get("country_code", "US"),
 
433
  lon=lon,
434
  radius_miles=data.get("radius_miles", 100),
435
  phases=data.get("phases") or [],
 
436
  include_eap=data.get("include_eap", False),
 
 
437
  )
438
 
439
  messages.append({"role": "assistant", "content": response.content})
 
478
  args = block.input
479
  radius = args.get("radius_miles", profile.radius_miles)
480
  phases = args.get("phases") or None
 
481
  study_type = args.get("study_type", "INTERVENTIONAL")
482
  status_msg = (
483
  f"[cyan]Searching:[/cyan] '[bold]{args['condition']}[/bold]' | "
484
  f"radius=[bold]{radius}[/bold] mi | "
485
  f"type=[bold]{study_type}[/bold] | "
 
 
 
 
 
486
  f"phases=[bold]{phases or 'all'}[/bold]"
487
  )
488
  try:
 
493
  lon=args["lon"],
494
  radius_miles=radius,
495
  phases=phases,
 
496
  study_type=study_type,
 
 
497
  max_results=args.get("max_results", 20),
498
  )
499
  ranked = _flatten_and_rank(studies, profile.lat, profile.lon)
pyproject.toml CHANGED
@@ -10,8 +10,8 @@ dependencies = [
10
  "openai>=1.0.0",
11
  "python-dotenv>=1.2.2",
12
  "rich>=13.0.0",
13
- <<<<<<< HEAD
14
  "gradio>=4.0.0",
15
  =======
16
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
17
  ]
 
10
  "openai>=1.0.0",
11
  "python-dotenv>=1.2.2",
12
  "rich>=13.0.0",
 
13
  "gradio>=4.0.0",
14
  =======
15
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
16
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
17
  ]
uv.lock CHANGED
@@ -1,7 +1,6 @@
1
  version = 1
2
  revision = 3
3
  requires-python = ">=3.11"
4
- <<<<<<< HEAD
5
  resolution-markers = [
6
  "python_full_version >= '3.14' and sys_platform == 'win32'",
7
  "python_full_version >= '3.14' and sys_platform == 'emscripten'",
@@ -23,7 +22,8 @@ wheels = [
23
  { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" },
24
  ]
25
  =======
26
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
27
 
28
  [[package]]
29
  name = "annotated-types"
@@ -67,7 +67,6 @@ wheels = [
67
  ]
68
 
69
  [[package]]
70
- <<<<<<< HEAD
71
  name = "audioop-lts"
72
  version = "0.2.2"
73
  source = { registry = "https://pypi.org/simple" }
@@ -125,16 +124,17 @@ wheels = [
125
 
126
  [[package]]
127
  =======
128
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
129
  name = "beacon"
130
  version = "0.1.0"
131
  source = { virtual = "." }
132
  dependencies = [
133
  { name = "anthropic" },
134
- <<<<<<< HEAD
135
  { name = "gradio" },
136
  =======
137
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
138
  { name = "langgraph" },
139
  { name = "openai" },
140
  { name = "python-dotenv" },
@@ -144,10 +144,10 @@ dependencies = [
144
  [package.metadata]
145
  requires-dist = [
146
  { name = "anthropic", specifier = ">=0.50.0" },
147
- <<<<<<< HEAD
148
  { name = "gradio", specifier = ">=4.0.0" },
149
  =======
150
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
151
  { name = "langgraph", specifier = ">=1.2.0" },
152
  { name = "openai", specifier = ">=1.0.0" },
153
  { name = "python-dotenv", specifier = ">=1.2.2" },
@@ -155,7 +155,6 @@ requires-dist = [
155
  ]
156
 
157
  [[package]]
158
- <<<<<<< HEAD
159
  name = "brotli"
160
  version = "1.2.0"
161
  source = { registry = "https://pypi.org/simple" }
@@ -205,7 +204,8 @@ wheels = [
205
 
206
  [[package]]
207
  =======
208
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
209
  name = "certifi"
210
  version = "2026.4.22"
211
  source = { registry = "https://pypi.org/simple" }
@@ -304,7 +304,6 @@ wheels = [
304
  ]
305
 
306
  [[package]]
307
- <<<<<<< HEAD
308
  name = "click"
309
  version = "8.3.3"
310
  source = { registry = "https://pypi.org/simple" }
@@ -318,7 +317,8 @@ wheels = [
318
 
319
  [[package]]
320
  =======
321
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
322
  name = "colorama"
323
  version = "0.4.6"
324
  source = { registry = "https://pypi.org/simple" }
@@ -346,7 +346,6 @@ wheels = [
346
  ]
347
 
348
  [[package]]
349
- <<<<<<< HEAD
350
  name = "fastapi"
351
  version = "0.136.1"
352
  source = { registry = "https://pypi.org/simple" }
@@ -446,7 +445,8 @@ wheels = [
446
 
447
  [[package]]
448
  =======
449
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
450
  name = "h11"
451
  version = "0.16.0"
452
  source = { registry = "https://pypi.org/simple" }
@@ -456,7 +456,6 @@ wheels = [
456
  ]
457
 
458
  [[package]]
459
- <<<<<<< HEAD
460
  name = "hf-gradio"
461
  version = "0.4.1"
462
  source = { registry = "https://pypi.org/simple" }
@@ -503,7 +502,8 @@ wheels = [
503
 
504
  [[package]]
505
  =======
506
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
507
  name = "httpcore"
508
  version = "1.0.9"
509
  source = { registry = "https://pypi.org/simple" }
@@ -532,7 +532,6 @@ wheels = [
532
  ]
533
 
534
  [[package]]
535
- <<<<<<< HEAD
536
  name = "huggingface-hub"
537
  version = "1.14.0"
538
  source = { registry = "https://pypi.org/simple" }
@@ -554,7 +553,8 @@ wheels = [
554
 
555
  [[package]]
556
  =======
557
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
558
  name = "idna"
559
  version = "3.14"
560
  source = { registry = "https://pypi.org/simple" }
@@ -564,7 +564,6 @@ wheels = [
564
  ]
565
 
566
  [[package]]
567
- <<<<<<< HEAD
568
  name = "jinja2"
569
  version = "3.1.6"
570
  source = { registry = "https://pypi.org/simple" }
@@ -578,7 +577,8 @@ wheels = [
578
 
579
  [[package]]
580
  =======
581
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
582
  name = "jiter"
583
  version = "0.14.0"
584
  source = { registry = "https://pypi.org/simple" }
@@ -810,7 +810,6 @@ wheels = [
810
  ]
811
 
812
  [[package]]
813
- <<<<<<< HEAD
814
  name = "markupsafe"
815
  version = "3.0.3"
816
  source = { registry = "https://pypi.org/simple" }
@@ -886,7 +885,8 @@ wheels = [
886
 
887
  [[package]]
888
  =======
889
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
890
  name = "mdurl"
891
  version = "0.1.2"
892
  source = { registry = "https://pypi.org/simple" }
@@ -896,7 +896,6 @@ wheels = [
896
  ]
897
 
898
  [[package]]
899
- <<<<<<< HEAD
900
  name = "numpy"
901
  version = "2.4.4"
902
  source = { registry = "https://pypi.org/simple" }
@@ -977,7 +976,8 @@ wheels = [
977
 
978
  [[package]]
979
  =======
980
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
981
  name = "openai"
982
  version = "2.36.0"
983
  source = { registry = "https://pypi.org/simple" }
@@ -1122,7 +1122,6 @@ wheels = [
1122
  ]
1123
 
1124
  [[package]]
1125
- <<<<<<< HEAD
1126
  name = "pandas"
1127
  version = "3.0.3"
1128
  source = { registry = "https://pypi.org/simple" }
@@ -1271,7 +1270,8 @@ wheels = [
1271
 
1272
  [[package]]
1273
  =======
1274
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
1275
  name = "pydantic"
1276
  version = "2.13.4"
1277
  source = { registry = "https://pypi.org/simple" }
@@ -1389,7 +1389,6 @@ wheels = [
1389
  ]
1390
 
1391
  [[package]]
1392
- <<<<<<< HEAD
1393
  name = "pydub"
1394
  version = "0.25.1"
1395
  source = { registry = "https://pypi.org/simple" }
@@ -1400,7 +1399,8 @@ wheels = [
1400
 
1401
  [[package]]
1402
  =======
1403
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
1404
  name = "pygments"
1405
  version = "2.20.0"
1406
  source = { registry = "https://pypi.org/simple" }
@@ -1410,7 +1410,6 @@ wheels = [
1410
  ]
1411
 
1412
  [[package]]
1413
- <<<<<<< HEAD
1414
  name = "python-dateutil"
1415
  version = "2.9.0.post0"
1416
  source = { registry = "https://pypi.org/simple" }
@@ -1424,7 +1423,8 @@ wheels = [
1424
 
1425
  [[package]]
1426
  =======
1427
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
1428
  name = "python-dotenv"
1429
  version = "1.2.2"
1430
  source = { registry = "https://pypi.org/simple" }
@@ -1434,7 +1434,6 @@ wheels = [
1434
  ]
1435
 
1436
  [[package]]
1437
- <<<<<<< HEAD
1438
  name = "python-multipart"
1439
  version = "0.0.28"
1440
  source = { registry = "https://pypi.org/simple" }
@@ -1454,7 +1453,8 @@ wheels = [
1454
 
1455
  [[package]]
1456
  =======
1457
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
1458
  name = "pyyaml"
1459
  version = "6.0.3"
1460
  source = { registry = "https://pypi.org/simple" }
@@ -1550,7 +1550,6 @@ wheels = [
1550
  ]
1551
 
1552
  [[package]]
1553
- <<<<<<< HEAD
1554
  name = "safehttpx"
1555
  version = "0.1.7"
1556
  source = { registry = "https://pypi.org/simple" }
@@ -1591,7 +1590,8 @@ wheels = [
1591
 
1592
  [[package]]
1593
  =======
1594
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
1595
  name = "sniffio"
1596
  version = "1.3.1"
1597
  source = { registry = "https://pypi.org/simple" }
@@ -1601,7 +1601,6 @@ wheels = [
1601
  ]
1602
 
1603
  [[package]]
1604
- <<<<<<< HEAD
1605
  name = "starlette"
1606
  version = "1.0.0"
1607
  source = { registry = "https://pypi.org/simple" }
@@ -1616,7 +1615,8 @@ wheels = [
1616
 
1617
  [[package]]
1618
  =======
1619
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
1620
  name = "tenacity"
1621
  version = "9.1.4"
1622
  source = { registry = "https://pypi.org/simple" }
@@ -1626,7 +1626,6 @@ wheels = [
1626
  ]
1627
 
1628
  [[package]]
1629
- <<<<<<< HEAD
1630
  name = "tomlkit"
1631
  version = "0.14.0"
1632
  source = { registry = "https://pypi.org/simple" }
@@ -1637,7 +1636,8 @@ wheels = [
1637
 
1638
  [[package]]
1639
  =======
1640
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
1641
  name = "tqdm"
1642
  version = "4.67.3"
1643
  source = { registry = "https://pypi.org/simple" }
@@ -1650,7 +1650,6 @@ wheels = [
1650
  ]
1651
 
1652
  [[package]]
1653
- <<<<<<< HEAD
1654
  name = "typer"
1655
  version = "0.25.1"
1656
  source = { registry = "https://pypi.org/simple" }
@@ -1667,7 +1666,8 @@ wheels = [
1667
 
1668
  [[package]]
1669
  =======
1670
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
1671
  name = "typing-extensions"
1672
  version = "4.15.0"
1673
  source = { registry = "https://pypi.org/simple" }
@@ -1689,7 +1689,6 @@ wheels = [
1689
  ]
1690
 
1691
  [[package]]
1692
- <<<<<<< HEAD
1693
  name = "tzdata"
1694
  version = "2026.2"
1695
  source = { registry = "https://pypi.org/simple" }
@@ -1700,7 +1699,8 @@ wheels = [
1700
 
1701
  [[package]]
1702
  =======
1703
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
1704
  name = "urllib3"
1705
  version = "2.7.0"
1706
  source = { registry = "https://pypi.org/simple" }
@@ -1810,7 +1810,6 @@ wheels = [
1810
  ]
1811
 
1812
  [[package]]
1813
- <<<<<<< HEAD
1814
  name = "uvicorn"
1815
  version = "0.47.0"
1816
  source = { registry = "https://pypi.org/simple" }
@@ -1825,7 +1824,8 @@ wheels = [
1825
 
1826
  [[package]]
1827
  =======
1828
- >>>>>>> 6977736 (Initial release: Beacon rare disease clinical trial finder)
 
1829
  name = "xxhash"
1830
  version = "3.7.0"
1831
  source = { registry = "https://pypi.org/simple" }
 
1
  version = 1
2
  revision = 3
3
  requires-python = ">=3.11"
 
4
  resolution-markers = [
5
  "python_full_version >= '3.14' and sys_platform == 'win32'",
6
  "python_full_version >= '3.14' and sys_platform == 'emscripten'",
 
22
  { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" },
23
  ]
24
  =======
25
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
26
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
27
 
28
  [[package]]
29
  name = "annotated-types"
 
67
  ]
68
 
69
  [[package]]
 
70
  name = "audioop-lts"
71
  version = "0.2.2"
72
  source = { registry = "https://pypi.org/simple" }
 
124
 
125
  [[package]]
126
  =======
127
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
128
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
129
  name = "beacon"
130
  version = "0.1.0"
131
  source = { virtual = "." }
132
  dependencies = [
133
  { name = "anthropic" },
 
134
  { name = "gradio" },
135
  =======
136
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
137
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
138
  { name = "langgraph" },
139
  { name = "openai" },
140
  { name = "python-dotenv" },
 
144
  [package.metadata]
145
  requires-dist = [
146
  { name = "anthropic", specifier = ">=0.50.0" },
 
147
  { name = "gradio", specifier = ">=4.0.0" },
148
  =======
149
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
150
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
151
  { name = "langgraph", specifier = ">=1.2.0" },
152
  { name = "openai", specifier = ">=1.0.0" },
153
  { name = "python-dotenv", specifier = ">=1.2.2" },
 
155
  ]
156
 
157
  [[package]]
 
158
  name = "brotli"
159
  version = "1.2.0"
160
  source = { registry = "https://pypi.org/simple" }
 
204
 
205
  [[package]]
206
  =======
207
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
208
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
209
  name = "certifi"
210
  version = "2026.4.22"
211
  source = { registry = "https://pypi.org/simple" }
 
304
  ]
305
 
306
  [[package]]
 
307
  name = "click"
308
  version = "8.3.3"
309
  source = { registry = "https://pypi.org/simple" }
 
317
 
318
  [[package]]
319
  =======
320
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
321
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
322
  name = "colorama"
323
  version = "0.4.6"
324
  source = { registry = "https://pypi.org/simple" }
 
346
  ]
347
 
348
  [[package]]
 
349
  name = "fastapi"
350
  version = "0.136.1"
351
  source = { registry = "https://pypi.org/simple" }
 
445
 
446
  [[package]]
447
  =======
448
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
449
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
450
  name = "h11"
451
  version = "0.16.0"
452
  source = { registry = "https://pypi.org/simple" }
 
456
  ]
457
 
458
  [[package]]
 
459
  name = "hf-gradio"
460
  version = "0.4.1"
461
  source = { registry = "https://pypi.org/simple" }
 
502
 
503
  [[package]]
504
  =======
505
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
506
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
507
  name = "httpcore"
508
  version = "1.0.9"
509
  source = { registry = "https://pypi.org/simple" }
 
532
  ]
533
 
534
  [[package]]
 
535
  name = "huggingface-hub"
536
  version = "1.14.0"
537
  source = { registry = "https://pypi.org/simple" }
 
553
 
554
  [[package]]
555
  =======
556
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
557
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
558
  name = "idna"
559
  version = "3.14"
560
  source = { registry = "https://pypi.org/simple" }
 
564
  ]
565
 
566
  [[package]]
 
567
  name = "jinja2"
568
  version = "3.1.6"
569
  source = { registry = "https://pypi.org/simple" }
 
577
 
578
  [[package]]
579
  =======
580
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
581
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
582
  name = "jiter"
583
  version = "0.14.0"
584
  source = { registry = "https://pypi.org/simple" }
 
810
  ]
811
 
812
  [[package]]
 
813
  name = "markupsafe"
814
  version = "3.0.3"
815
  source = { registry = "https://pypi.org/simple" }
 
885
 
886
  [[package]]
887
  =======
888
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
889
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
890
  name = "mdurl"
891
  version = "0.1.2"
892
  source = { registry = "https://pypi.org/simple" }
 
896
  ]
897
 
898
  [[package]]
 
899
  name = "numpy"
900
  version = "2.4.4"
901
  source = { registry = "https://pypi.org/simple" }
 
976
 
977
  [[package]]
978
  =======
979
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
980
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
981
  name = "openai"
982
  version = "2.36.0"
983
  source = { registry = "https://pypi.org/simple" }
 
1122
  ]
1123
 
1124
  [[package]]
 
1125
  name = "pandas"
1126
  version = "3.0.3"
1127
  source = { registry = "https://pypi.org/simple" }
 
1270
 
1271
  [[package]]
1272
  =======
1273
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
1274
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
1275
  name = "pydantic"
1276
  version = "2.13.4"
1277
  source = { registry = "https://pypi.org/simple" }
 
1389
  ]
1390
 
1391
  [[package]]
 
1392
  name = "pydub"
1393
  version = "0.25.1"
1394
  source = { registry = "https://pypi.org/simple" }
 
1399
 
1400
  [[package]]
1401
  =======
1402
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
1403
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
1404
  name = "pygments"
1405
  version = "2.20.0"
1406
  source = { registry = "https://pypi.org/simple" }
 
1410
  ]
1411
 
1412
  [[package]]
 
1413
  name = "python-dateutil"
1414
  version = "2.9.0.post0"
1415
  source = { registry = "https://pypi.org/simple" }
 
1423
 
1424
  [[package]]
1425
  =======
1426
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
1427
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
1428
  name = "python-dotenv"
1429
  version = "1.2.2"
1430
  source = { registry = "https://pypi.org/simple" }
 
1434
  ]
1435
 
1436
  [[package]]
 
1437
  name = "python-multipart"
1438
  version = "0.0.28"
1439
  source = { registry = "https://pypi.org/simple" }
 
1453
 
1454
  [[package]]
1455
  =======
1456
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
1457
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
1458
  name = "pyyaml"
1459
  version = "6.0.3"
1460
  source = { registry = "https://pypi.org/simple" }
 
1550
  ]
1551
 
1552
  [[package]]
 
1553
  name = "safehttpx"
1554
  version = "0.1.7"
1555
  source = { registry = "https://pypi.org/simple" }
 
1590
 
1591
  [[package]]
1592
  =======
1593
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
1594
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
1595
  name = "sniffio"
1596
  version = "1.3.1"
1597
  source = { registry = "https://pypi.org/simple" }
 
1601
  ]
1602
 
1603
  [[package]]
 
1604
  name = "starlette"
1605
  version = "1.0.0"
1606
  source = { registry = "https://pypi.org/simple" }
 
1615
 
1616
  [[package]]
1617
  =======
1618
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
1619
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
1620
  name = "tenacity"
1621
  version = "9.1.4"
1622
  source = { registry = "https://pypi.org/simple" }
 
1626
  ]
1627
 
1628
  [[package]]
 
1629
  name = "tomlkit"
1630
  version = "0.14.0"
1631
  source = { registry = "https://pypi.org/simple" }
 
1636
 
1637
  [[package]]
1638
  =======
1639
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
1640
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
1641
  name = "tqdm"
1642
  version = "4.67.3"
1643
  source = { registry = "https://pypi.org/simple" }
 
1650
  ]
1651
 
1652
  [[package]]
 
1653
  name = "typer"
1654
  version = "0.25.1"
1655
  source = { registry = "https://pypi.org/simple" }
 
1666
 
1667
  [[package]]
1668
  =======
1669
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
1670
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
1671
  name = "typing-extensions"
1672
  version = "4.15.0"
1673
  source = { registry = "https://pypi.org/simple" }
 
1689
  ]
1690
 
1691
  [[package]]
 
1692
  name = "tzdata"
1693
  version = "2026.2"
1694
  source = { registry = "https://pypi.org/simple" }
 
1699
 
1700
  [[package]]
1701
  =======
1702
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
1703
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
1704
  name = "urllib3"
1705
  version = "2.7.0"
1706
  source = { registry = "https://pypi.org/simple" }
 
1810
  ]
1811
 
1812
  [[package]]
 
1813
  name = "uvicorn"
1814
  version = "0.47.0"
1815
  source = { registry = "https://pypi.org/simple" }
 
1824
 
1825
  [[package]]
1826
  =======
1827
+ >>>>>>> f8d77e4 (Initial release: Beacon rare disease clinical trial finder)
1828
+ >>>>>>> 96a8855 (Initial release: Beacon rare disease clinical trial finder)
1829
  name = "xxhash"
1830
  version = "3.7.0"
1831
  source = { registry = "https://pypi.org/simple" }