unique_toolkit 2026.32.0.dev3__py3-none-any.whl → 2026.32.0.dev5__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.
@@ -1,3 +1,4 @@
1
+ from collections import defaultdict
1
2
  from typing import Any, Required, TypedDict
2
3
 
3
4
  from unique_toolkit.agentic.tools.openai_builtin import OpenAICodeInterpreterTool
@@ -7,8 +8,10 @@ from unique_toolkit.agentic.tools.openai_builtin.code_interpreter.postprocessors
7
8
  )
8
9
  from unique_toolkit.agentic.tools.schemas import ToolCallResponse
9
10
  from unique_toolkit.agentic.tools.tool_manager import ToolManager
11
+ from unique_toolkit.language_model.invocation_stats import LanguageModelInvocationStats
10
12
  from unique_toolkit.language_model.schemas import (
11
13
  LanguageModelStreamResponse,
14
+ LanguageModelTokenUsage,
12
15
  ResponsesLanguageModelStreamResponse,
13
16
  )
14
17
 
@@ -40,6 +43,16 @@ class AnalyticsToolName(TypedDict):
40
43
  display_name: str
41
44
 
42
45
 
46
+ class AnalyticsTokenUsage(TypedDict):
47
+ model_name: str
48
+ completion_tokens: int | None
49
+ prompt_tokens: int | None
50
+ total_tokens: int | None
51
+ reasoning_tokens: int | None
52
+ cached_tokens: int | None
53
+ cache_write_tokens: int | None
54
+
55
+
43
56
  class Analytics(TypedDict):
44
57
  answer_length: int
45
58
  artifacts_created_count: int | None
@@ -54,6 +67,7 @@ class Analytics(TypedDict):
54
67
  references_count: int
55
68
  skills_used: list[AnalyticsSkill]
56
69
  subagent_names_used: list[AnalyticsToolName]
70
+ tokens: list[AnalyticsTokenUsage]
57
71
  tool_call_count: int
58
72
  tools_used: list[AnalyticsTool]
59
73
  total_time_to_answer_ms: int | None
@@ -115,6 +129,7 @@ class DebugInfoManager:
115
129
  total_time_to_answer_ms: int | None = None,
116
130
  artifacts: ArtifactsDebugInfo | None = None,
117
131
  context_memory_updated: bool | None = None,
132
+ invocations: list[LanguageModelInvocationStats] | None = None,
118
133
  ) -> None:
119
134
  """Add a stable 'analytics' snapshot for downstream ROI/usage reporting.
120
135
 
@@ -152,6 +167,7 @@ class DebugInfoManager:
152
167
  loop_iteration_count=loop_iteration_count,
153
168
  subagent_names_used=_unique_tool_names(analytics_tools, "is_sub_agent"),
154
169
  mcp_tool_names_used=_unique_tool_names(analytics_tools, "is_mcp"),
170
+ tokens=_aggregate_tokens_by_model(invocations or []),
155
171
  total_time_to_answer_ms=total_time_to_answer_ms,
156
172
  artifacts_created_count=artifacts["count"] if artifacts else None,
157
173
  artifacts_created_filetype=artifacts["filetypes"] if artifacts else None,
@@ -163,6 +179,32 @@ class DebugInfoManager:
163
179
  self.add("analytics", analytics)
164
180
 
165
181
 
182
+ def _aggregate_tokens_by_model(
183
+ invocations: list[LanguageModelInvocationStats],
184
+ ) -> list[AnalyticsTokenUsage]:
185
+ usages_by_model: dict[str, list[LanguageModelTokenUsage]] = defaultdict(list)
186
+ for invocation in invocations:
187
+ usages_by_model[str(invocation.model_name)].append(invocation.token_usage)
188
+
189
+ totals: list[AnalyticsTokenUsage] = []
190
+ for model_name in sorted(usages_by_model):
191
+ usage = LanguageModelTokenUsage.sum_usages(usages_by_model[model_name])
192
+ if usage is None:
193
+ continue
194
+ totals.append(
195
+ AnalyticsTokenUsage(
196
+ model_name=model_name,
197
+ completion_tokens=usage.completion_tokens,
198
+ prompt_tokens=usage.prompt_tokens,
199
+ total_tokens=usage.total_tokens,
200
+ reasoning_tokens=usage.reasoning_tokens,
201
+ cached_tokens=usage.cached_tokens,
202
+ cache_write_tokens=usage.cache_write_tokens,
203
+ )
204
+ )
205
+ return totals
206
+
207
+
166
208
  def _unique_tool_names(
167
209
  analytics_tools: list[AnalyticsTool], flag: str
168
210
  ) -> list[AnalyticsToolName]:
@@ -41,6 +41,17 @@ class Postprocessor(ABC):
41
41
  """Override in subclasses whose `run()` makes its own LLM call(s)."""
42
42
  return []
43
43
 
44
+ def take_pending_invocation_stats(self) -> list[LanguageModelInvocationStats]:
45
+ """Pop usage incurred before this turn's `run()` was reached.
46
+
47
+ Called unconditionally at the start of every turn so usage from work
48
+ done ahead of postprocessing (e.g. a load-time LLM call) is still
49
+ reported on turns that exit before `run()` executes. Override in
50
+ subclasses that do such work; must clear the pending stats so `run()`
51
+ does not report them a second time.
52
+ """
53
+ return []
54
+
44
55
 
45
56
  class ResponsesApiPostprocessor(ABC):
46
57
  def __init__(self, name: str):
@@ -74,6 +85,13 @@ class ResponsesApiPostprocessor(ABC):
74
85
  """
