trodo-python 2.10.11__py3-none-any.whl → 2.11.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 +3 -2
- trodo/otel/helpers.py +13 -1
- trodo/otel/wrap_agent.py +16 -5
- {trodo_python-2.10.11.dist-info → trodo_python-2.11.0.dist-info}/METADATA +27 -2
- {trodo_python-2.10.11.dist-info → trodo_python-2.11.0.dist-info}/RECORD +7 -7
- {trodo_python-2.10.11.dist-info → trodo_python-2.11.0.dist-info}/WHEEL +0 -0
- {trodo_python-2.10.11.dist-info → trodo_python-2.11.0.dist-info}/top_level.txt +0 -0
trodo/__init__.py
CHANGED
|
@@ -19,7 +19,8 @@ Raw-HTTP LLM caller (no OTel integration for your client):
|
|
|
19
19
|
model='gemini-2.5-flash', provider='google',
|
|
20
20
|
input_tokens=resp['usageMetadata']['promptTokenCount'],
|
|
21
21
|
output_tokens=resp['usageMetadata']['candidatesTokenCount'],
|
|
22
|
-
prompt=body,
|
|
22
|
+
prompt=body['messages'], # the chat-message list sent to the model
|
|
23
|
+
completion=resp,
|
|
23
24
|
)
|
|
24
25
|
|
|
25
26
|
Custom tool:
|
|
@@ -40,7 +41,7 @@ Downstream microservice (join the caller's run instead of making a new one):
|
|
|
40
41
|
|
|
41
42
|
from __future__ import annotations
|
|
42
43
|
|
|
43
|
-
__version__ = "2.
|
|
44
|
+
__version__ = "2.11.0"
|
|
44
45
|
|
|
45
46
|
from typing import Any, Callable, Dict, List, Optional, Union
|
|
46
47
|
|
trodo/otel/helpers.py
CHANGED
|
@@ -297,6 +297,12 @@ def llm(
|
|
|
297
297
|
fall back to scalar-only extraction, or ``extract_usage_map=lambda r: {..}``
|
|
298
298
|
to build the map yourself.
|
|
299
299
|
|
|
300
|
+
The wrapped function's arguments become the span input. For the AI-score
|
|
301
|
+
detectors to see per-role content, pass the model call's chat-message list
|
|
302
|
+
(``[{"role", "content"}, ...]`` — roles system/user/assistant/tool +
|
|
303
|
+
``context`` for RAG docs) so the span input is exactly what the model
|
|
304
|
+
receives.
|
|
305
|
+
|
|
300
306
|
Usage::
|
|
301
307
|
|
|
302
308
|
answer = trodo.llm(
|
|
@@ -470,12 +476,18 @@ def track_llm_call(
|
|
|
470
476
|
``usage_details`` map, or a raw provider ``usage`` object to auto-extract
|
|
471
477
|
from (e.g. ``resp['usage']`` or ``resp['usageMetadata']``).
|
|
472
478
|
|
|
479
|
+
``prompt`` — prefer the chat-message list you sent to the model
|
|
480
|
+
(``[{"role", "content"}, ...]``, roles system/user/assistant/tool +
|
|
481
|
+
``context`` for RAG docs, any order, multiple per role). The backend
|
|
482
|
+
embeds each role separately, which powers the AI-score detectors. A plain
|
|
483
|
+
string or dict is stored as one opaque input.
|
|
484
|
+
|
|
473
485
|
Usage:
|
|
474
486
|
resp = httpx.post(url, json=body).json()
|
|
475
487
|
trodo.track_llm_call(
|
|
476
488
|
model='claude-sonnet-4', provider='anthropic',
|
|
477
489
|
usage=resp['usage'], # cache fields captured automatically
|
|
478
|
-
prompt=body, completion=resp,
|
|
490
|
+
prompt=body['messages'], completion=resp,
|
|
479
491
|
)
|
|
480
492
|
"""
|
|
481
493
|
if get_active_context() is None:
|
trodo/otel/wrap_agent.py
CHANGED
|
@@ -29,7 +29,7 @@ import time
|
|
|
29
29
|
import traceback
|
|
30
30
|
import uuid
|
|
31
31
|
from datetime import datetime, timezone
|
|
32
|
-
from typing import Any, Callable, Dict, Optional, Union
|
|
32
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
33
33
|
|
|
34
34
|
from .context import ActiveSpanContext, get_active_context, run_with_context
|
|
35
35
|
from .processor import TrodoSpanProcessor, TrodoRun, TrodoSpan
|
|
@@ -149,16 +149,19 @@ def _resolve_error(handle, exc_type, exc, tb) -> Dict[str, Optional[str]]:
|
|
|
149
149
|
"status_code": None, "stack_trace": None, "level": None}
|
|
150
150
|
|
|
151
151
|
|
|
152
|
-
def _prepare_value(value: Any, max_len: int = _MAX_VALUE_LEN) -> Optional[Union[str, Dict[str, Any]]]:
|
|
152
|
+
def _prepare_value(value: Any, max_len: int = _MAX_VALUE_LEN) -> Optional[Union[str, Dict[str, Any], List[Any]]]:
|
|
153
153
|
"""Prepare a value for storage in the JSONB input/output column.
|
|
154
154
|
|
|
155
|
-
Dicts/lists pass through as-is (stored as JSONB objects/arrays).
|
|
156
|
-
|
|
155
|
+
Dicts/lists pass through as-is (stored as JSONB objects/arrays). For LLM
|
|
156
|
+
span inputs, prefer a chat-message list ``[{"role", "content"}, ...]``
|
|
157
|
+
(roles system/user/assistant/tool + ``context`` for RAG docs, any order,
|
|
158
|
+
multiple per role) — the backend embeds each role separately and the
|
|
159
|
+
AI-score detectors key on them. Strings are truncated at max_len.
|
|
157
160
|
Everything else is JSON-serialised then truncated.
|
|
158
161
|
"""
|
|
159
162
|
if value is None:
|
|
160
163
|
return None
|
|
161
|
-
if isinstance(value, dict):
|
|
164
|
+
if isinstance(value, (dict, list)):
|
|
162
165
|
return value
|
|
163
166
|
if isinstance(value, str):
|
|
164
167
|
return value[:max_len] if len(value) > max_len else value
|
|
@@ -248,6 +251,9 @@ class RunHandle:
|
|
|
248
251
|
self.error_type: Optional[str] = None
|
|
249
252
|
|
|
250
253
|
def set_input(self, value: Any) -> None:
|
|
254
|
+
"""Set the run input. Prefer a chat-message list
|
|
255
|
+
``[{"role": "user", "content": ...}, ...]`` — the backend embeds the
|
|
256
|
+
user messages as the run's semantic input."""
|
|
251
257
|
self.input = _prepare_value(value)
|
|
252
258
|
|
|
253
259
|
def set_output(self, value: Any) -> None:
|
|
@@ -337,6 +343,11 @@ class SpanHandle:
|
|
|
337
343
|
return self.error_message is not None or self.error_type is not None
|
|
338
344
|
|
|
339
345
|
def set_input(self, value: Any) -> None:
|
|
346
|
+
"""Set the span input. For LLM spans prefer a chat-message list
|
|
347
|
+
``[{"role", "content"}, ...]`` (roles system/user/assistant/tool +
|
|
348
|
+
``context`` for RAG docs, any order, multiple per role) — each role is
|
|
349
|
+
embedded separately and powers the AI-score detectors. Anything else
|
|
350
|
+
is stored as one opaque input."""
|
|
340
351
|
self.input = _prepare_value(value)
|
|
341
352
|
|
|
342
353
|
def set_output(self, value: Any) -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trodo-python
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.11.0
|
|
4
4
|
Summary: Trodo Analytics SDK for Python — server-side event tracking
|
|
5
5
|
License: ISC
|
|
6
6
|
Keywords: analytics,tracking,trodo,server-side
|
|
@@ -250,6 +250,30 @@ search = trodo.retrieval('vector_search', vector_search)
|
|
|
250
250
|
docs = search(query)
|
|
251
251
|
```
|
|
252
252
|
|
|
253
|
+
### LLM span input: the chat-message list
|
|
254
|
+
|
|
255
|
+
For LLM spans, set the input to the **same messages you send to the model** —
|
|
256
|
+
a chat-message list, not one blob:
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
span.set_input([
|
|
260
|
+
{"role": "system", "content": system_prompt}, # rules & role
|
|
261
|
+
{"role": "context", "content": retrieved_docs}, # RAG docs (Trodo extension)
|
|
262
|
+
{"role": "user", "content": "Where is my order?"},
|
|
263
|
+
{"role": "assistant", "content": None, "tool_calls": [...]},
|
|
264
|
+
{"role": "tool", "content": '{"status": "shipped"}', "tool_call_id": "c1"},
|
|
265
|
+
{"role": "user", "content": "When will it arrive?"}, # multiple turns are fine
|
|
266
|
+
])
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Roles: the standard `system` / `user` / `assistant` / `tool` plus `context` —
|
|
270
|
+
a Trodo extension for RAG / retrieved documents. Any order, any number per
|
|
271
|
+
role; aliases `developer` / `model` / `function` normalise automatically.
|
|
272
|
+
Trodo embeds the input as a whole **and each role separately**, which powers
|
|
273
|
+
the AI-score detectors (system → rule adherence; user → trajectory/echo;
|
|
274
|
+
context else tool+assistant → grounding, contradiction, factual retention).
|
|
275
|
+
A plain string still works and is embedded as one vector.
|
|
276
|
+
|
|
253
277
|
### Raw-HTTP escape hatches
|
|
254
278
|
|
|
255
279
|
If your LLM client isn't OTel-instrumented and you can't wrap it as a
|
|
@@ -261,7 +285,8 @@ trodo.track_llm_call(
|
|
|
261
285
|
model='gemini-2.5-flash', provider='google',
|
|
262
286
|
input_tokens=resp['usageMetadata']['promptTokenCount'],
|
|
263
287
|
output_tokens=resp['usageMetadata']['candidatesTokenCount'],
|
|
264
|
-
prompt=body,
|
|
288
|
+
prompt=body['messages'], # the chat-message list sent to the model
|
|
289
|
+
completion=resp,
|
|
265
290
|
)
|
|
266
291
|
```
|
|
267
292
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
trodo/__init__.py,sha256=
|
|
1
|
+
trodo/__init__.py,sha256=fZ9qn_ObzXX7WLpEnhnlivN6sASv-u9vYXw_IWGNkf0,18229
|
|
2
2
|
trodo/client.py,sha256=dhGiOJmxdWmXDDDabHN865_cAxttJs9LWPeDwEFbAwI,19107
|
|
3
3
|
trodo/types.py,sha256=eySgUvCXROG2TxtxgiU0MNr5iH0DEcduK8bmYtTKG44,3138
|
|
4
4
|
trodo/user_context.py,sha256=9la6azzwEanVmdP4ps_xMoufbeWVeIGU-M8ychmgajg,7859
|
|
@@ -14,18 +14,18 @@ trodo/managers/people_manager.py,sha256=mMVnx40Mlifx6NGgChvohC9ViK6dQu2mkXNHbV8p
|
|
|
14
14
|
trodo/otel/__init__.py,sha256=yiRFXWUU45bAM2CV37XeO7zf1hmnmjufdP4XO50yEyE,624
|
|
15
15
|
trodo/otel/auto_instrument.py,sha256=hJesQMOOp86U66PampcGNINmBVvwVuU_WHVrbUMauug,20895
|
|
16
16
|
trodo/otel/context.py,sha256=iJ1rE42-SbO8VZHAxhIl2ZJXgNwLIVps5xLg8GKgfFc,1165
|
|
17
|
-
trodo/otel/helpers.py,sha256=
|
|
17
|
+
trodo/otel/helpers.py,sha256=XOMWcgZHaq5SQbkFxDaXPE4CDFjn01xjJmJ1vIxvwpw,20730
|
|
18
18
|
trodo/otel/processor.py,sha256=aqcTmzTw9cESgIp829pu_XCa5_dG_2MaeJNsqJZeqQU,7495
|
|
19
19
|
trodo/otel/register.py,sha256=bV_ePTfUvPugig2GZnylhzxi2QfoPWs96A9mGLPMrSQ,9387
|
|
20
20
|
trodo/otel/transport.py,sha256=hzZz8gwSMGJ8CxdijmLn1Ljt18owr9XTWy13DLbwYbw,2441
|
|
21
|
-
trodo/otel/wrap_agent.py,sha256=
|
|
21
|
+
trodo/otel/wrap_agent.py,sha256=_nFDhxPyl0RlNKj29cBNeRc_zAY5FUiHvTiNUWxTt0M,39049
|
|
22
22
|
trodo/queue/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
23
|
trodo/queue/batch_flusher.py,sha256=4Lg6T3Urwi9U0Q4FpFGPmjDYKg4ZliCTR-ND8BJvWaY,1298
|
|
24
24
|
trodo/queue/event_queue.py,sha256=EVFZrhlq_kwC3jJ2GK0wMhHISf9UzLCZNDnT_aZ2I2A,872
|
|
25
25
|
trodo/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
26
|
trodo/session/server_session.py,sha256=McsudEiq33XDq3nqxgzBcUvIjQxCMscwEuAPnYXrTjs,2136
|
|
27
27
|
trodo/session/session_manager.py,sha256=JrgH1VeicmtlxPR4dXEuJbxhi23OelkgwW3-9Slv80o,2525
|
|
28
|
-
trodo_python-2.
|
|
29
|
-
trodo_python-2.
|
|
30
|
-
trodo_python-2.
|
|
31
|
-
trodo_python-2.
|
|
28
|
+
trodo_python-2.11.0.dist-info/METADATA,sha256=kfO84FYYYUexa79fxuSjkj3ou4NJU1xTVeFAHYewz7M,21698
|
|
29
|
+
trodo_python-2.11.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
30
|
+
trodo_python-2.11.0.dist-info/top_level.txt,sha256=VCQu1CJWFmNsqTs1YxMcw4Mq35Tc3z3uI9RwHEXAayQ,6
|
|
31
|
+
trodo_python-2.11.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|