unique_toolkit 2026.32.0.dev4__py3-none-any.whl → 2026.32.0.dev6__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.
- unique_toolkit/agentic/debug_info_manager/debug_info_manager.py +66 -18
- unique_toolkit/language_model/invocation_stats.py +8 -1
- unique_toolkit/language_model/model_costs.py +118 -0
- unique_toolkit/monitoring/middleware.py +48 -10
- {unique_toolkit-2026.32.0.dev4.dist-info → unique_toolkit-2026.32.0.dev6.dist-info}/METADATA +3 -2
- {unique_toolkit-2026.32.0.dev4.dist-info → unique_toolkit-2026.32.0.dev6.dist-info}/RECORD +7 -6
- {unique_toolkit-2026.32.0.dev4.dist-info → unique_toolkit-2026.32.0.dev6.dist-info}/WHEEL +0 -0
|
@@ -43,14 +43,18 @@ class AnalyticsToolName(TypedDict):
|
|
|
43
43
|
display_name: str
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
class
|
|
47
|
-
model_name: str
|
|
46
|
+
class AnalyticsConsumption(TypedDict):
|
|
48
47
|
completion_tokens: int | None
|
|
49
48
|
prompt_tokens: int | None
|
|
50
49
|
total_tokens: int | None
|
|
51
50
|
reasoning_tokens: int | None
|
|
52
51
|
cached_tokens: int | None
|
|
53
52
|
cache_write_tokens: int | None
|
|
53
|
+
cost_usd: float | None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class AnalyticsConsumptionByLlm(AnalyticsConsumption):
|
|
57
|
+
model_name: str
|
|
54
58
|
|
|
55
59
|
|
|
56
60
|
class Analytics(TypedDict):
|
|
@@ -58,6 +62,10 @@ class Analytics(TypedDict):
|
|
|
58
62
|
artifacts_created_count: int | None
|
|
59
63
|
artifacts_created_filetype: list[str] | None
|
|
60
64
|
context_memory_updated: bool | None
|
|
65
|
+
# Sum of all model usages this turn. None when no invocations reported usage.
|
|
66
|
+
consumption: AnalyticsConsumption | None
|
|
67
|
+
# Per-model usage totals, sorted by model_name.
|
|
68
|
+
consumption_by_llm: list[AnalyticsConsumptionByLlm]
|
|
61
69
|
language_model: AnalyticsLanguageModel
|
|
62
70
|
loop_iteration_count: int
|
|
63
71
|
mcp_tool_names_used: list[AnalyticsToolName]
|
|
@@ -67,7 +75,6 @@ class Analytics(TypedDict):
|
|
|
67
75
|
references_count: int
|
|
68
76
|
skills_used: list[AnalyticsSkill]
|
|
69
77
|
subagent_names_used: list[AnalyticsToolName]
|
|
70
|
-
tokens: list[AnalyticsTokenUsage]
|
|
71
78
|
tool_call_count: int
|
|
72
79
|
tools_used: list[AnalyticsTool]
|
|
73
80
|
total_time_to_answer_ms: int | None
|
|
@@ -149,6 +156,7 @@ class DebugInfoManager:
|
|
|
149
156
|
# DisplayCodeInterpreterFilesPostProcessor) into debug_info["artifacts"].
|
|
150
157
|
# Absent when no Code Interpreter ran this turn → artifact fields stay
|
|
151
158
|
# None, preserving the always-present, null-when-unknown contract.
|
|
159
|
+
consumption_by_llm = _aggregate_consumption_by_llm(invocations or [])
|
|
152
160
|
analytics = Analytics(
|
|
153
161
|
tools_used=analytics_tools,
|
|
154
162
|
tool_call_count=len(analytics_tools),
|
|
@@ -167,7 +175,8 @@ class DebugInfoManager:
|
|
|
167
175
|
loop_iteration_count=loop_iteration_count,
|
|
168
176
|
subagent_names_used=_unique_tool_names(analytics_tools, "is_sub_agent"),
|
|
169
177
|
mcp_tool_names_used=_unique_tool_names(analytics_tools, "is_mcp"),
|
|
170
|
-
|
|
178
|
+
consumption_by_llm=consumption_by_llm,
|
|
179
|
+
consumption=_aggregate_consumption(invocations or []),
|
|
171
180
|
total_time_to_answer_ms=total_time_to_answer_ms,
|
|
172
181
|
artifacts_created_count=artifacts["count"] if artifacts else None,
|
|
173
182
|
artifacts_created_filetype=artifacts["filetypes"] if artifacts else None,
|
|
@@ -179,27 +188,66 @@ class DebugInfoManager:
|
|
|
179
188
|
self.add("analytics", analytics)
|
|
180
189
|
|
|
181
190
|
|
|
182
|
-
def
|
|
191
|
+
def _sum_invocation_costs(
|
|
192
|
+
invocations: list[LanguageModelInvocationStats],
|
|
193
|
+
) -> float | None:
|
|
194
|
+
"""Sum costs only when every invocation has a known cost; else None."""
|
|
195
|
+
costs = [invocation.cost_usd for invocation in invocations]
|
|
196
|
+
if not costs or any(cost is None for cost in costs):
|
|
197
|
+
return None
|
|
198
|
+
return sum(cost for cost in costs if cost is not None)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _to_analytics_consumption(
|
|
202
|
+
usage: LanguageModelTokenUsage,
|
|
203
|
+
cost_usd: float | None,
|
|
204
|
+
) -> AnalyticsConsumption:
|
|
205
|
+
return AnalyticsConsumption(
|
|
206
|
+
completion_tokens=usage.completion_tokens,
|
|
207
|
+
prompt_tokens=usage.prompt_tokens,
|
|
208
|
+
total_tokens=usage.total_tokens,
|
|
209
|
+
reasoning_tokens=usage.reasoning_tokens,
|
|
210
|
+
cached_tokens=usage.cached_tokens,
|
|
211
|
+
cache_write_tokens=usage.cache_write_tokens,
|
|
212
|
+
cost_usd=cost_usd,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _aggregate_consumption(
|
|
183
217
|
invocations: list[LanguageModelInvocationStats],
|
|
184
|
-
) ->
|
|
185
|
-
|
|
218
|
+
) -> AnalyticsConsumption | None:
|
|
219
|
+
"""Aggregate token usage and cost across all models for this turn."""
|
|
220
|
+
usage = LanguageModelTokenUsage.sum_usages(
|
|
221
|
+
invocation.token_usage for invocation in invocations
|
|
222
|
+
)
|
|
223
|
+
if usage is None:
|
|
224
|
+
return None
|
|
225
|
+
return _to_analytics_consumption(usage, _sum_invocation_costs(invocations))
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _aggregate_consumption_by_llm(
|
|
229
|
+
invocations: list[LanguageModelInvocationStats],
|
|
230
|
+
) -> list[AnalyticsConsumptionByLlm]:
|
|
231
|
+
invocations_by_model: dict[str, list[LanguageModelInvocationStats]] = defaultdict(
|
|
232
|
+
list
|
|
233
|
+
)
|
|
186
234
|
for invocation in invocations:
|
|
187
|
-
|
|
235
|
+
invocations_by_model[str(invocation.model_name)].append(invocation)
|
|
188
236
|
|
|
189
|
-
totals: list[
|
|
190
|
-
for model_name in sorted(
|
|
191
|
-
|
|
237
|
+
totals: list[AnalyticsConsumptionByLlm] = []
|
|
238
|
+
for model_name in sorted(invocations_by_model):
|
|
239
|
+
model_invocations = invocations_by_model[model_name]
|
|
240
|
+
usage = LanguageModelTokenUsage.sum_usages(
|
|
241
|
+
invocation.token_usage for invocation in model_invocations
|
|
242
|
+
)
|
|
192
243
|
if usage is None:
|
|
193
244
|
continue
|
|
194
245
|
totals.append(
|
|
195
|
-
|
|
246
|
+
AnalyticsConsumptionByLlm(
|
|
196
247
|
model_name=model_name,
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
reasoning_tokens=usage.reasoning_tokens,
|
|
201
|
-
cached_tokens=usage.cached_tokens,
|
|
202
|
-
cache_write_tokens=usage.cache_write_tokens,
|
|
248
|
+
**_to_analytics_consumption(
|
|
249
|
+
usage, _sum_invocation_costs(model_invocations)
|
|
250
|
+
),
|
|
203
251
|
)
|
|
204
252
|
)
|
|
205
253
|
return totals
|
|
@@ -11,6 +11,7 @@ from humps import camelize
|
|
|
11
11
|
from pydantic import BaseModel, BeforeValidator, ConfigDict
|
|
12
12
|
|
|
13
13
|
from unique_toolkit.language_model.infos import LanguageModelName
|
|
14
|
+
from unique_toolkit.language_model.model_costs import calculate_invocation_cost_usd
|
|
14
15
|
from unique_toolkit.language_model.schemas import LanguageModelTokenUsage
|
|
15
16
|
|
|
16
17
|
# `protected_namespaces=()` allows the `model_name` field name.
|
|
@@ -59,6 +60,7 @@ class LanguageModelInvocationStats(BaseModel):
|
|
|
59
60
|
model_name: ModelName
|
|
60
61
|
token_usage: LanguageModelTokenUsage
|
|
61
62
|
source: Source # e.g. "main_loop", tool/evaluation/postprocessor name
|
|
63
|
+
cost_usd: float | None = None
|
|
62
64
|
|
|
63
65
|
@classmethod
|
|
64
66
|
def from_usage(
|
|
@@ -67,4 +69,9 @@ class LanguageModelInvocationStats(BaseModel):
|
|
|
67
69
|
token_usage: LanguageModelTokenUsage,
|
|
68
70
|
source: str,
|
|
69
71
|
) -> Self:
|
|
70
|
-
return cls(
|
|
72
|
+
return cls(
|
|
73
|
+
model_name=model_name,
|
|
74
|
+
token_usage=token_usage,
|
|
75
|
+
source=source,
|
|
76
|
+
cost_usd=calculate_invocation_cost_usd(str(model_name), token_usage),
|
|
77
|
+
)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Load model prices and calculate the USD cost of LLM invocations."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import time
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import yaml
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
10
|
+
|
|
11
|
+
from unique_toolkit.language_model.schemas import LanguageModelTokenUsage
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(f"toolkit.language_model.{__name__}")
|
|
14
|
+
|
|
15
|
+
MODEL_COSTS_FILE_ENV = "MODEL_COSTS_FILE"
|
|
16
|
+
_LITELLM_PREFIX = "litellm:"
|
|
17
|
+
_TOKENS_PER_MILLION = 1_000_000
|
|
18
|
+
|
|
19
|
+
# Safety-only bound on how long a process may keep a loaded catalog. Not the
|
|
20
|
+
# primary refresh mechanism — just ensures a long-lived worker eventually
|
|
21
|
+
# re-reads a remounted price sheet.
|
|
22
|
+
_CACHE_MAX_AGE_SECONDS = 5 * 60
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ModelCost(BaseModel):
|
|
26
|
+
"""Per-million-token prices for one language model."""
|
|
27
|
+
|
|
28
|
+
model_config = ConfigDict(extra="forbid")
|
|
29
|
+
|
|
30
|
+
input: float
|
|
31
|
+
completion: float
|
|
32
|
+
currency: str = Field(default="USD", min_length=1)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ModelCostCatalog(BaseModel):
|
|
36
|
+
"""Versioned model-price catalog rendered by the platform Helm charts."""
|
|
37
|
+
|
|
38
|
+
model_config = ConfigDict(extra="forbid", populate_by_name=True)
|
|
39
|
+
|
|
40
|
+
cost_schema_version: int = Field(alias="costSchemaVersion")
|
|
41
|
+
models: dict[str, ModelCost] = Field(min_length=1)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
_catalog_cache: dict[Path, tuple[ModelCostCatalog, float]] = {}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _parse_model_cost_catalog(path: Path) -> ModelCostCatalog:
|
|
48
|
+
payload = yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
49
|
+
catalog = ModelCostCatalog.model_validate(payload)
|
|
50
|
+
if catalog.cost_schema_version != 1:
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"Unsupported model cost schema version: {catalog.cost_schema_version}"
|
|
53
|
+
)
|
|
54
|
+
return catalog
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _load_model_cost_catalog(path: Path) -> ModelCostCatalog:
|
|
58
|
+
now = time.monotonic()
|
|
59
|
+
cached = _catalog_cache.get(path)
|
|
60
|
+
if cached is not None:
|
|
61
|
+
catalog, loaded_at = cached
|
|
62
|
+
if now - loaded_at < _CACHE_MAX_AGE_SECONDS:
|
|
63
|
+
return catalog
|
|
64
|
+
|
|
65
|
+
catalog = _parse_model_cost_catalog(path)
|
|
66
|
+
_catalog_cache[path] = (catalog, now)
|
|
67
|
+
return catalog
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def load_model_cost_catalog(path: str | Path | None = None) -> ModelCostCatalog | None:
|
|
71
|
+
"""Load and cache the configured catalog, or return None when unavailable.
|
|
72
|
+
|
|
73
|
+
Returns None when the catalog path is unset, or when the file cannot be
|
|
74
|
+
read/parsed. Cost capture must never abort a successful LLM call over a
|
|
75
|
+
missing or invalid price sheet.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
configured_path = path or os.getenv(MODEL_COSTS_FILE_ENV)
|
|
79
|
+
if configured_path is None or not str(configured_path).strip():
|
|
80
|
+
return None
|
|
81
|
+
try:
|
|
82
|
+
return _load_model_cost_catalog(Path(configured_path))
|
|
83
|
+
except Exception as exc:
|
|
84
|
+
logger.warning(
|
|
85
|
+
"Failed to load model cost catalog from %s: %s",
|
|
86
|
+
configured_path,
|
|
87
|
+
exc,
|
|
88
|
+
)
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def calculate_invocation_cost_usd(
|
|
93
|
+
model_name: str,
|
|
94
|
+
token_usage: LanguageModelTokenUsage,
|
|
95
|
+
catalog: ModelCostCatalog | None = None,
|
|
96
|
+
) -> float | None:
|
|
97
|
+
"""Calculate invocation cost using prompt and completion token totals."""
|
|
98
|
+
|
|
99
|
+
if token_usage.prompt_tokens is None or token_usage.completion_tokens is None:
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
active_catalog = catalog if catalog is not None else load_model_cost_catalog()
|
|
103
|
+
if active_catalog is None:
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
normalized_model_name = str(model_name)
|
|
107
|
+
model_cost = active_catalog.models.get(normalized_model_name)
|
|
108
|
+
if model_cost is None and normalized_model_name.startswith(_LITELLM_PREFIX):
|
|
109
|
+
model_cost = active_catalog.models.get(
|
|
110
|
+
normalized_model_name.removeprefix(_LITELLM_PREFIX)
|
|
111
|
+
)
|
|
112
|
+
if model_cost is None:
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
token_usage.prompt_tokens * model_cost.input
|
|
117
|
+
+ token_usage.completion_tokens * model_cost.completion
|
|
118
|
+
) / _TOKENS_PER_MILLION
|
|
@@ -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__(
|
|
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
|
-
|
|
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()
|
{unique_toolkit-2026.32.0.dev4.dist-info → unique_toolkit-2026.32.0.dev6.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: unique-toolkit
|
|
3
|
-
Version: 2026.32.0.
|
|
3
|
+
Version: 2026.32.0.dev6
|
|
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.
|
|
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
|
|
@@ -33,6 +33,7 @@ Requires-Dist: tenacity>=9.1.4
|
|
|
33
33
|
Requires-Dist: pydantic-core>=2.41.5
|
|
34
34
|
Requires-Dist: cachetools>=5.0,<6
|
|
35
35
|
Requires-Dist: rapidfuzz>=3.0,<4
|
|
36
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
36
37
|
Requires-Dist: fastapi>=0.133.0,<1.0.0 ; extra == 'fastapi'
|
|
37
38
|
Requires-Dist: starlette>=1.3.1,<2.0.0 ; extra == 'fastapi'
|
|
38
39
|
Requires-Dist: uvicorn>=0.37.0,<0.38 ; extra == 'fastapi'
|
|
@@ -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=
|
|
86
|
+
unique_toolkit/agentic/debug_info_manager/debug_info_manager.py,sha256=fqhbKe0WWB8nZ7CzeEcgQGMHJajIMVOpKkUWOqiDdUE,12149
|
|
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
|
|
@@ -393,7 +393,8 @@ unique_toolkit/language_model/constants.py,sha256=B-topqW0r83dkC_25DeQfnPk3n53qz
|
|
|
393
393
|
unique_toolkit/language_model/default_language_model.py,sha256=mcyuFs_V6tp52C8xJSHiEHO4kFGj6xed1gvozvCRD7k,1181
|
|
394
394
|
unique_toolkit/language_model/functions.py,sha256=uYIsVQfF4uwW6cftlKhQElMLpXNaVe_57FFobhC7R4A,23503
|
|
395
395
|
unique_toolkit/language_model/infos.py,sha256=y1JLszdCyKBUTCjPobYsB02G2WXZOGoHzQtjNqJ7EOc,140831
|
|
396
|
-
unique_toolkit/language_model/invocation_stats.py,sha256=
|
|
396
|
+
unique_toolkit/language_model/invocation_stats.py,sha256=XAM_qckI-BoMBSgDpVcxFD1cFSdi1-GHZToHz_jLynM,2491
|
|
397
|
+
unique_toolkit/language_model/model_costs.py,sha256=WM1u2fWAKpPB_creS-ew8YAzc1V8oiaCbvhpSJaO8B0,3789
|
|
397
398
|
unique_toolkit/language_model/prompt.py,sha256=JSawaLjQg3VR-E2fK8engFyJnNdk21zaO8pPIodzN4Q,3991
|
|
398
399
|
unique_toolkit/language_model/reference.py,sha256=T3wr2jP_uRg5wKVIbKqLbxJlcgxfbZQGq3XdjbQ2kSo,6984
|
|
399
400
|
unique_toolkit/language_model/schemas.py,sha256=IUmp_JMHx0czadJlmsLL9nmc-nCPmVRVLp2T5DSYsqY,35402
|
|
@@ -403,7 +404,7 @@ unique_toolkit/language_model/stream_transform.py,sha256=iNNGpLY_7G6Bpp9Y4KtbIkQ
|
|
|
403
404
|
unique_toolkit/language_model/utils.py,sha256=bPQ4l6_YO71w-zaIPanUUmtbXC1_hCvLK0tAFc3VCRc,1902
|
|
404
405
|
unique_toolkit/monitoring/__init__.py,sha256=z1gkOCLxGcg936MFuLSeWFa0tREcBFSUbV09NxilBw4,1065
|
|
405
406
|
unique_toolkit/monitoring/gunicorn.py,sha256=urTFFS5zYHKoxb1TEQHcLqb2byi-aYgFRHGwgYRzYYI,844
|
|
406
|
-
unique_toolkit/monitoring/middleware.py,sha256=
|
|
407
|
+
unique_toolkit/monitoring/middleware.py,sha256=MNsUuKgt74mSnUwttXTB_AxX546Pz2zKoAP9OewVrwQ,4531
|
|
407
408
|
unique_toolkit/monitoring/registry.py,sha256=De2Bx5QBBRJQY2MM3xtntiJ2JU2FoKkVT7rNMG7ewgA,5874
|
|
408
409
|
unique_toolkit/protocols/support.py,sha256=x5edzbBmKAUuvm3oLFh52gkgQsqR5LU7M0t8RHK0DZk,5161
|
|
409
410
|
unique_toolkit/services/__init__.py,sha256=b9Wl7Wvz1JmZDbDexIZzBoXZ0wscXuczGAH8LoL-Qak,572
|
|
@@ -418,6 +419,6 @@ unique_toolkit/short_term_memory/service.py,sha256=2JMBEBn6GoyK-a0xvdcWpPft-3C74
|
|
|
418
419
|
unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
419
420
|
unique_toolkit/smart_rules/compile.py,sha256=vRe0aMJWuVIK-VsqomgxQImzke7MDmwgSxxJfYCa7zw,1386
|
|
420
421
|
unique_toolkit/test_utilities/events.py,sha256=FLrhMh0A_mQutAUjoD0FdMcVQ4Gq31r35OVGzRjvLro,6760
|
|
421
|
-
unique_toolkit-2026.32.0.
|
|
422
|
-
unique_toolkit-2026.32.0.
|
|
423
|
-
unique_toolkit-2026.32.0.
|
|
422
|
+
unique_toolkit-2026.32.0.dev6.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
423
|
+
unique_toolkit-2026.32.0.dev6.dist-info/METADATA,sha256=x3yw3QMqvumJ5CnzTP_nw7T1cOTgg6e3567TvDVCupk,7494
|
|
424
|
+
unique_toolkit-2026.32.0.dev6.dist-info/RECORD,,
|
|
File without changes
|