75
86
  return []
76
87
 
88
+ def take_pending_invocation_stats(self) -> list[LanguageModelInvocationStats]:
89
+ """Pop usage incurred before this turn's `run()` was reached.
90
+
91
+ See `Postprocessor.take_pending_invocation_stats` for why this exists.
92
+ """
93
+ return []
94
+
77
95
 
78
96
  class PostprocessorManager:
79
97
  """
@@ -196,6 +214,19 @@ class PostprocessorManager:
196
214
  """Per-LLM-call stats from every postprocessor that made its own LLM call(s)."""
197
215
  return list(self._invocation_stats)
198
216
 
217
+ def take_pending_invocation_stats(self) -> list[LanguageModelInvocationStats]:
218
+ """Pop pre-`run()` usage (e.g. load-time calls) from every postprocessor.
219
+
220
+ Call this unconditionally at the start of a turn, before it's known
221
+ whether `run_postprocessors` will even be reached this turn, so that
222
+ usage is never silently dropped on turns that exit early.
223
+ """
224
+ return [
225
+ invocation
226
+ for postprocessor in self._get_all_postprocessors()
227
+ for invocation in postprocessor.take_pending_invocation_stats()
228
+ ]
229
+
199
230
  async def execute_postprocessors(
200
231
  self,
201
232
  loop_response: LanguageModelStreamResponse,
@@ -4,12 +4,15 @@ import json
4
4
  import logging
5
5
  import re
6
6
  from datetime import datetime
7
- from typing import override
7
+ from typing import Any, override
8
8
 
9
9
  import unique_sdk
10
10
  from pydantic import Field, TypeAdapter, create_model
11
11
  from unique_sdk.api_resources._space import Space
12
- from unique_sdk.utils.chat_in_space import send_message_and_wait_for_completion
12
+ from unique_sdk.utils.chat_in_space import (
13
+ get_message_invocations,
14
+ send_message_and_wait_for_completion,
15
+ )
13
16
 
14
17
  from unique_toolkit._common.execution import SafeTaskExecutor
15
18
  from unique_toolkit._common.referencing import (
@@ -55,6 +58,7 @@ from unique_toolkit.language_model import (
55
58
  LanguageModelService,
56
59
  LanguageModelToolDescription,
57
60
  )
61
+ from unique_toolkit.language_model.invocation_stats import LanguageModelInvocationStats
58
62
 
59
63
  logger = logging.getLogger(__name__)
60
64
 
@@ -296,6 +300,13 @@ class SubAgentTool(Tool[SubAgentToolConfig]):
296
300
  if self.config.passthrough_config.enabled:
297
301
  await self._display_sub_agent_response(response)
298
302
 
303
+ sub_agent_invocations = _prefix_sub_agent_invocation_sources(
304
+ invocations=_parse_sub_agent_invocation_stats(
305
+ get_message_invocations(response)
306
+ ),
307
+ tool_name=self.name,
308
+ tool_call_id=tool_call.id,
309
+ )
299
310
  return ToolCallResponse(
300
311
  id=tool_call.id,
301
312
  name=tool_call.name,
@@ -307,9 +318,12 @@ class SubAgentTool(Tool[SubAgentToolConfig]):
307
318
  content_chunks=content_chunks,
308
319
  debug_info={
309
320
  "chat_id": response["chatId"],
321
+ "user_message_id": response.get("triggeringUserMessageId"),
322
+ "assistant_message_id": response.get("id"),
310
323
  "assistant_id": self.config.assistant_id,
311
324
  "display_name": self._display_name,
312
325
  },
326
+ invocation_stats=sub_agent_invocations,
313
327
  )
314
328
  except TimeoutError as e:
315
329
  raise e
@@ -495,6 +509,7 @@ class SubAgentTool(Tool[SubAgentToolConfig]):
495
509
  parentAssistantId=self.config.assistant_id,
496
510
  ),
497
511
  on_message_update=on_message_update,
512
+ wait_for_invocations=True,
498
513
  )
499
514
  except TimeoutError as e:
500
515
  await self._notify_progress(
@@ -513,6 +528,42 @@ class SubAgentTool(Tool[SubAgentToolConfig]):
513
528
  ) from e
514
529
 
515
530
 
531
+ def _parse_sub_agent_invocation_stats(
532
+ raw_invocations: list[dict[str, Any]],
533
+ ) -> list[LanguageModelInvocationStats]:
534
+ """Deserialize sub-agent invocation stats, ignoring malformed entries.
535
+
536
+ A single unparseable entry (e.g. a stale model name) must not fail the
537
+ whole tool call and drop an otherwise-successful sub-agent reply -- see
538
+ `_load_invocation_stats_from_debug_info` in unique_orchestrator, which
539
+ applies the same per-entry tolerance for the same reason.
540
+ """
541
+ invocations: list[LanguageModelInvocationStats] = []
542
+ for raw_invocation in raw_invocations:
543
+ try:
544
+ invocations.append(
545
+ LanguageModelInvocationStats.model_validate(raw_invocation)
546
+ )
547
+ except Exception:
548
+ logger.warning(
549
+ "Ignoring malformed sub-agent LLM invocation stats",
550
+ exc_info=True,
551
+ )
552
+ return invocations
553
+
554
+
555
+ def _prefix_sub_agent_invocation_sources(
556
+ invocations: list[LanguageModelInvocationStats],
557
+ tool_name: str,
558
+ tool_call_id: str,
559
+ ) -> list[LanguageModelInvocationStats]:
560
+ prefix = f"subagent.{tool_name}.{tool_call_id}"
561
+ return [
562
+ invocation.model_copy(update={"source": f"{prefix}.{invocation.source}"})
563
+ for invocation in invocations
564
+ ]
565
+
566
+
516
567
  def _format_response(tool_name: str, text: str, system_reminders: list[str]) -> str:
517
568
  if len(system_reminders) == 0:
518
569
  return text
@@ -17,6 +17,7 @@ from unique_toolkit.agentic.tools.a2a.tool.config import (
17
17
  from unique_toolkit.agentic.tools.a2a.tool.service import (
18
18
  _format_response,
19
19
  _get_sub_agent_system_reminders,
20
+ _parse_sub_agent_invocation_stats,
20
21
  _prepare_sub_agent_response_refs,
21
22
  _remove_extra_refs,
22
23
  _response_has_refs,
@@ -59,6 +60,78 @@ def refs_list() -> list[dict[str, Any]]:
59
60
  ]
60
61
 
61
62
 
63
+ # Tests for _parse_sub_agent_invocation_stats
64
+
65
+
66
+ @pytest.mark.ai
67
+ def test_parse_sub_agent_invocation_stats__skips_malformed_entry__keeps_valid_ones() -> (
68
+ None
69
+ ):
70
+ """
71
+ Purpose: Verify one malformed invocation entry doesn't discard the rest.
72
+ Why this matters: A single bad entry must not fail the whole sub-agent tool
73
+ call and drop an otherwise-successful reply.
74
+ Setup summary: Mix a valid entry with a malformed one (missing required
75
+ fields), verify only the valid entry survives.
76
+ """
77
+ # Arrange
78
+ raw_invocations = [
79
+ {
80
+ "modelName": "gpt-4",
81
+ "tokenUsage": {"totalTokens": 10},
82
+ "source": "main_loop",
83
+ },
84
+ {"modelName": "gpt-4"}, # missing required tokenUsage/source
85
+ ]
86
+
87
+ # Act
88
+ result = _parse_sub_agent_invocation_stats(raw_invocations)
89
+
90
+ # Assert
91
+ assert len(result) == 1
92
+ assert result[0].source == "main_loop"
93
+
94
+
95
+ @pytest.mark.ai
96
+ def test_parse_sub_agent_invocation_stats__returns_empty__when_all_malformed() -> None:
97
+ """
98
+ Purpose: Verify an all-malformed list returns empty instead of raising.
99
+ Why this matters: The sub-agent tool call must still succeed and return the
100
+ answer even if every persisted stats entry is unparseable.
101
+ Setup summary: Only malformed entries, assert empty list with no exception.
102
+ """
103
+ # Arrange
104
+ raw_invocations = [{"modelName": "gpt-4"}, {"source": "main_loop"}]
105
+
106
+ # Act
107
+ result = _parse_sub_agent_invocation_stats(raw_invocations)
108
+
109
+ # Assert
110
+ assert result == []
111
+
112
+
113
+ @pytest.mark.ai
114
+ def test_parse_sub_agent_invocation_stats__parses_all__when_all_valid() -> None:
115
+ """
116
+ Purpose: Verify parsing is unaffected by the malformed-entry tolerance
117
+ when every entry is well-formed.
118
+ Why this matters: The fix must not silently drop good data too.
119
+ Setup summary: Two valid entries, assert both are returned.
120
+ """
121
+ # Arrange
122
+ raw_invocations = [
123
+ {"modelName": "gpt-4", "tokenUsage": {"totalTokens": 3}, "source": "a"},
124
+ {"modelName": "gpt-4", "tokenUsage": {"totalTokens": 5}, "source": "b"},
125
+ ]
126
+
127
+ # Act
128
+ result = _parse_sub_agent_invocation_stats(raw_invocations)
129
+
130
+ # Assert
131
+ assert len(result) == 2
132
+ assert [inv.source for inv in result] == ["a", "b"]
133
+
134
+
62
135
  # Tests for _format_response
63
136
 
64
137
 
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import time
4
+ from collections.abc import Sequence
4
5
 
5
6
  from prometheus_client import ( # pyright: ignore[reportMissingImports]
6
7
  Counter,
@@ -16,12 +17,6 @@ _http_requests_total = Counter(
16
17
  ["method", "path", "status_code"],
17
18
  registry=REGISTRY,
18
19
  )
19
- _http_request_duration_seconds = Histogram(
20
- "python_http_request_duration_seconds",
21
- "HTTP request duration",
22
- ["method", "path"],
23
- registry=REGISTRY,
24
- )
25
20
  _http_requests_in_progress = Gauge(
26
21
  "python_http_requests_in_progress",
27
22
  "In-flight HTTP requests",
@@ -30,6 +25,35 @@ _http_requests_in_progress = Gauge(
30
25
  multiprocess_mode="livesum",
31
26
  )
32
27
 
28
+ # Created lazily on first MetricsMiddleware construction so services can choose the
29
+ # bucket layout. Prometheus allows a metric name to be registered only once per
30
+ # registry, so the buckets passed to the first middleware win for the process.
31
+ _http_request_duration_seconds: Histogram | None = None
32
+
33
+
34
+ def _get_or_create_duration_histogram(
35
+ buckets: Sequence[float] | None,
36
+ ) -> Histogram:
37
+ global _http_request_duration_seconds
38
+ if _http_request_duration_seconds is None:
39
+ if buckets is None:
40
+ _http_request_duration_seconds = Histogram(
41
+ "python_http_request_duration_seconds",
42
+ "HTTP request duration",
43
+ ["method", "path"],
44
+ registry=REGISTRY,
45
+ )
46
+ else:
47
+ _http_request_duration_seconds = Histogram(
48
+ "python_http_request_duration_seconds",
49
+ "HTTP request duration",
50
+ ["method", "path"],
51
+ registry=REGISTRY,
52
+ buckets=tuple(buckets),
53
+ )
54
+ return _http_request_duration_seconds
55
+
56
+
33
57
  _DEFAULT_EXCLUDED_PATHS = frozenset({"/health", "/metrics", "/"})
34
58
 
35
59
 
@@ -51,13 +75,29 @@ class MetricsMiddleware:
51
75
 
52
76
  # Quart
53
77
  app.asgi_app = MetricsMiddleware(app.asgi_app)
78
+
79
+ ``duration_buckets`` overrides the latency histogram buckets (seconds). Without
80
+ it, prometheus_client's defaults apply and their top finite bucket is 10s —
81
+ histogram_quantile cannot resolve past the largest finite bucket, so services
82
+ with slower endpoints should pass buckets covering their full latency range::
83
+
84
+ app.add_middleware(MetricsMiddleware, duration_buckets=(1, 5, 30, 60, 120))
85
+
86
+ The histogram is registered once per process; the buckets of the first
87
+ constructed middleware apply.
54
88
  """
