import anthropic from dotenv import load_dotenv import os from fasthtml.common import * from dataclasses import dataclass load_dotenv() # Shared styles SHARED_STYLES = """ @keyframes progress { 0% { width: 0%; } 50% { width: 70%; } 100% { width: 100%; } } .htmx-indicator { display: none; } .htmx-request .htmx-indicator { display: block; } .htmx-request.htmx-indicator { display: block; } .container { max-width: 800px; margin: 50px auto; padding: 20px; font-family: Arial, sans-serif; } .btn-primary { display: inline-block; padding: 10px 20px; background-color: #007bff; color: white; text-decoration: none; border-radius: 5px; border: none; cursor: pointer; } .content-box { white-space: pre-wrap; background-color: #f5f5f5; padding: 20px; border-radius: 5px; line-height: 1.6; } """ def create_loader(): """Create a reusable loading indicator""" return Div( id="loader", cls="htmx-indicator", style="margin-top: 20px;" )( Div("Loading drug information...", style="color: #007bff; font-weight: bold;"), Div(style="width: 100%; height: 4px; background-color: #e0e0e0; margin-top: 10px; border-radius: 2px;")( Div(cls="progress-bar", style="height: 100%; background-color: #007bff; animation: progress 1.5s ease-in-out infinite;") ) ) def stream_drug_info(drug_name): """Stream drug information from Anthropic API""" client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY")) with client.beta.messages.stream( model="claude-sonnet-4-5-20250929", max_tokens=5000, temperature=1, system="You are a professional doctor who when given a drug brand name which is given in form of tablets or syrup form, tells me about the following things: * Side Effects - Common and serious adverse effects * Medicinal Usage - Approved therapeutic indications and how it works", messages=[ { "role": "user", "content": [ { "type": "text", "text": f"Check about the drug brand name: {drug_name}" } ] } ] ) as stream: for text in stream.text_stream: yield text @dataclass class Drug: drug_name: str app, rt = fast_app() @rt('/') def get(): return Titled("Drug Information Predictor", Div(cls="container")( H1("Drug Information Finder"), P("Enter a drug name to get detailed information about it."), Form( id="drug-form", hx_post="/profile", hx_target="#results", hx_indicator="#loader", hx_swap="innerHTML", onsubmit="document.getElementById('drug-form').style.display='none';" )( Fieldset( Label('Drug Name:', Br(), Input(name="drug_name", placeholder="e.g., Gelusil", required=True)), ), Br(), Button("Get Drug Info", type="submit", cls="btn-primary"), ), create_loader(), Div(id="results"), Style(SHARED_STYLES) ) ) @rt('/profile', methods=['POST']) def post(drug: Drug): print(f"Received POST request for drug: {drug.drug_name}") # Debug print def generate(): # Hide the loader and show initial response yield Script(""" document.getElementById('loader').style.display = 'none'; """) yield Div(cls="container")( H1(f"Drug Information: {drug.drug_name}"), Div(id="streaming-content", cls="content-box"), Br(), A("Search Another Drug", href="/", cls="btn-primary") ) # Stream content chunks for chunk in stream_drug_info(drug.drug_name): yield Script(f""" document.getElementById('streaming-content').innerHTML += {repr(chunk)}; """) return generate() serve()