# app.py # Gradio Hugging Face Space connected to Google Gemini (via google-genai) # - Put your Gemini API key into Hugging Face Space secrets with name: GEMINI_API_KEY # - Add requirements.txt with: gradio\ngoogle-genai import os import io from google import genai from google.genai import types import gradio as gr # Initialize client using secret from HF environment CLIENT = genai.Client(api_key=os.environ.get("GEMINI_API_KEY")) MODEL = "gemini-2.5-flash" # or change to a model available in your project/region # Fixed short reply for text-only chats TEXT_ONLY_REPLY = "أنا R-RAY AI تم تدريبي على قواعد بيانات طبية ضخمة. كيف يمكنني مساعدتك اليوم؟" # Strict radiology-report instruction (Arabic) 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)))