trodo-python 1.2.0__py3-none-any.whl → 2.2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trodo-python
3
- Version: 1.2.0
3
+ Version: 2.2.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
@@ -136,9 +136,188 @@ trodo.set_group('user-123', 'company', 'acme')
136
136
 
137
137
  ---
138
138
 
139
- ## Agent Analytics
139
+ ## AI Agent Tracing (recommended)
140
140
 
141
- Track every step of your LLM agents. Each call counts as one event toward your plan limit.
141
+ One wrap around your agent captures every LLM call, tool call, and
142
+ nested step as a tree of spans — token counts, costs, inputs, outputs,
143
+ errors. Works with any stack: OpenAI, Anthropic, LangChain, LlamaIndex,
144
+ Gemini, raw HTTP, custom tools. Cost is derived server-side from
145
+ `(provider, model)` — the SDK only sends tokens.
146
+
147
+ ### 30-second quickstart
148
+
149
+ ```python
150
+ import trodo
151
+ trodo.init(site_id='your-site-id') # auto-instrument on by default
152
+
153
+ with trodo.wrap_agent('customer-support',
154
+ distinct_id=user_id,
155
+ conversation_id=session_id) as run:
156
+ run.set_input({'query': 'where did sales drop'})
157
+ answer = agent.run(query) # OpenAI/Anthropic/LangChain auto-captured
158
+ run.set_output(answer)
159
+ ```
160
+
161
+ Open the Agent Runs dashboard — the row shows tokens in/out, cost,
162
+ span count, tool count, error count, plus the full trace tree.
163
+
164
+ ### Auto-instrumentation
165
+
166
+ `trodo.init()` calls `enable_auto_instrument()` which registers every
167
+ installed OpenTelemetry instrumentor. No extra code required.
168
+
169
+ | Framework | Install |
170
+ |-----------|---------|
171
+ | OpenAI | `pip install opentelemetry-instrumentation-openai` |
172
+ | Anthropic | `pip install opentelemetry-instrumentation-anthropic` |
173
+ | LangChain | `pip install opentelemetry-instrumentation-langchain` |
174
+ | LlamaIndex | `pip install opentelemetry-instrumentation-llama-index` |
175
+ | Google Gemini | `pip install opentelemetry-instrumentation-google-generativeai` |
176
+ | Vertex AI | `pip install opentelemetry-instrumentation-vertexai` |
177
+ | Bedrock | `pip install opentelemetry-instrumentation-bedrock` |
178
+ | Cohere | `pip install opentelemetry-instrumentation-cohere` |
179
+ | Mistral | `pip install opentelemetry-instrumentation-mistralai` |
180
+ | Haystack | `pip install opentelemetry-instrumentation-haystack` |
181
+ | httpx / requests | bundled — generic HTTP spans for raw-HTTP callers |
182
+
183
+ Opt out with `trodo.init(site_id=..., auto_instrument=False)`.
184
+
185
+ ### Span helpers
186
+
187
+ Typed function wrappers for custom code — every call becomes a span
188
+ with the args auto-captured as `input`, return value as `output`,
189
+ exception as `error`. Dual-form: helper **and** decorator.
190
+
191
+ ```python
192
+ # trace — generic span
193
+ prepared = trodo.trace('prepare', prepare_fn)(payload)
194
+
195
+ @trodo.trace('step')
196
+ def step(): ...
197
+
198
+ # tool — tool span (auto tool_name, kind='tool')
199
+ run_funnel = trodo.tool('run_funnel_query', run_funnel_query)
200
+ result = run_funnel(team_id=1, preset='day7')
201
+
202
+ @trodo.tool(name='fetch_user')
203
+ async def fetch_user(uid): ...
204
+
205
+ # llm — LLM span, auto-extracts OpenAI / Anthropic / Gemini usage
206
+ answer = trodo.llm(
207
+ 'answer', call_openai, model='gpt-4o-mini', provider='openai',
208
+ )(messages)
209
+ # Records input_tokens / output_tokens from response['usage'].
210
+
211
+ # retrieval — vector search / RAG retriever span
212
+ search = trodo.retrieval('vector_search', vector_search)
213
+ docs = search(query)
214
+ ```
215
+
216
+ ### Raw-HTTP escape hatches
217
+
218
+ If your LLM client isn't OTel-instrumented and you can't wrap it as a
219
+ function, record a span post-hoc:
220
+
221
+ ```python
222
+ resp = httpx.post(url, json=body).json()
223
+ trodo.track_llm_call(
224
+ model='gemini-2.5-flash', provider='google',
225
+ input_tokens=resp['usageMetadata']['promptTokenCount'],
226
+ output_tokens=resp['usageMetadata']['candidatesTokenCount'],
227
+ prompt=body, completion=resp,
228
+ )
229
+ ```
230
+
231
+ For advanced cases, get a raw OTel tracer — the Trodo processor is
232
+ already subscribed:
233
+
234
+ ```python
235
+ tracer = trodo.get_tracer('my.module')
236
+ with tracer.start_as_current_span('custom') as sp:
237
+ sp.set_attribute('gen_ai.system', 'my-llm')
238
+ ```
239
+
240
+ ### Cross-service runs
241
+
242
+ When one service calls another, the downstream service **joins** the
243
+ caller's run instead of creating its own. All spans nest under a single
244
+ timeline in the dashboard.
245
+
246
+ ```python
247
+ # Caller (FastAPI / Flask / Django / …) — outbound:
248
+ import httpx
249
+ httpx.post(url, headers=trodo.propagation_headers(), json=body)
250
+
251
+ # Downstream (FastAPI):
252
+ from fastapi import FastAPI
253
+ app = FastAPI()
254
+ app.middleware('http')(trodo.fastapi_middleware())
255
+ # Every LLM call / @tool / trace helper inside handlers now nests under
256
+ # the caller's run — no extra wiring.
257
+
258
+ # Or manually:
259
+ with trodo.join_run(
260
+ run_id=headers['x-trodo-run-id'],
261
+ parent_span_id=headers['x-trodo-parent-span-id'],
262
+ ):
263
+ ...
264
+ ```
265
+
266
+ ### Long-lived sessions across processes — `start_run` / `end_run`
267
+
268
+ `wrap_agent` is a context manager — it opens *and* closes the run in one
269
+ call stack. For sessions that live across many HTTP requests (an MCP
270
+ server, a websocket-pinned chat, scheduled jobs that resume on different
271
+ workers), use `start_run` to open the run from one process and `end_run`
272
+ to finalise it later. Between the two, any process can use `join_run` to
273
+ add child spans. Same `run_id` threads through everything.
274
+
275
+ ```python
276
+ # Process A — open the run for an MCP session.
277
+ run_id = trodo.start_run(
278
+ 'external_mcp_session',
279
+ distinct_id=str(user_id),
280
+ conversation_id=mcp_session_id,
281
+ )
282
+ redis.set(f"mcp:run:{mcp_session_id}", run_id, ex=3600)
283
+
284
+ # Process B (later, possibly a different worker) — append a tool span.
285
+ run_id = redis.get(f"mcp:run:{mcp_session_id}").decode()
286
+ with trodo.join_run(run_id, name='tool.run_funnel_query', kind='tool') as span:
287
+ span.set_input(args)
288
+ span.set_output(result)
289
+
290
+ # When the session ends (timeout sweeper, explicit close):
291
+ trodo.end_run(run_id, status='ok')
292
+ ```
293
+
294
+ ### Conversation binding & feedback
295
+
296
+ ```python
297
+ with trodo.wrap_agent(
298
+ 'chat', distinct_id=user_id, conversation_id=session_id,
299
+ ) as run:
300
+ ...
301
+ # Later:
302
+ trodo.feedback(run.run_id, satisfaction='positive', rating=5)
303
+ ```
304
+
305
+ ### Cookbook
306
+
307
+ Runnable scenarios that double as integration tests live in
308
+ `sandbox/scenarios/` — `span_helpers.py`, `raw_http.py`,
309
+ `custom_tools.py`, `cross_service.py`, `concurrent_100.py`,
310
+ `long_run.py`, plus opt-in `openai_auto.py`, `anthropic_auto.py`,
311
+ `langchain_chain.py`. Run them with
312
+ `python -m sandbox.run_all` from the SDK root.
313
+
314
+ ---
315
+
316
+ ## Agent Analytics (legacy event-based API)
317
+
318
+ The older per-event API below is still supported but superseded by
319
+ `wrap_agent` + span helpers above. Use it only if you're already wired
320
+ into it; new integrations should prefer the tracing API.
142
321
 
