We are going to build a clinical note analyzer that turns unstructured discharge summaries into structured JSON with patient demographics, diagnoses, medications, and safety flags. This is useful for developers building health record pipelines, triage tools, or research data extractors who need predictable costs even when notes run long.
What you'll need
You need Python 3.10 or newer, the OpenAI SDK, and an Oxlo.ai API key.
- Python 3.10+
pip install openai- An Oxlo.ai API key from https://portal.oxlo.ai
Because Oxlo.ai uses flat per-request pricing, you can send long clinical notes and detailed system prompts without the cost scaling by token count. See the exact rates at https://oxlo.ai/pricing.
Step 1: Configure the Oxlo.ai client
Instantiate the OpenAI client pointing at Oxlo.ai. I use llama-3.3-70b as the workhorse for reliable JSON generation.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[{"role": "user", "content": "ping"}],
)
print(response.choices[0].message.content)
Step 2: Define the extraction system prompt
The prompt needs to be explicit about medical entities and output schema. Because Oxlo.ai charges per request rather than per token, I make this prompt as verbose as necessary without worrying about input length.
SYSTEM_PROMPT = """You are a clinical data extraction assistant. Your job is to read an unstructured clinical note and return a single JSON object with exactly these keys:
- patient_id: string, or null if not found
- age: integer, or null
- sex: "male", "female", or null
- diagnoses: list of strings
- medications: list of objects with keys "name", "dose", "frequency"
- allergies: list of strings
- procedures: list of strings
- follow_up_instructions: string, or null
Rules:
1. Extract only what is explicitly stated in the note. Do not infer diagnoses.
2. If a medication dose or frequency is missing, use null for that field.
3. Return strictly valid JSON. No markdown fences, no commentary.
"""
Step 3: Ingest and sanitize raw notes
I wrap the raw note in a user message and enforce JSON mode so the model cannot return free text.
import json
def extract_clinical_data(raw_note: str):
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": raw_note},
],
response_format={"type": "json_object"},
temperature=0.1,
)
return response.choices[0].message.content
Step 4: Add a drug interaction safety pass
After extraction, I run a second call to check for potentially dangerous combinations. For this reasoning step I switch to kimi-k2.6 on Oxlo.ai. Since pricing is request-based, sending the full medication list plus context to a large reasoning model costs the same flat rate regardless of how long the note is.
SAFETY_PROMPT = """You are a clinical safety reviewer. Given a JSON blob of patient medications and allergies, list any potential drug-drug interactions or allergy conflicts. Return a JSON object with keys:
- interactions: list of objects with keys "drugs" (list of two strings), "severity" ("mild", "moderate", "severe"), and "note" (string explanation)
- allergy_flags: list of objects with keys "allergen" and "flagged_drug"
If none are found, return empty lists. Return strictly valid JSON."""
def check_safety(extracted_json: str):
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SAFETY_PROMPT},
{"role": "user", "content": extracted_json},
],
response_format={"type": "json_object"},
temperature=0.2,
)
return response.choices[0].message.content
Step 5: Wire the pipeline together
I combine the two calls into a single pipeline function and pretty-print the results.
def analyze_note(raw_note: str):
extracted = extract_clinical_data(raw_note)
safety = check_safety(extracted)
return {
"structured_data": json.loads(extracted),
"safety_review": json.loads(safety)
}
if __name__ == "__main__":
sample_note = """
Admission Date: 2024-03-15
Patient ID: P-8842
Age: 67, Sex: Male
Chief Complaint: Chest pain, shortness of breath
Diagnoses: Acute myocardial infarction, Hypertension, Type 2 Diabetes
Procedures: Cardiac catheterization with stent placement
Medications: Aspirin 81 mg daily, Metformin 500 mg twice daily, Lisinopril 10 mg daily, Atorvastatin 40 mg nightly
Allergies: Penicillin
Follow-up: Cardiology clinic in 1 week, primary care in 2 weeks.
"""
result = analyze_note(sample_note)
print(json.dumps(result, indent=2))
Run it
Executing the script produces structured data and a safety review in one shot.
{
"structured_data": {
"patient_id": "P-8842",
"age": 67,
"sex": "male",
"diagnoses": [
"Acute myocardial infarction",
"Hypertension",
"Type 2 Diabetes"
],
"medications": [
{"name": "Aspirin", "dose": "81 mg", "frequency": "daily"},
{"name": "Metformin", "dose": "500 mg", "frequency": "twice daily"},
{"name": "Lisinopril", "dose": "10 mg", "frequency": "daily"},
{"name": "Atorvastatin", "dose": "40 mg", "frequency": "nightly"}
],
"allergies": ["Penicillin"],
"procedures": ["Cardiac catheterization with stent placement"],
"follow_up_instructions": "Cardiology clinic in 1 week, primary care in 2 weeks."
},
"safety_review": {
"interactions": [],
"allergy_flags": []
}
}
Wrap-up
From here, you can add function calling to query a live drug interaction database such as RxNorm for real-time checks instead of relying solely on model knowledge. You can also expand the pipeline to batch process entire EHR exports, which is where Oxlo.ai's per-request pricing really shines because long documents do not inflate your bill.







