|
27 | 27 | from api.admin import admin_bp |
28 | 28 | from azure.monitor.opentelemetry import configure_azure_monitor |
29 | 29 | from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware |
| 30 | +from event_utils import track_event_if_configured |
30 | 31 |
|
31 | 32 | # In-memory task storage for generation tasks |
32 | 33 | # In production, this should be replaced with Redis or similar |
|
59 | 60 | logging.getLogger("azure.identity").setLevel(logging.WARNING) |
60 | 61 | logging.getLogger("azure.cosmos").setLevel(logging.WARNING) |
61 | 62 | logging.getLogger("api.admin").setLevel(logging.WARNING) |
| 63 | + logging.getLogger("httpx").setLevel(logging.WARNING) |
62 | 64 | # Apply ASGI middleware for request tracing (Quart is not auto-instrumented by configure_azure_monitor) |
63 | 65 | # Exclude health probes, post-deploy admin calls, and polling endpoints from telemetry |
64 | 66 | app.asgi_app = OpenTelemetryMiddleware( |
@@ -162,8 +164,11 @@ async def chat(): |
162 | 164 | user_id = data.get("user_id", "anonymous") |
163 | 165 |
|
164 | 166 | if not message: |
| 167 | + track_event_if_configured("Error_Chat_Message_Empty", {"conversation_id": conversation_id, "user_id": user_id}) |
165 | 168 | return jsonify({"error": "Message is required"}), 400 |
166 | 169 |
|
| 170 | + track_event_if_configured("Chat_Request_Received", {"conversation_id": conversation_id, "user_id": user_id}) |
| 171 | + |
167 | 172 | orchestrator = get_orchestrator() |
168 | 173 |
|
169 | 174 | # Try to save to CosmosDB but don't fail if it's unavailable |
@@ -259,8 +264,11 @@ async def parse_brief(): |
259 | 264 | user_id = data.get("user_id", "anonymous") |
260 | 265 |
|
261 | 266 | if not brief_text: |
| 267 | + track_event_if_configured("Error_Brief_Text_Empty", {"conversation_id": conversation_id, "user_id": user_id}) |
262 | 268 | return jsonify({"error": "Brief text is required"}), 400 |
263 | 269 |
|
| 270 | + track_event_if_configured("Brief_Parse_Request", {"conversation_id": conversation_id, "user_id": user_id}) |
| 271 | + |
264 | 272 | orchestrator = get_orchestrator() |
265 | 273 | generated_title = None |
266 | 274 |
|
@@ -294,6 +302,7 @@ async def parse_brief(): |
294 | 302 |
|
295 | 303 | # Check if request was blocked due to harmful content |
296 | 304 | if rai_blocked: |
| 305 | + track_event_if_configured("Error_RAI_Check_Failed", {"conversation_id": conversation_id, "user_id": user_id, "status": "Brief parse blocked by RAI"}) |
297 | 306 | # Save the refusal as assistant response |
298 | 307 | try: |
299 | 308 | cosmos_service = await get_cosmos_service() |
@@ -396,8 +405,11 @@ async def confirm_brief(): |
396 | 405 | try: |
397 | 406 | brief = CreativeBrief(**brief_data) |
398 | 407 | except Exception as e: |
| 408 | + track_event_if_configured("Error_Brief_Invalid_Format", {"conversation_id": conversation_id, "user_id": user_id, "error": str(e)}) |
399 | 409 | return jsonify({"error": f"Invalid brief format: {str(e)}"}), 400 |
400 | 410 |
|
| 411 | + track_event_if_configured("Brief_Confirmed", {"conversation_id": conversation_id, "user_id": user_id}) |
| 412 | + |
401 | 413 | # Try to save the confirmed brief to CosmosDB, preserving existing messages |
402 | 414 | try: |
403 | 415 | cosmos_service = await get_cosmos_service() |
@@ -458,8 +470,11 @@ async def select_products(): |
458 | 470 | user_id = data.get("user_id", "anonymous") |
459 | 471 |
|
460 | 472 | if not request_text: |
| 473 | + track_event_if_configured("Error_Product_Request_Empty", {"conversation_id": conversation_id, "user_id": user_id}) |
461 | 474 | return jsonify({"error": "Request text is required"}), 400 |
462 | 475 |
|
| 476 | + track_event_if_configured("Product_Selection_Request", {"conversation_id": conversation_id, "user_id": user_id}) |
| 477 | + |
463 | 478 | # Save user message |
464 | 479 | try: |
465 | 480 | cosmos_service = await get_cosmos_service() |
@@ -605,13 +620,15 @@ async def _run_generation_task(task_id: str, brief: CreativeBrief, products_data |
605 | 620 | _generation_tasks[task_id]["status"] = "completed" |
606 | 621 | _generation_tasks[task_id]["result"] = response |
607 | 622 | _generation_tasks[task_id]["completed_at"] = datetime.now(timezone.utc).isoformat() |
| 623 | + track_event_if_configured("Generation_Completed", {"task_id": task_id, "conversation_id": conversation_id, "user_id": user_id}) |
608 | 624 | logger.info(f"Task {task_id} marked as completed") |
609 | 625 |
|
610 | 626 | except Exception as e: |
611 | 627 | logger.exception(f"Generation task {task_id} failed: {e}") |
612 | 628 | _generation_tasks[task_id]["status"] = "failed" |
613 | 629 | _generation_tasks[task_id]["error"] = str(e) |
614 | 630 | _generation_tasks[task_id]["completed_at"] = datetime.now(timezone.utc).isoformat() |
| 631 | + track_event_if_configured("Error_Generation_Failed", {"task_id": task_id, "conversation_id": conversation_id, "user_id": user_id, "error": str(e)}) |
615 | 632 |
|
616 | 633 |
|
617 | 634 | @app.route("/api/generate/start", methods=["POST"]) |
@@ -647,6 +664,7 @@ async def start_generation(): |
647 | 664 | try: |
648 | 665 | brief = CreativeBrief(**brief_data) |
649 | 666 | except Exception as e: |
| 667 | + track_event_if_configured("Error_Generation_Invalid_Brief", {"conversation_id": conversation_id, "user_id": user_id, "error": str(e)}) |
650 | 668 | return jsonify({"error": f"Invalid brief format: {str(e)}"}), 400 |
651 | 669 |
|
652 | 670 | # Create task ID |
@@ -689,6 +707,8 @@ async def start_generation(): |
689 | 707 |
|
690 | 708 | logger.info(f"Started generation task {task_id} for conversation {conversation_id}") |
691 | 709 |
|
| 710 | + track_event_if_configured("Generation_Started", {"task_id": task_id, "conversation_id": conversation_id, "user_id": user_id, "generate_images": str(generate_images)}) |
| 711 | + |
692 | 712 | return jsonify({ |
693 | 713 | "task_id": task_id, |
694 | 714 | "status": "pending", |
@@ -959,13 +979,17 @@ async def regenerate_content(): |
959 | 979 | user_id = data.get("user_id", "anonymous") |
960 | 980 |
|
961 | 981 | if not modification_request: |
| 982 | + track_event_if_configured("Error_Regeneration_Request_Empty", {"conversation_id": conversation_id, "user_id": user_id}) |
962 | 983 | return jsonify({"error": "modification_request is required"}), 400 |
963 | 984 |
|
964 | 985 | try: |
965 | 986 | brief = CreativeBrief(**brief_data) |
966 | 987 | except Exception as e: |
| 988 | + track_event_if_configured("Error_Regeneration_Invalid_Brief", {"conversation_id": conversation_id, "user_id": user_id, "error": str(e)}) |
967 | 989 | return jsonify({"error": f"Invalid brief format: {str(e)}"}), 400 |
968 | 990 |
|
| 991 | + track_event_if_configured("Regeneration_Request", {"conversation_id": conversation_id, "user_id": user_id}) |
| 992 | + |
969 | 993 | # Save user request for regeneration |
970 | 994 | try: |
971 | 995 | cosmos_service = await get_cosmos_service() |
@@ -1029,6 +1053,7 @@ async def generate(): |
1029 | 1053 |
|
1030 | 1054 | # Check for RAI block |
1031 | 1055 | if response.get("rai_blocked"): |
| 1056 | + track_event_if_configured("Error_RAI_Check_Failed", {"conversation_id": conversation_id, "user_id": user_id, "status": "Regeneration blocked by RAI"}) |
1032 | 1057 | yield f"data: {json.dumps({'type': 'error', 'content': response.get('error', 'Request blocked by content safety'), 'rai_blocked': True, 'is_final': True})}\n\n" |
1033 | 1058 | yield "data: [DONE]\n\n" |
1034 | 1059 | return |
@@ -1412,6 +1437,7 @@ async def delete_conversation(conversation_id: str): |
1412 | 1437 | try: |
1413 | 1438 | cosmos_service = await get_cosmos_service() |
1414 | 1439 | await cosmos_service.delete_conversation(conversation_id, user_id) |
| 1440 | + track_event_if_configured("Conversation_Deleted", {"conversation_id": conversation_id, "user_id": user_id}) |
1415 | 1441 | return jsonify({"success": True, "message": "Conversation deleted"}) |
1416 | 1442 | except Exception as e: |
1417 | 1443 | logger.warning(f"Failed to delete conversation: {e}") |
@@ -1463,6 +1489,7 @@ async def delete_all_conversations(): |
1463 | 1489 | try: |
1464 | 1490 | cosmos_service = await get_cosmos_service() |
1465 | 1491 | deleted_count = await cosmos_service.delete_all_conversations(user_id) |
| 1492 | + track_event_if_configured("Conversations_All_Deleted", {"user_id": user_id, "deleted_count": str(deleted_count)}) |
1466 | 1493 | return jsonify({ |
1467 | 1494 | "success": True, |
1468 | 1495 | "message": f"Deleted {deleted_count} conversations", |
|
0 commit comments