143
322
  **Before you start:** register your agent in **Integrations → AI Agents** in the dashboard to get an `agent_id` (`agt_xxxxxxxx`).
144
323
 
@@ -161,6 +340,7 @@ trodo.track_agent_call(AgentCallProps(
161
340
  provider='anthropic',
162
341
  system_prompt_version='v2', # optional — track prompt iterations
163
342
  distinct_id=user_id, # optional — link to a Trodo user
343
+ metadata={'thread_source': 'slack', 'locale': 'en'}, # optional — agent_calls.metadata JSONB
164
344
  ))
165
345
  ```
166
346
 
@@ -1,23 +1,29 @@
1
- trodo/__init__.py,sha256=xbXCxPZtQfCgJeB1Yg_Rv067DOXCDztLXpo-f8XkL68,4837
2
- trodo/client.py,sha256=FkcUYymCMVJA9U509I5Lba9EZAYkjHb5-Fpoi3fI3oc,12122
3
- trodo/types.py,sha256=aX8aQrENDwSpouxqshHxDbjjzkvAveeClSSL0TDEygQ,4610
1
+ trodo/__init__.py,sha256=lo_QFNEk_4ylQ_4-v0RBfdA8ed30M5QjlA7ylJAiAjU,12870
2
+ trodo/client.py,sha256=x_HjIyLMowUU-w73GYETEZWWHKjWBC-4LpG6lfLBRmU,15499
3
+ trodo/types.py,sha256=eySgUvCXROG2TxtxgiU0MNr5iH0DEcduK8bmYtTKG44,3138
4
4
  trodo/user_context.py,sha256=9la6azzwEanVmdP4ps_xMoufbeWVeIGU-M8ychmgajg,7859
5
5
  trodo/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  trodo/api/async_client.py,sha256=rZN4aJ2QiKyrHBK260bApCUB9JaMWU6BQtzoSJZh7xk,3408
7
- trodo/api/endpoints.py,sha256=JaJ5hjjKPi4aO6R25oasQTasEptg1FFFkDUIqO7uOXs,835
8
- trodo/api/http_client.py,sha256=h1p37Z-kmVxwCnodb5j8MXiP-cRwOKiF2lznoIW1bFI,3252
7
+ trodo/api/endpoints.py,sha256=HKQ3d_Mxf0y4HwlHor0XkSAwUVj-4Xvv--rzE9njxjM,1027
8
+ trodo/api/http_client.py,sha256=AELbKiABR066px2Uhq_dtkSsiVqMMP23p8QeoywzeHg,4391
9
9
  trodo/auto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  trodo/auto/auto_event_manager.py,sha256=cztuRsRkNoJE5R4NfSfTrTJTGl4jx2Yb-Ncy0aVAPo8,4247
11
11
  trodo/managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  trodo/managers/group_manager.py,sha256=ki3Se3qEoSZfREX63oeDeBmEfZF-ISHLE8azEtLg0tM,3542
13
13
  trodo/managers/people_manager.py,sha256=mMVnx40Mlifx6NGgChvohC9ViK6dQu2mkXNHbV8pK1E,2882
14
+ trodo/otel/__init__.py,sha256=yiRFXWUU45bAM2CV37XeO7zf1hmnmjufdP4XO50yEyE,624
15
+ trodo/otel/auto_instrument.py,sha256=J_neFxvO-3YACUvtetY4RdM8xYA_79SZUgPry6hXrm8,9434
16
+ trodo/otel/context.py,sha256=iJ1rE42-SbO8VZHAxhIl2ZJXgNwLIVps5xLg8GKgfFc,1165
17
+ trodo/otel/helpers.py,sha256=cvgFrdT8yP92P9mttloiHPr_eTCe8cC4NVrxrJo_I-A,13234
18
+ trodo/otel/processor.py,sha256=jVZkslZlw50G5uRAa7-GMRgn_yvae58EmlWTZL8tMkQ,6285
19
+ trodo/otel/wrap_agent.py,sha256=mwHYwxtg9A9VUBdlbCKLswrNP0v5oY8ELpvt1JVYE8Q,17495
14
20
  trodo/queue/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
21
  trodo/queue/batch_flusher.py,sha256=4Lg6T3Urwi9U0Q4FpFGPmjDYKg4ZliCTR-ND8BJvWaY,1298
16
22
  trodo/queue/event_queue.py,sha256=EVFZrhlq_kwC3jJ2GK0wMhHISf9UzLCZNDnT_aZ2I2A,872
17
23
  trodo/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
24
  trodo/session/server_session.py,sha256=uBAq1QSYPUUaHFSeoOyM5Yr65dLb8T82OOx3D1BrdrE,1970
19
25
  trodo/session/session_manager.py,sha256=JrgH1VeicmtlxPR4dXEuJbxhi23OelkgwW3-9Slv80o,2525
20
- trodo_python-1.2.0.dist-info/METADATA,sha256=GNdzVrFziphK3z0HtY02YyMNNBgGrFuYpsf8EgML54Y,10179
21
- trodo_python-1.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
22
- trodo_python-1.2.0.dist-info/top_level.txt,sha256=VCQu1CJWFmNsqTs1YxMcw4Mq35Tc3z3uI9RwHEXAayQ,6
23
- trodo_python-1.2.0.dist-info/RECORD,,
26
+ trodo_python-2.2.0.dist-info/METADATA,sha256=2WsoZx5P03S5LG7JDUsCfBwTLVGSCVYa3R8ZS8KKLqw,16353
27
+ trodo_python-2.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
28
+ trodo_python-2.2.0.dist-info/top_level.txt,sha256=VCQu1CJWFmNsqTs1YxMcw4Mq35Tc3z3uI9RwHEXAayQ,6
29
+ trodo_python-2.2.0.dist-info/RECORD,,