nebu 0.1.118__py3-none-any.whl → 0.1.119__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -388,7 +388,13 @@ def start_health_check_subprocess() -> Optional[subprocess.Popen]:
388
388
  """Start the health check consumer subprocess."""
389
389
  global REDIS_HEALTH_STREAM, REDIS_HEALTH_CONSUMER_GROUP
390
390
 
391
+ print(f"[DEBUG] start_health_check_subprocess called")
392
+ print(f"[DEBUG] REDIS_URL: {REDIS_URL}")
393
+ print(f"[DEBUG] REDIS_HEALTH_STREAM: {REDIS_HEALTH_STREAM}")
394
+ print(f"[DEBUG] REDIS_HEALTH_CONSUMER_GROUP: {REDIS_HEALTH_CONSUMER_GROUP}")
395
+
391
396
  if not all([REDIS_URL, REDIS_HEALTH_STREAM, REDIS_HEALTH_CONSUMER_GROUP]):
397
+ print(f"[DEBUG] Health check not configured - missing required variables")
392
398
  logger.warning(
393
399
  "[Consumer] Health check stream not configured. Health consumer subprocess not started."
394
400
  )
@@ -399,6 +405,11 @@ def start_health_check_subprocess() -> Optional[subprocess.Popen]:
399
405
  assert isinstance(REDIS_HEALTH_STREAM, str)
400
406
  assert isinstance(REDIS_HEALTH_CONSUMER_GROUP, str)
401
407
 
408
+ print(
409
+ f"[DEBUG] Starting health check subprocess for stream: {REDIS_HEALTH_STREAM}"
410
+ )
411
+ print(f"[DEBUG] Health consumer group: {REDIS_HEALTH_CONSUMER_GROUP}")
412
+
402
413
  # Prepare environment variables for the subprocess
403
414
  health_env = os.environ.copy()
404
415
  health_env["REDIS_HEALTH_STREAM"] = REDIS_HEALTH_STREAM
@@ -412,6 +423,8 @@ def start_health_check_subprocess() -> Optional[subprocess.Popen]:
412
423
  "nebu.processors.consumer_health_worker",
413
424
  ]
414
425
 
426
+ print(f"[DEBUG] Health subprocess command: {' '.join(health_cmd)}")
427
+
415
428
  process = subprocess.Popen(
416
429
  health_cmd,
417
430
  stdout=subprocess.PIPE,
@@ -422,12 +435,17 @@ def start_health_check_subprocess() -> Optional[subprocess.Popen]:
422
435
  bufsize=1, # Line buffered
423
436
  )
424
437
 
438
+ print(
439
+ f"[DEBUG] Health check subprocess started successfully with PID {process.pid}"
440
+ )
425
441
  logger.info(
426
442
  f"[Consumer] Health check subprocess started with PID {process.pid}"
427
443
  )
428
444
  return process
429
445
 
430
446
  except Exception as e:
447
+ print(f"[DEBUG] Failed to start health check subprocess: {e}")
448
+ print(f"[DEBUG] Exception type: {type(e)}")
431
449
  logger.error(f"[Consumer] Failed to start health check subprocess: {e}")
432
450
  logger.exception("Health Subprocess Start Error Traceback:")
433
451
  return None
@@ -435,13 +453,17 @@ def start_health_check_subprocess() -> Optional[subprocess.Popen]:
435
453
 
436
454
  def monitor_health_subprocess(process: subprocess.Popen) -> None:
437
455
  """Monitor the health check subprocess and log its output."""
456
+ print(f"[DEBUG] monitor_health_subprocess started for PID {process.pid}")
438
457
  try:
439
458
  # Read output from the subprocess
440
459
  if process.stdout:
441
460
  for line in iter(process.stdout.readline, ""):
461
+ print(f"[DEBUG] [HealthSubprocess] {line.strip()}")
442
462
  logger.info(f"[HealthSubprocess] {line.strip()}")
443
463
  process.stdout.close() if process.stdout else None
464
+ print(f"[DEBUG] monitor_health_subprocess finished for PID {process.pid}")
444
465
  except Exception as e:
466
+ print(f"[DEBUG] Error monitoring health subprocess: {e}")
445
467
  logger.error(f"[Consumer] Error monitoring health subprocess: {e}")
446
468
 
447
469
 
@@ -449,33 +471,45 @@ def check_health_subprocess() -> bool:
449
471
  """Check if the health subprocess is still running and restart if needed."""
450
472
  global health_subprocess
451
473
 
474
+ print(f"[DEBUG] check_health_subprocess called")
475
+
452
476
  if health_subprocess is None:
477
+ print(f"[DEBUG] health_subprocess is None")
453
478
  return False
454
479
 
455
480
  # Check if process is still running
456
- if health_subprocess.poll() is None:
481
+ poll_result = health_subprocess.poll()
482
+ print(f"[DEBUG] health_subprocess.poll() returned: {poll_result}")
483
+
484
+ if poll_result is None:
485
+ print(f"[DEBUG] Health subprocess still running (PID {health_subprocess.pid})")
457
486
  return True # Still running
