nebu 0.1.117__py3-none-any.whl → 0.1.118__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.
@@ -5,13 +5,16 @@ import os
5
5
  import socket
6
6
  import sys
7
7
  import time
8
- from typing import Dict, List, Optional, Tuple, cast
8
+ from typing import Any, Dict, List, Optional, Tuple, cast
9
9
 
10
10
  import redis
11
11
  import socks
12
12
  from redis import ConnectionError, ResponseError
13
13
  from redis.exceptions import TimeoutError as RedisTimeoutError
14
14
 
15
+ # Assuming these are imported from other modules
16
+ from nebu.processors.models import V1ProcessorHealthResponse
17
+
15
18
 
16
19
  def setup_health_logging():
17
20
  """Set up logging for the health check worker to write to a dedicated file."""
@@ -50,16 +53,53 @@ def process_health_check_message(
50
53
  """Processes a single health check message."""
51
54
  logger.info(f"Processing health check message {message_id}: {message_data}")
52
55
 
53
- # Parse the message if it contains JSON data
56
+ health_status = "ok"
57
+ health_message: Optional[str] = "Health check processed successfully."
58
+ details: Optional[Dict[str, Any]] = None
59
+ return_stream: Optional[str] = None
60
+
54
61
  try:
55
62
  if "data" in message_data:
56
63
  data = json.loads(message_data["data"])
57
64
  logger.info(f"Health check data: {data}")
65
+ # Example: Extract return_stream if present in the health check data
66
+ 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"]}
70
+
58
71
  except (json.JSONDecodeError, KeyError) as e:
59
72
  logger.warning(f"Could not parse health check message data: {e}")
73
+ health_status = "error"
74
+ health_message = f"Failed to parse health check message data: {e}"
75
+ details = {"error": str(e)}
76
+
77
+ # Construct the health response
78
+ health_response = V1ProcessorHealthResponse(
79
+ status=health_status, message=health_message, details=details
80
+ )
81
+
82
+ logger.info(
83
+ f"Health response for message {message_id}: {health_response.model_dump_json()}"
84
+ )
60
85
 
61
- # You could add more logic here, e.g., update an internal health status,
62
- # send a response, perform actual health checks, etc.
86
+ # If a return_stream is specified, send the response there
87
+ if return_stream:
88
+ try:
89
+ # It's good practice to set a maxlen for the return stream to prevent it from growing indefinitely
90
+ redis_conn.xadd(
91
+ return_stream,
92
+ health_response.model_dump(), # type: ignore[arg-type]
93
+ maxlen=1000,
94
+ approximate=True,
95
+ )
96
+ logger.info(
97
+ f"Sent health response for {message_id} to stream: {return_stream}"
98
+ )
99
+ except Exception as e_resp_send:
100
+ logger.error(
101
+ f"Failed to send health response for {message_id} to stream {return_stream}: {e_resp_send}"
102
+ )
63
103
 
64
104
  # Acknowledge the health check message
65
105
  try:
nebu/processors/models.py CHANGED
@@ -149,3 +149,9 @@ class V1OpenAIStreamResponse(BaseModel):
149
149
  content: Any # Using Any for ChatCompletionResponse
150
150
  created_at: int
151
151
  user_id: Optional[str] = None
152
+
153
+
154
+ class V1ProcessorHealthResponse(BaseModel):
155
+ status: str
156
+ message: Optional[str] = None
157
+ details: Optional[Any] = None
@@ -1,7 +1,8 @@
1
1
  import json
2
2
  import threading
3
3
  import time
4
- import uuid
4
+
5
+ # import uuid # Removed unused import
5
6
  from typing import (
6
7
  Any,
7
8
  Dict,
@@ -23,6 +24,7 @@ from nebu.meta import V1ResourceMetaRequest, V1ResourceReference
23
24
  from nebu.processors.models import (
24
25
  V1ContainerRequest,
25
26
  V1Processor,
27
+ V1ProcessorHealthResponse,
26
28
  V1ProcessorRequest,
27
29
  V1Processors,
28
30
  V1ProcessorScaleRequest,
@@ -246,7 +248,7 @@ class Processor(Generic[InputType, OutputType]):
246
248
 
247
249
  # --- Wait for health check if requested ---
248
250
  if wait_for_healthy:
249
- self.wait_for_health_check()
251
+ self.wait_for_healthy()
250
252
 
251
253
  def __call__(
252
254
  self,
@@ -559,11 +561,11 @@ class Processor(Generic[InputType, OutputType]):
559
561
  else:
560
562
  logger.info(f"No active log stream to stop for {self.name}.")
561
563
 
562
- def wait_for_health_check(
564
+ def wait_for_healthy(
563
565
  self, timeout: float = 3600.0, retry_interval: float = 5.0
564
566
  ) -> None:
565
567
  """
566
- Wait for the processor to respond to health checks.
568
+ Wait for the processor to respond to health checks using the health endpoint.
567
569
 
568
570
  Args:
569
571
  timeout: Maximum time to wait for health check in seconds
@@ -573,40 +575,25 @@ class Processor(Generic[InputType, OutputType]):
573
575
  raise ValueError("Processor not found, cannot perform health check")
574
576
 
575
577
  logger.info(
576
- f"Waiting for processor {self.processor.metadata.name} to be healthy..."
578
+ f"Waiting for processor {self.processor.metadata.name} to be healthy via health endpoint..."
577
579
  )
578
580
 
579
581
  start_time = time.time()
580
582
  while time.time() - start_time < timeout:
581
583
  try:
582
- # Create a health check message
583
- health_check_data = {
584
- "kind": "HealthCheck",
585
- "id": str(uuid.uuid4()),
586
- "content": {},
587
- "created_at": time.time(),
588
- }
589
-
590
- # Send health check and wait for response
591
- response = self.send(
592
- data=health_check_data, # type: ignore[arg-type]
593
- wait=True,
594
- timeout=30.0, # Short timeout for individual health check
584
+ health_response = self.health() # Use the new health() method
585
+ logger.info(
586
+ f">>> Health check response: {health_response.model_dump_json()}"
595
587
  )
596
- logger.info(f">>> Health check response: {response}")
597
588
 
598
589
  # Check if the response indicates health
599
- if response and isinstance(response, dict):
600
- status = response.get("status")
601
- if status == "healthy":
602
- logger.info(
603
- f"Processor {self.processor.metadata.name} is healthy!"
604
- )
605
- return
606
-
607
- logger.info(
608
- f"Health check attempt failed, retrying in {retry_interval}s..."
609
- )
590
+ if health_response.status == "ok": # Check for "ok" status
591
+ logger.info(f"Processor {self.processor.metadata.name} is healthy!")
592
+ return
593
+ else:
594
+ logger.info(
595
+ f"Processor {self.processor.metadata.name} reported status: {health_response.status}. Retrying in {retry_interval}s..."
596
+ )
610
597
 
611
598
  except Exception as e:
612
599
  logger.info(
@@ -619,3 +606,39 @@ class Processor(Generic[InputType, OutputType]):
619
606
  raise TimeoutError(
620
607
  f"Processor {self.processor.metadata.name} failed to become healthy within {timeout} seconds"
621
608
  )
609
+
610
+ def health(self) -> V1ProcessorHealthResponse:
611
+ """
612
+ Performs a health check on the processor by calling the health endpoint.
613
+ """
614
+ if (
615
+ not self.processor
616
+ or not self.processor.metadata.name
617
+ or not self.processor.metadata.namespace
618
+ ):
619
+ raise ValueError(
620
+ "Processor not found or missing metadata (name/namespace), cannot perform health check."
621
+ )
622
+
623
+ health_url = f"{self.orign_host}/v1/processors/{self.processor.metadata.namespace}/{self.processor.metadata.name}/health"
624
+ logger.debug(f"Calling health check endpoint: {health_url}")
625
+
626
+ try:
627
+ response = requests.get(
628
+ health_url,
629
+ headers={"Authorization": f"Bearer {self.api_key}"},
630
+ timeout=30.0, # Standard timeout for a health check
631
+ )
632
+ response.raise_for_status() # Raise an exception for HTTP errors
633
+ health_response_data = response.json()
634
+ return V1ProcessorHealthResponse.model_validate(health_response_data)
635
+ except requests.exceptions.RequestException as e:
636
+ logger.error(f"Health check request to {health_url} failed: {e}")
637
+ # Optionally, return a V1ProcessorHealthResponse indicating an error
638
+ # For now, re-raising the exception or a custom one might be better
639
+ raise RuntimeError(f"Failed to get health status: {e}") from e
640
+ except Exception as e:
641
+ logger.error(f"An unexpected error occurred during health check: {e}")
642
+ raise RuntimeError(
643
+ f"Unexpected error during health status retrieval: {e}"
644
+ ) from e
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nebu
3
- Version: 0.1.117
3
+ Version: 0.1.118
4
4
  Summary: A globally distributed container runtime
5
5
  Requires-Python: >=3.10.14
6
6
  Description-Content-Type: text/markdown
@@ -14,16 +14,16 @@ nebu/containers/models.py,sha256=0j6NGy4yto-enRDh_4JH_ZTbHrLdSpuMOqNQPnIrwC4,681
14
14
  nebu/namespaces/models.py,sha256=EqUOpzhVBhvJw2P92ONDUbIgC31M9jMmcaG5vyOrsWg,497
15
15
  nebu/namespaces/namespace.py,sha256=oeZyGqsIGIrppyjif1ZONsdTmqRgd9oSLFE1BChXTTE,5247
16
16
  nebu/processors/consumer.py,sha256=18p3QKa9AgVdTv-HH3Iu7cwXTw1pjCy5pKuYqYt0Ndc,68524
17
- nebu/processors/consumer_health_worker.py,sha256=4tS2vyd71tXMCy93Ae_TQi_ESaZVLPO16f9Gf4Eavcg,7946
17
+ nebu/processors/consumer_health_worker.py,sha256=e6-WX8zrK5hprBvpU9al7CpiydYPdJp6wqjA33TyUvw,9509
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
21
- nebu/processors/models.py,sha256=g4B1t6Rgoy-NUEHBLeQc0EENzHXLDlWSio8Muv7cTDU,4093
22
- nebu/processors/processor.py,sha256=oi9QdpvZV91UzVHz63pBFQw9-BeY7xvtJkJblFF_xew,24753
21
+ nebu/processors/models.py,sha256=8-TmKha2_QAnPlXcZxYjrCSPDCX7FFcMDMcHK77jK0U,4223
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.117.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
26
- nebu-0.1.117.dist-info/METADATA,sha256=T-j8N4SlzlgRjOk8QvUCc6sCUGvdSBllPd45bEghRUw,1798
27
- nebu-0.1.117.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
28
- nebu-0.1.117.dist-info/top_level.txt,sha256=uLIbEKJeGSHWOAJN5S0i5XBGwybALlF9bYoB1UhdEgQ,5
29
- nebu-0.1.117.dist-info/RECORD,,
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,,
File without changes