55
89
 
56
- def __init__(self, app, excluded_paths: set[str] | None = None):
90
+ def __init__(
91
+ self,
92
+ app,
93
+ excluded_paths: set[str] | None = None,
94
+ duration_buckets: Sequence[float] | None = None,
95
+ ):
57
96
  self.app = app
58
97
  self._excluded_paths = (
59
98
  excluded_paths if excluded_paths is not None else _DEFAULT_EXCLUDED_PATHS
60
99
  )
100
+ self._duration = _get_or_create_duration_histogram(duration_buckets)
61
101
 
62
102
  async def __call__(self, scope, receive, send):
63
103
  # Pass through non-HTTP (WebSocket, lifespan)
@@ -87,9 +127,7 @@ class MetricsMiddleware:
87
127
  finally:
88
128
  duration = time.perf_counter() - start
89
129
  _http_requests_in_progress.labels(method=method).dec()
90
- _http_request_duration_seconds.labels(method=method, path=path).observe(
91
- duration
92
- )
130
+ self._duration.labels(method=method, path=path).observe(duration)
93
131
  _http_requests_total.labels(
94
132
  method=method, path=path, status_code=status_code
95
133
  ).inc()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-toolkit
3
- Version: 2026.32.0.dev3
3
+ Version: 2026.32.0.dev5
4
4
  Summary:
