Spaces:
Sleeping
Sleeping
KevinIsInCoding claude[bot] KevinIsInCoding commited on
feat: add Observational study type support (#7)
Browse filesAdd support for filtering observational studies from ClinicalTrials.gov
alongside existing Interventional and Expanded Access searches.
- Extend SEARCH_TRIALS_TOOL enum with OBSERVATIONAL study_type
- Add studyType:obs aggFilter handling in search_trials_api
- Add include_observational field to PatientProfile and SUBMIT_PROFILE_TOOL
- Update INTAKE_SYSTEM prompt to present observational studies as an option
- Update RESEARCH_SYSTEM prompt to guide separate observational searches
- Propagate include_observational from intake tool result in both CLI and web app
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: KevinIsInCoding <KevinIsInCoding@users.noreply.github.com>
- app.py +1 -0
- clinical_trials_guru.py +27 -7
app.py
CHANGED
|
@@ -65,6 +65,7 @@ def _intake_turn(
|
|
| 65 |
radius_miles=data.get("radius_miles", 100),
|
| 66 |
phases=data.get("phases") or [],
|
| 67 |
include_eap=data.get("include_eap", False),
|
|
|
|
| 68 |
lang=lang,
|
| 69 |
)
|
| 70 |
return text or UI[lang]["got_it"], messages, profile
|
|
|
|
| 65 |
radius_miles=data.get("radius_miles", 100),
|
| 66 |
phases=data.get("phases") or [],
|
| 67 |
include_eap=data.get("include_eap", False),
|
| 68 |
+
include_observational=data.get("include_observational", False),
|
| 69 |
lang=lang,
|
| 70 |
)
|
| 71 |
return text or UI[lang]["got_it"], messages, profile
|
clinical_trials_guru.py
CHANGED
|
@@ -70,6 +70,10 @@ SUBMIT_PROFILE_TOOL: anthropic.types.ToolParam = {
|
|
| 70 |
"type": "boolean",
|
| 71 |
"description": "Whether patient is interested in Expanded Access Programs (compassionate use)",
|
| 72 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
},
|
| 74 |
"required": ["disease", "age", "onset_months", "diagnosis_months", "zip_code"],
|
| 75 |
},
|
|
@@ -82,7 +86,8 @@ SEARCH_TRIALS_TOOL: anthropic.types.ToolParam = {
|
|
| 82 |
"Results are pre-ranked by distance from the patient's location. "
|
| 83 |
"Call multiple times with different parameters (synonyms, broader radius, "
|
| 84 |
"different phases) if initial results are sparse. "
|
| 85 |
-
"Use study_type='EXPANDED_ACCESS' to search for Expanded Access Programs (EAP / compassionate use)."
|
|
|
|
| 86 |
),
|
| 87 |
"input_schema": {
|
| 88 |
"type": "object",
|
|
@@ -107,8 +112,8 @@ SEARCH_TRIALS_TOOL: anthropic.types.ToolParam = {
|
|
| 107 |
},
|
| 108 |
"study_type": {
|
| 109 |
"type": "string",
|
| 110 |
-
"enum": ["INTERVENTIONAL", "EXPANDED_ACCESS"],
|
| 111 |
-
"description": "INTERVENTIONAL (default) for clinical trials; EXPANDED_ACCESS for EAP/compassionate use.",
|
| 112 |
},
|
| 113 |
},
|
| 114 |
"required": ["condition", "lat", "lon", "radius_miles"],
|
|
@@ -161,13 +166,17 @@ OPTIONAL (ask based on disease):
|
|
| 161 |
Not Applicable β Studies that do not fall into the standard phase framework
|
| 162 |
(e.g., device feasibility studies, behavioral/observational trials,
|
| 163 |
or studies where phase designation is not required by FDA).
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
- Expanded Access Programs (EAP / compassionate use) β a pathway for patients who
|
| 165 |
do not qualify for or cannot access a clinical trial to receive an investigational
|
| 166 |
drug, biologic, or device outside of a trial. Also called "compassionate use."
|
| 167 |
The treatment is not yet FDA-approved; a physician must submit the EAP request
|
| 168 |
to the drug sponsor and obtain FDA authorization. EAP does not guarantee efficacy
|
| 169 |
but may be an option when no approved treatments remain.
|
| 170 |
-
- Or
|
| 171 |
|
| 172 |
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.\
|
| 173 |
"""
|
|
@@ -180,20 +189,22 @@ Results are already ranked by geographic distance from the patient.
|
|
| 180 |
Workflow:
|
| 181 |
1. Search for the patient's disease. Use both the full medical name and common abbreviation.
|
| 182 |
- If the patient wants clinical trials, search with study_type="INTERVENTIONAL".
|
|
|
|
| 183 |
- If the patient wants Expanded Access Programs (EAP), also search with study_type="EXPANDED_ACCESS".
|
| 184 |
-
-
|
| 185 |
2. IMPORTANT β phase filtering: Never pass phases=["1","2","3","4"] to mean "all phases."
|
| 186 |
Always pass phases=[] (omit the field) when the patient has no phase preference.
|
| 187 |
NA-phase trials (device feasibility studies, unphased interventions) only appear
|
| 188 |
when no phase filter is applied. Passing explicit phase numbers silently excludes them.
|
|
|
|
| 189 |
3. If fewer than 3 results are found, retry with: a wider radius, a disease synonym,
|
| 190 |
or drop phase filters entirely (phases=[]).
|
| 191 |
-
4. Produce a final report. Use separate sections for Clinical Trials and Expanded Access
|
| 192 |
List the top 10 results per section ranked by site proximity.
|
| 193 |
For EACH entry use exactly this format (repeat the block per entry):
|
| 194 |
|
| 195 |
π **[Closest hospital/facility name]** β [City, State] ([X] mi)
|
| 196 |
-
**Trial:** [Full title] ([Phase] β or "Expanded Access"
|
| 197 |
**Sponsor:** [Lead sponsor]
|
| 198 |
**Principal Investigator:** [Name β or "Not listed" if absent]
|
| 199 |
**Contact:** [Phone number] | [Email address] (use "Not listed" for any missing field)
|
|
@@ -207,6 +218,7 @@ Workflow:
|
|
| 207 |
---
|
| 208 |
|
| 209 |
5. After the results add a short "Next steps" section (bullet points).
|
|
|
|
| 210 |
For EAP results, note that patients typically need a physician to submit the EAP request.
|
| 211 |
|
| 212 |
IMPORTANT: Only report trials returned by the search_clinical_trials tool. Do NOT suggest,
|
|
@@ -233,6 +245,7 @@ class PatientProfile:
|
|
| 233 |
radius_miles: int = 100
|
| 234 |
phases: list[str] = field(default_factory=list)
|
| 235 |
include_eap: bool = False
|
|
|
|
| 236 |
lang: str = "en"
|
| 237 |
|
| 238 |
def summary(self) -> str:
|
|
@@ -259,6 +272,8 @@ class PatientProfile:
|
|
| 259 |
labels = [_phase_label(p) for p in self.phases]
|
| 260 |
lines.append(f"Phases: {', '.join(labels)}")
|
| 261 |
interests = ["Clinical trials"]
|
|
|
|
|
|
|
| 262 |
if self.include_eap:
|
| 263 |
interests.append("Expanded Access Programs (EAP)")
|
| 264 |
lines.append(f"Study type interest: {', '.join(interests)}")
|
|
@@ -302,6 +317,7 @@ def search_trials_api(
|
|
| 302 |
study_type: str = "INTERVENTIONAL",
|
| 303 |
) -> list[dict]:
|
| 304 |
is_eap = study_type == "EXPANDED_ACCESS"
|
|
|
|
| 305 |
params: dict[str, str | int] = {
|
| 306 |
"query.cond": condition,
|
| 307 |
"filter.overallStatus": "AVAILABLE" if is_eap else "RECRUITING",
|
|
@@ -312,8 +328,11 @@ def search_trials_api(
|
|
| 312 |
# aggFilters accepts only one value; studyType and phase can't be combined.
|
| 313 |
# RECRUITING status already excludes EAPs, so studyType:int is only needed
|
| 314 |
# when no phase filter is applied. studyType:int returns all phases including N/A.
|
|
|
|
| 315 |
if is_eap:
|
| 316 |
params["aggFilters"] = "studyType:exp"
|
|
|
|
|
|
|
| 317 |
elif phases:
|
| 318 |
# Exclude "na" from the phase filter β N/A trials have no phase value to match on;
|
| 319 |
# they appear naturally when no phase filter is applied (studyType:int branch).
|
|
@@ -471,6 +490,7 @@ def run_intake_agent(client: anthropic.Anthropic) -> PatientProfile:
|
|
| 471 |
radius_miles=data.get("radius_miles", 100),
|
| 472 |
phases=data.get("phases") or [],
|
| 473 |
include_eap=data.get("include_eap", False),
|
|
|
|
| 474 |
)
|
| 475 |
|
| 476 |
messages.append({"role": "assistant", "content": response.content})
|
|
|
|
| 70 |
"type": "boolean",
|
| 71 |
"description": "Whether patient is interested in Expanded Access Programs (compassionate use)",
|
| 72 |
},
|
| 73 |
+
"include_observational": {
|
| 74 |
+
"type": "boolean",
|
| 75 |
+
"description": "Whether patient is interested in observational studies (no experimental treatment; researchers observe and measure outcomes)",
|
| 76 |
+
},
|
| 77 |
},
|
| 78 |
"required": ["disease", "age", "onset_months", "diagnosis_months", "zip_code"],
|
| 79 |
},
|
|
|
|
| 86 |
"Results are pre-ranked by distance from the patient's location. "
|
| 87 |
"Call multiple times with different parameters (synonyms, broader radius, "
|
| 88 |
"different phases) if initial results are sparse. "
|
| 89 |
+
"Use study_type='EXPANDED_ACCESS' to search for Expanded Access Programs (EAP / compassionate use). "
|
| 90 |
+
"Use study_type='OBSERVATIONAL' to search for observational studies (no experimental treatment assigned)."
|
| 91 |
),
|
| 92 |
"input_schema": {
|
| 93 |
"type": "object",
|
|
|
|
| 112 |
},
|
| 113 |
"study_type": {
|
| 114 |
"type": "string",
|
| 115 |
+
"enum": ["INTERVENTIONAL", "EXPANDED_ACCESS", "OBSERVATIONAL"],
|
| 116 |
+
"description": "INTERVENTIONAL (default) for clinical trials; EXPANDED_ACCESS for EAP/compassionate use; OBSERVATIONAL for observational studies.",
|
| 117 |
},
|
| 118 |
},
|
| 119 |
"required": ["condition", "lat", "lon", "radius_miles"],
|
|
|
|
| 166 |
Not Applicable β Studies that do not fall into the standard phase framework
|
| 167 |
(e.g., device feasibility studies, behavioral/observational trials,
|
| 168 |
or studies where phase designation is not required by FDA).
|
| 169 |
+
- Observational studies β studies where researchers observe participants and collect
|
| 170 |
+
data without assigning treatments. No experimental drug or intervention is given.
|
| 171 |
+
Patients may contribute valuable data to disease understanding, registries, or
|
| 172 |
+
natural history studies. Often have broader eligibility than interventional trials.
|
| 173 |
- Expanded Access Programs (EAP / compassionate use) β a pathway for patients who
|
| 174 |
do not qualify for or cannot access a clinical trial to receive an investigational
|
| 175 |
drug, biologic, or device outside of a trial. Also called "compassionate use."
|
| 176 |
The treatment is not yet FDA-approved; a physician must submit the EAP request
|
| 177 |
to the drug sponsor and obtain FDA authorization. EAP does not guarantee efficacy
|
| 178 |
but may be an option when no approved treatments remain.
|
| 179 |
+
- Or any combination; or all types (default if no preference)
|
| 180 |
|
| 181 |
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.\
|
| 182 |
"""
|
|
|
|
| 189 |
Workflow:
|
| 190 |
1. Search for the patient's disease. Use both the full medical name and common abbreviation.
|
| 191 |
- If the patient wants clinical trials, search with study_type="INTERVENTIONAL".
|
| 192 |
+
- If the patient wants observational studies, also search with study_type="OBSERVATIONAL".
|
| 193 |
- If the patient wants Expanded Access Programs (EAP), also search with study_type="EXPANDED_ACCESS".
|
| 194 |
+
- Run a separate search for each study_type the patient is interested in.
|
| 195 |
2. IMPORTANT β phase filtering: Never pass phases=["1","2","3","4"] to mean "all phases."
|
| 196 |
Always pass phases=[] (omit the field) when the patient has no phase preference.
|
| 197 |
NA-phase trials (device feasibility studies, unphased interventions) only appear
|
| 198 |
when no phase filter is applied. Passing explicit phase numbers silently excludes them.
|
| 199 |
+
Phase filters do not apply to OBSERVATIONAL or EXPANDED_ACCESS searches.
|
| 200 |
3. If fewer than 3 results are found, retry with: a wider radius, a disease synonym,
|
| 201 |
or drop phase filters entirely (phases=[]).
|
| 202 |
+
4. Produce a final report. Use separate sections for Clinical Trials, Observational Studies, and Expanded Access as applicable.
|
| 203 |
List the top 10 results per section ranked by site proximity.
|
| 204 |
For EACH entry use exactly this format (repeat the block per entry):
|
| 205 |
|
| 206 |
π **[Closest hospital/facility name]** β [City, State] ([X] mi)
|
| 207 |
+
**Trial:** [Full title] ([Phase] β or "Observational" / "Expanded Access" as applicable)
|
| 208 |
**Sponsor:** [Lead sponsor]
|
| 209 |
**Principal Investigator:** [Name β or "Not listed" if absent]
|
| 210 |
**Contact:** [Phone number] | [Email address] (use "Not listed" for any missing field)
|
|
|
|
| 218 |
---
|
| 219 |
|
| 220 |
5. After the results add a short "Next steps" section (bullet points).
|
| 221 |
+
For observational studies, note that participation typically involves check-ins, surveys, or sample collection with no experimental treatment.
|
| 222 |
For EAP results, note that patients typically need a physician to submit the EAP request.
|
| 223 |
|
| 224 |
IMPORTANT: Only report trials returned by the search_clinical_trials tool. Do NOT suggest,
|
|
|
|
| 245 |
radius_miles: int = 100
|
| 246 |
phases: list[str] = field(default_factory=list)
|
| 247 |
include_eap: bool = False
|
| 248 |
+
include_observational: bool = False
|
| 249 |
lang: str = "en"
|
| 250 |
|
| 251 |
def summary(self) -> str:
|
|
|
|
| 272 |
labels = [_phase_label(p) for p in self.phases]
|
| 273 |
lines.append(f"Phases: {', '.join(labels)}")
|
| 274 |
interests = ["Clinical trials"]
|
| 275 |
+
if self.include_observational:
|
| 276 |
+
interests.append("Observational studies")
|
| 277 |
if self.include_eap:
|
| 278 |
interests.append("Expanded Access Programs (EAP)")
|
| 279 |
lines.append(f"Study type interest: {', '.join(interests)}")
|
|
|
|
| 317 |
study_type: str = "INTERVENTIONAL",
|
| 318 |
) -> list[dict]:
|
| 319 |
is_eap = study_type == "EXPANDED_ACCESS"
|
| 320 |
+
is_observational = study_type == "OBSERVATIONAL"
|
| 321 |
params: dict[str, str | int] = {
|
| 322 |
"query.cond": condition,
|
| 323 |
"filter.overallStatus": "AVAILABLE" if is_eap else "RECRUITING",
|
|
|
|
| 328 |
# aggFilters accepts only one value; studyType and phase can't be combined.
|
| 329 |
# RECRUITING status already excludes EAPs, so studyType:int is only needed
|
| 330 |
# when no phase filter is applied. studyType:int returns all phases including N/A.
|
| 331 |
+
# Observational studies use studyType:obs; phases don't apply to them.
|
| 332 |
if is_eap:
|
| 333 |
params["aggFilters"] = "studyType:exp"
|
| 334 |
+
elif is_observational:
|
| 335 |
+
params["aggFilters"] = "studyType:obs"
|
| 336 |
elif phases:
|
| 337 |
# Exclude "na" from the phase filter β N/A trials have no phase value to match on;
|
| 338 |
# they appear naturally when no phase filter is applied (studyType:int branch).
|
|
|
|
| 490 |
radius_miles=data.get("radius_miles", 100),
|
| 491 |
phases=data.get("phases") or [],
|
| 492 |
include_eap=data.get("include_eap", False),
|
| 493 |
+
include_observational=data.get("include_observational", False),
|
| 494 |
)
|
| 495 |
|
| 496 |
messages.append({"role": "assistant", "content": response.content})
|