Update app.py
Browse files
app.py
CHANGED
|
@@ -25,6 +25,13 @@ logger = logging.getLogger(__name__)
|
|
| 25 |
# Load environment variables
|
| 26 |
load_dotenv()
|
| 27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
# --- 1. NEW HELPER FUNCTIONS TO FIX 'TypeError' ---
|
| 29 |
def hydrate_history(raw_history_list: list) -> list:
|
| 30 |
"""Converts a list of dicts from session back into LangChain Message objects."""
|
|
@@ -371,13 +378,46 @@ def reset_metrics(domain):
|
|
| 371 |
return jsonify({"success": True, "message": f"Metrics reset for {domain}"})
|
| 372 |
except Exception as e:
|
| 373 |
return jsonify({"error": str(e)}), 500
|
| 374 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 375 |
# --- 3. NEW API-ONLY ROUTES ---
|
| 376 |
|
| 377 |
@app.route("/api/medical", methods=["POST"])
|
| 378 |
def medical_api():
|
| 379 |
try:
|
| 380 |
data = request.json
|
|
|
|
|
|
|
|
|
|
|
|
|
| 381 |
query = data.get("query")
|
| 382 |
if not query:
|
| 383 |
return jsonify({"error": "No query provided"}), 400
|
|
@@ -390,8 +430,43 @@ def medical_api():
|
|
| 390 |
if not agent:
|
| 391 |
return jsonify({"error": "Medical RAG system not loaded"}), 500
|
| 392 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 393 |
# Run the agent
|
| 394 |
-
response_dict = agent.answer(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 395 |
|
| 396 |
# Return the full, clean JSON response
|
| 397 |
return jsonify(response_dict)
|
|
@@ -400,7 +475,73 @@ def medical_api():
|
|
| 400 |
logger.error(f"Error on /api/medical: {e}", exc_info=True)
|
| 401 |
return jsonify({"error": str(e)}), 500
|
| 402 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 404 |
if __name__ == "__main__":
|
| 405 |
logger.info("Starting Flask app for deployment testing...")
|
| 406 |
app.run(host="0.0.0.0", port=7860, debug=False)
|
|
|
|
| 25 |
# Load environment variables
|
| 26 |
load_dotenv()
|
| 27 |
|
| 28 |
+
# These are your "customers". You give them a key.
|
| 29 |
+
# In a real app, this would be in a database.
|
| 30 |
+
VALID_API_KEYS = {
|
| 31 |
+
"anak_mail_123": "Dr. Amelia Ritahani",
|
| 32 |
+
"irfan_test_key_456": "Irfan (self)",
|
| 33 |
+
"sistem_gelap": "Gelap"
|
| 34 |
+
}
|
| 35 |
# --- 1. NEW HELPER FUNCTIONS TO FIX 'TypeError' ---
|
| 36 |
def hydrate_history(raw_history_list: list) -> list:
|
| 37 |
"""Converts a list of dicts from session back into LangChain Message objects."""
|
|
|
|
| 378 |
return jsonify({"success": True, "message": f"Metrics reset for {domain}"})
|
| 379 |
except Exception as e:
|
| 380 |
return jsonify({"error": str(e)}), 500
|
| 381 |
+
|
| 382 |
+
# Helper function to check API key
|
| 383 |
+
def check_api_key(request_data):
|
| 384 |
+
api_key = request_data.get("api_key")
|
| 385 |
+
if not api_key or api_key not in VALID_API_KEYS:
|
| 386 |
+
return False, {"error": "Invalid or missing API key"}, 401
|
| 387 |
+
logger.info(f"API request authenticated for user: {VALID_API_KEYS[api_key]}")
|
| 388 |
+
return True, None, None
|
| 389 |
+
|
| 390 |
+
# Helper function to save and process uploaded files (Base64)
|
| 391 |
+
def process_base64_file(base64_string, file_type):
|
| 392 |
+
try:
|
| 393 |
+
# Decode the base64 string
|
| 394 |
+
file_bytes = base64.b64decode(base64_string)
|
| 395 |
+
|
| 396 |
+
# Save to a temporary file
|
| 397 |
+
upload_dir = "Uploads"
|
| 398 |
+
os.makedirs(upload_dir, exist_ok=True)
|
| 399 |
+
# Use a unique filename
|
| 400 |
+
temp_filename = f"{file_type}_{int(time.time())}.tmp"
|
| 401 |
+
temp_path = os.path.join(upload_dir, temp_filename)
|
| 402 |
+
|
| 403 |
+
with open(temp_path, 'wb') as f:
|
| 404 |
+
f.write(file_bytes)
|
| 405 |
+
|
| 406 |
+
logger.info(f"Saved temporary {file_type} to {temp_path}")
|
| 407 |
+
return temp_path
|
| 408 |
+
except Exception as e:
|
| 409 |
+
logger.error(f"Error decoding/saving base64 file: {e}")
|
| 410 |
+
return None
|
| 411 |
# --- 3. NEW API-ONLY ROUTES ---
|
| 412 |
|
| 413 |
@app.route("/api/medical", methods=["POST"])
|
| 414 |
def medical_api():
|
| 415 |
try:
|
| 416 |
data = request.json
|
| 417 |
+
is_valid, error_response, status_code = check_api_key(data)
|
| 418 |
+
if not is_valid:
|
| 419 |
+
return jsonify(error_response), status_code
|
| 420 |
+
|
| 421 |
query = data.get("query")
|
| 422 |
if not query:
|
| 423 |
return jsonify({"error": "No query provided"}), 400
|
|
|
|
| 430 |
if not agent:
|
| 431 |
return jsonify({"error": "Medical RAG system not loaded"}), 500
|
| 432 |
|
| 433 |
+
# --- Handle File Uploads (Base64) ---
|
| 434 |
+
enhanced_query = query
|
| 435 |
+
temp_file_path = None
|
| 436 |
+
|
| 437 |
+
if data.get("document_base64"):
|
| 438 |
+
logger.info("API: Processing base64 document for Swarm")
|
| 439 |
+
doc_text = base64.b64decode(data.get("document_base64")).decode('utf-8')
|
| 440 |
+
swarm_answer = run_medical_swarm(doc_text, query)
|
| 441 |
+
response_dict = {
|
| 442 |
+
"answer": markdown_bold_to_html(swarm_answer),
|
| 443 |
+
"thoughts": "Swarm analysis complete.",
|
| 444 |
+
"validation": (True, "Swarm output generated."),
|
| 445 |
+
"source": "Medical Swarm",
|
| 446 |
+
"response_time": 0 # Not tracked for swarm in this path
|
| 447 |
+
}
|
| 448 |
+
return jsonify(response_dict)
|
| 449 |
+
|
| 450 |
+
elif data.get("image_base64"):
|
| 451 |
+
logger.info("API: Processing base64 image")
|
| 452 |
+
temp_file_path = process_base64_file(data.get("image_base64"), "image")
|
| 453 |
+
if not temp_file_path:
|
| 454 |
+
return jsonify({"error": "Invalid base64 image data"}), 400
|
| 455 |
+
|
| 456 |
+
with open(temp_file_path, "rb") as img_file:
|
| 457 |
+
img_data = base64.b64encode(img_file.read()).decode("utf-8")
|
| 458 |
+
|
| 459 |
+
vision_prompt = f"Analyze image. Query: '{query}'"
|
| 460 |
+
message = HumanMessage(content=[{"type": "text", "text": vision_prompt}, {"type": "image_url", "image_url": f"data:image/jpeg;base64,{img_data}"}])
|
| 461 |
+
visual_prediction = llm.invoke([message]).content
|
| 462 |
+
enhanced_query = (f'User Query: "{query}" Context from Image: "{visual_prediction}"')
|
| 463 |
+
|
| 464 |
# Run the agent
|
| 465 |
+
response_dict = agent.answer(enhanced_query, chat_history=history_for_agent)
|
| 466 |
+
|
| 467 |
+
# Clean up temp file
|
| 468 |
+
if temp_file_path and os.path.exists(temp_file_path):
|
| 469 |
+
os.remove(temp_file_path)
|
| 470 |
|
| 471 |
# Return the full, clean JSON response
|
| 472 |
return jsonify(response_dict)
|
|
|
|
| 475 |
logger.error(f"Error on /api/medical: {e}", exc_info=True)
|
| 476 |
return jsonify({"error": str(e)}), 500
|
| 477 |
|
| 478 |
+
@app.route("/api/islamic", methods=["POST"])
|
| 479 |
+
def islamic_api():
|
| 480 |
+
try:
|
| 481 |
+
data = request.json
|
| 482 |
+
is_valid, error_response, status_code = check_api_key(data)
|
| 483 |
+
if not is_valid: return jsonify(error_response), status_code
|
| 484 |
+
|
| 485 |
+
query = data.get("query")
|
| 486 |
+
if not query: return jsonify({"error": "No query provided"}), 400
|
| 487 |
+
|
| 488 |
+
raw_history = data.get("history", [])
|
| 489 |
+
history_for_agent = hydrate_history(raw_history)
|
| 490 |
+
|
| 491 |
+
agent = rag_systems['islamic']
|
| 492 |
+
if not agent: return jsonify({"error": "Islamic RAG system not loaded"}), 500
|
| 493 |
+
|
| 494 |
+
enhanced_query = query
|
| 495 |
+
temp_file_path = None
|
| 496 |
+
|
| 497 |
+
if data.get("image_base64"):
|
| 498 |
+
logger.info("API: Processing base64 image")
|
| 499 |
+
temp_file_path = process_base64_file(data.get("image_base64"), "image")
|
| 500 |
+
if not temp_file_path:
|
| 501 |
+
return jsonify({"error": "Invalid base64 image data"}), 400
|
| 502 |
+
|
| 503 |
+
with open(temp_file_path, "rb") as img_file:
|
| 504 |
+
img_data = base64.b64encode(img_file.read()).decode("utf-8")
|
| 505 |
+
|
| 506 |
+
vision_prompt = f"Analyze image. Query: '{query}'"
|
| 507 |
+
message = HumanMessage(content=[{"type": "text", "text": vision_prompt}, {"type": "image_url", "image_url": f"data:image/jpeg;base64,{img_data}"}])
|
| 508 |
+
visual_prediction = llm.invoke([message]).content
|
| 509 |
+
enhanced_query = (f'User Query: "{query}" Context from Image: "{visual_prediction}"')
|
| 510 |
|
| 511 |
+
response_dict = agent.answer(enhanced_query, chat_history=history_for_agent)
|
| 512 |
+
|
| 513 |
+
if temp_file_path and os.path.exists(temp_file_path):
|
| 514 |
+
os.remove(temp_file_path)
|
| 515 |
+
|
| 516 |
+
return jsonify(response_dict)
|
| 517 |
+
|
| 518 |
+
except Exception as e:
|
| 519 |
+
logger.error(f"Error on /api/islamic: {e}", exc_info=True)
|
| 520 |
+
return jsonify({"error": str(e)}), 500
|
| 521 |
+
|
| 522 |
+
@app.route("/api/insurance", methods=["POST"])
|
| 523 |
+
def insurance_api():
|
| 524 |
+
try:
|
| 525 |
+
data = request.json
|
| 526 |
+
is_valid, error_response, status_code = check_api_key(data)
|
| 527 |
+
if not is_valid: return jsonify(error_response), status_code
|
| 528 |
+
|
| 529 |
+
query = data.get("query")
|
| 530 |
+
if not query: return jsonify({"error": "No query provided"}), 400
|
| 531 |
+
|
| 532 |
+
raw_history = data.get("history", [])
|
| 533 |
+
history_for_agent = hydrate_history(raw_history)
|
| 534 |
+
|
| 535 |
+
agent = rag_systems['insurance']
|
| 536 |
+
if not agent: return jsonify({"error": "Insurance RAG system not loaded"}), 500
|
| 537 |
+
|
| 538 |
+
response_dict = agent.answer(query, chat_history=history_for_agent)
|
| 539 |
+
return jsonify(response_dict)
|
| 540 |
+
|
| 541 |
+
except Exception as e:
|
| 542 |
+
logger.error(f"Error on /api/insurance: {e}", exc_info=True)
|
| 543 |
+
return jsonify({"error": str(e)}), 500
|
| 544 |
+
|
| 545 |
if __name__ == "__main__":
|
| 546 |
logger.info("Starting Flask app for deployment testing...")
|
| 547 |
app.run(host="0.0.0.0", port=7860, debug=False)
|