5
5
  Author: Cedric Klinkert, Martin Fadler, Sadique Sheik, Fabian Schläpfer, Pascal Hauri, Ejiroghene Oki-Peter, Rami Azouz, Ricardo Visini, Sanzio Monti
6
6
  Author-email: Cedric Klinkert <cedric.klinkert@unique.ch>, Martin Fadler <martin.fadler@unique.ch>, Sadique Sheik <sadique@unique.ch>, Fabian Schläpfer <fabian@unique.ch>, Pascal Hauri <pascal@unique.ch>, Ejiroghene Oki-Peter <oki-peter.ejiroghene@unique.ai>, Rami Azouz <rami.ext@unique.ch>, Ricardo Visini <ricardo.visini@unique.ch>, Sanzio Monti <sanzio.monti@unique.ai>
@@ -16,7 +16,7 @@ Requires-Dist: sseclient>=0.0.27,<0.0.28
16
16
  Requires-Dist: openai>=1.105.0,<3
17
17
  Requires-Dist: platformdirs>=4.0.0,<5
18
18
  Requires-Dist: pillow>=12.1.1
19
- Requires-Dist: unique-sdk>=2026.32.0.dev2,<2026.32.0rc0
19
+ Requires-Dist: unique-sdk>=2026.32.0.dev8,<2026.32.0rc0
20
20
  Requires-Dist: jambo>=0.1.2,<0.2
