sigmashake 0.1.1__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.
sigmashake/__init__.py ADDED
@@ -0,0 +1,25 @@
1
+ """SigmaShake Python SDK -- agent-first, async-native, type-safe."""
2
+
3
+ from ._version import __version__
4
+ from .client import SigmaShake
5
+ from .exceptions import (
6
+ AuthenticationError,
7
+ AuthorizationError,
8
+ NotFoundError,
9
+ RateLimitError,
10
+ ServerError,
11
+ SigmaShakeError,
12
+ ValidationError,
13
+ )
14
+
15
+ __all__ = [
16
+ "__version__",
17
+ "SigmaShake",
18
+ "SigmaShakeError",
19
+ "AuthenticationError",
20
+ "AuthorizationError",
21
+ "NotFoundError",
22
+ "ValidationError",
23
+ "RateLimitError",
24
+ "ServerError",
25
+ ]
sigmashake/_version.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.1.1"
sigmashake/accounts.py ADDED
@@ -0,0 +1,91 @@
1
+ """Account management operations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, List, Optional, TYPE_CHECKING
6
+
7
+ from .models import (
8
+ Account,
9
+ AddSeatBody,
10
+ Seat,
11
+ Subscription,
12
+ TenantUsage,
13
+ Tier,
14
+ UpdateSubscriptionBody,
15
+ )
16
+
17
+ if TYPE_CHECKING:
18
+ from .client import _HTTPTransport
19
+
20
+
21
+ class AccountsResource:
22
+ """Account CRUD and subscription management."""
23
+
24
+ def __init__(self, transport: _HTTPTransport) -> None:
25
+ self._t = transport
26
+
27
+ # -- accounts -------------------------------------------------------------
28
+
29
+ def create(self, name: str, tier: str = "free") -> Account:
30
+ body = {"name": name, "tier": tier}
31
+ data = self._t.request("POST", "/v1/accounts", json=body)
32
+ return Account.model_validate(data)
33
+
34
+ async def async_create(self, name: str, tier: str = "free") -> Account:
35
+ body = {"name": name, "tier": tier}
36
+ data = await self._t.async_request("POST", "/v1/accounts", json=body)
37
+ return Account.model_validate(data)
38
+
39
+ def get(self, account_id: str) -> Account:
40
+ data = self._t.request("GET", f"/v1/accounts/{account_id}")
41
+ return Account.model_validate(data)
42
+
43
+ async def async_get(self, account_id: str) -> Account:
44
+ data = await self._t.async_request("GET", f"/v1/accounts/{account_id}")
45
+ return Account.model_validate(data)
46
+
47
+ def get_usage(self, account_id: str) -> TenantUsage:
48
+ data = self._t.request("GET", f"/v1/accounts/{account_id}/usage")
49
+ return TenantUsage.model_validate(data)
50
+
51
+ async def async_get_usage(self, account_id: str) -> TenantUsage:
52
+ data = await self._t.async_request("GET", f"/v1/accounts/{account_id}/usage")
53
+ return TenantUsage.model_validate(data)
54
+
55
+ # -- subscriptions --------------------------------------------------------
56
+
57
+ def get_subscription(self, account_id: str) -> Subscription:
58
+ data = self._t.request("GET", f"/v1/accounts/{account_id}/subscription")
59
+ return Subscription.model_validate(data)
60
+
61
+ async def async_get_subscription(self, account_id: str) -> Subscription:
62
+ data = await self._t.async_request("GET", f"/v1/accounts/{account_id}/subscription")
63
+ return Subscription.model_validate(data)
64
+
65
+ def update_subscription(self, account_id: str, **kwargs: Any) -> Subscription:
66
+ data = self._t.request("PATCH", f"/v1/accounts/{account_id}/subscription", json=kwargs)
67
+ return Subscription.model_validate(data)
68
+
69
+ async def async_update_subscription(self, account_id: str, **kwargs: Any) -> Subscription:
70
+ data = await self._t.async_request("PATCH", f"/v1/accounts/{account_id}/subscription", json=kwargs)
71
+ return Subscription.model_validate(data)
72
+
73
+ # -- seats ----------------------------------------------------------------
74
+
75
+ def add_seat(self, account_id: str, user_email: str, role: str = "member") -> Seat:
76
+ body = {"user_email": user_email, "role": role}
77
+ data = self._t.request("POST", f"/v1/accounts/{account_id}/seats", json=body)
78
+ return Seat.model_validate(data)
79
+
80
+ async def async_add_seat(self, account_id: str, user_email: str, role: str = "member") -> Seat:
81
+ body = {"user_email": user_email, "role": role}
82
+ data = await self._t.async_request("POST", f"/v1/accounts/{account_id}/seats", json=body)
83
+ return Seat.model_validate(data)
84
+
85
+ def list_seats(self, account_id: str) -> List[Seat]:
86
+ data = self._t.request("GET", f"/v1/accounts/{account_id}/seats")
87
+ return [Seat.model_validate(s) for s in data.get("seats", data if isinstance(data, list) else [])]
88
+
89
+ async def async_list_seats(self, account_id: str) -> List[Seat]:
90
+ data = await self._t.async_request("GET", f"/v1/accounts/{account_id}/seats")
91
+ return [Seat.model_validate(s) for s in data.get("seats", data if isinstance(data, list) else [])]
sigmashake/agents.py ADDED
@@ -0,0 +1,47 @@
1
+ """Agent lifecycle operations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, List, Optional, TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from .client import _HTTPTransport
9
+
10
+
11
+ class AgentsResource:
12
+ """Agent registration and management."""
13
+
14
+ def __init__(self, transport: _HTTPTransport) -> None:
15
+ self._t = transport
16
+
17
+ def register(
18
+ self,
19
+ agent_id: str,
20
+ agent_type: str,
21
+ metadata: Optional[Dict[str, Any]] = None,
22
+ ) -> Dict[str, Any]:
23
+ body = {"agent_id": agent_id, "agent_type": agent_type, "metadata": metadata or {}}
24
+ return self._t.request("POST", "/v1/agents", json=body)
25
+
26
+ async def async_register(
27
+ self,
28
+ agent_id: str,
29
+ agent_type: str,
30
+ metadata: Optional[Dict[str, Any]] = None,
31
+ ) -> Dict[str, Any]:
32
+ body = {"agent_id": agent_id, "agent_type": agent_type, "metadata": metadata or {}}
33
+ return await self._t.async_request("POST", "/v1/agents", json=body)
34
+
35
+ def get(self, agent_id: str) -> Dict[str, Any]:
36
+ return self._t.request("GET", f"/v1/agents/{agent_id}")
37
+
38
+ async def async_get(self, agent_id: str) -> Dict[str, Any]:
39
+ return await self._t.async_request("GET", f"/v1/agents/{agent_id}")
40
+
41
+ def list(self, limit: int = 100, offset: int = 0) -> List[Dict[str, Any]]:
42
+ data = self._t.request("GET", "/v1/agents", params={"limit": limit, "offset": offset})
43
+ return data.get("agents", []) if isinstance(data, dict) else data
44
+
45
+ async def async_list(self, limit: int = 100, offset: int = 0) -> List[Dict[str, Any]]:
46
+ data = await self._t.async_request("GET", "/v1/agents", params={"limit": limit, "offset": offset})
47
+ return data.get("agents", []) if isinstance(data, dict) else data
sigmashake/auth.py ADDED
@@ -0,0 +1,57 @@
1
+ """Auth and Identity token management."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, List, TYPE_CHECKING
6
+
7
+ from .models import (
8
+ IdentityTokenResponse,
9
+ TokenResponse,
10
+ )
11
+
12
+ if TYPE_CHECKING:
13
+ from .client import _HTTPTransport
14
+
15
+
16
+ class AuthResource:
17
+ """Token creation and validation."""
18
+
19
+ def __init__(self, transport: _HTTPTransport) -> None:
20
+ self._t = transport
21
+
22
+ def create_token(self, agent_id: str, scopes: List[str] | None = None) -> TokenResponse:
23
+ body = {"agent_id": agent_id, "scopes": scopes or []}
24
+ data = self._t.request("POST", "/v1/auth/token", json=body)
25
+ return TokenResponse.model_validate(data)
26
+
27
+ async def async_create_token(self, agent_id: str, scopes: List[str] | None = None) -> TokenResponse:
28
+ body = {"agent_id": agent_id, "scopes": scopes or []}
29
+ data = await self._t.async_request("POST", "/v1/auth/token", json=body)
30
+ return TokenResponse.model_validate(data)
31
+
32
+
33
+ class IdentityResource:
34
+ """Agent identity issuance."""
35
+
36
+ def __init__(self, transport: _HTTPTransport) -> None:
37
+ self._t = transport
38
+
39
+ def issue(
40
+ self,
41
+ agent_id: str,
42
+ capabilities: List[str] | None = None,
43
+ ttl_secs: int = 3600,
44
+ ) -> IdentityTokenResponse:
45
+ body = {"agent_id": agent_id, "capabilities": capabilities or [], "ttl_secs": ttl_secs}
46
+ data = self._t.request("POST", "/v1/identity/issue", json=body)
47
+ return IdentityTokenResponse.model_validate(data)
48
+
49
+ async def async_issue(
50
+ self,
51
+ agent_id: str,
52
+ capabilities: List[str] | None = None,
53
+ ttl_secs: int = 3600,
54
+ ) -> IdentityTokenResponse:
55
+ body = {"agent_id": agent_id, "capabilities": capabilities or [], "ttl_secs": ttl_secs}
56
+ data = await self._t.async_request("POST", "/v1/identity/issue", json=body)
57
+ return IdentityTokenResponse.model_validate(data)
sigmashake/client.py ADDED
@@ -0,0 +1,217 @@
1
+ """Main SigmaShake client -- sync and async HTTP transport."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, Optional
6
+
7
+ import httpx
8
+
9
+ from .auth import AuthResource, IdentityResource
10
+ from .accounts import AccountsResource
11
+ from .agents import AgentsResource
12
+ from .db import DBResource
13
+ from .exceptions import raise_for_status
14
+ from .fleet import FleetResource
15
+ from .gateway import GatewayResource
16
+ from .memory import MemoryResource
17
+ from .shield import ShieldResource
18
+ from .soc import SOCResource
19
+
20
+
21
+ _DEFAULT_BASE_URL = "https://api.sigmashake.com"
22
+ _DEFAULT_TIMEOUT = 30.0
23
+
24
+
25
+ class _HTTPTransport:
26
+ """Thin wrapper around httpx providing sync and async request methods."""
27
+
28
+ def __init__(
29
+ self,
30
+ *,
31
+ api_key: str,
32
+ base_url: str,
33
+ timeout: float,
34
+ async_mode: bool,
35
+ ) -> None:
36
+ self._base_url = base_url.rstrip("/")
37
+ self._async_mode = async_mode
38
+ headers = {
39
+ "Authorization": f"Bearer {api_key}",
40
+ "Content-Type": "application/json",
41
+ "User-Agent": "sigmashake-python/0.1.0",
42
+ }
43
+ if async_mode:
44
+ self._async_client = httpx.AsyncClient(
45
+ base_url=self._base_url,
46
+ headers=headers,
47
+ timeout=timeout,
48
+ )
49
+ self._sync_client = None
50
+ else:
51
+ self._sync_client = httpx.Client(
52
+ base_url=self._base_url,
53
+ headers=headers,
54
+ timeout=timeout,
55
+ )
56
+ self._async_client = None
57
+
58
+ # -- sync -----------------------------------------------------------------
59
+
60
+ def request(
61
+ self,
62
+ method: str,
63
+ path: str,
64
+ *,
65
+ json: Optional[Dict[str, Any]] = None,
66
+ params: Optional[Dict[str, Any]] = None,
67
+ ) -> Dict[str, Any]:
68
+ assert self._sync_client is not None, "Use async_request in async_mode"
69
+ resp = self._sync_client.request(method, path, json=json, params=params)
70
+ body = resp.json() if resp.content else {}
71
+ raise_for_status(resp.status_code, body)
72
+ return body
73
+
74
+ # -- async ----------------------------------------------------------------
75
+
76
+ async def async_request(
77
+ self,
78
+ method: str,
79
+ path: str,
80
+ *,
81
+ json: Optional[Dict[str, Any]] = None,
82
+ params: Optional[Dict[str, Any]] = None,
83
+ ) -> Dict[str, Any]:
84
+ assert self._async_client is not None, "Use request in sync mode"
85
+ resp = await self._async_client.request(method, path, json=json, params=params)
86
+ body = resp.json() if resp.content else {}
87
+ raise_for_status(resp.status_code, body)
88
+ return body
89
+
90
+ # -- lifecycle ------------------------------------------------------------
91
+
92
+ def close(self) -> None:
93
+ if self._sync_client is not None:
94
+ self._sync_client.close()
95
+
96
+ async def aclose(self) -> None:
97
+ if self._async_client is not None:
98
+ await self._async_client.aclose()
99
+
100
+
101
+ class SigmaShake:
102
+ """Top-level SigmaShake client.
103
+
104
+ Usage (sync)::
105
+
106
+ client = SigmaShake(api_key="sk-...")
107
+ token = client.auth.create_token(agent_id="a1", scopes=["read"])
108
+
109
+ Usage (async)::
110
+
111
+ async with SigmaShake(api_key="sk-...", async_mode=True) as client:
112
+ token = await client.auth.create_token(agent_id="a1", scopes=["read"])
113
+ """
114
+
115
+ def __init__(
116
+ self,
117
+ *,
118
+ api_key: str,
119
+ base_url: str = _DEFAULT_BASE_URL,
120
+ timeout: float = _DEFAULT_TIMEOUT,
121
+ async_mode: bool = False,
122
+ ) -> None:
123
+ self._transport = _HTTPTransport(
124
+ api_key=api_key,
125
+ base_url=base_url,
126
+ timeout=timeout,
127
+ async_mode=async_mode,
128
+ )
129
+ self._async_mode = async_mode
130
+
131
+ # Resource namespaces
132
+ self.auth = AuthResource(self._transport)
133
+ self.identity = IdentityResource(self._transport)
134
+ self.accounts = AccountsResource(self._transport)
135
+ self.agents = AgentsResource(self._transport)
136
+ self.shield = ShieldResource(self._transport)
137
+ self.documents = AgentsResource(self._transport) # alias kept for back-compat
138
+ self.soc = SOCResource(self._transport)
139
+ self.gateway = GatewayResource(self._transport)
140
+ self.db = DBResource(self._transport)
141
+ self.memory = MemoryResource(self._transport)
142
+ self.fleet = FleetResource(self._transport)
143
+ # documents uses its own resource
144
+ self.documents = _DocumentsResource(self._transport)
145
+
146
+ # -- context managers -----------------------------------------------------
147
+
148
+ def __enter__(self) -> "SigmaShake":
149
+ return self
150
+
151
+ def __exit__(self, *_: Any) -> None:
152
+ self.close()
153
+
154
+ async def __aenter__(self) -> "SigmaShake":
155
+ return self
156
+
157
+ async def __aexit__(self, *_: Any) -> None:
158
+ await self.aclose()
159
+
160
+ def close(self) -> None:
161
+ self._transport.close()
162
+
163
+ async def aclose(self) -> None:
164
+ await self._transport.aclose()
165
+
166
+
167
+ class _DocumentsResource:
168
+ """Document mutation and search operations."""
169
+
170
+ def __init__(self, transport: _HTTPTransport) -> None:
171
+ self._t = transport
172
+
173
+ def create(
174
+ self,
175
+ resource: str,
176
+ action: str,
177
+ payload: Optional[Dict[str, Any]] = None,
178
+ ) -> Any:
179
+ from .models import MutationResponse
180
+ body = {"resource": resource, "action": action, "payload": payload or {}}
181
+ data = self._t.request("POST", "/v1/documents", json=body)
182
+ return MutationResponse.model_validate(data)
183
+
184
+ async def async_create(
185
+ self,
186
+ resource: str,
187
+ action: str,
188
+ payload: Optional[Dict[str, Any]] = None,
189
+ ) -> Any:
190
+ from .models import MutationResponse
191
+ body = {"resource": resource, "action": action, "payload": payload or {}}
192
+ data = await self._t.async_request("POST", "/v1/documents", json=body)
193
+ return MutationResponse.model_validate(data)
194
+
195
+ def search(
196
+ self,
197
+ query: str,
198
+ limit: int = 10,
199
+ offset: int = 0,
200
+ filters: Optional[Dict[str, Any]] = None,
201
+ ) -> Any:
202
+ from .models import SearchResponse
203
+ body = {"query": query, "limit": limit, "offset": offset, "filters": filters or {}}
204
+ data = self._t.request("POST", "/v1/documents/search", json=body)
205
+ return SearchResponse.model_validate(data)
206
+
207
+ async def async_search(
208
+ self,
209
+ query: str,
210
+ limit: int = 10,
211
+ offset: int = 0,
212
+ filters: Optional[Dict[str, Any]] = None,
213
+ ) -> Any:
214
+ from .models import SearchResponse
215
+ body = {"query": query, "limit": limit, "offset": offset, "filters": filters or {}}
216
+ data = await self._t.async_request("POST", "/v1/documents/search", json=body)
217
+ return SearchResponse.model_validate(data)
sigmashake/db.py ADDED
@@ -0,0 +1,213 @@
1
+ """Database operations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, List, Optional, TYPE_CHECKING
6
+
7
+ from .models import (
8
+ ClusterStatusResponse,
9
+ QueryResponse,
10
+ ScrollQueryResponse,
11
+ )
12
+
13
+ if TYPE_CHECKING:
14
+ from .client import _HTTPTransport
15
+
16
+
17
+ class DBResource:
18
+ """SigmaShake database operations."""
19
+
20
+ def __init__(self, transport: _HTTPTransport) -> None:
21
+ self._t = transport
22
+
23
+ # -- tables ---------------------------------------------------------------
24
+
25
+ def create_table(
26
+ self,
27
+ table_name: str,
28
+ columns: List[Dict[str, str]],
29
+ ) -> Dict[str, Any]:
30
+ body = {"table_name": table_name, "columns": columns}
31
+ return self._t.request("POST", "/v1/db/tables", json=body)
32
+
33
+ async def async_create_table(
34
+ self,
35
+ table_name: str,
36
+ columns: List[Dict[str, str]],
37
+ ) -> Dict[str, Any]:
38
+ body = {"table_name": table_name, "columns": columns}
39
+ return await self._t.async_request("POST", "/v1/db/tables", json=body)
40
+
41
+ # -- insert ---------------------------------------------------------------
42
+
43
+ def insert(
44
+ self,
45
+ table_name: str,
46
+ columns: List[Dict[str, Any]],
47
+ ) -> Dict[str, Any]:
48
+ body = {"table_name": table_name, "columns": columns}
49
+ return self._t.request("POST", "/v1/db/insert", json=body)
50
+
51
+ async def async_insert(
52
+ self,
53
+ table_name: str,
54
+ columns: List[Dict[str, Any]],
55
+ ) -> Dict[str, Any]:
56
+ body = {"table_name": table_name, "columns": columns}
57
+ return await self._t.async_request("POST", "/v1/db/insert", json=body)
58
+
59
+ # -- query ----------------------------------------------------------------
60
+
61
+ def query(
62
+ self,
63
+ table_name: str,
64
+ filters: Optional[List[Dict[str, Any]]] = None,
65
+ columns: Optional[List[str]] = None,
66
+ limit: Optional[int] = None,
67
+ offset: Optional[int] = None,
68
+ ) -> QueryResponse:
69
+ body: Dict[str, Any] = {"table_name": table_name}
70
+ if filters:
71
+ body["filters"] = filters
72
+ if columns:
73
+ body["columns"] = columns
74
+ if limit is not None:
75
+ body["limit"] = limit
76
+ if offset is not None:
77
+ body["offset"] = offset
78
+ data = self._t.request("POST", "/v1/db/query", json=body)
79
+ return QueryResponse.model_validate(data)
80
+
81
+ async def async_query(
82
+ self,
83
+ table_name: str,
84
+ filters: Optional[List[Dict[str, Any]]] = None,
85
+ columns: Optional[List[str]] = None,
86
+ limit: Optional[int] = None,
87
+ offset: Optional[int] = None,
88
+ ) -> QueryResponse:
89
+ body: Dict[str, Any] = {"table_name": table_name}
90
+ if filters:
91
+ body["filters"] = filters
92
+ if columns:
93
+ body["columns"] = columns
94
+ if limit is not None:
95
+ body["limit"] = limit
96
+ if offset is not None:
97
+ body["offset"] = offset
98
+ data = await self._t.async_request("POST", "/v1/db/query", json=body)
99
+ return QueryResponse.model_validate(data)
100
+
101
+ # -- vector search --------------------------------------------------------
102
+
103
+ def vector_search(
104
+ self,
105
+ table_name: str,
106
+ column: str,
107
+ vector: List[float],
108
+ top_k: int = 10,
109
+ filters: Optional[List[Dict[str, Any]]] = None,
110
+ ) -> QueryResponse:
111
+ body: Dict[str, Any] = {
112
+ "table_name": table_name,
113
+ "column": column,
114
+ "vector": vector,
115
+ "top_k": top_k,
116
+ }
117
+ if filters:
118
+ body["filters"] = filters
119
+ data = self._t.request("POST", "/v1/db/vector-search", json=body)
120
+ return QueryResponse.model_validate(data)
121
+
122
+ async def async_vector_search(
123
+ self,
124
+ table_name: str,
125
+ column: str,
126
+ vector: List[float],
127
+ top_k: int = 10,
128
+ filters: Optional[List[Dict[str, Any]]] = None,
129
+ ) -> QueryResponse:
130
+ body: Dict[str, Any] = {
131
+ "table_name": table_name,
132
+ "column": column,
133
+ "vector": vector,
134
+ "top_k": top_k,
135
+ }
136
+ if filters:
137
+ body["filters"] = filters
138
+ data = await self._t.async_request("POST", "/v1/db/vector-search", json=body)
139
+ return QueryResponse.model_validate(data)
140
+
141
+ # -- scroll ---------------------------------------------------------------
142
+
143
+ def scroll(
144
+ self,
145
+ table_name: str,
146
+ batch_size: int = 100,
147
+ cursor: Optional[str] = None,
148
+ filters: Optional[List[Dict[str, Any]]] = None,
149
+ ) -> ScrollQueryResponse:
150
+ body: Dict[str, Any] = {"table_name": table_name, "batch_size": batch_size}
151
+ if cursor:
152
+ body["cursor"] = cursor
153
+ if filters:
154
+ body["filters"] = filters
155
+ data = self._t.request("POST", "/v1/db/scroll", json=body)
156
+ return ScrollQueryResponse.model_validate(data)
157
+
158
+ async def async_scroll(
159
+ self,
160
+ table_name: str,
161
+ batch_size: int = 100,
162
+ cursor: Optional[str] = None,
163
+ filters: Optional[List[Dict[str, Any]]] = None,
164
+ ) -> ScrollQueryResponse:
165
+ body: Dict[str, Any] = {"table_name": table_name, "batch_size": batch_size}
166
+ if cursor:
167
+ body["cursor"] = cursor
168
+ if filters:
169
+ body["filters"] = filters
170
+ data = await self._t.async_request("POST", "/v1/db/scroll", json=body)
171
+ return ScrollQueryResponse.model_validate(data)
172
+
173
+ # -- cluster --------------------------------------------------------------
174
+
175
+ def init_cluster(
176
+ self,
177
+ cluster_name: str,
178
+ node_count: int = 1,
179
+ replication_factor: int = 1,
180
+ config: Optional[Dict[str, Any]] = None,
181
+ ) -> ClusterStatusResponse:
182
+ body = {
183
+ "cluster_name": cluster_name,
184
+ "node_count": node_count,
185
+ "replication_factor": replication_factor,
186
+ "config": config or {},
187
+ }
188
+ data = self._t.request("POST", "/v1/db/cluster/init", json=body)
189
+ return ClusterStatusResponse.model_validate(data)
190
+
191
+ async def async_init_cluster(
192
+ self,
193
+ cluster_name: str,
194
+ node_count: int = 1,
195
+ replication_factor: int = 1,
196
+ config: Optional[Dict[str, Any]] = None,
197
+ ) -> ClusterStatusResponse:
198
+ body = {
199
+ "cluster_name": cluster_name,
200
+ "node_count": node_count,
201
+ "replication_factor": replication_factor,
202
+ "config": config or {},
203
+ }
204
+ data = await self._t.async_request("POST", "/v1/db/cluster/init", json=body)
205
+ return ClusterStatusResponse.model_validate(data)
206
+
207
+ def cluster_status(self, cluster_name: str) -> ClusterStatusResponse:
208
+ data = self._t.request("GET", f"/v1/db/cluster/{cluster_name}/status")
209
+ return ClusterStatusResponse.model_validate(data)
210
+
211
+ async def async_cluster_status(self, cluster_name: str) -> ClusterStatusResponse:
212
+ data = await self._t.async_request("GET", f"/v1/db/cluster/{cluster_name}/status")
213
+ return ClusterStatusResponse.model_validate(data)