# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import time import json import re from flask import ( Flask, send_from_directory, request, jsonify, Response, stream_with_context, send_file, ) from flask_cors import CORS from evaluation import evaluate_report, evaluation_prompt from gemini import gemini_get_text_response from medgemma import medgemma_get_text_response from interview_simulator import stream_interview from cache import create_cache_zip # Application setup app = Flask( __name__, static_folder=os.environ.get("FRONTEND_BUILD", "frontend/build"), static_url_path="/", ) CORS(app, resources={r"/api/*": {"origins": "http://localhost:3000"}}) # -------- Routes -------- @app.route("/") def serve_index(): """Serve the main frontend entry-point.""" return send_from_directory(app.static_folder, "index.html") @app.route("/api/stream_conversation", methods=["GET"]) def stream_conversation(): """ Stream a simulated interview via Server-Sent Events (SSE). """ patient = request.args.get("patient", "Patient") condition = request.args.get("condition", "unknown condition") def event_stream(): try: for msg in stream_interview(patient, condition): yield f"data: {msg}\n\n" except Exception as e: yield f"data: Error: {e}\n\n" raise return Response(stream_with_context(event_stream()), mimetype="text/event-stream") @app.route("/api/evaluate_report", methods=["POST"]) def evaluate_report_call(): """ Evaluate a given medical report. Expect JSON with "report" and "condition" keys. """ data = request.get_json() or {} report = data.get("report", "").strip() condition = data.get("condition", "").strip() if not report: return jsonify({"error": "Report is required"}), 400 if not condition: return jsonify({"error": "Condition is required"}), 400 evaluation = evaluate_report(report, condition) return jsonify({"evaluation": evaluation}) @app.route("/api/download_cache", methods=["GET"]) def download_cache_zip(): """ Generate and send a zip archive of the cache directory. """ zip_path, error = create_cache_zip() if error: return jsonify({"error": error}), 500 if not os.path.isfile(zip_path): return jsonify({"error": f"File not found: {zip_path}"}), 404 return send_file(zip_path, as_attachment=True) @app.route("/") def static_proxy(path): """ Serve static assets; fallback to index.html for SPA routes. """ fs_path = os.path.join(app.static_folder, path) if os.path.isfile(fs_path): return send_from_directory(app.static_folder, path) return send_from_directory(app.static_folder, "index.html") # -------- CLI launch -------- if __name__ == "__main__": app.run(host="0.0.0.0", port=7860, threaded=True)