tracecast 0.2.2__tar.gz → 0.2.3__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.
- {tracecast-0.2.2/tracecast.egg-info → tracecast-0.2.3}/PKG-INFO +1 -1
- {tracecast-0.2.2 → tracecast-0.2.3}/pyproject.toml +1 -1
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/dashboard/aggregator.py +82 -16
- tracecast-0.2.3/tracecast/dashboard/static/assets/index-BbNHE9WD.js +151 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/dashboard/static/index.html +1 -1
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/integrations/langchain.py +19 -2
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/integrations/llm.py +75 -1
- {tracecast-0.2.2 → tracecast-0.2.3/tracecast.egg-info}/PKG-INFO +1 -1
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast.egg-info/SOURCES.txt +1 -1
- tracecast-0.2.2/tracecast/dashboard/static/assets/index-CBra-omk.js +0 -151
- {tracecast-0.2.2 → tracecast-0.2.3}/LICENSE +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/README.md +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/setup.cfg +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_context_propagation.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_cost_calculator.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_dashboard_mount.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_dashboard_pushdown.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_dashboard_sessions.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_decorators.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_e2e_langgraph.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_exporters_db.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_integration_frameworks.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_integration_langchain.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_json_exporter.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_langchain_callback.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_llm_wrappers.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_logging.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_mongo_exporter.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_prompt_dashboard.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_prompts.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_roundtrip.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_schema_v2.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_score.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_score_dashboard.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_scoring_api.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_serve.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_span.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_streaming.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_token_counter.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_trace.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_trace_span.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tests/test_tracer.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/__init__.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/core/__init__.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/core/cost_calculator.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/core/logger.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/core/scoring.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/core/token_counter.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/core/tracer.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/dashboard/__init__.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/dashboard/asgi_middleware.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/dashboard/blueprint.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/dashboard/eval_reader.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/dashboard/prompt_reader.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/dashboard/reader.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/dashboard/router.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/dashboard/score_reader.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/dashboard/standalone.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/dashboard/static/assets/index-DllJJzmE.css +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/decorators.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/eval/__init__.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/eval/cli.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/eval/compare.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/eval/dataset.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/eval/decorator.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/eval/judge.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/eval/metrics.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/eval/models.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/eval/online.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/eval/runner.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/eval/scorers.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/exporters/__init__.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/exporters/_eval_store.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/exporters/base.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/exporters/dict_exporter.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/exporters/json_file.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/exporters/mongo.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/exporters/postgres.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/exporters/query.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/instrument.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/instrumentors/__init__.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/instrumentors/_llamaindex_handler.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/instrumentors/_streaming.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/instrumentors/anthropic_inst.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/instrumentors/base.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/instrumentors/crewai_inst.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/instrumentors/gemini_inst.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/instrumentors/langchain_inst.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/instrumentors/llamaindex_inst.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/instrumentors/openai_inst.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/integrations/__init__.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/middleware.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/models/__init__.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/models/score.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/models/span.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/models/trace.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/prompts/__init__.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/prompts/client.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/prompts/models.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast/serve.py +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast.egg-info/dependency_links.txt +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast.egg-info/entry_points.txt +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast.egg-info/requires.txt +0 -0
- {tracecast-0.2.2 → tracecast-0.2.3}/tracecast.egg-info/top_level.txt +0 -0
|
@@ -43,11 +43,20 @@ def compute_metrics(
|
|
|
43
43
|
|
|
44
44
|
cost_by_model: dict[str, float] = defaultdict(float)
|
|
45
45
|
cost_by_project: dict[str, float] = defaultdict(float)
|
|
46
|
+
models_breakdown: dict[str, dict] = defaultdict(lambda: {"calls": 0, "total_tokens": 0, "total_tokens_in": 0, "total_tokens_out": 0, "cost_usd": 0.0})
|
|
46
47
|
for t in filtered:
|
|
47
|
-
if t.model:
|
|
48
|
-
cost_by_model[t.model] += t.cost_usd
|
|
49
48
|
if t.project_id:
|
|
50
49
|
cost_by_project[t.project_id] += t.cost_usd
|
|
50
|
+
for s in t.spans:
|
|
51
|
+
if s.type.value == "llm" and s.model:
|
|
52
|
+
cost_by_model[s.model] += s.cost_usd
|
|
53
|
+
models_breakdown[s.model]["calls"] += 1
|
|
54
|
+
models_breakdown[s.model]["total_tokens"] += s.total_tokens
|
|
55
|
+
models_breakdown[s.model]["total_tokens_in"] += s.tokens_in
|
|
56
|
+
models_breakdown[s.model]["total_tokens_out"] += s.tokens_out
|
|
57
|
+
models_breakdown[s.model]["cost_usd"] = round(
|
|
58
|
+
models_breakdown[s.model]["cost_usd"] + s.cost_usd, 6
|
|
59
|
+
)
|
|
51
60
|
|
|
52
61
|
traces_over_time = _group_by_day(filtered)
|
|
53
62
|
tokens_by_model_over_time = _group_by_day_and_model(filtered)
|
|
@@ -63,6 +72,7 @@ def compute_metrics(
|
|
|
63
72
|
"avg_latency_ms": round(avg_latency, 1),
|
|
64
73
|
"cost_by_model": dict(cost_by_model),
|
|
65
74
|
"cost_by_project": dict(cost_by_project),
|
|
75
|
+
"models_breakdown": {k: v for k, v in sorted(models_breakdown.items())},
|
|
66
76
|
"traces_over_time": traces_over_time,
|
|
67
77
|
"tokens_by_model_over_time": tokens_by_model_over_time,
|
|
68
78
|
}
|
|
@@ -136,8 +146,53 @@ def _trace_summary(trace: Trace) -> dict:
|
|
|
136
146
|
|
|
137
147
|
|
|
138
148
|
def build_graph(trace: Trace) -> dict:
|
|
149
|
+
# T5: exclude broken orphan LLM spans (pid=None + 0 tokens = wrap_openai async bug).
|
|
150
|
+
# Temporary until wrap_openai async fix lands in tracecast lib.
|
|
151
|
+
valid_spans = [
|
|
152
|
+
s for s in trace.spans
|
|
153
|
+
if not (
|
|
154
|
+
s.parent_span_id is None
|
|
155
|
+
and s.type.value == "llm"
|
|
156
|
+
and s.tokens_in == 0
|
|
157
|
+
and s.tokens_out == 0
|
|
158
|
+
)
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
span_by_id = {s.span_id: s for s in valid_spans}
|
|
162
|
+
valid_ids = set(span_by_id.keys())
|
|
163
|
+
|
|
164
|
+
# Build parent→children map
|
|
165
|
+
children: dict[str, list] = defaultdict(list)
|
|
166
|
+
for s in valid_spans:
|
|
167
|
+
if s.parent_span_id and s.parent_span_id in span_by_id:
|
|
168
|
+
children[s.parent_span_id].append(s.span_id)
|
|
169
|
+
|
|
170
|
+
# Aggregate tokens + primary_model bottom-up (DFS, memoized).
|
|
171
|
+
# primary_model = model of the LLM descendant with most tokens (for parent card display).
|
|
172
|
+
agg_cache: dict[str, tuple] = {}
|
|
173
|
+
|
|
174
|
+
def _agg(sid: str) -> tuple:
|
|
175
|
+
if sid in agg_cache:
|
|
176
|
+
return agg_cache[sid]
|
|
177
|
+
s = span_by_id[sid]
|
|
178
|
+
ti, to_, cost = s.tokens_in, s.tokens_out, s.cost_usd
|
|
179
|
+
primary_model: str | None = s.model if s.type.value == "llm" else None
|
|
180
|
+
best_child_tok = 0
|
|
181
|
+
for child_id in children.get(sid, []):
|
|
182
|
+
ci, co, cc, cm = _agg(child_id)
|
|
183
|
+
ti += ci
|
|
184
|
+
to_ += co
|
|
185
|
+
cost += cc
|
|
186
|
+
child_tok = ci + co
|
|
187
|
+
if cm is not None and child_tok > best_child_tok:
|
|
188
|
+
primary_model = cm
|
|
189
|
+
best_child_tok = child_tok
|
|
190
|
+
agg_cache[sid] = (ti, to_, cost, primary_model)
|
|
191
|
+
return ti, to_, cost, primary_model
|
|
192
|
+
|
|
139
193
|
nodes = []
|
|
140
|
-
for s in
|
|
194
|
+
for s in valid_spans:
|
|
195
|
+
ti, to_, cost, pm = _agg(s.span_id)
|
|
141
196
|
nodes.append({
|
|
142
197
|
"id": s.span_id,
|
|
143
198
|
"parent_span_id": s.parent_span_id,
|
|
@@ -145,18 +200,26 @@ def build_graph(trace: Trace) -> dict:
|
|
|
145
200
|
"type": s.type.value,
|
|
146
201
|
"status": s.status.value if hasattr(s.status, "value") else s.status,
|
|
147
202
|
"model": s.model,
|
|
148
|
-
"
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
"
|
|
203
|
+
"primary_model": pm,
|
|
204
|
+
"tokens_in": ti,
|
|
205
|
+
"tokens_out": to_,
|
|
206
|
+
"total_tokens": ti + to_,
|
|
207
|
+
"cost_usd": round(cost, 6),
|
|
152
208
|
"latency_ms": s.latency_ms,
|
|
153
209
|
"error": s.error,
|
|
154
210
|
})
|
|
211
|
+
|
|
212
|
+
# Filter edges to only reference valid span ids (removes orphan edges)
|
|
213
|
+
valid_edges = [
|
|
214
|
+
e for e in trace.edges
|
|
215
|
+
if e.get("from") in valid_ids and e.get("to") in valid_ids
|
|
216
|
+
]
|
|
217
|
+
|
|
155
218
|
return {
|
|
156
219
|
"trace_id": trace.trace_id,
|
|
157
220
|
"name": trace.name,
|
|
158
221
|
"nodes": nodes,
|
|
159
|
-
"edges":
|
|
222
|
+
"edges": valid_edges,
|
|
160
223
|
}
|
|
161
224
|
|
|
162
225
|
|
|
@@ -327,15 +390,18 @@ def _group_by_day(traces: List[Trace]) -> list:
|
|
|
327
390
|
|
|
328
391
|
|
|
329
392
|
def _group_by_day_and_model(traces: List[Trace]) -> list:
|
|
330
|
-
"""Group by (date, model) for multi-series token/cost chart."""
|
|
393
|
+
"""Group by (date, model) at span level for multi-series token/cost chart."""
|
|
331
394
|
groups: dict[tuple, dict] = {}
|
|
332
395
|
for t in traces:
|
|
333
396
|
day = t.started_at.strftime("%Y-%m-%d")
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
397
|
+
for s in t.spans:
|
|
398
|
+
if s.type.value != "llm" or not s.model:
|
|
399
|
+
continue
|
|
400
|
+
model = s.model
|
|
401
|
+
key = (day, model)
|
|
402
|
+
if key not in groups:
|
|
403
|
+
groups[key] = {"date": day, "model": model, "tokens_in": 0, "tokens_out": 0, "cost_usd": 0.0}
|
|
404
|
+
groups[key]["tokens_in"] += s.tokens_in
|
|
405
|
+
groups[key]["tokens_out"] += s.tokens_out
|
|
406
|
+
groups[key]["cost_usd"] = round(groups[key]["cost_usd"] + s.cost_usd, 6)
|
|
341
407
|
return sorted(groups.values(), key=lambda x: (x["date"], x["model"]))
|