infralo 0.1.0.dev1__tar.gz → 0.1.0.dev2__tar.gz

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.
Files changed (56) hide show
  1. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/PKG-INFO +1 -1
  2. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/pyproject.toml +1 -3
  3. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/src/infralo/_models.py +1 -0
  4. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/src/infralo/client.py +127 -9
  5. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/src/infralo/exporter.py +37 -9
  6. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/src/infralo/trace.py +64 -0
  7. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/tests/test_sdk.py +100 -0
  8. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/uv.lock +2 -207
  9. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/.gitignore +0 -0
  10. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/.pre-commit-config.yaml +0 -0
  11. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/README.md +0 -0
  12. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/README.md +0 -0
  13. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/agno/README.md +0 -0
  14. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/agno/agno_complex_nesting.py +0 -0
  15. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/agno/agno_integration.py +0 -0
  16. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/agno/pyproject.toml +0 -0
  17. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/agno/uv.lock +0 -0
  18. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/core/README.md +0 -0
  19. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/core/complex_nesting.py +0 -0
  20. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/core/complex_nesting_decorator.py +0 -0
  21. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/core/mcp_integration.py +0 -0
  22. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/core/parallel_execution.py +0 -0
  23. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/core/pattern_a_context_manager.py +0 -0
  24. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/core/pattern_b_decorator.py +0 -0
  25. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/core/pattern_c_manual.py +0 -0
  26. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/core/pyproject.toml +0 -0
  27. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/core/tool_first_execution.py +0 -0
  28. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/core/uv.lock +0 -0
  29. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/crewai/README.md +0 -0
  30. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/crewai/crewai_complex_nesting.py +0 -0
  31. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/crewai/crewai_integration.py +0 -0
  32. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/crewai/pyproject.toml +0 -0
  33. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/crewai/uv.lock +0 -0
  34. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/google_adk/README.md +0 -0
  35. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/google_adk/google_adk_complex_nesting.py +0 -0
  36. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/google_adk/google_adk_integration.py +0 -0
  37. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/google_adk/pyproject.toml +0 -0
  38. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/google_adk/uv.lock +0 -0
  39. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/langgraph/README.md +0 -0
  40. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/langgraph/langgraph_complex_nesting.py +0 -0
  41. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/langgraph/langgraph_integration.py +0 -0
  42. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/langgraph/pyproject.toml +0 -0
  43. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/examples/langgraph/uv.lock +0 -0
  44. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/src/infralo/__init__.py +0 -0
  45. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/src/infralo/integrations/__init__.py +0 -0
  46. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/src/infralo/integrations/agno.py +0 -0
  47. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/src/infralo/integrations/crewai.py +0 -0
  48. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/src/infralo/integrations/google_adk.py +0 -0
  49. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/src/infralo/integrations/langgraph.py +0 -0
  50. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/src/infralo/span.py +0 -0
  51. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/tests/conftest.py +0 -0
  52. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/tests/integrations/__init__.py +0 -0
  53. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/tests/integrations/test_agno.py +0 -0
  54. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/tests/integrations/test_crewai.py +0 -0
  55. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/tests/integrations/test_google_adk.py +0 -0
  56. {infralo-0.1.0.dev1 → infralo-0.1.0.dev2}/tests/integrations/test_langgraph.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: infralo
3
- Version: 0.1.0.dev1
3
+ Version: 0.1.0.dev2
4
4
  Summary: Infralo SDK
5
5
  License: MIT
6
6
  Keywords: agents,ai,llm,observability,tracing
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "infralo"
7
- version = "0.1.0.dev1"
7
+ version = "0.1.0.dev2"
8
8
  description = "Infralo SDK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -32,8 +32,6 @@ dev = [
32
32
  "pytest>=8",
33
33
  "pytest-asyncio>=0.23",
34
34
  "respx>=0.21", # mock httpx
35
- "build>=1.5.0",
36
- "twine>=6.2.0",
37
35
  ]
38
36
 
39
37
  [tool.hatch.build.targets.wheel]
@@ -58,6 +58,7 @@ class ToolSpanData:
58
58
  output_payload: Any = None
59
59
 
60
60
  metadata: dict = field(default_factory=dict)
61
+ attempts: int = 0
61
62
 
62
63
  def to_dict(self) -> dict:
63
64
  """Serialise to the ToolCallSpanCreate JSON shape."""
@@ -18,8 +18,10 @@ import inspect
18
18
  import uuid
19
19
  from typing import Any, Callable, TypeVar
20
20
 
21
+ import httpx
22
+
21
23
  from infralo.exporter import SpanExporter
22
- from infralo.trace import Trace, _active_trace
24
+ from infralo.trace import Trace, _active_trace, get_active_trace
23
25
 
24
26
  F = TypeVar("F", bound=Callable)
25
27
 
@@ -87,6 +89,7 @@ class Infralo:
87
89
  trace_id=trace_id or str(uuid.uuid4()),
88
90
  session_id=session_id,
89
91
  exporter=self._exporter,
92
+ client=self,
90
93
  )
91
94
 
92
95
  # ── @tool decorator ──────────────────────────────────────────────────────
@@ -162,10 +165,8 @@ class Infralo:
162
165
  def sync_wrapper(*args: Any, **kwargs: Any) -> Any:
163
166
  trace = _active_trace.get()
164
167
  if trace is None:
165
- raise RuntimeError(
166
- f"@infralo.tool '{tool_name}' was called outside of an active trace. "
167
- "Wrap your code with `with infralo.start_trace(): ...`."
168
- )
168
+ _pop_infralo_kwargs(kwargs)
169
+ return func(*args, **kwargs)
169
170
 
170
171
  infralo_kwargs = _pop_infralo_kwargs(kwargs)
171
172
  metadata: dict = infralo_kwargs.get("_infralo_metadata", {})
@@ -194,10 +195,8 @@ class Infralo:
194
195
  async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
195
196
  trace = _active_trace.get()
196
197
  if trace is None:
197
- raise RuntimeError(
198
- f"@infralo.tool '{tool_name}' was called outside of an active trace. "
199
- "Wrap your code with `with infralo.start_trace(): ...`."
200
- )
198
+ _pop_infralo_kwargs(kwargs)
199
+ return await func(*args, **kwargs)
201
200
 
