From 08a5eb2b13f51689a1897814b886992faa59eba7 Mon Sep 17 00:00:00 2001 From: Anand Jayarajan Date: Thu, 9 Apr 2026 10:39:38 -0400 Subject: [PATCH 1/6] Add deployment logs fetch support in SDK --- centml/sdk/api.py | 39 ++++++++++++++++++ examples/sdk/get_deployment_logs.py | 61 +++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 examples/sdk/get_deployment_logs.py diff --git a/centml/sdk/api.py b/centml/sdk/api.py index f35312d..8cc2a9d 100644 --- a/centml/sdk/api.py +++ b/centml/sdk/api.py @@ -151,6 +151,45 @@ def invite_user(self, email: str): def get_capacity(self, cluster_id=None): return self._api.list_cluster_capacity_capacity_get(cluster_id=cluster_id).results + def get_deployment_revisions(self, deployment_id: int): + return self._api.get_deployment_revisions_deployments_revisions_deployment_id_get( + deployment_id=deployment_id + ).results + + def get_deployment_logs( + self, + deployment_id: int, + revision_number: int, + start_time: int, + end_time: int, + start_from_head: bool = True, + line_count: int = None, + ): + """Fetch all logs for a deployment within a time window, handling pagination automatically. + + start_time and end_time are Unix timestamps in milliseconds. + Use get_deployment_revisions() to find the current revision number. + """ + all_events = [] + next_page_token = None + + while True: + response = self._api.get_deployment_logs_v3_deployments_logs_v3_deployment_id_revision_number_get( + deployment_id=deployment_id, + revision_number=revision_number, + start_time=start_time, + end_time=end_time, + next_page_token=next_page_token, + start_from_head=start_from_head, + line_count=line_count, + ) + all_events.extend(response.events) + next_page_token = response.next_page_token + if not next_page_token: + break + + return all_events + @contextmanager def get_centml_client(): diff --git a/examples/sdk/get_deployment_logs.py b/examples/sdk/get_deployment_logs.py new file mode 100644 index 0000000..cfd49cf --- /dev/null +++ b/examples/sdk/get_deployment_logs.py @@ -0,0 +1,61 @@ +from datetime import datetime, timezone, timedelta + +from centml.sdk.api import get_centml_client + +# --- Configuration --- +DEPLOYMENT_ID = 1234 # Replace with your deployment ID +REVISION_NUMBER = 10 +HOURS_BACK = 1 # Fetch logs from the last N hours + + +def format_event(event: dict) -> str: + timestamp_ms = ( + event.get("timestamp") + or event.get("time") + or event.get("ts") + or "" + ) + message = ( + event.get("message") + or event.get("msg") + or event.get("log") + or str(event) + ) + if timestamp_ms: + ts = datetime.fromtimestamp(int(timestamp_ms) / 1000, tz=timezone.utc).isoformat() + return f"[{ts}] {message}" + return message + + +def main(): + end_time = int(datetime.now(timezone.utc).timestamp() * 1000) + start_time = end_time - int(timedelta(hours=HOURS_BACK).total_seconds() * 1000) + + print(f"Fetching logs for deployment {DEPLOYMENT_ID}") + print( + f"Time window: " + f"{datetime.fromtimestamp(start_time / 1000, tz=timezone.utc).isoformat()} → " + f"{datetime.fromtimestamp(end_time / 1000, tz=timezone.utc).isoformat()}" + ) + print() + + with get_centml_client() as cclient: + events = cclient.get_deployment_logs( + deployment_id=DEPLOYMENT_ID, + revision_number=REVISION_NUMBER, + start_time=start_time, + end_time=end_time, + start_from_head=False, + ) + + if not events: + print("No logs found in the given time window.") + return + + print(f"Found {len(events)} log entries:\n") + for event in events: + print(format_event(event)) + + +if __name__ == "__main__": + main() From e5adbfee811093db6d6cd98e8a797926d0c4715f Mon Sep 17 00:00:00 2001 From: Anand Jayarajan Date: Thu, 9 Apr 2026 10:47:43 -0400 Subject: [PATCH 2/6] Add support for streaming mode in logs --- centml/sdk/api.py | 46 ++++++++++++++++------------- examples/sdk/get_deployment_logs.py | 17 ++++++++++- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/centml/sdk/api.py b/centml/sdk/api.py index 8cc2a9d..b99385e 100644 --- a/centml/sdk/api.py +++ b/centml/sdk/api.py @@ -164,31 +164,37 @@ def get_deployment_logs( end_time: int, start_from_head: bool = True, line_count: int = None, + stream: bool = False, ): - """Fetch all logs for a deployment within a time window, handling pagination automatically. + """Fetch logs for a deployment within a time window, handling pagination automatically. start_time and end_time are Unix timestamps in milliseconds. Use get_deployment_revisions() to find the current revision number. + + If stream=True, returns a generator that yields events as each page is fetched. + If stream=False (default), returns a flat list of all events. """ - all_events = [] - next_page_token = None - - while True: - response = self._api.get_deployment_logs_v3_deployments_logs_v3_deployment_id_revision_number_get( - deployment_id=deployment_id, - revision_number=revision_number, - start_time=start_time, - end_time=end_time, - next_page_token=next_page_token, - start_from_head=start_from_head, - line_count=line_count, - ) - all_events.extend(response.events) - next_page_token = response.next_page_token - if not next_page_token: - break - - return all_events + def _iter_events(): + next_page_token = None + while True: + response = self._api.get_deployment_logs_v3_deployments_logs_v3_deployment_id_revision_number_get( + deployment_id=deployment_id, + revision_number=revision_number, + start_time=start_time, + end_time=end_time, + next_page_token=next_page_token, + start_from_head=start_from_head, + line_count=line_count, + ) + yield from response.events + next_page_token = response.next_page_token + if not next_page_token: + break + + if stream: + return _iter_events() + + return list(_iter_events()) @contextmanager diff --git a/examples/sdk/get_deployment_logs.py b/examples/sdk/get_deployment_logs.py index cfd49cf..a01881e 100644 --- a/examples/sdk/get_deployment_logs.py +++ b/examples/sdk/get_deployment_logs.py @@ -3,7 +3,7 @@ from centml.sdk.api import get_centml_client # --- Configuration --- -DEPLOYMENT_ID = 1234 # Replace with your deployment ID +DEPLOYMENT_ID = 8170 # Replace with your deployment ID REVISION_NUMBER = 10 HOURS_BACK = 1 # Fetch logs from the last N hours @@ -39,6 +39,21 @@ def main(): ) print() + # --- Streaming: print events as each page arrives --- + print("=== Streaming mode ===") + with get_centml_client() as cclient: + for event in cclient.get_deployment_logs( + deployment_id=DEPLOYMENT_ID, + revision_number=REVISION_NUMBER, + start_time=start_time, + end_time=end_time, + start_from_head=False, + stream=True, + ): + print(format_event(event)) + + # --- Batch: collect all events then process --- + print("\n=== Batch mode ===") with get_centml_client() as cclient: events = cclient.get_deployment_logs( deployment_id=DEPLOYMENT_ID, From 1fb56d3fa472ded948eb0c404dcb95994d5ccb43 Mon Sep 17 00:00:00 2001 From: Anand Jayarajan Date: Thu, 9 Apr 2026 10:54:50 -0400 Subject: [PATCH 3/6] Fix lint and mypy --- centml/sdk/api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/centml/sdk/api.py b/centml/sdk/api.py index b99385e..8690c2f 100644 --- a/centml/sdk/api.py +++ b/centml/sdk/api.py @@ -162,8 +162,8 @@ def get_deployment_logs( revision_number: int, start_time: int, end_time: int, + line_count: int = 100, start_from_head: bool = True, - line_count: int = None, stream: bool = False, ): """Fetch logs for a deployment within a time window, handling pagination automatically. @@ -174,6 +174,7 @@ def get_deployment_logs( If stream=True, returns a generator that yields events as each page is fetched. If stream=False (default), returns a flat list of all events. """ + def _iter_events(): next_page_token = None while True: From c2e114eab8a6e629b57dc03af03480b2bbdf637c Mon Sep 17 00:00:00 2001 From: Anand Jayarajan Date: Thu, 9 Apr 2026 10:59:33 -0400 Subject: [PATCH 4/6] Fix example --- examples/sdk/get_deployment_logs.py | 55 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/examples/sdk/get_deployment_logs.py b/examples/sdk/get_deployment_logs.py index a01881e..04a05a5 100644 --- a/examples/sdk/get_deployment_logs.py +++ b/examples/sdk/get_deployment_logs.py @@ -28,6 +28,7 @@ def format_event(event: dict) -> str: def main(): + stream = False end_time = int(datetime.now(timezone.utc).timestamp() * 1000) start_time = end_time - int(timedelta(hours=HOURS_BACK).total_seconds() * 1000) @@ -39,37 +40,35 @@ def main(): ) print() - # --- Streaming: print events as each page arrives --- - print("=== Streaming mode ===") with get_centml_client() as cclient: - for event in cclient.get_deployment_logs( - deployment_id=DEPLOYMENT_ID, - revision_number=REVISION_NUMBER, - start_time=start_time, - end_time=end_time, - start_from_head=False, - stream=True, - ): - print(format_event(event)) + if stream: + # Streaming: print events as each page arrives + for event in cclient.get_deployment_logs( + deployment_id=DEPLOYMENT_ID, + revision_number=REVISION_NUMBER, + start_time=start_time, + end_time=end_time, + start_from_head=False, + stream=stream, + ): + print(format_event(event)) + else: + # Batch: collect all events then process + events = cclient.get_deployment_logs( + deployment_id=DEPLOYMENT_ID, + revision_number=REVISION_NUMBER, + start_time=start_time, + end_time=end_time, + start_from_head=False, + ) - # --- Batch: collect all events then process --- - print("\n=== Batch mode ===") - with get_centml_client() as cclient: - events = cclient.get_deployment_logs( - deployment_id=DEPLOYMENT_ID, - revision_number=REVISION_NUMBER, - start_time=start_time, - end_time=end_time, - start_from_head=False, - ) - - if not events: - print("No logs found in the given time window.") - return + if not events: + print("No logs found in the given time window.") + return - print(f"Found {len(events)} log entries:\n") - for event in events: - print(format_event(event)) + print(f"Found {len(events)} log entries:\n") + for event in events: + print(format_event(event)) if __name__ == "__main__": From 4ed0a6367ed69e10c5e1b771fcdda347f3647fa8 Mon Sep 17 00:00:00 2001 From: Anand Jayarajan Date: Thu, 9 Apr 2026 10:59:52 -0400 Subject: [PATCH 5/6] Fix --- examples/sdk/get_deployment_logs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/sdk/get_deployment_logs.py b/examples/sdk/get_deployment_logs.py index 04a05a5..13e92c3 100644 --- a/examples/sdk/get_deployment_logs.py +++ b/examples/sdk/get_deployment_logs.py @@ -28,7 +28,7 @@ def format_event(event: dict) -> str: def main(): - stream = False + stream = True end_time = int(datetime.now(timezone.utc).timestamp() * 1000) start_time = end_time - int(timedelta(hours=HOURS_BACK).total_seconds() * 1000) From 5dbb716fb0159d6070e67df5a9b080eb1b8e58b8 Mon Sep 17 00:00:00 2001 From: Anand Jayarajan Date: Thu, 9 Apr 2026 11:36:15 -0400 Subject: [PATCH 6/6] CHange deployment id back to 1234 --- examples/sdk/get_deployment_logs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/sdk/get_deployment_logs.py b/examples/sdk/get_deployment_logs.py index 13e92c3..6c7d7ea 100644 --- a/examples/sdk/get_deployment_logs.py +++ b/examples/sdk/get_deployment_logs.py @@ -3,7 +3,7 @@ from centml.sdk.api import get_centml_client # --- Configuration --- -DEPLOYMENT_ID = 8170 # Replace with your deployment ID +DEPLOYMENT_ID = 1234 # Replace with your deployment ID REVISION_NUMBER = 10 HOURS_BACK = 1 # Fetch logs from the last N hours