458
487
 
459
488
  # Process has exited
460
489
  exit_code = health_subprocess.returncode
490
+ print(f"[DEBUG] Health subprocess exited with code {exit_code}")
461
491
  logger.warning(
462
492
  f"[Consumer] Health subprocess exited with code {exit_code}. Restarting..."
463
493
  )
464
494
 
465
495
  # Start a new health subprocess
496
+ print(f"[DEBUG] Attempting to restart health subprocess...")
466
497
  health_subprocess = start_health_check_subprocess()
467
498
 
468
499
  if health_subprocess:
500
+ print(f"[DEBUG] Health subprocess restarted successfully")
469
501
  # Start monitoring thread for the new subprocess
470
502
  monitor_thread = threading.Thread(
471
503
  target=monitor_health_subprocess, args=(health_subprocess,), daemon=True
472
504
  )
473
505
  monitor_thread.start()
506
+ print(f"[DEBUG] Monitor thread started for health subprocess")
474
507
  logger.info(
475
508
  "[Consumer] Health subprocess restarted and monitoring thread started."
476
509
  )
477
510
  return True
478
511
  else:
512
+ print(f"[DEBUG] Failed to restart health subprocess")
479
513
  logger.error("[Consumer] Failed to restart health subprocess.")
480
514
  return False
481
515
 
@@ -714,6 +748,9 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
714
748
  user_id = None
715
749
  try:
716
750
  payload_str = message_data.get("data")
751
+ print(f"[DEBUG] Raw message_data: {message_data}")
752
+ print(f"[DEBUG] Payload string: {payload_str}")
753
+
717
754
  if (
718
755
  not payload_str
719
756
  ): # Covers None and empty string, isinstance check is redundant
@@ -722,6 +759,7 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
722
759
  )
723
760
  try:
724
761
  raw_payload = json.loads(payload_str)
762
+ print(f"[DEBUG] Parsed raw_payload: {json.dumps(raw_payload, indent=2)}")
725
763
  except json.JSONDecodeError as json_err:
726
764
  raise ValueError(f"Failed to parse JSON payload: {json_err}") from json_err
727
765
  if not isinstance(raw_payload, dict):
@@ -734,6 +772,9 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
734
772
  # --- Extract fields from the *inner* message content for HealthCheck and regular processing ---
735
773
  # The actual message content is inside raw_payload["content"]
736
774
  inner_content_data = raw_payload.get("content", {})
775
+ print(
776
+ f"[DEBUG] Extracted inner_content_data: {json.dumps(inner_content_data, indent=2) if isinstance(inner_content_data, dict) else inner_content_data}"
777
+ )
737
778
 
738
779
  # Add debug logging for content structure analysis
739
780
  logger.debug(f">> inner_content_data type: {type(inner_content_data)}")
@@ -746,6 +787,7 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
746
787
  # we can't reliably get 'kind' or other fields from it.
747
788
  # This case is more relevant for non-StreamMessage processors.
748
789
  # For HealthChecks, we expect a structured 'content'.
790
+ print(f"[DEBUG] inner_content_data is not a dict, using defaults")
749
791
  logger.warning(
750
792
  f"Received non-dict inner_content_data: {inner_content_data}. HealthCheck might be missed if applicable."
751
793
  )
@@ -762,6 +804,8 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
762
804
  and "content" in inner_content_data
763
805
  )
764
806
 
807
+ print(f"[DEBUG] has_message_structure: {has_message_structure}")
808
+ print(f"[DEBUG] inner_content_data keys: {list(inner_content_data.keys())}")
765
809
  logger.debug(f">> has_message_structure: {has_message_structure}")
766
810
 
767
811
  if has_message_structure:
@@ -770,6 +814,13 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
770
814
  inner_msg_id = inner_content_data.get("id", "")
771
815
  actual_content_to_process = inner_content_data.get("content", {})
772
816
  inner_created_at_str = inner_content_data.get("created_at")
817
+ print(f"[DEBUG] Using nested structure:")
818
+ print(f"[DEBUG] inner_kind: '{inner_kind}'")
819
+ print(f"[DEBUG] inner_msg_id: '{inner_msg_id}'")
820
+ print(
821
+ f"[DEBUG] actual_content_to_process: {actual_content_to_process}"
822
+ )
823
+ print(f"[DEBUG] inner_created_at_str: {inner_created_at_str}")
773
824
  logger.debug(
774
825
  f">> Using nested structure - inner_kind: {inner_kind}, inner_msg_id: {inner_msg_id}"
775
826
  )
@@ -786,6 +837,13 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
786
837
  inner_created_at_str = raw_payload.get(
787
838
  "created_at"
788
839
  ) # Get created_at from outer payload
