Python API implements streaming interfaces (#577)

* Tech spec

* Python CLI utilities updated to use the API including streaming features

* Added type safety to Python API

* Completed missing auth token support in CLI
This commit is contained in:
cybermaggedon 2025-12-04 17:38:57 +00:00 committed by GitHub
parent b957004db9
commit 01aeede78b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
53 changed files with 4489 additions and 715 deletions

View file

@ -3,6 +3,7 @@ import requests
import json
import base64
import time
from typing import Optional
from . library import Library
from . flow import Flow
@ -26,7 +27,7 @@ def check_error(response):
class Api:
def __init__(self, url="http://localhost:8088/", timeout=60):
def __init__(self, url="http://localhost:8088/", timeout=60, token: Optional[str] = None):
self.url = url
@ -36,6 +37,16 @@ class Api:
self.url += "api/v1/"
self.timeout = timeout
self.token = token
# Lazy initialization for new clients
self._socket_client = None
self._bulk_client = None
self._async_flow = None
self._async_socket_client = None
self._async_bulk_client = None
self._metrics = None
self._async_metrics = None
def flow(self):
return Flow(api=self)
@ -50,8 +61,12 @@ class Api:
url = f"{self.url}{path}"
headers = {}
if self.token:
headers["Authorization"] = f"Bearer {self.token}"
# Invoke the API, input is passed as JSON
resp = requests.post(url, json=request, timeout=self.timeout)
resp = requests.post(url, json=request, timeout=self.timeout, headers=headers)
# Should be a 200 status code
if resp.status_code != 200:
@ -72,3 +87,96 @@ class Api:
def collection(self):
return Collection(self)
# New synchronous methods
def socket(self):
"""Synchronous WebSocket-based interface for streaming operations"""
if self._socket_client is None:
from . socket_client import SocketClient
# Extract base URL (remove api/v1/ suffix)
base_url = self.url.rsplit("api/v1/", 1)[0].rstrip("/")
self._socket_client = SocketClient(base_url, self.timeout, self.token)
return self._socket_client
def bulk(self):
"""Synchronous bulk operations interface for import/export"""
if self._bulk_client is None:
from . bulk_client import BulkClient
# Extract base URL (remove api/v1/ suffix)
base_url = self.url.rsplit("api/v1/", 1)[0].rstrip("/")
self._bulk_client = BulkClient(base_url, self.timeout, self.token)
return self._bulk_client
def metrics(self):
"""Synchronous metrics interface"""
if self._metrics is None:
from . metrics import Metrics
# Extract base URL (remove api/v1/ suffix)
base_url = self.url.rsplit("api/v1/", 1)[0].rstrip("/")
self._metrics = Metrics(base_url, self.timeout, self.token)
return self._metrics
# New asynchronous methods
def async_flow(self):
"""Asynchronous REST-based flow interface"""
if self._async_flow is None:
from . async_flow import AsyncFlow
self._async_flow = AsyncFlow(self.url, self.timeout, self.token)
return self._async_flow
def async_socket(self):
"""Asynchronous WebSocket-based interface for streaming operations"""
if self._async_socket_client is None:
from . async_socket_client import AsyncSocketClient
# Extract base URL (remove api/v1/ suffix)
base_url = self.url.rsplit("api/v1/", 1)[0].rstrip("/")
self._async_socket_client = AsyncSocketClient(base_url, self.timeout, self.token)
return self._async_socket_client
def async_bulk(self):
"""Asynchronous bulk operations interface for import/export"""
if self._async_bulk_client is None:
from . async_bulk_client import AsyncBulkClient
# Extract base URL (remove api/v1/ suffix)
base_url = self.url.rsplit("api/v1/", 1)[0].rstrip("/")
self._async_bulk_client = AsyncBulkClient(base_url, self.timeout, self.token)
return self._async_bulk_client
def async_metrics(self):
"""Asynchronous metrics interface"""
if self._async_metrics is None:
from . async_metrics import AsyncMetrics
# Extract base URL (remove api/v1/ suffix)
base_url = self.url.rsplit("api/v1/", 1)[0].rstrip("/")
self._async_metrics = AsyncMetrics(base_url, self.timeout, self.token)
return self._async_metrics
# Resource management
def close(self):
"""Close all synchronous connections"""
if self._socket_client:
self._socket_client.close()
if self._bulk_client:
self._bulk_client.close()
async def aclose(self):
"""Close all asynchronous connections"""
if self._async_socket_client:
await self._async_socket_client.aclose()
if self._async_bulk_client:
await self._async_bulk_client.aclose()
if self._async_flow:
await self._async_flow.aclose()
# Context manager support
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
async def __aenter__(self):
return self
async def __aexit__(self, *args):
await self.aclose()