202
201
  infralo_kwargs = _pop_infralo_kwargs(kwargs)
203
202
  metadata: dict = infralo_kwargs.get("_infralo_metadata", {})
@@ -232,6 +231,125 @@ class Infralo:
232
231
  return decorator(fn)
233
232
  return decorator
234
233
 
234
+ # ── Approvals ────────────────────────────────────────────────────────────
235
+
236
+ def create_approval(
237
+ self,
238
+ title: str,
239
+ callback_url: str,
240
+ *,
241
+ description: str | None = None,
242
+ review_data: dict[str, Any] | None = None,
243
+ callback_headers: dict[str, str] | None = None,
244
+ client_reference_id: str | None = None,
245
+ trace_id: str | None = None,
246
+ timeout: float = 15.0,
247
+ ) -> dict[str, Any]:
248
+ """
249
+ Create a human-in-the-loop approval request.
250
+
251
+ Args:
252
+ title: Short summary shown to reviewers.
253
+ callback_url: Webhook URL called when the approval request is reviewed.
254
+ description: Detailed context for reviewers.
255
+ review_data: Arbitrary structured payload required for review.
256
+ callback_headers: Custom HTTP headers to pass to callback webhook.
257
+ client_reference_id: Opaque ID for client-side correlation.
258
+ trace_id: Associated trace ID (auto-populated from active trace if omitted).
259
+ timeout: HTTP request timeout in seconds (default: 15.0).
260
+
261
+ Returns:
262
+ dict containing the approval object data returned by the backend.
263
+ """
264
+ if trace_id is None:
265
+ active_trace = get_active_trace()
266
+ if active_trace is not None:
267
+ trace_id = active_trace.trace_id
268
+
269
+ payload: dict[str, Any] = {
270
+ "title": title,
271
+ "callback_url": callback_url,
272
+ }
273
+ if description is not None:
274
+ payload["description"] = description
275
+ if review_data is not None:
276
+ payload["review_data"] = review_data
277
+ if callback_headers is not None:
278
+ payload["callback_headers"] = callback_headers
279
+ if client_reference_id is not None:
280
+ payload["client_reference_id"] = client_reference_id
281
+ if trace_id is not None:
282
+ payload["trace_id"] = trace_id
283
+
284
+ resp = httpx.post(
285
+ f"{self._exporter.endpoint}/api/v1/approvals",
286
+ headers={
287
+ "Authorization": f"Bearer {self._exporter.api_key}",
288
+ "Content-Type": "application/json",
289
+ },
290
+ json=payload,
291
+ timeout=timeout,
292
+ )
293
+ resp.raise_for_status()
294
+ resp_json = resp.json()
295
+ if isinstance(resp_json, dict) and "data" in resp_json:
296
+ return resp_json["data"]
297
+ return resp_json
298
+
299
+ async def acreate_approval(
300
+ self,
301
+ title: str,
302
+ callback_url: str,
303
+ *,
304
+ description: str | None = None,
305
+ review_data: dict[str, Any] | None = None,
306
+ callback_headers: dict[str, str] | None = None,
307
+ client_reference_id: str | None = None,
308
+ trace_id: str | None = None,
309
+ timeout: float = 15.0,
310
+ ) -> dict[str, Any]:
311
+ """
312
+ Async version of `create_approval`. Create a human-in-the-loop approval request.
313
+ """
314
+ if trace_id is None:
315
+ active_trace = get_active_trace()
316
+ if active_trace is not None:
317
+ trace_id = active_trace.trace_id
318
+
319
+ payload: dict[str, Any] = {
320
+ "title": title,
321
+ "callback_url": callback_url,
322
+ }
323
+ if description is not None:
324
+ payload["description"] = description
325
+ if review_data is not None:
326
+ payload["review_data"] = review_data
327
+ if callback_headers is not None:
328
+ payload["callback_headers"] = callback_headers
329
+ if client_reference_id is not None:
330
+ payload["client_reference_id"] = client_reference_id
331
+ if trace_id is not None:
332
+ payload["trace_id"] = trace_id
333
+
334
+ async with httpx.AsyncClient() as client:
335
+ resp = await client.post(
336
+ f"{self._exporter.endpoint}/api/v1/approvals",
337
+ headers={
338
+ "Authorization": f"Bearer {self._exporter.api_key}",
339
+ "Content-Type": "application/json",
340
+ },
341
+ json=payload,
342
+ timeout=timeout,
343
+ )
344
+ resp.raise_for_status()
345
+ resp_json = resp.json()
346
+ if isinstance(resp_json, dict) and "data" in resp_json:
347
+ return resp_json["data"]
348
+ return resp_json
349
+
350
+ request_approval = create_approval
351
+ arequest_approval = acreate_approval
352
+
235
353
  # ── Utilities ────────────────────────────────────────────────────────────
236
354
 
237
355
  def flush(self, timeout: float = 10.0) -> None:
@@ -135,6 +135,26 @@ class SpanExporter:
135
135
  # Reset backoff on success
136
136
  self._current_interval = self.flush_interval
137
137
 
138
+ except httpx.HTTPStatusError as exc:
139
+ status_code = exc.response.status_code
140
+ # Drop spans immediately for permanent client errors (400, 401, 403, 422)
141
+ # except temporary errors like 408 (timeout) and 429 (rate limit)
142
+ if 400 <= status_code < 500 and status_code not in (408, 429):
143
+ logger.error(
144
+ "infralo: fatal client error %d while flushing %d span(s): %s — "
145
+ "dropping spans",
146
+ status_code,
147
+ len(batch),
148
+ exc.response.text,
149
+ )
150
+ else:
151
+ logger.warning(
152
+ "infralo: failed to flush %d span(s): %s — re-enqueueing for retry",
153
+ len(batch),
154
+ exc,
155
+ )
156
+ self._current_interval = min(self._current_interval * 2.0, self._max_backoff)
157
+ self._re_enqueue_spans(batch)
138
158
  except Exception as exc:
139
159
  logger.warning(
140
160
  "infralo: failed to flush %d span(s): %s — re-enqueueing for retry",
@@ -143,12 +163,20 @@ class SpanExporter:
143
163
  )
144
164
  # Apply exponential backoff
145
165
  self._current_interval = min(self._current_interval * 2.0, self._max_backoff)