840
+ print(f"[DEBUG] Using direct structure:")
841
+ print(f"[DEBUG] inner_kind: '{inner_kind}' (from outer payload)")
842
+ print(f"[DEBUG] inner_msg_id: '{inner_msg_id}' (from outer payload)")
843
+ print(
844
+ f"[DEBUG] actual_content_to_process: {actual_content_to_process}"
845
+ )
846
+ print(f"[DEBUG] inner_created_at_str: {inner_created_at_str}")
789
847
  logger.debug(
790
848
  f">> Using direct structure - inner_kind: {inner_kind}, inner_msg_id: {inner_msg_id}"
791
849
  )
@@ -810,31 +868,117 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
810
868
  handle = raw_payload.get("handle") # from outer
811
869
  adapter = raw_payload.get("adapter") # from outer
812
870
  api_key = raw_payload.get("api_key") # from outer
871
+
872
+ print(f"[DEBUG] Extracted outer envelope data:")
873
+ print(f"[DEBUG] return_stream: {return_stream}")
874
+ print(f"[DEBUG] user_id: {user_id}")
875
+ print(f"[DEBUG] orgs: {orgs}")
876
+ print(f"[DEBUG] handle: {handle}")
877
+ print(f"[DEBUG] adapter: {adapter}")
878
+ print(f"[DEBUG] api_key length: {len(api_key) if api_key else 0}")
879
+
813
880
  logger.debug(f">> Extracted API key length: {len(api_key) if api_key else 0}")
814
881
 
815
882
  # --- Health Check Logic ---
816
883
  # Use inner_kind for health check
817
- if inner_kind == "HealthCheck":
884
+ print(f"[DEBUG] Checking if message is HealthCheck. inner_kind: '{inner_kind}'")
885
+ print(
886
+ f"[DEBUG] Message kind comparison: '{inner_kind}' == 'HealthCheckRequest' -> {inner_kind == 'HealthCheckRequest'}"
887
+ )
888
+
889
+ if inner_kind == "HealthCheckRequest":
890
+ print(f"[DEBUG] *** HEALTH CHECK REQUEST MESSAGE DETECTED ***")
891
+ print(f"[DEBUG] Message ID: {message_id}")
892
+ print(f"[DEBUG] Inner message ID: {inner_msg_id}")
893
+ print(f"[DEBUG] Return stream: {return_stream}")
894
+ print(f"[DEBUG] User ID: {user_id}")
895
+ print(f"[DEBUG] Content: {actual_content_to_process}")
896
+
818
897
  logger.info(
819
- f"Received HealthCheck message {message_id} (inner_id: {inner_msg_id})"
898
+ f"Received HealthCheckRequest message {message_id} (inner_id: {inner_msg_id})"
820
899
  )
821
- health_response = {
822
- "kind": "StreamResponseMessage",
823
- "id": message_id, # Use outer message_id for response ID
824
- "content": {"status": "healthy", "checked_message_id": inner_msg_id},
825
- "status": "success",
826
- "created_at": datetime.now(timezone.utc).isoformat(),
827
- "user_id": user_id,
828
- }
829
- if return_stream:
830
- assert isinstance(return_stream, str)
831
- r.xadd(return_stream, {"data": json.dumps(health_response)})
832
- logger.info(f"Sent health check response to {return_stream}")
833
900
 
