|
|
|
|
|
|
|
|
|
|
|
import os |
|
import io |
|
from google import genai |
|
from google.genai import types |
|
import gradio as gr |
|
|
|
|
|
CLIENT = genai.Client(api_key=os.environ.get("GEMINI_API_KEY")) |
|
MODEL = "gemini-2.5-flash" |
|
|
|
|
|
TEXT_ONLY_REPLY = "أنا R-RAY AI تم تدريبي على قواعد بيانات طبية ضخمة. كيف يمكنني مساعدتك اليوم؟" |
|
|
|
|
|
RADIology_PROMPT_TEMPLATE = '''أنت "R-RAY AI" — نموذج متخصص في تقارير الأشعة، تدربت على قواعد بيانات طبية ضخمة. |
|
تعليمات صارمة: |
|
1) عند استلام صور شعاعية (واحدة أو أكثر) — أجب **فقط** بتقرير طبي احترافي بصيغة تقارير الأشعة المستعملة سريريًا. |
|
2) لا تضف جمل ترويجية أو اعتذارات أو نص خارج إطار التقرير. |
|
3) إخراج التقرير يجب أن يحتوي على الأقسام التالية بالترتيب: عنوان الدراسة، تاريخ الاستلام (التاريخ اليومي تلقائيًا)، رقم المريض (إن وُرد)، التاريخ السريري/شكوى المريض (من نص المستخدم إن وُجد)، التقنية (نوع الصور: X-ray, CT, MRI + الإطارات/القطاعات المرفقة)، وصف تفصيلي للنتائج، خاتمة/تقرير تشخيصي موجز مع توصية واحدة على الأكثر. |
|
4) إذا كان هناك أكثر من صورة — اذكر كل صورة مرقمة ووصف كل واحدة تحت قسم "الصور" ثم ادمج النتائج في قسم "النتائج". |
|
5) اللغة: العربية الفصحى طبية. الطول: تقرير كامل ومحترف (لا يقل عن 6 جمل في الوصف ما لم تكن الصور طبيعية تمامًا). |
|
|
|
البيانات المرسلة: |
|
{clinical_text} |
|
|
|
الصور المرسلة: {image_list} |
|
|
|
أعد فقط التقرير الطبي بالصيغة المطلوبة. انتهى. |
|
''' |
|
|
|
|
|
def make_parts_from_images(image_files): |
|
parts = [] |
|
for idx, img in enumerate(image_files, start=1): |
|
img_bytes = img.read() |
|
try: |
|
part = types.Part.from_bytes(img_bytes) |
|
except Exception: |
|
try: |
|
part = types.Part.from_image(img_bytes) |
|
except Exception: |
|
import base64 |
|
b64 = base64.b64encode(img_bytes).decode('utf-8') |
|
part = types.Part.from_text(f"[BASE64_IMAGE_{idx}] {b64}") |
|
parts.append(part) |
|
return parts |
|
|
|
|
|
def analyze(images, clinical_text): |
|
clinical_text = (clinical_text or "").strip() |
|
images = images or [] |
|
|
|
if len(images) == 0: |
|
return TEXT_ONLY_REPLY |
|
|
|
image_names = [] |
|
for i, f in enumerate(images, start=1): |
|
name = getattr(f, "name", None) or f"image_{i}" |
|
image_names.append(name) |
|
image_list_text = ", ".join(image_names) |
|
|
|
prompt_text = RADIology_PROMPT_TEMPLATE.format( |
|
clinical_text=clinical_text if clinical_text else "(لا توجد معلومات سريرية إضافية)", |
|
image_list=image_list_text, |
|
) |
|
|
|
parts = make_parts_from_images(images) |
|
parts.append(types.Part.from_text(prompt_text)) |
|
|
|
contents = [ |
|
types.Content( |
|
role="user", |
|
parts=parts, |
|
) |
|
] |
|
|
|
try: |
|
response = CLIENT.models.generate_content( |
|
model=MODEL, |
|
contents=contents, |
|
config=types.GenerateContentConfig( |
|
thinking_config=types.ThinkingConfig(thinking_budget=-1), |
|
), |
|
) |
|
return response.text |
|
except Exception as e: |
|
return f"خطأ أثناء الاتصال بنموذج Gemini: {e}" |
|
|
|
|
|
with gr.Blocks() as demo: |
|
gr.Markdown("# R-RAY AI — تقارير الأشعة\nارفع صور شعاعية (X-ray, CT, MRI) ويمكنك أيضًا ارفاق ملاحظات سريرية قصيرة.") |
|
|
|
with gr.Row(): |
|
image_input = gr.File(label="تحميل صور (يمكن رفع أكثر من صورة)", file_types=[".png", ".jpg", ".jpeg", ".dcm"], file_types_description="Images", type="binary", file_count="multiple") |
|
clinical_input = gr.Textbox(label="معلومات سريرية / تاريخ المريض (اختياري)", lines=4) |
|
|
|
output = gr.Textbox(label="تقرير الأشعة (الناتج)", lines=20) |
|
|
|
btn = gr.Button("تحليل وإصدار التقرير") |
|
btn.click(fn=analyze, inputs=[image_input, clinical_input], outputs=[output]) |
|
|
|
gr.Markdown("**ملاحظة:** هذا النموذج للاختبار/التطوير فقط ولا يغني عن استشارة طبية حقيقية.") |
|
|
|
if __name__ == "__main__": |
|
demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860))) |
|
|