|
import gradio as gr |
|
from openai import OpenAI |
|
import base64 |
|
from PIL import Image |
|
import io |
|
import json |
|
|
|
|
|
client = OpenAI( |
|
base_url="https://integrate.api.nvidia.com/v1", |
|
api_key="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" |
|
) |
|
|
|
|
|
css = """ |
|
.test-card { |
|
border-left: 4px solid #4a90e2; |
|
padding: 1rem; |
|
margin-bottom: 1rem; |
|
background-color: #ffffff; |
|
border-radius: 8px; |
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1); |
|
} |
|
.test-card h4 { |
|
color: #2c3e50; |
|
margin-top: 0; |
|
margin-bottom: 0.5rem; |
|
} |
|
.test-card p { |
|
color: #333333; |
|
margin-bottom: 0.25rem; |
|
} |
|
.critical { |
|
background-color: #ffebee; |
|
color: #c62828; |
|
font-weight: bold; |
|
padding: 0.1rem 0.3rem; |
|
border-radius: 3px; |
|
} |
|
.abnormal { |
|
background-color: #fff8e1; |
|
color: #ff8f00; |
|
padding: 0.1rem 0.3rem; |
|
border-radius: 3px; |
|
} |
|
.normal { |
|
background-color: #e8f5e9; |
|
color: #2e7d32; |
|
padding: 0.1rem 0.3rem; |
|
border-radius: 3px; |
|
} |
|
.summary-card { |
|
background-color: #e3f2fd; |
|
padding: 1rem; |
|
border-radius: 8px; |
|
margin-bottom: 1rem; |
|
border-left: 4px solid #1565c0; |
|
} |
|
.summary-card h4 { |
|
color: #0d47a1; |
|
} |
|
""" |
|
|
|
|
|
def get_prompt_template(mode, rtype): |
|
base_prompt = { |
|
"Standard Report": """Extract all key information from this test report in structured JSON format...""", |
|
"Detailed Analysis": """Analyze this test report thoroughly and provide...""", |
|
"Comparative Assessment": """Perform comparative analysis of this test report including...""" |
|
} |
|
|
|
type_specific = { |
|
"Medical/Lab": " Focus on clinical significance and health implications.", |
|
"Academic": " Analyze performance patterns and competency areas.", |
|
"Technical": " Evaluate against technical specifications and tolerances.", |
|
"Other": "" |
|
} |
|
|
|
return base_prompt[mode] + type_specific[rtype] |
|
|
|
def analyze_report(file, analysis_mode, report_type): |
|
|
|
if file is None: |
|
return "Please upload a file first", None, None |
|
|
|
|
|
if file.name.lower().endswith(('.jpg', '.jpeg', '.png')): |
|
image = Image.open(file) |
|
buffered = io.BytesIO() |
|
image.save(buffered, format="JPEG") |
|
image_b64 = base64.b64encode(buffered.getvalue()).decode('utf-8') |
|
media_content = { "type": "image_url", "image_url": { "url": f"data:image/png;base64,{image_b64}" } } |
|
else: |
|
media_content = { "type": "text", "text": "PDF content extraction placeholder" } |
|
|
|
|
|
user_prompt = get_prompt_template(analysis_mode, report_type) |
|
|
|
|
|
completion = client.chat.completions.create( |
|
model="nvidia/llama-3.1-nemotron-nano-vl-8b-v1", |
|
messages=[ |
|
{ |
|
"role": "system", |
|
"content": "You are an expert test report analyzer. Provide accurate, detailed analysis of reports in valid JSON format." |
|
}, |
|
{ |
|
"role": "user", |
|
"content": [ |
|
media_content, |
|
{ "type": "text", "text": user_prompt } |
|
] |
|
} |
|
], |
|
temperature=0.2, |
|
top_p=0.01, |
|
max_tokens=2500 |
|
) |
|
|
|
full_response = completion.choices[0].message.content |
|
|
|
|
|
try: |
|
json_start = full_response.find('{') |
|
json_end = full_response.rfind('}') + 1 |
|
json_str = full_response[json_start:json_end] |
|
parsed_data = json.loads(json_str) |
|
|
|
|
|
html_output = generate_html_output(parsed_data) |
|
|
|
return full_response, html_output, parsed_data.get('summary', 'No summary available') |
|
|
|
except Exception as e: |
|
return f"Error parsing response: {str(e)}", None, None |
|
|
|
def generate_html_output(parsed_data): |
|
html = "<div style='font-family: Arial, sans-serif;'>" |
|
|
|
|
|
metadata = parsed_data.get('metadata', {}) |
|
html += "<div style='display: flex; margin-bottom: 20px;'>" |
|
html += f"<div style='flex: 1;'><b>Patient/Subject:</b> {metadata.get('patient_name', 'Not identified')}</div>" |
|
html += f"<div style='flex: 1;'><b>Report Date:</b> {metadata.get('report_date', 'Unknown')}</div>" |
|
|
|
|
|
findings = parsed_data.get('results', parsed_data.get('findings', [])) |
|
abnormal_count = sum(1 for f in findings if f.get('status', '') in ['abnormal', 'critical']) |
|
status = "critical" if any(f.get('status', '') == 'critical' for f in findings) else "abnormal" if abnormal_count > 0 else "normal" |
|
status_class = f"class='{status}'" if status in ['critical', 'abnormal', 'normal'] else "" |
|
html += f"<div style='flex: 1;'><b>Status:</b> <span {status_class}>{status.upper()}</span></div>" |
|
html += "</div>" |
|
|
|
|
|
html += "<h3>Test Results</h3>" |
|
if findings: |
|
for finding in findings: |
|
status_class = finding.get('status', 'normal') |
|
html += f""" |
|
<div class='test-card'> |
|
<h4>{finding.get('test_name', 'Unnamed Test')}</h4> |
|
<p><b>Value:</b> <span class='{status_class}'>{finding.get('value', '')} {finding.get('unit', '')}</span></p> |
|
<p><b>Normal range:</b> {finding.get('normal_range', '')}</p> |
|
<p><b>Interpretation:</b> {finding.get('interpretation', '')}</p> |
|
</div> |
|
""" |
|
else: |
|
html += "<p>No test results were extracted from this report</p>" |
|
|
|
|
|
if 'assessment' in parsed_data or 'summary' in parsed_data: |
|
assessment = parsed_data.get('assessment', {}) |
|
summary = assessment.get('summary', parsed_data.get('summary', '')) |
|
|
|
if summary: |
|
html += f""" |
|
<div class='summary-card'> |
|
<h4>Summary</h4> |
|
<p>{summary}</p> |
|
</div> |
|
""" |
|
|
|
if 'recommendations' in assessment and assessment['recommendations']: |
|
html += "<h4>Recommendations</h4><ul>" |
|
for rec in assessment['recommendations']: |
|
html += f"<li>{rec}</li>" |
|
html += "</ul>" |
|
|
|
html += "</div>" |
|
return html |
|
|
|
|
|
with gr.Blocks(css=css) as demo: |
|
gr.Markdown(""" |
|
# Koshur AI Test Report Analyzer |
|
Advanced analysis of medical, laboratory, and academic test reports. |
|
Identify key findings, anomalies, and generate insights automatically. |
|
""") |
|
|
|
with gr.Row(): |
|
with gr.Column(): |
|
file_input = gr.File(label="Upload Test Report", file_types=[".jpg", ".jpeg", ".png", ".pdf"]) |
|
analysis_mode = gr.Dropdown( |
|
label="Analysis Mode", |
|
choices=["Standard Report", "Detailed Analysis", "Comparative Assessment"], |
|
value="Standard Report" |
|
) |
|
report_type = gr.Dropdown( |
|
label="Report Type", |
|
choices=["Medical/Lab", "Academic", "Technical", "Other"], |
|
value="Medical/Lab" |
|
) |
|
analyze_btn = gr.Button("Analyze Report", variant="primary") |
|
|
|
with gr.Column(): |
|
json_output = gr.Textbox(label="Raw JSON Output", interactive=False) |
|
html_output = gr.HTML(label="Analysis Results") |
|
summary_output = gr.Textbox(label="Summary", interactive=False) |
|
|
|
analyze_btn.click( |
|
fn=analyze_report, |
|
inputs=[file_input, analysis_mode, report_type], |
|
outputs=[json_output, html_output, summary_output] |
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
demo.launch() |