834
- assert isinstance(REDIS_STREAM, str)
835
- assert isinstance(REDIS_CONSUMER_GROUP, str)
836
- r.xack(REDIS_STREAM, REDIS_CONSUMER_GROUP, message_id)
837
- logger.info(f"Acknowledged HealthCheck message {message_id}")
901
+ # Forward to health stream for health worker subprocess to process
902
+ if REDIS_HEALTH_STREAM:
903
+ print(
904
+ f"[DEBUG] Forwarding health check to health stream: {REDIS_HEALTH_STREAM}"
905
+ )
906
+ try:
907
+ # Forward the entire message data to the health stream
908
+ health_message_data = {
909
+ "data": json.dumps(
910
+ {
911
+ "kind": inner_kind,
912
+ "id": inner_msg_id,
913
+ "content": actual_content_to_process,
914
+ "created_at": inner_created_at.isoformat(),
915
+ "return_stream": return_stream,
916
+ "user_id": user_id,
917
+ "orgs": orgs,
918
+ "handle": handle,
919
+ "adapter": adapter,
920
+ "api_key": api_key,
921
+ "original_message_id": message_id, # Include original message ID for tracking
922
+ }
923
+ )
924
+ }
925
+
926
+ print(
927
+ f"[DEBUG] Health message data to forward: {json.dumps(health_message_data, indent=2)}"
928
+ )
929
+
930
+ r.xadd(REDIS_HEALTH_STREAM, health_message_data) # type: ignore[arg-type]
931
+ print(
932
+ f"[DEBUG] Health check forwarded successfully to {REDIS_HEALTH_STREAM}"
933
+ )
934
+ logger.info(
935
+ f"Forwarded HealthCheckRequest {message_id} to health stream {REDIS_HEALTH_STREAM}"
936
+ )
937
+
938
+ except Exception as e:
939
+ print(
940
+ f"[DEBUG] ERROR forwarding health check to health stream: {e}"
941
+ )
942
+ logger.error(f"Error forwarding health check to health stream: {e}")
943
+ # Fall back to sending error response directly
944
+ _send_error_response(
945
+ message_id,
946
+ f"Failed to forward health check: {e}",
947
+ traceback.format_exc(),
948
+ return_stream,
949
+ user_id,
950
+ )
951
+ else:
952
+ print(
953
+ f"[DEBUG] No health stream configured, cannot forward health check"
954
+ )
955
+ logger.warning(
956
+ "No health stream configured for health check forwarding"
957
+ )
958
+ # Send error response since we can't process health checks
959
+ _send_error_response(
960
+ message_id,
961
+ "Health check stream not configured",
962
+ "REDIS_HEALTH_STREAM environment variable not set",
963
+ return_stream,
964
+ user_id,
965
+ )
966
+
967
+ # Acknowledge the original message since we've forwarded it
968
+ print(f"[DEBUG] Acknowledging original health check message {message_id}")
969
+ try:
970
+ assert isinstance(REDIS_STREAM, str)
971
+ assert isinstance(REDIS_CONSUMER_GROUP, str)
972
+ r.xack(REDIS_STREAM, REDIS_CONSUMER_GROUP, message_id)
973
+ print(
974
+ f"[DEBUG] Health check message {message_id} acknowledged successfully"
975
+ )
976
+ logger.info(f"Acknowledged HealthCheckRequest message {message_id}")
977
+ except Exception as e:
978
+ print(f"[DEBUG] ERROR acknowledging health check message: {e}")
979
+ logger.error(f"Error acknowledging health check message: {e}")
980
+
981
+ print(f"[DEBUG] *** HEALTH CHECK FORWARDING COMPLETE ***")
838
982
  return # Exit early for health checks
839
983
  # --- End Health Check Logic ---
840
984
 