21
21
  Requires-Dist: jinja2>=3.1.6,<4
22
22
  Requires-Dist: docxtpl>=0.20.1,<0.21
@@ -83,7 +83,7 @@ unique_toolkit/_common/utils/write_configuration.py,sha256=fzvr4C-XBL3OSM3Od9Tbq
83
83
  unique_toolkit/_common/validate_required_values.py,sha256=cA8GPwkV1EPsG6ffIkmAn3Dx4LRRMC1EAURmT7PyuCg,551
84
84
  unique_toolkit/_common/validators.py,sha256=ElnkMsyEY24TfzfTVHvireyT39EnZgW5N40T0P4b6gE,2638
85
85
  unique_toolkit/agentic/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
86
- unique_toolkit/agentic/debug_info_manager/debug_info_manager.py,sha256=qbQp-e9BO-vGLb6IDbYAM8q8xt2iXMRoC5PX-SjlsRk,8920
86
+ unique_toolkit/agentic/debug_info_manager/debug_info_manager.py,sha256=VZLDOTAJJ_jVOZ4Zw4C2F-uCv3vsjZTcvbljGBYKh-A,10501
87
87
  unique_toolkit/agentic/debug_info_manager/test/test_debug_info_manager.py,sha256=bDjrHLqfwtd7ewdIdyTFHabG-15t3iXvs34ywoYXcik,11888
