trodo-python 2.24.0__py3-none-any.whl → 2.25.0__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.
- trodo/__init__.py +1 -1
- trodo/_version.py +1 -1
- trodo/otel/auto_instrument.py +73 -2
- trodo/otel/prompt_trace.py +185 -1
- trodo/otel/register.py +8 -0
- trodo/prompts/types.py +6 -0
- {trodo_python-2.24.0.dist-info → trodo_python-2.25.0.dist-info}/METADATA +1 -1
- {trodo_python-2.24.0.dist-info → trodo_python-2.25.0.dist-info}/RECORD +10 -10
- {trodo_python-2.24.0.dist-info → trodo_python-2.25.0.dist-info}/WHEEL +0 -0
- {trodo_python-2.24.0.dist-info → trodo_python-2.25.0.dist-info}/top_level.txt +0 -0
trodo/__init__.py
CHANGED
trodo/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
SDK_VERSION = "2.
|
|
1
|
+
SDK_VERSION = "2.25.0"
|
trodo/otel/auto_instrument.py
CHANGED
|
@@ -14,7 +14,11 @@ from datetime import datetime, timezone
|
|
|
14
14
|
from typing import Any, Callable, Dict, Iterable, List, Optional
|
|
15
15
|
|
|
16
16
|
from .context import get_active_context
|
|
17
|
-
from .prompt_trace import
|
|
17
|
+
from .prompt_trace import (
|
|
18
|
+
prompt_attributes,
|
|
19
|
+
prompt_ref_from_fingerprint,
|
|
20
|
+
remember_prompt,
|
|
21
|
+
)
|
|
18
22
|
from .processor import TrodoSpan, TrodoSpanProcessor
|
|
19
23
|
|
|
20
24
|
# Always-on, one-shot warnings. Silent skips (missing opentelemetry-sdk) are the
|
|
@@ -313,7 +317,7 @@ def otel_span_to_trodo_span(otel_span: Any) -> Optional[TrodoSpan]:
|
|
|
313
317
|
if temperature is None:
|
|
314
318
|
temperature = attrs.get("ai.settings.temperature")
|
|
315
319
|
|
|
316
|
-
|
|
320
|
+
trodo_span = TrodoSpan(
|
|
317
321
|
span_id=span_id,
|
|
318
322
|
run_id=run_id,
|
|
319
323
|
parent_span_id=parent_span_id,
|
|
@@ -338,6 +342,26 @@ def otel_span_to_trodo_span(otel_span: Any) -> Optional[TrodoSpan]:
|
|
|
338
342
|
output=completion,
|
|
339
343
|
attributes=attrs or None,
|
|
340
344
|
)
|
|
345
|
+
# Exact-match prompt link for auto-instrumented model calls. compile()
|
|
346
|
+
# before the run leaves no run context and no carrier on this serialised
|
|
347
|
+
# input, so compare the input's content fingerprint with what compile()
|
|
348
|
+
# registered. Overrides the on_start "most recent in this run" guess, which
|
|
349
|
+
# an exact match is strictly better than. No match changes nothing.
|
|
350
|
+
if kind == "llm" and prompt:
|
|
351
|
+
ref = prompt_ref_from_fingerprint(prompt)
|
|
352
|
+
if ref:
|
|
353
|
+
trodo_span.attributes = {**(trodo_span.attributes or {}), **prompt_attributes(ref)}
|
|
354
|
+
state = _span_prompt_state.pop(id(otel_span), None)
|
|
355
|
+
if state is None and ctx is not None:
|
|
356
|
+
state = getattr(ctx, "prompt_state", None)
|
|
357
|
+
remember_prompt(state, ref)
|
|
358
|
+
return trodo_span
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
# Run prompt state captured at on_start (contextvars are often gone by on_end),
|
|
362
|
+
# keyed by id(span) and bounded so spans that never end cannot leak.
|
|
363
|
+
_SPAN_STATE_MAX = 1024
|
|
364
|
+
_span_prompt_state: "Dict[int, Any]" = {}
|
|
341
365
|
|
|
342
366
|
|
|
343
367
|
# Subclass the REAL SpanProcessor when the sdk is importable (it always is on
|
|
@@ -385,11 +409,16 @@ class _OtelAdapter(_SpanProcessorBase): # type: ignore[valid-type,misc]
|
|
|
385
409
|
current = state.get("current") if isinstance(state, dict) else None
|
|
386
410
|
for k, v in prompt_attributes(current).items():
|
|
387
411
|
set_attr(k, v)
|
|
412
|
+
if isinstance(state, dict):
|
|
413
|
+
if len(_span_prompt_state) >= _SPAN_STATE_MAX:
|
|
414
|
+
_span_prompt_state.pop(next(iter(_span_prompt_state)), None)
|
|
415
|
+
_span_prompt_state[id(span)] = state
|
|
388
416
|
except Exception:
|
|
389
417
|
pass # never break user code
|
|
390
418
|
|
|
391
419
|
def on_end(self, span: Any) -> None:
|
|
392
420
|
trodo_span = otel_span_to_trodo_span(span)
|
|
421
|
+
_span_prompt_state.pop(id(span), None)
|
|
393
422
|
if trodo_span is not None:
|
|
394
423
|
self._processor.enqueue_span(trodo_span)
|
|
395
424
|
|
|
@@ -401,6 +430,48 @@ class _OtelAdapter(_SpanProcessorBase): # type: ignore[valid-type,misc]
|
|
|
401
430
|
return True
|
|
402
431
|
|
|
403
432
|
|
|
433
|
+
class OtlpPromptLinker(_SpanProcessorBase): # type: ignore[valid-type,misc]
|
|
434
|
+
"""OTLP mode: link provider spans to a managed prompt by exact fingerprint.
|
|
435
|
+
|
|
436
|
+
In OTLP mode finished spans go straight to the exporter, so there is no
|
|
437
|
+
TrodoSpan to enrich. ``on_ending`` (opentelemetry-sdk >= 1.43) is the last
|
|
438
|
+
point where the span is still writable. Register this BEFORE the batch
|
|
439
|
+
processor. On older SDKs ``on_ending`` is never called and this is inert.
|
|
440
|
+
"""
|
|
441
|
+
|
|
442
|
+
def __init__(self) -> None:
|
|
443
|
+
try:
|
|
444
|
+
super().__init__()
|
|
445
|
+
except TypeError:
|
|
446
|
+
pass
|
|
447
|
+
|
|
448
|
+
def on_start(self, span: Any, parent_context: Any = None) -> None:
|
|
449
|
+
return None
|
|
450
|
+
|
|
451
|
+
def on_ending(self, span: Any) -> None:
|
|
452
|
+
try:
|
|
453
|
+
attrs = dict(getattr(span, "attributes", None) or {})
|
|
454
|
+
if _infer_kind(attrs) != "llm":
|
|
455
|
+
return
|
|
456
|
+
inp = _extract_input(attrs)
|
|
457
|
+
ref = prompt_ref_from_fingerprint(inp) if inp else None
|
|
458
|
+
if not ref:
|
|
459
|
+
return
|
|
460
|
+
for k, v in prompt_attributes(ref).items():
|
|
461
|
+
span.set_attribute(k, v)
|
|
462
|
+
except Exception:
|
|
463
|
+
pass # never break the export pipeline
|
|
464
|
+
|
|
465
|
+
def on_end(self, span: Any) -> None:
|
|
466
|
+
return None
|
|
467
|
+
|
|
468
|
+
def shutdown(self) -> None:
|
|
469
|
+
return None
|
|
470
|
+
|
|
471
|
+
def force_flush(self, timeout_millis: int = 30_000) -> bool:
|
|
472
|
+
return True
|
|
473
|
+
|
|
474
|
+
|
|
404
475
|
def _instrument(module_name: str, *preferred: str) -> None:
|
|
405
476
|
"""Import ``module_name`` and call ``instrument()`` on its Instrumentor.
|
|
406
477
|
|
trodo/otel/prompt_trace.py
CHANGED
|
@@ -9,13 +9,25 @@ moved to a different version.
|
|
|
9
9
|
The link is best-effort and never raises: if there is no active run context
|
|
10
10
|
(the prompt was compiled outside ``wrap_agent``), recording is a silent no-op.
|
|
11
11
|
|
|
12
|
+
A third route covers compile-before-run followed by a model call made by an
|
|
13
|
+
AUTO-INSTRUMENTED provider SDK (no run context at compile time, no carrier on
|
|
14
|
+
the span's serialised input): compile() registers a sha256 fingerprint of the
|
|
15
|
+
rendered (role, text) list in a small bounded registry, and a model span whose
|
|
16
|
+
input normalises to the same fingerprint is linked. Exact match or nothing --
|
|
17
|
+
never a "last compiled prompt wins" guess. Only hashes are retained, never
|
|
18
|
+
message text. Providers that rewrite the messages will not match, by design.
|
|
19
|
+
|
|
12
20
|
Mirrors ``sdks/trodo-node-sdk/src/otel/promptTrace.ts``.
|
|
13
21
|
"""
|
|
14
22
|
|
|
15
23
|
from __future__ import annotations
|
|
16
24
|
|
|
25
|
+
import hashlib
|
|
26
|
+
import json
|
|
27
|
+
import threading
|
|
28
|
+
import time
|
|
17
29
|
from collections import OrderedDict
|
|
18
|
-
from typing import Any, Dict, List, Optional
|
|
30
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
19
31
|
|
|
20
32
|
from .context import get_active_context
|
|
21
33
|
|
|
@@ -176,3 +188,175 @@ def _stamp_active_otel_span(ref: PromptRef) -> None:
|
|
|
176
188
|
span.set_attribute(k, v)
|
|
177
189
|
except Exception: # noqa: BLE001
|
|
178
190
|
pass # not in OTLP mode / opentelemetry not installed — no-op
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# ---------------------------------------------------------------------------
|
|
194
|
+
# Content-fingerprint registry
|
|
195
|
+
# ---------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
#: Max fingerprints kept process-wide (LRU). Keep in sync with the Node SDK.
|
|
198
|
+
FINGERPRINT_MAX_ENTRIES = 256
|
|
199
|
+
#: How long a fingerprint stays matchable. Keep in sync with the Node SDK.
|
|
200
|
+
FINGERPRINT_TTL_SECONDS = 10 * 60
|
|
201
|
+
|
|
202
|
+
# fingerprint -> (ref, registered_at). Insertion order == age order.
|
|
203
|
+
_fingerprints: "OrderedDict[str, Tuple[PromptRef, float]]" = OrderedDict()
|
|
204
|
+
_fp_lock = threading.Lock()
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _now() -> float:
|
|
208
|
+
return time.time()
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _text_of_content(content: Any) -> Optional[str]:
|
|
212
|
+
if isinstance(content, str):
|
|
213
|
+
return content
|
|
214
|
+
if not isinstance(content, list):
|
|
215
|
+
return None
|
|
216
|
+
out = ""
|
|
217
|
+
for part in content:
|
|
218
|
+
if isinstance(part, str):
|
|
219
|
+
out += part
|
|
220
|
+
elif isinstance(part, dict):
|
|
221
|
+
# {type:'text', text} (OpenAI/Anthropic/compiled), {content} (OTel GenAI parts)
|
|
222
|
+
if isinstance(part.get("text"), str):
|
|
223
|
+
out += part["text"]
|
|
224
|
+
elif part.get("type") == "text" and isinstance(part.get("content"), str):
|
|
225
|
+
out += part["content"]
|
|
226
|
+
return out
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _normalise_message(m: Any) -> Optional[Tuple[str, str]]:
|
|
230
|
+
if not isinstance(m, dict):
|
|
231
|
+
return None
|
|
232
|
+
# OpenInference indexed messages nest as {message: {role, content}}.
|
|
233
|
+
if isinstance(m.get("message"), dict):
|
|
234
|
+
m = m["message"]
|
|
235
|
+
role = m.get("role")
|
|
236
|
+
if not isinstance(role, str):
|
|
237
|
+
return None
|
|
238
|
+
content = m.get("content")
|
|
239
|
+
text = _text_of_content(content if content is not None else m.get("parts"))
|
|
240
|
+
if text is None:
|
|
241
|
+
return None
|
|
242
|
+
role = role.lower()
|
|
243
|
+
return ("system" if role == "developer" else role, text)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def normalise_messages(value: Any) -> Optional[List[Tuple[str, str]]]:
|
|
247
|
+
"""Normalise a provider request's messages into a list of (role, text).
|
|
248
|
+
|
|
249
|
+
Handles an OpenAI-style list, Anthropic ``{system, messages}`` (system
|
|
250
|
+
becomes a leading 'system' message), a CompiledPrompt, and the JSON string
|
|
251
|
+
form provider instrumentations record. None when not message-shaped.
|
|
252
|
+
"""
|
|
253
|
+
v = value
|
|
254
|
+
if hasattr(v, "messages") and not isinstance(v, (dict, list, str)):
|
|
255
|
+
v = getattr(v, "messages") # CompiledPrompt
|
|
256
|
+
if isinstance(v, (bytes, bytearray)):
|
|
257
|
+
return None
|
|
258
|
+
if isinstance(v, str):
|
|
259
|
+
t = v.strip()
|
|
260
|
+
if not t or t[0] not in "[{":
|
|
261
|
+
return None
|
|
262
|
+
try:
|
|
263
|
+
v = json.loads(t)
|
|
264
|
+
except Exception: # noqa: BLE001
|
|
265
|
+
return None
|
|
266
|
+
out: List[Tuple[str, str]] = []
|
|
267
|
+
if isinstance(v, dict):
|
|
268
|
+
items = v.get("messages")
|
|
269
|
+
if not isinstance(items, list):
|
|
270
|
+
return None
|
|
271
|
+
if v.get("system") is not None:
|
|
272
|
+
sys_text = _text_of_content(v["system"])
|
|
273
|
+
if sys_text is not None:
|
|
274
|
+
out.append(("system", sys_text))
|
|
275
|
+
elif isinstance(v, list):
|
|
276
|
+
items = v
|
|
277
|
+
else:
|
|
278
|
+
return None
|
|
279
|
+
for m in items:
|
|
280
|
+
n = _normalise_message(m)
|
|
281
|
+
if n is None:
|
|
282
|
+
return None # any non-message item: not a messages list, no guessing
|
|
283
|
+
out.append(n)
|
|
284
|
+
return out or None
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def fingerprint_messages(value: Any) -> Optional[str]:
|
|
288
|
+
"""sha256 over the normalised (role, text) list; length-prefixed."""
|
|
289
|
+
norm = normalise_messages(value)
|
|
290
|
+
if norm is None:
|
|
291
|
+
return None
|
|
292
|
+
h = hashlib.sha256()
|
|
293
|
+
for role, text in norm:
|
|
294
|
+
rb = text.encode("utf-8", "surrogatepass")
|
|
295
|
+
h.update(f"{len(role)}:{role}|{len(rb)}:".encode("utf-8"))
|
|
296
|
+
h.update(rb)
|
|
297
|
+
h.update(b";")
|
|
298
|
+
return h.hexdigest()
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _prune(now: float) -> None:
|
|
302
|
+
while _fingerprints:
|
|
303
|
+
key = next(iter(_fingerprints))
|
|
304
|
+
if now - _fingerprints[key][1] > FINGERPRINT_TTL_SECONDS:
|
|
305
|
+
del _fingerprints[key]
|
|
306
|
+
else:
|
|
307
|
+
break # insertion order == age order, so the rest are younger
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def register_compiled_messages(messages: Any, ref: PromptRef) -> None:
|
|
311
|
+
"""Register the rendered messages of a just-compiled prompt.
|
|
312
|
+
|
|
313
|
+
Best-effort, never raises, retains only a hash. Two prompt versions that
|
|
314
|
+
render byte-identical content collide on purpose (the messages cannot tell
|
|
315
|
+
them apart); the most recent compile wins.
|
|
316
|
+
"""
|
|
317
|
+
try:
|
|
318
|
+
if not ref or not ref.get("name"):
|
|
319
|
+
return
|
|
320
|
+
fp = fingerprint_messages(messages)
|
|
321
|
+
if fp is None:
|
|
322
|
+
return
|
|
323
|
+
now = _now()
|
|
324
|
+
with _fp_lock:
|
|
325
|
+
_prune(now)
|
|
326
|
+
_fingerprints.pop(fp, None)
|
|
327
|
+
_fingerprints[fp] = (ref, now)
|
|
328
|
+
while len(_fingerprints) > FINGERPRINT_MAX_ENTRIES:
|
|
329
|
+
_fingerprints.popitem(last=False)
|
|
330
|
+
except Exception: # noqa: BLE001
|
|
331
|
+
pass # traceability must never break compile
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def prompt_ref_from_fingerprint(value: Any) -> Optional[PromptRef]:
|
|
335
|
+
"""Exact-match lookup for a provider span's input.
|
|
336
|
+
|
|
337
|
+
Zero cost when nothing has been compiled: the input is never parsed.
|
|
338
|
+
"""
|
|
339
|
+
if not _fingerprints or value is None:
|
|
340
|
+
return None
|
|
341
|
+
try:
|
|
342
|
+
with _fp_lock:
|
|
343
|
+
_prune(_now())
|
|
344
|
+
if not _fingerprints:
|
|
345
|
+
return None
|
|
346
|
+
fp = fingerprint_messages(value)
|
|
347
|
+
if fp is None:
|
|
348
|
+
return None
|
|
349
|
+
with _fp_lock:
|
|
350
|
+
hit = _fingerprints.get(fp)
|
|
351
|
+
return hit[0] if hit else None
|
|
352
|
+
except Exception: # noqa: BLE001
|
|
353
|
+
return None
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def _reset_fingerprints_for_tests() -> None:
|
|
357
|
+
with _fp_lock:
|
|
358
|
+
_fingerprints.clear()
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def _fingerprint_count_for_tests() -> int:
|
|
362
|
+
return len(_fingerprints)
|
trodo/otel/register.py
CHANGED
|
@@ -44,6 +44,12 @@ _registered: Optional[RegisterOtelResult] = None
|
|
|
44
44
|
_otlp_processor: Optional[Any] = None
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
def _prompt_linker() -> Any:
|
|
48
|
+
from .auto_instrument import OtlpPromptLinker
|
|
49
|
+
|
|
50
|
+
return OtlpPromptLinker()
|
|
51
|
+
|
|
52
|
+
|
|
47
53
|
def flush_otlp_pipeline() -> None:
|
|
48
54
|
"""Force-flush the 'otlp'-mode export pipeline. No-op in 'trodo' mode."""
|
|
49
55
|
if _otlp_processor is not None:
|
|
@@ -172,6 +178,7 @@ def _setup_otlp_mode(
|
|
|
172
178
|
if existing_class not in ("ProxyTracerProvider", "NoOpTracerProvider"):
|
|
173
179
|
# User's pipeline owns the provider — just attach our processor.
|
|
174
180
|
if hasattr(existing, "add_span_processor"):
|
|
181
|
+
existing.add_span_processor(_prompt_linker())
|
|
175
182
|
existing.add_span_processor(batch_processor)
|
|
176
183
|
# Auto-instrumentations are presumably already wired by the existing
|
|
177
184
|
# pipeline. We still set the transport so wrap_agent dispatches.
|
|
@@ -189,6 +196,7 @@ def _setup_otlp_mode(
|
|
|
189
196
|
"trodo.sdk.mode": "otlp",
|
|
190
197
|
})
|
|
191
198
|
provider = TracerProvider(resource=resource)
|
|
199
|
+
provider.add_span_processor(_prompt_linker())
|
|
192
200
|
provider.add_span_processor(batch_processor)
|
|
193
201
|
otel_trace.set_tracer_provider(provider)
|
|
194
202
|
|
trodo/prompts/types.py
CHANGED
|
@@ -95,6 +95,7 @@ class ManagedPrompt:
|
|
|
95
95
|
try:
|
|
96
96
|
from ..otel.prompt_trace import (
|
|
97
97
|
record_compiled_prompt,
|
|
98
|
+
register_compiled_messages,
|
|
98
99
|
tag_carrier_with_prompt,
|
|
99
100
|
)
|
|
100
101
|
|
|
@@ -113,6 +114,11 @@ class ManagedPrompt:
|
|
|
113
114
|
# since callers pass one or the other.
|
|
114
115
|
tag_carrier_with_prompt(compiled, ref)
|
|
115
116
|
tag_carrier_with_prompt(getattr(compiled, "messages", None), ref)
|
|
117
|
+
# (3) a content fingerprint, so an auto-instrumented provider
|
|
118
|
+
# span handed exactly these messages (compiled before the
|
|
119
|
+
# run; no carrier on the serialised input) links by exact
|
|
120
|
+
# match.
|
|
121
|
+
register_compiled_messages(getattr(compiled, "messages", None), ref)
|
|
116
122
|
except Exception: # noqa: BLE001
|
|
117
123
|
pass # traceability must never break compile
|
|
118
124
|
return compiled
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
trodo/__init__.py,sha256=
|
|
2
|
-
trodo/_version.py,sha256=
|
|
1
|
+
trodo/__init__.py,sha256=ftnDcQvvOi9VnJoynkzzZCNwqHRhC7pbXS836bTwizo,27913
|
|
2
|
+
trodo/_version.py,sha256=50fFgmf3lAeDxqh-LvVujTqoWvZoG7fTVAY5gKvc-yQ,23
|
|
3
3
|
trodo/client.py,sha256=6k6HeKrGV2720kK17--u6BjbhAUFZNUzpd9yEYiaQOs,24137
|
|
4
4
|
trodo/types.py,sha256=eySgUvCXROG2TxtxgiU0MNr5iH0DEcduK8bmYtTKG44,3138
|
|
5
5
|
trodo/user_context.py,sha256=uHCI2WYoOI3cNwdIUEuZdX4VYSu14pzm348ksn6hn_c,8195
|
|
@@ -20,18 +20,18 @@ trodo/managers/people_manager.py,sha256=mMVnx40Mlifx6NGgChvohC9ViK6dQu2mkXNHbV8p
|
|
|
20
20
|
trodo/managers/prompt_manager.py,sha256=qFVHgh7PW-SlgHp62ybirdYsJBg-fcQVMTTU3jPqFvo,13435
|
|
21
21
|
trodo/managers/user_manager.py,sha256=faJYX3CHrD7ulYiShV7FhThSMX9aJ3kdOgU4Qtdy5FM,2844
|
|
22
22
|
trodo/otel/__init__.py,sha256=yiRFXWUU45bAM2CV37XeO7zf1hmnmjufdP4XO50yEyE,624
|
|
23
|
-
trodo/otel/auto_instrument.py,sha256=
|
|
23
|
+
trodo/otel/auto_instrument.py,sha256=M5YVpFoPqn9bQohDFM7yyADrVGJPqFBToJLnBO7nOSA,29029
|
|
24
24
|
trodo/otel/context.py,sha256=Jd0aTc0Q-1dM5kXXinhZD8YtnpdKgvneNiODyboVGKY,1418
|
|
25
25
|
trodo/otel/helpers.py,sha256=XOMWcgZHaq5SQbkFxDaXPE4CDFjn01xjJmJ1vIxvwpw,20730
|
|
26
26
|
trodo/otel/processor.py,sha256=WgtLdXT9gpR4TZ_9iuoX-kBP4UN4nelCtoDnogtW_Qc,8289
|
|
27
|
-
trodo/otel/prompt_trace.py,sha256
|
|
28
|
-
trodo/otel/register.py,sha256=
|
|
27
|
+
trodo/otel/prompt_trace.py,sha256=-kywRBU0Q2gWntS9HU-P5i7Kc5U-IJU7UVLjkdC2jw4,13593
|
|
28
|
+
trodo/otel/register.py,sha256=7X3AfFQ5YvHGOUmdq8J38LQGvOJFPD907yT3uAp90lI,9607
|
|
29
29
|
trodo/otel/transport.py,sha256=hzZz8gwSMGJ8CxdijmLn1Ljt18owr9XTWy13DLbwYbw,2441
|
|
30
30
|
trodo/otel/wrap_agent.py,sha256=qx2jnuVIMZTv-PpRt-qFWqMWz1YO4jqY5pwgBQirVDo,44519
|
|
31
31
|
trodo/prompts/__init__.py,sha256=yunNc8WkTSfEEkRR-NZWXAt_ZB3Ee2INRh-4RE7uHj4,806
|
|
32
32
|
trodo/prompts/compile.py,sha256=XoKDKz6Yaofhrk-87AVW-SnwbiXLvfp161AL2bAFzNY,8802
|
|
33
33
|
trodo/prompts/template.py,sha256=eGO2ZVNOw6JK7Ffj1UCYDMJFYyX5qQW2POlmFJSQpAo,9975
|
|
34
|
-
trodo/prompts/types.py,sha256=
|
|
34
|
+
trodo/prompts/types.py,sha256=Mjn6x6f7dI3TIVHSvzM2xs_gchuh5sCwUfBsW-YLVm0,5654
|
|
35
35
|
trodo/queue/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
36
|
trodo/queue/batch_flusher.py,sha256=4Lg6T3Urwi9U0Q4FpFGPmjDYKg4ZliCTR-ND8BJvWaY,1298
|
|
37
37
|
trodo/queue/event_queue.py,sha256=EVFZrhlq_kwC3jJ2GK0wMhHISf9UzLCZNDnT_aZ2I2A,872
|
|
@@ -40,7 +40,7 @@ trodo/session/server_session.py,sha256=McsudEiq33XDq3nqxgzBcUvIjQxCMscwEuAPnYXrT
|
|
|
40
40
|
trodo/session/session_manager.py,sha256=7ht5LeeZX1HLLfPeNV_a8NkXGbHl3up0CRtCGr3EzjQ,2995
|
|
41
41
|
trodo/util/__init__.py,sha256=Z9c4rPPdKg06Kk3byKheDqksSgR4WNvS475oQ5sNljc,54
|
|
42
42
|
trodo/util/lru.py,sha256=QIsM7s6J_E9ZjiXatSyReZIEeGypTAcgrPWWy8YsRa4,2465
|
|
43
|
-
trodo_python-2.
|
|
44
|
-
trodo_python-2.
|
|
45
|
-
trodo_python-2.
|
|
46
|
-
trodo_python-2.
|
|
43
|
+
trodo_python-2.25.0.dist-info/METADATA,sha256=_e2LmqFYjaZQdhh6Nr-Uj9wr_f88ZYLZDlD17N431as,25308
|
|
44
|
+
trodo_python-2.25.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
45
|
+
trodo_python-2.25.0.dist-info/top_level.txt,sha256=VCQu1CJWFmNsqTs1YxMcw4Mq35Tc3z3uI9RwHEXAayQ,6
|
|
46
|
+
trodo_python-2.25.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|