146
-
147
- # Best-effort re-enqueue for next flush cycle
148
- for span in batch:
149
- try:
150
- self._queue.put_nowait(span)
151
- except queue.Full:
152
- logger.warning(
153
- "infralo: queue full during retry — dropping span %s", span.span_id
154
- )
166
+ self._re_enqueue_spans(batch)
167
+
168
+ def _re_enqueue_spans(self, batch: list[ToolSpanData]) -> None:
169
+ """Best-effort re-enqueue with max retry attempt checks (limit: 5)."""
170
+ for span in batch:
171
+ span.attempts += 1
172
+ if span.attempts > 5:
173
+ logger.error(
174
+ "infralo: dropping span %s after %d failed attempts",
175
+ span.span_id,
176
+ span.attempts - 1,
177
+ )
178
+ continue
179
+ try:
180
+ self._queue.put_nowait(span)
181
+ except queue.Full:
182
+ logger.warning("infralo: queue full during retry — dropping span %s", span.span_id)
@@ -38,6 +38,7 @@ from typing import TYPE_CHECKING, Any
38
38
  import httpx
39
39
 
40
40
  if TYPE_CHECKING:
41
+ from infralo.client import Infralo
41
42
  from infralo.exporter import SpanExporter
42
43
  from infralo.span import ToolSpan
43
44
 
@@ -82,6 +83,7 @@ class Trace:
82
83
  trace_id: str,
83
84
  session_id: str,
84
85
  exporter: "SpanExporter",
86
+ client: "Infralo | None" = None,
85
87
  ) -> None:
86
88
  self.trace_id = trace_id
87
89
  self.session_id = session_id
@@ -91,6 +93,7 @@ class Trace:
91
93
  self.last_tool_span_id: str | None = None
92
94
 
93
95
  self._exporter = exporter
96
+ self._client = client
94
97
  self._token: Token | None = None
95
98
 
96
99
  # Thread-safe monotone sequence counter.
@@ -384,6 +387,67 @@ class Trace:
384
387
  async with self.parallel():
385
388
  return await asyncio.gather(*coros)
386
389
 
390
+ # ── Approvals ────────────────────────────────────────────────────────────
391
+
392
+ def create_approval(
393
+ self,
394
+ title: str,
395
+ callback_url: str,
396
+ *,
397
+ description: str | None = None,
398
+ review_data: dict[str, Any] | None = None,
399
+ callback_headers: dict[str, str] | None = None,
400
+ client_reference_id: str | None = None,
401
+ timeout: float = 15.0,
402
+ ) -> dict[str, Any]:
403
+ """
404
+ Create a human-in-the-loop approval request associated with this trace.
405
+ Delegates directly to the parent Infralo client instance.
406
+ """
407
+ if self._client is None:
408
+ raise RuntimeError("Trace instance is not bound to an Infralo client instance.")
409
+ return self._client.create_approval(
410
+ title=title,
411
+ callback_url=callback_url,
412
+ description=description,
413
+ review_data=review_data,
414
+ callback_headers=callback_headers,
415
+ client_reference_id=client_reference_id,
416
+ trace_id=self.trace_id,
417
+ timeout=timeout,
418
+ )
419
+
420
+ async def acreate_approval(
421
+ self,
422
+ title: str,
423
+ callback_url: str,
424
+ *,
425
+ description: str | None = None,
426
+ review_data: dict[str, Any] | None = None,
427
+ callback_headers: dict[str, str] | None = None,
428
+ client_reference_id: str | None = None,
429
+ timeout: float = 15.0,
430
+ ) -> dict[str, Any]:
431
+ """
432
+ Async version of `create_approval`. Create an approval request associated with this trace.
433
+ Delegates directly to the parent Infralo client instance.
434
+ """
435
+ if self._client is None:
436
+ raise RuntimeError("Trace instance is not bound to an Infralo client instance.")
437
+ return await self._client.acreate_approval(
438
+ title=title,
439
+ callback_url=callback_url,
440
+ description=description,
441
+ review_data=review_data,
442
+ callback_headers=callback_headers,
443
+ client_reference_id=client_reference_id,
444
+ trace_id=self.trace_id,
445
+ timeout=timeout,
446
+ )
447
+
448
+ request_approval = create_approval
449
+ arequest_approval = acreate_approval
450
+
387
451
 
388
452
  _patched_httpx = False
389
453
 
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import json
2
3
  import logging
3
4
 
4
5
  import pytest
@@ -223,3 +224,102 @@ def test_tool_call_first_before_llm():
223
224
  assert "x-infralo-parent-span-id" not in headers
224
225
 
225
226
  infralo.shutdown()