88
88
  unique_toolkit/agentic/evaluation/config.py,sha256=u5-iuT-4mfA2_9UZjZ-TN3YutVf35aR4EsF9l61Odnk,1696
89
89
  unique_toolkit/agentic/evaluation/context_relevancy/prompts/__init__.py,sha256=IKGvHW2viBylTUjEJNKvIXO4YrYwdJXBHSxA9rKtiRI,355
@@ -138,7 +138,7 @@ unique_toolkit/agentic/loop_runner/runners/qwen/qwen_runner.py,sha256=_hS65fq1eH
138
138
  unique_toolkit/agentic/message_log_manager/__init__.py,sha256=3-KY_sGkPbNoSnrzwPY0FQIJNnsz4NHXvocXgGRUeuE,169
139
139
  unique_toolkit/agentic/message_log_manager/service.py,sha256=yXeV_70ATUJrFi_KYRJ8PRmRbo27kQjY5cjazxDFkpI,9840
140
140
  unique_toolkit/agentic/message_log_order.py,sha256=GaZOlAkFS2BpTc72af6Nit9inm6LskBYTze5Q7dd9cs,1041
141
- unique_toolkit/agentic/postprocessor/postprocessor_manager.py,sha256=Qz4JCRB28GWV0cZbmkI739vnn--1J1o9Uk_x9fk6Z3s,9378
141
+ unique_toolkit/agentic/postprocessor/postprocessor_manager.py,sha256=76n2aNqjsM6P6SJDuKtlGTFdIlaVHr2klsz9BhgNUAM,10761
142
142
  unique_toolkit/agentic/reference_manager/reference_manager.py,sha256=axqjSNFoR6oGVKg_fK1D2IsA9sxvreyPutRxK17dBxI,4844
143
143
  unique_toolkit/agentic/reference_manager/tests/test_external_context.py,sha256=FlCr8rcvrJDkVbKW6pDylSTmP0v0luH_3TGBvXm4KZw,1246
144
144
  unique_toolkit/agentic/responses_api/__init__.py,sha256=9WTO-ef7fGE9Y1QtZJFm8Q_jkwK8Srtl-HWvpAD2Wxs,668
@@ -173,8 +173,8 @@ unique_toolkit/agentic/tools/a2a/tool/__init__.py,sha256=9x8nGZsMcpF8ch0RKoelGPl
173
173
  unique_toolkit/agentic/tools/a2a/tool/_memory.py,sha256=w8bxjokrqHQZgApd55b5rHXF-DpgJwaKTg4CvLBLamc,1034
174
174
  unique_toolkit/agentic/tools/a2a/tool/_schema.py,sha256=wMwyunViTnxaURvenkATEvyfXn5LvLaP0HxbYqdZGls,158
175
175
  unique_toolkit/agentic/tools/a2a/tool/config.py,sha256=v4rZQU42RjUTC5Md-Cye1rUIWDlHpuhc3MzcyvpGQNw,9748
