trodo-python 1.2.0__tar.gz → 2.1.0__tar.gz
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_python-1.2.0 → trodo_python-2.1.0}/PKG-INFO +155 -3
- {trodo_python-1.2.0 → trodo_python-2.1.0}/README.md +154 -2
- {trodo_python-1.2.0 → trodo_python-2.1.0}/pyproject.toml +1 -1
- trodo_python-2.1.0/trodo/__init__.py +430 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/api/endpoints.py +5 -1
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/api/http_client.py +30 -2
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/client.py +193 -112
- trodo_python-2.1.0/trodo/otel/__init__.py +21 -0
- trodo_python-2.1.0/trodo/otel/auto_instrument.py +281 -0
- trodo_python-2.1.0/trodo/otel/context.py +44 -0
- trodo_python-2.1.0/trodo/otel/helpers.py +407 -0
- trodo_python-2.1.0/trodo/otel/processor.py +165 -0
- trodo_python-2.1.0/trodo/otel/wrap_agent.py +438 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/types.py +12 -68
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo_python.egg-info/PKG-INFO +155 -3
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo_python.egg-info/SOURCES.txt +6 -0
- trodo_python-1.2.0/trodo/__init__.py +0 -177
- {trodo_python-1.2.0 → trodo_python-2.1.0}/setup.cfg +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/api/__init__.py +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/api/async_client.py +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/auto/__init__.py +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/auto/auto_event_manager.py +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/managers/__init__.py +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/managers/group_manager.py +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/managers/people_manager.py +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/queue/__init__.py +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/queue/batch_flusher.py +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/queue/event_queue.py +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/session/__init__.py +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/session/server_session.py +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/session/session_manager.py +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo/user_context.py +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo_python.egg-info/dependency_links.txt +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo_python.egg-info/requires.txt +0 -0
- {trodo_python-1.2.0 → trodo_python-2.1.0}/trodo_python.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trodo-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 2.1.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,160 @@ trodo.set_group('user-123', 'company', 'acme')
|
|
|
136
136
|
|
|
137
137
|
---
|
|
138
138
|
|
|
139
|
-
## Agent
|
|
139
|
+
## AI Agent Tracing (recommended)
|
|
140
140
|
|
|
141
|
-
|
|
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
|
+
### Conversation binding & feedback
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
with trodo.wrap_agent(
|
|
270
|
+
'chat', distinct_id=user_id, conversation_id=session_id,
|
|
271
|
+
) as run:
|
|
272
|
+
...
|
|
273
|
+
# Later:
|
|
274
|
+
trodo.feedback(run.run_id, satisfaction='positive', rating=5)
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Cookbook
|
|
278
|
+
|
|
279
|
+
Runnable scenarios that double as integration tests live in
|
|
280
|
+
`sandbox/scenarios/` — `span_helpers.py`, `raw_http.py`,
|
|
281
|
+
`custom_tools.py`, `cross_service.py`, `concurrent_100.py`,
|
|
282
|
+
`long_run.py`, plus opt-in `openai_auto.py`, `anthropic_auto.py`,
|
|
283
|
+
`langchain_chain.py`. Run them with
|
|
284
|
+
`python -m sandbox.run_all` from the SDK root.
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Agent Analytics (legacy event-based API)
|
|
289
|
+
|
|
290
|
+
The older per-event API below is still supported but superseded by
|
|
291
|
+
`wrap_agent` + span helpers above. Use it only if you're already wired
|
|
292
|
+
into it; new integrations should prefer the tracing API.
|
|
142
293
|
|
|
143
294
|
**Before you start:** register your agent in **Integrations → AI Agents** in the dashboard to get an `agent_id` (`agt_xxxxxxxx`).
|
|
144
295
|
|
|
@@ -161,6 +312,7 @@ trodo.track_agent_call(AgentCallProps(
|
|
|
161
312
|
provider='anthropic',
|
|
162
313
|
system_prompt_version='v2', # optional — track prompt iterations
|
|
163
314
|
distinct_id=user_id, # optional — link to a Trodo user
|
|
315
|
+
metadata={'thread_source': 'slack', 'locale': 'en'}, # optional — agent_calls.metadata JSONB
|
|
164
316
|
))
|
|
165
317
|
```
|
|
166
318
|
|
|
@@ -109,9 +109,160 @@ trodo.set_group('user-123', 'company', 'acme')
|
|
|
109
109
|
|
|
110
110
|
---
|
|
111
111
|
|
|
112
|
-
## Agent
|
|
112
|
+
## AI Agent Tracing (recommended)
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
One wrap around your agent captures every LLM call, tool call, and
|
|
115
|
+
nested step as a tree of spans — token counts, costs, inputs, outputs,
|
|
116
|
+
errors. Works with any stack: OpenAI, Anthropic, LangChain, LlamaIndex,
|
|
117
|
+
Gemini, raw HTTP, custom tools. Cost is derived server-side from
|
|
118
|
+
`(provider, model)` — the SDK only sends tokens.
|
|
119
|
+
|
|
120
|
+
### 30-second quickstart
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
import trodo
|
|
124
|
+
trodo.init(site_id='your-site-id') # auto-instrument on by default
|
|
125
|
+
|
|
126
|
+
with trodo.wrap_agent('customer-support',
|
|
127
|
+
distinct_id=user_id,
|
|
128
|
+
conversation_id=session_id) as run:
|
|
129
|
+
run.set_input({'query': 'where did sales drop'})
|
|
130
|
+
answer = agent.run(query) # OpenAI/Anthropic/LangChain auto-captured
|
|
131
|
+
run.set_output(answer)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Open the Agent Runs dashboard — the row shows tokens in/out, cost,
|
|
135
|
+
span count, tool count, error count, plus the full trace tree.
|
|
136
|
+
|
|
137
|
+
### Auto-instrumentation
|
|
138
|
+
|
|
139
|
+
`trodo.init()` calls `enable_auto_instrument()` which registers every
|
|
140
|
+
installed OpenTelemetry instrumentor. No extra code required.
|
|
141
|
+
|
|
142
|
+
| Framework | Install |
|
|
143
|
+
|-----------|---------|
|
|
144
|
+
| OpenAI | `pip install opentelemetry-instrumentation-openai` |
|
|
145
|
+
| Anthropic | `pip install opentelemetry-instrumentation-anthropic` |
|
|
146
|
+
| LangChain | `pip install opentelemetry-instrumentation-langchain` |
|
|
147
|
+
| LlamaIndex | `pip install opentelemetry-instrumentation-llama-index` |
|
|
148
|
+
| Google Gemini | `pip install opentelemetry-instrumentation-google-generativeai` |
|
|
149
|
+
| Vertex AI | `pip install opentelemetry-instrumentation-vertexai` |
|
|
150
|
+
| Bedrock | `pip install opentelemetry-instrumentation-bedrock` |
|
|
151
|
+
| Cohere | `pip install opentelemetry-instrumentation-cohere` |
|
|
152
|
+
| Mistral | `pip install opentelemetry-instrumentation-mistralai` |
|
|
153
|
+
| Haystack | `pip install opentelemetry-instrumentation-haystack` |
|
|
154
|
+
| httpx / requests | bundled — generic HTTP spans for raw-HTTP callers |
|
|
155
|
+
|
|
156
|
+
Opt out with `trodo.init(site_id=..., auto_instrument=False)`.
|
|
157
|
+
|
|
158
|
+
### Span helpers
|
|
159
|
+
|
|
160
|
+
Typed function wrappers for custom code — every call becomes a span
|
|
161
|
+
with the args auto-captured as `input`, return value as `output`,
|
|
162
|
+
exception as `error`. Dual-form: helper **and** decorator.
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
# trace — generic span
|
|
166
|
+
prepared = trodo.trace('prepare', prepare_fn)(payload)
|
|
167
|
+
|
|
168
|
+
@trodo.trace('step')
|
|
169
|
+
def step(): ...
|
|
170
|
+
|
|
171
|
+
# tool — tool span (auto tool_name, kind='tool')
|
|
172
|
+
run_funnel = trodo.tool('run_funnel_query', run_funnel_query)
|
|
173
|
+
result = run_funnel(team_id=1, preset='day7')
|
|
174
|
+
|
|
175
|
+
@trodo.tool(name='fetch_user')
|
|
176
|
+
async def fetch_user(uid): ...
|
|
177
|
+
|
|
178
|
+
# llm — LLM span, auto-extracts OpenAI / Anthropic / Gemini usage
|
|
179
|
+
answer = trodo.llm(
|
|
180
|
+
'answer', call_openai, model='gpt-4o-mini', provider='openai',
|
|
181
|
+
)(messages)
|
|
182
|
+
# Records input_tokens / output_tokens from response['usage'].
|
|
183
|
+
|
|
184
|
+
# retrieval — vector search / RAG retriever span
|
|
185
|
+
search = trodo.retrieval('vector_search', vector_search)
|
|
186
|
+
docs = search(query)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Raw-HTTP escape hatches
|
|
190
|
+
|
|
191
|
+
If your LLM client isn't OTel-instrumented and you can't wrap it as a
|
|
192
|
+
function, record a span post-hoc:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
resp = httpx.post(url, json=body).json()
|
|
196
|
+
trodo.track_llm_call(
|
|
197
|
+
model='gemini-2.5-flash', provider='google',
|
|
198
|
+
input_tokens=resp['usageMetadata']['promptTokenCount'],
|
|
199
|
+
output_tokens=resp['usageMetadata']['candidatesTokenCount'],
|
|
200
|
+
prompt=body, completion=resp,
|
|
201
|
+
)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
For advanced cases, get a raw OTel tracer — the Trodo processor is
|
|
205
|
+
already subscribed:
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
tracer = trodo.get_tracer('my.module')
|
|
209
|
+
with tracer.start_as_current_span('custom') as sp:
|
|
210
|
+
sp.set_attribute('gen_ai.system', 'my-llm')
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Cross-service runs
|
|
214
|
+
|
|
215
|
+
When one service calls another, the downstream service **joins** the
|
|
216
|
+
caller's run instead of creating its own. All spans nest under a single
|
|
217
|
+
timeline in the dashboard.
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
# Caller (FastAPI / Flask / Django / …) — outbound:
|
|
221
|
+
import httpx
|
|
222
|
+
httpx.post(url, headers=trodo.propagation_headers(), json=body)
|
|
223
|
+
|
|
224
|
+
# Downstream (FastAPI):
|
|
225
|
+
from fastapi import FastAPI
|
|
226
|
+
app = FastAPI()
|
|
227
|
+
app.middleware('http')(trodo.fastapi_middleware())
|
|
228
|
+
# Every LLM call / @tool / trace helper inside handlers now nests under
|
|
229
|
+
# the caller's run — no extra wiring.
|
|
230
|
+
|
|
231
|
+
# Or manually:
|
|
232
|
+
with trodo.join_run(
|
|
233
|
+
run_id=headers['x-trodo-run-id'],
|
|
234
|
+
parent_span_id=headers['x-trodo-parent-span-id'],
|
|
235
|
+
):
|
|
236
|
+
...
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Conversation binding & feedback
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
with trodo.wrap_agent(
|
|
243
|
+
'chat', distinct_id=user_id, conversation_id=session_id,
|
|
244
|
+
) as run:
|
|
245
|
+
...
|
|
246
|
+
# Later:
|
|
247
|
+
trodo.feedback(run.run_id, satisfaction='positive', rating=5)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Cookbook
|
|
251
|
+
|
|
252
|
+
Runnable scenarios that double as integration tests live in
|
|
253
|
+
`sandbox/scenarios/` — `span_helpers.py`, `raw_http.py`,
|
|
254
|
+
`custom_tools.py`, `cross_service.py`, `concurrent_100.py`,
|
|
255
|
+
`long_run.py`, plus opt-in `openai_auto.py`, `anthropic_auto.py`,
|
|
256
|
+
`langchain_chain.py`. Run them with
|
|
257
|
+
`python -m sandbox.run_all` from the SDK root.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Agent Analytics (legacy event-based API)
|
|
262
|
+
|
|
263
|
+
The older per-event API below is still supported but superseded by
|
|
264
|
+
`wrap_agent` + span helpers above. Use it only if you're already wired
|
|
265
|
+
into it; new integrations should prefer the tracing API.
|
|
115
266
|
|
|
116
267
|
**Before you start:** register your agent in **Integrations → AI Agents** in the dashboard to get an `agent_id` (`agt_xxxxxxxx`).
|
|
117
268
|
|
|
@@ -134,6 +285,7 @@ trodo.track_agent_call(AgentCallProps(
|
|
|
134
285
|
provider='anthropic',
|
|
135
286
|
system_prompt_version='v2', # optional — track prompt iterations
|
|
136
287
|
distinct_id=user_id, # optional — link to a Trodo user
|
|
288
|
+
metadata={'thread_source': 'slack', 'locale': 'en'}, # optional — agent_calls.metadata JSONB
|
|
137
289
|
))
|
|
138
290
|
```
|
|
139
291
|
|