227
+
228
+
229
+ def test_create_approval_sync():
230
+ infralo = Infralo(api_key="vk_test", endpoint="http://localhost:12345", flush_interval=60)
231
+ with respx.mock:
232
+ route = respx.post("http://localhost:12345/api/v1/approvals").mock(
233
+ return_value=Response(
234
+ 200,
235
+ json={
236
+ "message": "Approval request created successfully",
237
+ "data": {
238
+ "approval_id": "appr_123",
239
+ "title": "Deploy to prod",
240
+ "status": "pending",
241
+ "trace_id": "trace-99",
242
+ },
243
+ },
244
+ )
245
+ )
246
+
247
+ with infralo.start_trace(trace_id="trace-99"):
248
+ res = infralo.create_approval(
249
+ title="Deploy to prod",
250
+ callback_url="https://example.com/callback",
251
+ description="Deploy feature X",
252
+ review_data={"env": "prod"},
253
+ )
254
+
255
+ assert route.called
256
+ req_json = json.loads(route.calls.last.request.content)
257
+ assert req_json["title"] == "Deploy to prod"
258
+ assert req_json["callback_url"] == "https://example.com/callback"
259
+ assert req_json["trace_id"] == "trace-99"
260
+ assert req_json["review_data"] == {"env": "prod"}
261
+
262
+ assert res["approval_id"] == "appr_123"
263
+ assert res["status"] == "pending"
264
+
265
+ infralo.shutdown()
266
+
267
+
268
+ @pytest.mark.asyncio
269
+ async def test_create_approval_async():
270
+ infralo = Infralo(api_key="vk_test", endpoint="http://localhost:12345", flush_interval=60)
271
+ with respx.mock:
272
+ route = respx.post("http://localhost:12345/api/v1/approvals").mock(
273
+ return_value=Response(
274
+ 200,
275
+ json={
276
+ "message": "Approval request created successfully",
277
+ "data": {
278
+ "approval_id": "appr_456",
279
+ "title": "Publish blog",
280
+ "status": "pending",
281
+ },
282
+ },
283
+ )
284
+ )
285
+
286
+ res = await infralo.acreate_approval(
287
+ title="Publish blog",
288
+ callback_url="https://example.com/webhook",
289
+ client_reference_id="ref_789",
290
+ )
291
+
292
+ assert route.called
293
+ req_json = json.loads(route.calls.last.request.content)
294
+ assert req_json["title"] == "Publish blog"
295
+ assert req_json["client_reference_id"] == "ref_789"
296
+ assert res["approval_id"] == "appr_456"
297
+
298
+ infralo.shutdown()
299
+
300
+
301
+ def test_trace_create_approval():
302
+ infralo = Infralo(api_key="vk_test", endpoint="http://localhost:12345", flush_interval=60)
303
+ with respx.mock:
304
+ route = respx.post("http://localhost:12345/api/v1/approvals").mock(
305
+ return_value=Response(
306
+ 200,
307
+ json={
308
+ "message": "Approval request created successfully",
309
+ "data": {"approval_id": "appr_789"},
310
+ },
311
+ )
312
+ )
313
+
314
+ with infralo.start_trace(trace_id="trace-abc") as trace:
315
+ res = trace.create_approval(
316
+ title="Action approval",
317
+ callback_url="https://example.com/cb",
318
+ )
319
+
320
+ assert route.called
321
+ req_json = json.loads(route.calls.last.request.content)
322
+ assert req_json["trace_id"] == "trace-abc"
323
+ assert res["approval_id"] == "appr_789"
324
+
325
+ infralo.shutdown()
@@ -315,15 +315,6 @@ wheels = [
315
315
  { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" },
316
316
  ]
317
317
 
318
- [[package]]
319
- name = "backports-tarfile"
320
- version = "1.2.0"
321
- source = { registry = "https://pypi.org/simple" }
322
- sdist = { url = "https://files.pythonhosted.org/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", size = 86406, upload-time = "2024-05-28T17:01:54.731Z" }
323
- wheels = [
324
- { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181, upload-time = "2024-05-28T17:01:53.112Z" },
325
- ]
326
-
327
318
  [[package]]
328
319
  name = "bcrypt"
329
320
  version = "5.0.0"
@@ -878,15 +869,6 @@ wheels = [
878
869
  { url = "https://files.pythonhosted.org/packages/a7/5f/ed01f9a3cdffbd5a008556fc7b2a08ddb1cc6ace7effa7340604b1d16699/docstring_parser-0.18.0-py3-none-any.whl", hash = "sha256:b3fcbed555c47d8479be0796ef7e19c2670d428d72e96da63f3a40122860374b", size = 22484, upload-time = "2026-04-14T04:09:18.638Z" },
879
870
  ]
880
871
 
881
- [[package]]
882
- name = "docutils"
883
- version = "0.23"
884
- source = { registry = "https://pypi.org/simple" }
885
- sdist = { url = "https://files.pythonhosted.org/packages/39/a4/5180d9afc57e8fca05601dd652bdff19604c218814037fe90ffc7625a50a/docutils-0.23.tar.gz", hash = "sha256:746f5060322511280a1e50eb76846ed6bf2342984b2ac04dc42caa1a8d78799e", size = 2303823, upload-time = "2026-05-27T17:41:06.934Z" }
886
- wheels = [
887
- { url = "https://files.pythonhosted.org/packages/32/91/30151a39f7570f448ed84529390628a651d7f27c87d73c9b887f8189695e/docutils-0.23-py3-none-any.whl", hash = "sha256:25d013af9bf23bc1c7b2b093dff4208166c53a94786c9e447808335ef1185fea", size = 634701, upload-time = "2026-05-27T17:40:58.442Z" },
888
- ]
889
-
890
872
  [[package]]
891
873
  name = "durationpy"
892
874
  version = "0.10"
@@ -1485,18 +1467,6 @@ wheels = [
1485
1467
  { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" },
1486
1468
  ]
1487
1469
 
1488
- [[package]]
1489
- name = "id"
1490
- version = "1.6.1"
1491
- source = { registry = "https://pypi.org/simple" }
1492
- dependencies = [
1493
- { name = "urllib3" },
1494
- ]
1495
- sdist = { url = "https://files.pythonhosted.org/packages/6d/04/c2156091427636080787aac190019dc64096e56a23b7364d3c1764ee3a06/id-1.6.1.tar.gz", hash = "sha256:d0732d624fb46fd4e7bc4e5152f00214450953b9e772c182c1c22964def1a069", size = 18088, upload-time = "2026-02-04T16:19:41.26Z" }
1496
- wheels = [
1497
- { url = "https://files.pythonhosted.org/packages/42/77/de194443bf38daed9452139e960c632b0ef9f9a5dd9ce605fdf18ca9f1b1/id-1.6.1-py3-none-any.whl", hash = "sha256:f5ec41ed2629a508f5d0988eda142e190c9c6da971100612c4de9ad9f9b237ca", size = 14689, upload-time = "2026-02-04T16:19:40.051Z" },
1498
- ]
1499
-
1500
1470
  [[package]]
1501
1471
  name = "idna"
1502
1472
  version = "3.18"
@@ -1511,7 +1481,7 @@ name = "importlib-metadata"
1511
1481
  version = "9.0.0"
1512
1482
  source = { registry = "https://pypi.org/simple" }
1513
1483
  dependencies = [
1514
- { name = "zipp", marker = "python_full_version < '3.12'" },
1484
+ { name = "zipp", marker = "python_full_version < '3.11'" },
1515
1485
  ]
1516
1486
  sdist = { url = "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", size = 56405, upload-time = "2026-03-20T06:42:56.999Z" }
1517
1487
  wheels = [
@@ -1529,7 +1499,7 @@ wheels = [
1529
1499
 
1530
1500
  [[package]]
1531
1501
  name = "infralo"
1532
- version = "0.1.0.dev1"
1502
+ version = "0.1.0.dev2"
1533
1503
  source = { editable = "." }
1534
1504
  dependencies = [
1535
1505
  { name = "httpx" },
@@ -1552,13 +1522,11 @@ langgraph = [
1552
1522
 
1553
1523
  [package.dev-dependencies]
1554
1524
  dev = [
1555
- { name = "build" },
1556
1525
  { name = "prek" },
1557
1526
  { name = "pytest" },
1558
1527
  { name = "pytest-asyncio" },
1559
1528
  { name = "respx" },
1560
1529
  { name = "ruff" },
1561
- { name = "twine" },
1562
1530
  ]
1563
1531
 
1564
1532
  [package.metadata]
@@ -1574,13 +1542,11 @@ provides-extras = ["agno", "langgraph", "crewai", "google-adk"]
1574
1542
 
1575
1543
  [package.metadata.requires-dev]
1576
1544
  dev = [
1577
- { name = "build", specifier = ">=1.5.0" },
1578
1545
  { name = "prek", specifier = "==0.3.11" },
1579
1546
  { name = "pytest", specifier = ">=8" },
1580
1547
  { name = "pytest-asyncio", specifier = ">=0.23" },
1581
1548
  { name = "respx", specifier = ">=0.21" },
1582
1549
  { name = "ruff", specifier = "==0.15.5" },
1583
- { name = "twine", specifier = ">=6.2.0" },
1584
1550
  ]
1585
1551
 
1586
1552
  [[package]]
@@ -1614,51 +1580,6 @@ wheels = [
1614
1580
  { url = "https://files.pythonhosted.org/packages/80/8d/f668a30fff4d25b36533355e23aeb0b5724df4628eb974124ed64b7bcf8d/instructor-1.15.4-py3-none-any.whl", hash = "sha256:00e0ecda80fd9746fb6d082d3f9641e193adb1d8849f0775f91519a82aeff968", size = 252522, upload-time = "2026-06-28T07:36:36.863Z" },
1615
1581
  ]
1616
1582
 
1617
- [[package]]
1618
- name = "jaraco-classes"
1619
- version = "3.4.0"
1620
- source = { registry = "https://pypi.org/simple" }
1621
- dependencies = [
1622
- { name = "more-itertools" },
1623
- ]
1624
- sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" }
1625
- wheels = [
1626
- { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" },
1627
- ]
1628
-
1629
- [[package]]
1630
- name = "jaraco-context"
1631
- version = "6.1.2"
1632
- source = { registry = "https://pypi.org/simple" }
1633
- dependencies = [
1634
- { name = "backports-tarfile", marker = "python_full_version < '3.12'" },
1635
- ]
1636
- sdist = { url = "https://files.pythonhosted.org/packages/af/50/4763cd07e722bb6285316d390a164bc7e479db9d90daa769f22578f698b4/jaraco_context-6.1.2.tar.gz", hash = "sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3", size = 16801, upload-time = "2026-03-20T22:13:33.922Z" }
1637
- wheels = [
1638
- { url = "https://files.pythonhosted.org/packages/f2/58/bc8954bda5fcda97bd7c19be11b85f91973d67a706ed4a3aec33e7de22db/jaraco_context-6.1.2-py3-none-any.whl", hash = "sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535", size = 7871, upload-time = "2026-03-20T22:13:32.808Z" },
1639
- ]
1640
-
1641
- [[package]]
1642
- name = "jaraco-functools"
1643
- version = "4.6.0"
1644
- source = { registry = "https://pypi.org/simple" }
1645
- dependencies = [
1646
- { name = "more-itertools" },
1647
- ]
1648
- sdist = { url = "https://files.pythonhosted.org/packages/6c/1f/c23395957d41ccf27c4e535c3d334c4051e5395b3752057ba4cbaec35c56/jaraco_functools-4.6.0.tar.gz", hash = "sha256:880c577ec9720b3a052d5bc611fb9f2269b3d87902ef42440df443b88e443280", size = 20837, upload-time = "2026-07-14T01:28:02.544Z" }
1649
- wheels = [
1650
- { url = "https://files.pythonhosted.org/packages/02/36/ecc85bc96c273dc8a11273ed4782272975e6338d4a3e9228621175edf0e3/jaraco_functools-4.6.0-py3-none-any.whl", hash = "sha256:99e3dc0060c5cbe8fcd1cdb36258e2a65ca40f1566b2033b12abb1bb44dd3c30", size = 11677, upload-time = "2026-07-14T01:28:01.59Z" },
1651
- ]
1652
-
1653
- [[package]]
1654
- name = "jeepney"
1655
- version = "0.9.0"
1656
- source = { registry = "https://pypi.org/simple" }
1657
- sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" }
1658
- wheels = [
1659
- { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" },
1660
- ]
1661
-
1662
1583
  [[package]]
1663
1584
  name = "jinja2"
1664
1585
  version = "3.1.6"
@@ -1871,24 +1792,6 @@ wheels = [
1871
1792
  { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" },
1872
1793
  ]
1873
1794
 
1874
- [[package]]
1875
- name = "keyring"
1876
- version = "25.7.0"
1877
- source = { registry = "https://pypi.org/simple" }
1878
- dependencies = [
1879
- { name = "importlib-metadata", marker = "python_full_version < '3.12'" },
1880
- { name = "jaraco-classes" },
1881
- { name = "jaraco-context" },
1882
- { name = "jaraco-functools" },
1883
- { name = "jeepney", marker = "sys_platform == 'linux'" },
1884
- { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" },
1885
- { name = "secretstorage", marker = "sys_platform == 'linux'" },
1886
- ]
1887
- sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" }
1888
- wheels = [
1889
- { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" },
1890
- ]
1891
-
1892
1795
  [[package]]
1893
1796
  name = "kubernetes"
1894
1797
  version = "36.0.3"
@@ -2361,15 +2264,6 @@ wheels = [
2361
2264
  { url = "https://files.pythonhosted.org/packages/a0/0f/59204bf136d1201f8d7884cfbaf7498c5b4674e87a4c693f9bde63741ce1/mmh3-5.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:dfd51b4c56b673dfbc43d7d27ef857dd91124801e2806c69bb45585ce0fa019b", size = 40391, upload-time = "2026-03-05T15:55:56.697Z" },
2362
2265
  ]
2363
2266
 
2364
- [[package]]
2365
- name = "more-itertools"
2366
- version = "11.1.0"
2367
- source = { registry = "https://pypi.org/simple" }
2368
- sdist = { url = "https://files.pythonhosted.org/packages/de/1d/f4da6f02cdffe04d6362210b807146a26044c88d839208aec273bb0d9184/more_itertools-11.1.0.tar.gz", hash = "sha256:48e8f4d9e7e5878571ecf6f2b4e57634f93cd474cc8cfbd2376f2d11b396e30d", size = 145772, upload-time = "2026-05-22T14:14:29.909Z" }
2369
- wheels = [
2370
- { url = "https://files.pythonhosted.org/packages/e8/3d/1087453384dbde46a8c7f9356eead2c58be8a7bf156bca40243377c85715/more_itertools-11.1.0-py3-none-any.whl", hash = "sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192", size = 72226, upload-time = "2026-05-22T14:14:28.824Z" },
2371
- ]
2372
-
2373
2267
  [[package]]
2374
2268
  name = "mpmath"
2375
2269
  version = "1.3.0"
@@ -2517,40 +2411,6 @@ wheels = [
2517
2411
  { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" },
2518
2412
  ]
2519
2413
 
2520
- [[package]]
2521
- name = "nh3"
2522
- version = "0.3.6"
2523
- source = { registry = "https://pypi.org/simple" }
2524
- sdist = { url = "https://files.pythonhosted.org/packages/5e/1b/ef84624f14954d270f74060a19fc550dd4f06656399447569afb584d8c06/nh3-0.3.6.tar.gz", hash = "sha256:f3736c9dd3d1856f80cd031715b84ca75cda2bbb1ac802c3da26bfce590838d7", size = 24684, upload-time = "2026-06-22T00:47:02.008Z" }
2525
- wheels = [
2526
- { url = "https://files.pythonhosted.org/packages/99/3e/6506aa4f23dc7b7993a2d0a45dca3ce864ec48380adfe15a173e643c63e8/nh3-0.3.6-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2411e8c3cee81a1ddd62c2a5d50585c28aa5566d373ad1db92536b95ddb24ef2", size = 1421679, upload-time = "2026-06-22T00:46:20.248Z" },
2527
- { url = "https://files.pythonhosted.org/packages/e3/e1/e96e7864a7a53bd6b6fab7e9632467382a2a2c1f3fed951918ad131542fb/nh3-0.3.6-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e196fa70c2ff2eb4de7d3df3108f8f358c1d69dff20d45b11f20a5aa227ffb6d", size = 792570, upload-time = "2026-06-22T00:46:22.179Z" },
2528
- { url = "https://files.pythonhosted.org/packages/59/62/5b6108bedaef2b2637fed04c87bdbcb5967b9961758b41f0e466ef22a022/nh3-0.3.6-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:34d2b0d934156b87ee114f599a3ba9b8b9e17b5d79652ba3a13fa50903de965e", size = 842243, upload-time = "2026-06-22T00:46:23.801Z" },
2529
- { url = "https://files.pythonhosted.org/packages/4b/4a/526f199626bfcb496bc01a268051b44737962005553b158e985ed7e64865/nh3-0.3.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2f14b7ae1fca99c4a66c981aac3974e7fbc1ca30a12673d223ae1df76680917", size = 1001468, upload-time = "2026-06-22T00:46:25.481Z" },
2530
- { url = "https://files.pythonhosted.org/packages/49/09/0d8e3101636d9ad88cdefb2914e764cb8e876ebdbb4286bfc251277d9c67/nh3-0.3.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:889932a97fb4abb6f95fef1914c0d269ebfb60011e67121c1163059b9449dbb4", size = 1082933, upload-time = "2026-06-22T00:46:27.15Z" },
2531
- { url = "https://files.pythonhosted.org/packages/09/a1/ea83abe738a3fbaa203dfdb836ca7cbab0e7e9609faaee4fe1d4652599c0/nh3-0.3.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:edb2b4a1a27523e6cc7c417f8d21ce3d005243548b93e56b762b66b0c7f589f9", size = 1043120, upload-time = "2026-06-22T00:46:28.89Z" },
2532
- { url = "https://files.pythonhosted.org/packages/66/69/0654482b8635012fbae67826bd6c381abb05d841ac7388b9b4666300fdad/nh3-0.3.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:43bc1ed3fa0716295fabee29ba42b2667e4a51d140b0a68e092170a765474fa6", size = 1023824, upload-time = "2026-06-22T00:46:30.453Z" },
2533
- { url = "https://files.pythonhosted.org/packages/ed/a6/1f7285ffadc8307c4dbeb08d21b920536d5117785056d1079e998c4dfa44/nh3-0.3.6-cp314-cp314t-win32.whl", hash = "sha256:597a8e843bea00b2eb5520658dc24a9bb032e7fc9e7c2c0c4cd29420220c9796", size = 599253, upload-time = "2026-06-22T00:46:32.072Z" },
2534
- { url = "https://files.pythonhosted.org/packages/36/ea/5542f3c45da4c00290d9d67a65e996702e23e613c4b627de3e09cb9fe357/nh3-0.3.6-cp314-cp314t-win_amd64.whl", hash = "sha256:4713502748f564fee0633b37b3403783ce0a3af3a3d148ad91025a5bdadb7bc6", size = 612553, upload-time = "2026-06-22T00:46:33.53Z" },
2535
- { url = "https://files.pythonhosted.org/packages/66/35/26bd47e6af5915a628281dccdac354ddf4e32f7397047894270acd8c9870/nh3-0.3.6-cp314-cp314t-win_arm64.whl", hash = "sha256:69bbb92865a693d909db3a700d3c01537533844d0948c1e9323561ce06ecda41", size = 595151, upload-time = "2026-06-22T00:46:34.878Z" },
2536
- { url = "https://files.pythonhosted.org/packages/f3/ab/a7653bce9a3b204be6a6931767a9e23595807bb84790ce6685e4d7e5bd08/nh3-0.3.6-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a43ebd7543555c3ac1bc353023d0794e75cb76f6f18f19c32e95441496c0cc25", size = 1443564, upload-time = "2026-06-22T00:46:36.66Z" },
2537
- { url = "https://files.pythonhosted.org/packages/41/21/e1084ab18eb589506335c7c7576f2d4643e9a0c0e33983ef0e549a256b96/nh3-0.3.6-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1b160831c9cdb06a6c79c2f9cdb11386602938f9af260d1c457a85add4f6f69", size = 838002, upload-time = "2026-06-22T00:46:38.101Z" },
2538
- { url = "https://files.pythonhosted.org/packages/b0/94/f48d08e6f72a406300fa11d8acd929fea1a80d4bf750fa292cb10785f126/nh3-0.3.6-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d14bf7982e7a77c0c775634c29c07ce08b38a046df73e1c1f139b3e82f18a38e", size = 823045, upload-time = "2026-06-22T00:46:39.495Z" },
2539
- { url = "https://files.pythonhosted.org/packages/25/bb/431615ba1d1d3eb63cde0f974f2114edf863a8a3f6049a12fed23fc241d3/nh3-0.3.6-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:44673b27010051ab5a5e438a86ec31bbda61d4a77d7e900af6b7be3037c1abae", size = 1093171, upload-time = "2026-06-22T00:46:41.21Z" },
2540
- { url = "https://files.pythonhosted.org/packages/0e/24/a0d80182a18919665fefd19c1c06f1d1df1c9a6455d0252de40c034a0bc3/nh3-0.3.6-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6b7beece07525dc6e6b0fc2f104442de2ba328360ad00e50cbe2e1fd620447d", size = 1049217, upload-time = "2026-06-22T00:46:42.804Z" },
2541
- { url = "https://files.pythonhosted.org/packages/0a/13/6f1e302ca674ac74362e150848ad56a1be5145391204f74facdb8e94df12/nh3-0.3.6-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:455469a29951edc92bc48b47ac2281c3f2609e6c4f6a047056449f8c2c23facf", size = 917372, upload-time = "2026-06-22T00:46:44.495Z" },
2542
- { url = "https://files.pythonhosted.org/packages/5b/67/314f6151bad77a93d751978a344033e1fc890822f05f0416079338e34231/nh3-0.3.6-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:905f877dc66dd7aea4a76e54bcb26acb5ff8216f720c0017ccf63e0e6035698e", size = 806699, upload-time = "2026-06-22T00:46:45.99Z" },
2543
- { url = "https://files.pythonhosted.org/packages/3c/a6/bfaa00046e58603507dcfc266c4778e3ab7adf68a5dedd73b6274b8d9314/nh3-0.3.6-cp38-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:25c733bee928530556b1db0ea46c52cf5aa686146e38e60a6fc7cb801ef91cec", size = 835165, upload-time = "2026-06-22T00:46:47.617Z" },
2544
- { url = "https://files.pythonhosted.org/packages/30/a8/fb2c38845efb703a9173bffdfc745fc64d2b0e55cfc73a3647d2f028250c/nh3-0.3.6-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f90d9a0cfdbee218994fdaaeeb5a0fde62d08f35e4eef0378ec1e2200172fd0", size = 858282, upload-time = "2026-06-22T00:46:49.276Z" },
2545
- { url = "https://files.pythonhosted.org/packages/68/17/06e72a18ee9b572914447338237ca7eb164c0df901f141bc10d1282247a2/nh3-0.3.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:82ca5bf427ad1b216b65ede1a2e2d87dc49bec417ceba0f297213107d3cd9d78", size = 1014328, upload-time = "2026-06-22T00:46:51.026Z" },
2546
- { url = "https://files.pythonhosted.org/packages/11/f9/3966c61455668c08853bf5e33b4bed93c421f3194ce4de896dc248d6f6ce/nh3-0.3.6-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:f5ed5fe84aee7f39db95c214a7421bf0499fbf500fec6d86a4e29bfc37971438", size = 1098207, upload-time = "2026-06-22T00:46:52.674Z" },
2547
- { url = "https://files.pythonhosted.org/packages/19/d3/479cb4ae440424825735d60525b53e3c77fd60fd6e6afc0e984f00eb0178/nh3-0.3.6-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:082675ff87b9385ec430ffe6d5847ba7456cc39b73720cd4add472f9f4cffd56", size = 1056961, upload-time = "2026-06-22T00:46:54.335Z" },
2548
- { url = "https://files.pythonhosted.org/packages/17/0c/6cdb5ee1e127be50dc8391e54bddc1f64e87bf4bfad0c55633320e2e02db/nh3-0.3.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:36d06341bd501240d320f5942481ed5e6846136b666e1ba4faf802b78ebc875f", size = 1033829, upload-time = "2026-06-22T00:46:56.258Z" },
2549
- { url = "https://files.pythonhosted.org/packages/e9/55/9de666ad975d6ccd77d799ea0add55ee2347aa81286ce21b2a97c070746b/nh3-0.3.6-cp38-abi3-win32.whl", hash = "sha256:5276ef17bdba9ad8040575c74072008b13aae429436e9d0429e718bb5f90f4da", size = 609081, upload-time = "2026-06-22T00:46:57.665Z" },
2550
- { url = "https://files.pythonhosted.org/packages/82/fa/2b5d684e3edf1e81bfd02d298c78c3e3da77ca1d8a2be3183a79544a7548/nh3-0.3.6-cp38-abi3-win_amd64.whl", hash = "sha256:f338ac7d594c067679f1e99b4f5ec3906842979560f9d8f15d6bdfa39a353b10", size = 624461, upload-time = "2026-06-22T00:46:59.163Z" },
2551
- { url = "https://files.pythonhosted.org/packages/7b/e5/7cafee2f0413ca4cb0ef3bd111e94d408a48810008b283ad8aee00dd1809/nh3-0.3.6-cp38-abi3-win_arm64.whl", hash = "sha256:69f365963f63a1e9bff53bdbb3c542c7c2efed3e163c9d5d83a772a2ac468c21", size = 603060, upload-time = "2026-06-22T00:47:00.596Z" },
2552
- ]
2553
-
2554
2414
  [[package]]
2555
2415
  name = "numpy"
2556
2416
  version = "2.2.6"
@@ -4099,15 +3959,6 @@ wheels = [
4099
3959
  { url = "https://files.pythonhosted.org/packages/23/ed/4532e9388e65fa16b46776ef47ad631a64eda1631884488af707666350ed/pywin32-312-cp315-cp315-win_arm64.whl", hash = "sha256:a8597d28f267b39074aef51fa593530082b39cbe5a074226096857b1fed2dfb9", size = 6840337, upload-time = "2026-06-04T07:49:57.531Z" },
4100
3960
  ]
4101
3961
 
4102
- [[package]]
4103
- name = "pywin32-ctypes"
4104
- version = "0.2.3"
4105
- source = { registry = "https://pypi.org/simple" }
4106
- sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" }
4107
- wheels = [
4108
- { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" },
4109
- ]
4110
-
4111
3962
  [[package]]
4112
3963
  name = "pyyaml"
4113
3964
  version = "6.0.3"
@@ -4172,20 +4023,6 @@ wheels = [
4172
4023
  { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
4173
4024
  ]
4174
4025
 
4175
- [[package]]
4176
- name = "readme-renderer"
4177
- version = "45.0"
4178
- source = { registry = "https://pypi.org/simple" }
4179
- dependencies = [
4180
- { name = "docutils" },
4181
- { name = "nh3" },
4182
- { name = "pygments" },
4183
- ]
4184
- sdist = { url = "https://files.pythonhosted.org/packages/02/51/d3a6ea424652c60f05600d8c2e01a55c913755e7cdad64afabbd1aa16f44/readme_renderer-45.0.tar.gz", hash = "sha256:030a8fac74904f8fba11ad1bb6964e3f76e896dc7e5e71f16af190c9056696d1", size = 36172, upload-time = "2026-06-09T21:05:17.37Z" }
4185
- wheels = [
4186
- { url = "https://files.pythonhosted.org/packages/97/1b/295bf2fa3e740131778065e5ffa2c481f0e7210182d408e9a2c244ff5b0c/readme_renderer-45.0-py3-none-any.whl", hash = "sha256:3385ed220117104a2bceb4a9dac8c5fdf6d1f96890d7ea2a9c7174fd5c84091f", size = 14134, upload-time = "2026-06-09T21:05:15.85Z" },
4187
- ]
4188
-
4189
4026
  [[package]]
4190
4027
  name = "referencing"
4191
4028
  version = "0.37.0"
@@ -4374,15 +4211,6 @@ wheels = [
4374
4211
  { url = "https://files.pythonhosted.org/packages/1d/4a/221da6ca167db45693d8d26c7dc79ccfc978a440251bf6721c9aaf251ac0/respx-0.23.1-py2.py3-none-any.whl", hash = "sha256:b18004b029935384bccfa6d7d9d74b4ec9af73a081cc28600fffc0447f4b8c1a", size = 25557, upload-time = "2026-04-08T14:37:14.613Z" },
4375
4212
  ]
4376
4213
 
4377
- [[package]]
4378
- name = "rfc3986"
4379
- version = "2.0.0"
4380
- source = { registry = "https://pypi.org/simple" }
4381
- sdist = { url = "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", size = 49026, upload-time = "2022-01-10T00:52:30.832Z" }
4382
- wheels = [
4383
- { url = "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", size = 31326, upload-time = "2022-01-10T00:52:29.594Z" },
4384
- ]
4385
-
4386
4214
  [[package]]
4387
4215
  name = "rich"
4388
4216
  version = "14.3.4"
@@ -4675,19 +4503,6 @@ wheels = [
4675
4503
  { url = "https://files.pythonhosted.org/packages/fe/4e/cd76eca6db6115604b7626668e891c9dd03330384082e33662fb0f113614/ruff-0.15.5-py3-none-win_arm64.whl", hash = "sha256:b498d1c60d2fe5c10c45ec3f698901065772730b411f164ae270bb6bfcc4740b", size = 10965572, upload-time = "2026-03-05T20:06:16.984Z" },
4676
4504
  ]
4677
4505
 
4678
- [[package]]
4679
- name = "secretstorage"
4680
- version = "3.5.0"
4681
- source = { registry = "https://pypi.org/simple" }
4682
- dependencies = [
4683
- { name = "cryptography" },
4684
- { name = "jeepney" },
4685
- ]
4686
- sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" }
4687
- wheels = [
4688
- { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" },
4689
- ]
4690
-
4691
4506
  [[package]]
4692
4507
  name = "shellingham"
4693
4508
  version = "1.5.4"
@@ -4836,26 +4651,6 @@ wheels = [
4836
4651
  { url = "https://files.pythonhosted.org/packages/22/2a/5e5e750890ada51017d18d0d4c30da696e5b5bd3180947729927628fc3cb/tqdm-4.68.4-py3-none-any.whl", hash = "sha256:5168118b2368f48c561afda8020fd79195b1bdb0bdf8086b88442c267a315dc2", size = 676612, upload-time = "2026-07-07T09:58:16.256Z" },
4837
4652
  ]
4838
4653
 
4839
- [[package]]
4840
- name = "twine"
4841
- version = "6.2.0"
4842
- source = { registry = "https://pypi.org/simple" }
4843
- dependencies = [
4844
- { name = "id" },
4845
- { name = "keyring", marker = "platform_machine != 'ppc64le' and platform_machine != 's390x'" },
4846
- { name = "packaging" },
4847
- { name = "readme-renderer" },
4848
- { name = "requests" },
4849
- { name = "requests-toolbelt" },
4850
- { name = "rfc3986" },
4851
- { name = "rich" },
4852
- { name = "urllib3" },
4853
- ]
4854
- sdist = { url = "https://files.pythonhosted.org/packages/e0/a8/949edebe3a82774c1ec34f637f5dd82d1cf22c25e963b7d63771083bbee5/twine-6.2.0.tar.gz", hash = "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf", size = 172262, upload-time = "2025-09-04T15:43:17.255Z" }
4855
- wheels = [
4856
- { url = "https://files.pythonhosted.org/packages/3a/7a/882d99539b19b1490cac5d77c67338d126e4122c8276bf640e411650c830/twine-6.2.0-py3-none-any.whl", hash = "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8", size = 42727, upload-time = "2025-09-04T15:43:15.994Z" },
4857
- ]
4858
-
4859
4654
  [[package]]
4860
4655
  name = "typer"
4861
4656
  version = "0.26.8"
File without changes
File without changes