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 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, completion=resp,
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.10.11"
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
- Strings are truncated at max_len.
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.10.11
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, completion=resp,
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=aUErMBjRUxOxfmsKnCMtBNIwcgAObOPu06zGy1YW9fc,18167
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=4HsjMOrE-7zuvaRSiGXxV7ZyfXQ5gLxtR3HdpLut9sk,20054
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=CWzu_yUwHYft5lvi4uY8LX3ceQvCjH8HYLbyM0xOk0s,38220
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.10.11.dist-info/METADATA,sha256=prtRZUT6xKP4qPbLFha6dIiOAaUbkKANEdBqsYRZGgY,20484
29
- trodo_python-2.10.11.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
30
- trodo_python-2.10.11.dist-info/top_level.txt,sha256=VCQu1CJWFmNsqTs1YxMcw4Mq35Tc3z3uI9RwHEXAayQ,6
31
- trodo_python-2.10.11.dist-info/RECORD,,
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,,