176
- unique_toolkit/agentic/tools/a2a/tool/service.py,sha256=5jOtXSU9oggG1dVUd7PAb3hd_-MLyGnSR5_lUQvYPfA,22160
177
- unique_toolkit/agentic/tools/a2a/tool/test/test_service_utils.py,sha256=x4leRxnGgqD-XyXPkW2z1ekv4Ja7EStXd-24LGadPZA,24858
176
+ unique_toolkit/agentic/tools/a2a/tool/service.py,sha256=j3kQZ1P-gYyXE2SmbbyNIVC1tIgTm8pJkCwrfUB9YU8,24173
177
+ unique_toolkit/agentic/tools/a2a/tool/test/test_service_utils.py,sha256=0w3721KiJot54PIy105DNiOGviLiiKNHi6kECDGvO1k,27199
178
178
  unique_toolkit/agentic/tools/agent_chunks_hanlder.py,sha256=eO0g46wZOQlmWqYYVGiszRVe8K2LX-9qW30ZCdGg5eE,1943
179
179
  unique_toolkit/agentic/tools/config.py,sha256=AOtBn2tL1rIoPPzl1csAHLD5aetYqPL-jAqkIoSBxyE,7468
180
180
  unique_toolkit/agentic/tools/experimental/__init__.py,sha256=ArEcXHGPtMRNsDnsyo9v1fev0EZWdJ6D7NWdPEFLQKM,956
@@ -403,7 +403,7 @@ unique_toolkit/language_model/stream_transform.py,sha256=iNNGpLY_7G6Bpp9Y4KtbIkQ
403
403
  unique_toolkit/language_model/utils.py,sha256=bPQ4l6_YO71w-zaIPanUUmtbXC1_hCvLK0tAFc3VCRc,1902
404
404
  unique_toolkit/monitoring/__init__.py,sha256=z1gkOCLxGcg936MFuLSeWFa0tREcBFSUbV09NxilBw4,1065
405
405
  unique_toolkit/monitoring/gunicorn.py,sha256=urTFFS5zYHKoxb1TEQHcLqb2byi-aYgFRHGwgYRzYYI,844
406
- unique_toolkit/monitoring/middleware.py,sha256=JNh5v_INAkexuLtXhCG9n5n2t6Pnf8ZdPTnimAVS0_s,2931
406
+ unique_toolkit/monitoring/middleware.py,sha256=MNsUuKgt74mSnUwttXTB_AxX546Pz2zKoAP9OewVrwQ,4531
407
407
  unique_toolkit/monitoring/registry.py,sha256=De2Bx5QBBRJQY2MM3xtntiJ2JU2FoKkVT7rNMG7ewgA,5874
408
408
  unique_toolkit/protocols/support.py,sha256=x5edzbBmKAUuvm3oLFh52gkgQsqR5LU7M0t8RHK0DZk,5161
409
409
  unique_toolkit/services/__init__.py,sha256=b9Wl7Wvz1JmZDbDexIZzBoXZ0wscXuczGAH8LoL-Qak,572
@@ -418,6 +418,6 @@ unique_toolkit/short_term_memory/service.py,sha256=2JMBEBn6GoyK-a0xvdcWpPft-3C74
418
418
  unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
419
419
  unique_toolkit/smart_rules/compile.py,sha256=vRe0aMJWuVIK-VsqomgxQImzke7MDmwgSxxJfYCa7zw,1386
420
420
  unique_toolkit/test_utilities/events.py,sha256=FLrhMh0A_mQutAUjoD0FdMcVQ4Gq31r35OVGzRjvLro,6760
421
- unique_toolkit-2026.32.0.dev3.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
422
- unique_toolkit-2026.32.0.dev3.dist-info/METADATA,sha256=56S1t7gvRT_iCfhx_SeRNDxt1DERgazGT-bWdZ2CsB0,7465
423
- unique_toolkit-2026.32.0.dev3.dist-info/RECORD,,
421
+ unique_toolkit-2026.32.0.dev5.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
422
+ unique_toolkit-2026.32.0.dev5.dist-info/METADATA,sha256=1bEknHHM6C2M0YX9ZPdElqUVEOA1AnI_u-_zmXVKrQM,7465
423
+ unique_toolkit-2026.32.0.dev5.dist-info/RECORD,,