@@ -1210,19 +1354,31 @@ logger.info(
1210
1354
 
1211
1355
  # Start the health check consumer subprocess
1212
1356
  if REDIS_HEALTH_STREAM and REDIS_HEALTH_CONSUMER_GROUP:
1357
+ print(f"[DEBUG] === HEALTH SUBPROCESS INITIALIZATION ===")
1358
+ print(f"[DEBUG] REDIS_HEALTH_STREAM is set: {REDIS_HEALTH_STREAM}")
1359
+ print(f"[DEBUG] REDIS_HEALTH_CONSUMER_GROUP is set: {REDIS_HEALTH_CONSUMER_GROUP}")
1360
+
1213
1361
  health_subprocess = start_health_check_subprocess()
1214
1362
  if health_subprocess:
1363
+ print(
1364
+ f"[DEBUG] Health subprocess started successfully, starting monitor thread..."
1365
+ )
1215
1366
  # Start monitoring thread for subprocess output
1216
1367
  monitor_thread = threading.Thread(
1217
1368
  target=monitor_health_subprocess, args=(health_subprocess,), daemon=True
1218
1369
  )
1219
1370
  monitor_thread.start()
1371
+ print(f"[DEBUG] Monitor thread started for health subprocess")
1220
1372
  logger.info(
1221
1373
  f"[Consumer] Health check subprocess for {REDIS_HEALTH_STREAM} started and monitoring thread started."
1222
1374
  )
1223
1375
  else:
1376
+ print(f"[DEBUG] Health subprocess failed to start")
1224
1377
  logger.error("[Consumer] Failed to start health check subprocess.")
1225
1378
  else:
1379
+ print(f"[DEBUG] === HEALTH SUBPROCESS NOT CONFIGURED ===")
1380
+ print(f"[DEBUG] REDIS_HEALTH_STREAM: {REDIS_HEALTH_STREAM}")
1381
+ print(f"[DEBUG] REDIS_HEALTH_CONSUMER_GROUP: {REDIS_HEALTH_CONSUMER_GROUP}")
1226
1382
  logger.warning(
1227
1383
  "[Consumer] Health check stream not configured. Health consumer subprocess not started."
1228
1384
  )
@@ -1232,10 +1388,15 @@ try:
1232
1388
  logger.debug(
1233
1389
  f"[{datetime.now(timezone.utc).isoformat()}] --- Top of main loop ---"
1234
1390
  ) # Added log
1391
+ print(f"[DEBUG] === MAIN LOOP ITERATION START ===")
1235
1392
 
1236
1393
  # --- Check Health Subprocess Status ---
1237
1394
  if health_subprocess:
1238
- check_health_subprocess()
1395
+ print(f"[DEBUG] Checking health subprocess status...")
1396
+ health_status = check_health_subprocess()
1397
+ print(f"[DEBUG] Health subprocess status check result: {health_status}")
1398
+ else:
1399
+ print(f"[DEBUG] No health subprocess to check")
1239
1400
 
1240
1401
  # --- Check for Code Updates ---
1241
1402
  if not disable_hot_reload:
@@ -1448,6 +1609,11 @@ try:
1448
1609
  logger.debug(
1449
1610
  f"[{datetime.now(timezone.utc).isoformat()}] Calling xreadgroup (block=5000ms)..."
1450
1611
  ) # Added log
1612
+ print(f"[DEBUG] About to call xreadgroup...")
1613
+ print(f"[DEBUG] Stream: {REDIS_STREAM}")
1614
+ print(f"[DEBUG] Consumer group: {REDIS_CONSUMER_GROUP}")
1615
+ print(f"[DEBUG] Consumer name: {consumer_name}")
1616
+
1451
1617
  messages = r.xreadgroup(
1452
1618
  REDIS_CONSUMER_GROUP,
1453
1619
  consumer_name,
@@ -1456,10 +1622,14 @@ try:
1456
1622
  block=5000, # Use milliseconds for block
1457
1623
  )
1458
1624
 
1625
+ print(f"[DEBUG] xreadgroup returned: {messages}")
1626
+ print(f"[DEBUG] Messages type: {type(messages)}")
1627
+
1459
1628
  if not messages:
1460
1629
  logger.trace(
1461
1630
  f"[{datetime.now(timezone.utc).isoformat()}] xreadgroup timed out (no new messages)."
1462
1631
  ) # Added log
1632
+ print(f"[DEBUG] No messages received (timeout or empty)")
1463
1633
  # logger.debug("[Consumer] No new messages.") # Reduce verbosity
1464
1634
  continue
1465
1635
  # Removed the else block here
@@ -1475,6 +1645,8 @@ try:
1475
1645
  stream_name_str, stream_messages = typed_messages[0]
1476
1646
  num_msgs = len(stream_messages)
1477
1647
 
1648
+ print(f"[DEBUG] Processing {num_msgs} message(s) from stream {stream_name_str}")
1649
+
1478
1650
  # Log reception and count before processing
1479
1651
  logger.info(
1480
1652
  f"[{datetime.now(timezone.utc).isoformat()}] xreadgroup returned {num_msgs} message(s). Processing..."
@@ -1492,8 +1664,13 @@ try:
1492
1664
  # for k, v in msg_data_bytes_dict.items() } # No longer needed
1493
1665
  # print(f"Processing message {message_id_str}") # Reduce verbosity
1494
1666
  # print(f"Message data: {message_data_str_dict}") # Reduce verbosity
1667
+ print(f"[DEBUG] === PROCESSING MESSAGE {message_id_str} ===")
1668
+ print(f"[DEBUG] Message data keys: {list(message_data_str_dict.keys())}")
1669
+
1495
1670
  process_message(message_id_str, message_data_str_dict)
1496
1671
 
1672
+ print(f"[DEBUG] === FINISHED PROCESSING MESSAGE {message_id_str} ===")
1673
+
1497
1674
  except ConnectionError as e:
1498
1675
  logger.error(f"Redis connection error: {e}. Reconnecting in 5s...")
1499
1676
  time.sleep(5)
@@ -51,68 +51,131 @@ def process_health_check_message(
51
51
  health_group: str,
52
52
  ) -> None:
53
53
  """Processes a single health check message."""
54
+ print(f"[HEALTH DEBUG] === PROCESSING HEALTH CHECK MESSAGE ===")
55
+ print(f"[HEALTH DEBUG] Message ID: {message_id}")
56
+ print(f"[HEALTH DEBUG] Message data: {message_data}")
57
+ print(f"[HEALTH DEBUG] Health stream: {health_stream}")
58
+ print(f"[HEALTH DEBUG] Health group: {health_group}")
59
+
54
60
  logger.info(f"Processing health check message {message_id}: {message_data}")
55
61
 
56
62
  health_status = "ok"
57
63
  health_message: Optional[str] = "Health check processed successfully."
58
64
  details: Optional[Dict[str, Any]] = None
59
65
  return_stream: Optional[str] = None
66
+ original_message_id: Optional[str] = None
67
+ user_id: Optional[str] = None
60
68
 
61
69
  try:
62
70
  if "data" in message_data:
63
- data = json.loads(message_data["data"])
71
+ print(f"[HEALTH DEBUG] Found 'data' field in message")
72
+ data_str = message_data["data"]
73
+ print(f"[HEALTH DEBUG] Raw data string: {data_str}")
74
+
75
+ data = json.loads(data_str)
76
+ print(f"[HEALTH DEBUG] Parsed data: {json.dumps(data, indent=2)}")
64
77
  logger.info(f"Health check data: {data}")
65
- # Example: Extract return_stream if present in the health check data
78
+
79
+ # Extract important fields from the forwarded message
66
80
  return_stream = data.get("return_stream")
67
- # Example: Update details if provided
68
- if "check_details" in data:
69
- details = {"processed_details": data["check_details"]}
81
+ original_message_id = data.get("original_message_id")
82
+ user_id = data.get("user_id")
83
+ inner_kind = data.get("kind")
84
+ inner_id = data.get("id")
85
+ content = data.get("content")
86
+
87
+ print(f"[HEALTH DEBUG] Extracted fields:")
88
+ print(f"[HEALTH DEBUG] return_stream: {return_stream}")
89
+ print(f"[HEALTH DEBUG] original_message_id: {original_message_id}")
90
+ print(f"[HEALTH DEBUG] user_id: {user_id}")
91
+ print(f"[HEALTH DEBUG] inner_kind: {inner_kind}")
92
+ print(f"[HEALTH DEBUG] inner_id: {inner_id}")
93
+ print(f"[HEALTH DEBUG] content: {content}")
94
+
95
+ # Update details with health check info
96
+ details = {
97
+ "checked_message_id": inner_id,
98
+ "original_message_id": original_message_id,
99
+ "health_stream": health_stream,
100
+ }
101
+
102
+ if content and isinstance(content, dict):
103
+ details.update({"check_content": content})
104
+ print(f"[HEALTH DEBUG] Added check_content to details")
105
+ else:
106
+ print(f"[HEALTH DEBUG] No 'data' field found in message_data")
70
107
 
71
108
  except (json.JSONDecodeError, KeyError) as e:
109
+ print(f"[HEALTH DEBUG] ERROR parsing message data: {e}")
72
110
  logger.warning(f"Could not parse health check message data: {e}")
73
111
  health_status = "error"
74
112
  health_message = f"Failed to parse health check message data: {e}"
75
113
  details = {"error": str(e)}
76
114
 
77
- # Construct the health response
115
+ # Construct the health response using V1ProcessorHealthResponse
78
116
  health_response = V1ProcessorHealthResponse(
79
117
  status=health_status, message=health_message, details=details
80
118
  )
81
119
 
120
+ print(
121
+ f"[HEALTH DEBUG] Constructed V1ProcessorHealthResponse: {health_response.model_dump_json()}"
122
+ )
82
123
  logger.info(
83
124
  f"Health response for message {message_id}: {health_response.model_dump_json()}"
84
125
  )
85
126
 
86
- # If a return_stream is specified, send the response there
127
+ # Send the response to the return stream in the format expected by the system
87
128
  if return_stream:
129
+ print(
130
+ f"[HEALTH DEBUG] Sending V1ProcessorHealthResponse to return_stream: {return_stream}"
131
+ )
88
132
  try:
89
- # It's good practice to set a maxlen for the return stream to prevent it from growing indefinitely
133
+ # Send the V1ProcessorHealthResponse directly (not wrapped in StreamResponseMessage)
134
+ response_data = health_response.model_dump()
135
+
136
+ print(
137
+ f"[HEALTH DEBUG] Sending V1ProcessorHealthResponse directly: {json.dumps(response_data, indent=2)}"
138
+ )
139
+
140
+ # Send to return stream
90
141
  redis_conn.xadd(
91
142
  return_stream,
92
- health_response.model_dump(), # type: ignore[arg-type]
143
+ {"data": json.dumps(response_data)},
93
144
  maxlen=1000,
94
145
  approximate=True,
95
146
  )
147
+ print(
148
+ f"[HEALTH DEBUG] V1ProcessorHealthResponse sent successfully to {return_stream}"
149
+ )
96
150
  logger.info(
97
151
  f"Sent health response for {message_id} to stream: {return_stream}"
98
152
  )
99
153
  except Exception as e_resp_send:
154
+ print(f"[HEALTH DEBUG] ERROR sending response: {e_resp_send}")
100
155
  logger.error(
101
156
  f"Failed to send health response for {message_id} to stream {return_stream}: {e_resp_send}"
102
157
  )
158
+ else:
159
+ print(f"[HEALTH DEBUG] No return_stream specified, not sending response")
103
160
 
104
161
  # Acknowledge the health check message
162
+ print(f"[HEALTH DEBUG] Acknowledging message {message_id}")
105
163
  try:
106
164
  redis_conn.xack(health_stream, health_group, message_id)
165
+ print(f"[HEALTH DEBUG] Message {message_id} acknowledged successfully")
107
166
  logger.info(f"Acknowledged health check message {message_id}")
108
167
  except Exception as e_ack:
168
+ print(f"[HEALTH DEBUG] ERROR acknowledging message: {e_ack}")
109
169
  logger.error(
110
170
  f"Failed to acknowledge health check message {message_id}: {e_ack}"
111
171
  )
112
172
 
173
+ print(f"[HEALTH DEBUG] === FINISHED PROCESSING HEALTH CHECK MESSAGE ===")
174
+
113
175
 
114
176
  def main():
115
177
  """Main function for the health check consumer subprocess."""
178
+ print(f"[HEALTH DEBUG] === HEALTH WORKER STARTING ===")
116
179
  logger = setup_health_logging()
117
180
 
118
181
  # Get environment variables
@@ -120,7 +183,13 @@ def main():
120
183
  health_stream = os.environ.get("REDIS_HEALTH_STREAM")
121
184
  health_group = os.environ.get("REDIS_HEALTH_CONSUMER_GROUP")
122
185
 
186
+ print(f"[HEALTH DEBUG] Environment variables:")
187
+ print(f"[HEALTH DEBUG] REDIS_URL: {redis_url}")
188
+ print(f"[HEALTH DEBUG] REDIS_HEALTH_STREAM: {health_stream}")
189
+ print(f"[HEALTH DEBUG] REDIS_HEALTH_CONSUMER_GROUP: {health_group}")
190
+
123
191
  if not all([redis_url, health_stream, health_group]):
192
+ print(f"[HEALTH DEBUG] ERROR: Missing required environment variables")
124
193
  logger.error(
125
194
  "Missing required environment variables: REDIS_URL, REDIS_HEALTH_STREAM, REDIS_HEALTH_CONSUMER_GROUP"
126
195
  )
@@ -131,43 +200,64 @@ def main():
131
200
  assert isinstance(health_stream, str)
132
201
  assert isinstance(health_group, str)
133
202
 
203
+ print(
204
+ f"[HEALTH DEBUG] Starting health consumer for stream: {health_stream}, group: {health_group}"
205
+ )
134
206
  logger.info(
135
207
  f"Starting health consumer for stream: {health_stream}, group: {health_group}"
136
208
  )
137
209
 
138
210
  # Configure SOCKS proxy
211
+ print(f"[HEALTH DEBUG] Configuring SOCKS proxy...")
139
212
  socks.set_default_proxy(socks.SOCKS5, "localhost", 1055)
140
213
  socket.socket = socks.socksocket
141
214
  logger.info("Configured SOCKS5 proxy for socket connections via localhost:1055")
142
215
 
143
216
  health_redis_conn: Optional[redis.Redis] = None
144
217
  health_consumer_name = f"health-consumer-{os.getpid()}-{socket.gethostname()}"
218
+ print(f"[HEALTH DEBUG] Health consumer name: {health_consumer_name}")
145
219
 
220
+ print(f"[HEALTH DEBUG] === ENTERING MAIN LOOP ===")
146
221
  while True:
147
222
  try:
148
223
  if health_redis_conn is None:
224
+ print(f"[HEALTH DEBUG] Connecting to Redis for health stream...")
149
225
  logger.info("Connecting to Redis for health stream...")
150
226
  health_redis_conn = redis.from_url(redis_url, decode_responses=True)
151
227
  health_redis_conn.ping()
228
+ print(f"[HEALTH DEBUG] Connected to Redis successfully")
152
229
  logger.info("Connected to Redis for health stream.")
153
230
 
154
231
  # Create health consumer group if it doesn't exist
232
+ print(f"[HEALTH DEBUG] Creating/checking consumer group...")
155
233
  try:
156
234
  health_redis_conn.xgroup_create(
157
235
  health_stream, health_group, id="0", mkstream=True
158
236
  )
237
+ print(
238
+ f"[HEALTH DEBUG] Created consumer group {health_group} for stream {health_stream}"
239
+ )
159
240
  logger.info(
160
241
  f"Created consumer group {health_group} for stream {health_stream}"
161
242
  )
162
243
  except ResponseError as e_group:
163
244
  if "BUSYGROUP" in str(e_group):
245
+ print(
246
+ f"[HEALTH DEBUG] Consumer group {health_group} already exists"
247
+ )
164
248
  logger.info(f"Consumer group {health_group} already exists.")
165
249
  else:
250
+ print(
251
+ f"[HEALTH DEBUG] ERROR creating health consumer group: {e_group}"
252
+ )
166
253
  logger.error(f"Error creating health consumer group: {e_group}")
167
254
  time.sleep(5)
168
255
  health_redis_conn = None
169
256
  continue
170
257
  except Exception as e_group_other:
258
+ print(
259
+ f"[HEALTH DEBUG] UNEXPECTED ERROR creating health consumer group: {e_group_other}"
260
+ )
171
261
  logger.error(
172
262
  f"Unexpected error creating health consumer group: {e_group_other}"
173
263
  )
@@ -178,6 +268,7 @@ def main():
178
268
  # Read from health stream
179
269
  assert health_redis_conn is not None
180
270
 
271
+ print(f"[HEALTH DEBUG] Reading from health stream {health_stream}...")
181
272
  health_streams_arg: Dict[str, object] = {health_stream: ">"}
182
273
  raw_messages = health_redis_conn.xreadgroup(
183
274
  health_group,
@@ -187,13 +278,21 @@ def main():
187
278
  block=5000, # Block for 5 seconds
188
279
  )
189
280
 
281
+ print(f"[HEALTH DEBUG] xreadgroup returned: {raw_messages}")
282
+ print(f"[HEALTH DEBUG] Messages type: {type(raw_messages)}")
283
+
190
284
  if raw_messages:
285
+ print(f"[HEALTH DEBUG] Found messages to process")
191
286
  # Cast to expected type for decode_responses=True
192
287
  messages = cast(
193
288
  List[Tuple[str, List[Tuple[str, Dict[str, str]]]]], raw_messages
194
289
  )
195
- for _stream_name, stream_messages in messages:
290
+ for stream_name, stream_messages in messages:
291
+ print(
292
+ f"[HEALTH DEBUG] Processing stream: {stream_name} with {len(stream_messages)} message(s)"
293
+ )
196
294
  for message_id, message_data in stream_messages:
295
+ print(f"[HEALTH DEBUG] Processing message {message_id}")
197
296
  process_health_check_message(
198
297
  message_id,
199
298
  message_data,
@@ -202,6 +301,8 @@ def main():
202
301
  health_stream,
203
302
  health_group,
204
303
  )
304
+ else:
305
+ print(f"[HEALTH DEBUG] No messages received (timeout)")
205
306
 
206
307
  except (ConnectionError, RedisTimeoutError, TimeoutError) as e_conn:
207
308
  logger.error(f"Redis connection error: {e_conn}. Reconnecting in 5s...")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nebu
3
- Version: 0.1.118
3
+ Version: 0.1.119
4
4
  Summary: A globally distributed container runtime
5
5
  Requires-Python: >=3.10.14
6
6
  Description-Content-Type: text/markdown
@@ -13,8 +13,8 @@ nebu/containers/container.py,sha256=Mrh_gvMsTvDkj3CwpqIPzJ72IMw0gQIg64y548vq0yg,
13
13
  nebu/containers/models.py,sha256=0j6NGy4yto-enRDh_4JH_ZTbHrLdSpuMOqNQPnIrwC4,6815
14
14
  nebu/namespaces/models.py,sha256=EqUOpzhVBhvJw2P92ONDUbIgC31M9jMmcaG5vyOrsWg,497
15
15
  nebu/namespaces/namespace.py,sha256=oeZyGqsIGIrppyjif1ZONsdTmqRgd9oSLFE1BChXTTE,5247
16
- nebu/processors/consumer.py,sha256=18p3QKa9AgVdTv-HH3Iu7cwXTw1pjCy5pKuYqYt0Ndc,68524
17
- nebu/processors/consumer_health_worker.py,sha256=e6-WX8zrK5hprBvpU9al7CpiydYPdJp6wqjA33TyUvw,9509
16
+ nebu/processors/consumer.py,sha256=a3hSV_Os55LJXJG4B_HlP-_wxA39UJlEN_Obkv9VnV8,77490
17
+ nebu/processors/consumer_health_worker.py,sha256=7qT3Iu3aUoPqg0Doy-MWN7RMpoS1yXljLMaF3QikF2M,14483
18
18
  nebu/processors/consumer_process_worker.py,sha256=h--eNFKaLbUayxn88mB8oGGdrU2liE1dnwm_TPlewX8,36960
19
19
  nebu/processors/decorate.py,sha256=5p9pQrk_H8-Fj0UjsgSVCYx7Jk7KFuhMZtNhkKvpmkQ,61306
20
20
  nebu/processors/default.py,sha256=cy4ETMdbdRGkrvbYec1o60h7mGDlGN5JsuUph0ENtDU,364
@@ -22,8 +22,8 @@ nebu/processors/models.py,sha256=8-TmKha2_QAnPlXcZxYjrCSPDCX7FFcMDMcHK77jK0U,422
22
22
  nebu/processors/processor.py,sha256=APzvh4wB97pazl0SJewJEfi8sjQzQAMXZjtONNjaF_0,26081
23
23
  nebu/redis/models.py,sha256=coPovAcVXnOU1Xh_fpJL4PO3QctgK9nBe5QYoqEcnxg,1230
24
24
  nebu/services/service.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
- nebu-0.1.118.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
26
- nebu-0.1.118.dist-info/METADATA,sha256=6zVkM6OCzcti9On0yu4aPCzWtWSgL7xPIv8y4jo_Q2U,1798
27
- nebu-0.1.118.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
28
- nebu-0.1.118.dist-info/top_level.txt,sha256=uLIbEKJeGSHWOAJN5S0i5XBGwybALlF9bYoB1UhdEgQ,5
29
- nebu-0.1.118.dist-info/RECORD,,
25
+ nebu-0.1.119.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
26
+ nebu-0.1.119.dist-info/METADATA,sha256=Cruy0OeHat3cD1YXI7cbO9JaNIcb1gJ2LXIl50LK0WA,1798
27
+ nebu-0.1.119.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
28
+ nebu-0.1.119.dist-info/top_level.txt,sha256=uLIbEKJeGSHWOAJN5S0i5XBGwybALlF9bYoB1UhdEgQ,5
29
+ nebu-0.1.119.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.8.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5