tracecast 0.2.5__tar.gz → 0.2.7__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.
Files changed (107) hide show
  1. {tracecast-0.2.5/tracecast.egg-info → tracecast-0.2.7}/PKG-INFO +1 -1
  2. {tracecast-0.2.5 → tracecast-0.2.7}/pyproject.toml +1 -1
  3. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_cost_calculator.py +51 -1
  4. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_dashboard_pushdown.py +46 -0
  5. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/core/cost_calculator.py +26 -0
  6. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/dashboard/reader.py +22 -6
  7. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/dashboard/router.py +1 -1
  8. tracecast-0.2.5/tracecast/dashboard/static/assets/index-CPXZquql.js → tracecast-0.2.7/tracecast/dashboard/static/assets/index-BMIlEmLS.js +28 -28
  9. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/dashboard/static/index.html +1 -1
  10. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/instrumentors/openai_inst.py +165 -0
  11. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/models/span.py +5 -3
  12. {tracecast-0.2.5 → tracecast-0.2.7/tracecast.egg-info}/PKG-INFO +1 -1
  13. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast.egg-info/SOURCES.txt +1 -1
  14. {tracecast-0.2.5 → tracecast-0.2.7}/LICENSE +0 -0
  15. {tracecast-0.2.5 → tracecast-0.2.7}/README.md +0 -0
  16. {tracecast-0.2.5 → tracecast-0.2.7}/setup.cfg +0 -0
  17. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_aggregator_graph.py +0 -0
  18. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_context_propagation.py +0 -0
  19. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_dashboard_mount.py +0 -0
  20. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_dashboard_sessions.py +0 -0
  21. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_decorators.py +0 -0
  22. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_e2e_langgraph.py +0 -0
  23. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_exporters_db.py +0 -0
  24. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_integration_frameworks.py +0 -0
  25. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_integration_langchain.py +0 -0
  26. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_json_exporter.py +0 -0
  27. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_langchain_callback.py +0 -0
  28. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_llm_wrappers.py +0 -0
  29. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_logging.py +0 -0
  30. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_mongo_exporter.py +0 -0
  31. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_prompt_dashboard.py +0 -0
  32. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_prompts.py +0 -0
  33. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_roundtrip.py +0 -0
  34. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_schema_v2.py +0 -0
  35. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_score.py +0 -0
  36. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_score_dashboard.py +0 -0
  37. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_scoring_api.py +0 -0
  38. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_serve.py +0 -0
  39. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_span.py +0 -0
  40. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_streaming.py +0 -0
  41. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_token_counter.py +0 -0
  42. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_trace.py +0 -0
  43. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_trace_span.py +0 -0
  44. {tracecast-0.2.5 → tracecast-0.2.7}/tests/test_tracer.py +0 -0
  45. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/__init__.py +0 -0
  46. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/core/__init__.py +0 -0
  47. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/core/logger.py +0 -0
  48. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/core/scoring.py +0 -0
  49. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/core/token_counter.py +0 -0
  50. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/core/tracer.py +0 -0
  51. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/dashboard/__init__.py +0 -0
  52. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/dashboard/aggregator.py +0 -0
  53. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/dashboard/asgi_middleware.py +0 -0
  54. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/dashboard/blueprint.py +0 -0
  55. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/dashboard/eval_reader.py +0 -0
  56. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/dashboard/prompt_reader.py +0 -0
  57. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/dashboard/score_reader.py +0 -0
  58. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/dashboard/standalone.py +0 -0
  59. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/dashboard/static/assets/index-B07hiBI2.js +0 -0
  60. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/dashboard/static/assets/index-CY5YOA8F.js +0 -0
  61. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/dashboard/static/assets/index-DllJJzmE.css +0 -0
  62. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/dashboard/static/assets/index-Dt0awpJJ.js +0 -0
  63. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/decorators.py +0 -0
  64. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/eval/__init__.py +0 -0
  65. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/eval/cli.py +0 -0
  66. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/eval/compare.py +0 -0
  67. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/eval/dataset.py +0 -0
  68. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/eval/decorator.py +0 -0
  69. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/eval/judge.py +0 -0
  70. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/eval/metrics.py +0 -0
  71. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/eval/models.py +0 -0
  72. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/eval/online.py +0 -0
  73. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/eval/runner.py +0 -0
  74. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/eval/scorers.py +0 -0
  75. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/exporters/__init__.py +0 -0
  76. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/exporters/_eval_store.py +0 -0
  77. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/exporters/base.py +0 -0
  78. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/exporters/dict_exporter.py +0 -0
  79. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/exporters/json_file.py +0 -0
  80. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/exporters/mongo.py +0 -0
  81. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/exporters/postgres.py +0 -0
  82. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/exporters/query.py +0 -0
  83. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/instrument.py +0 -0
  84. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/instrumentors/__init__.py +0 -0
  85. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/instrumentors/_llamaindex_handler.py +0 -0
  86. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/instrumentors/_streaming.py +0 -0
  87. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/instrumentors/anthropic_inst.py +0 -0
  88. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/instrumentors/base.py +0 -0
  89. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/instrumentors/crewai_inst.py +0 -0
  90. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/instrumentors/gemini_inst.py +0 -0
  91. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/instrumentors/langchain_inst.py +0 -0
  92. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/instrumentors/llamaindex_inst.py +0 -0
  93. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/integrations/__init__.py +0 -0
  94. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/integrations/langchain.py +0 -0
  95. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/integrations/llm.py +0 -0
  96. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/middleware.py +0 -0
  97. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/models/__init__.py +0 -0
  98. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/models/score.py +0 -0
  99. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/models/trace.py +0 -0
  100. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/prompts/__init__.py +0 -0
  101. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/prompts/client.py +0 -0
  102. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/prompts/models.py +0 -0
  103. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast/serve.py +0 -0
  104. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast.egg-info/dependency_links.txt +0 -0
  105. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast.egg-info/entry_points.txt +0 -0
  106. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast.egg-info/requires.txt +0 -0
  107. {tracecast-0.2.5 → tracecast-0.2.7}/tracecast.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tracecast
3
- Version: 0.2.5
3
+ Version: 0.2.7
4
4
  Summary: LLM observability SDK — framework-agnostic tracing for AI applications
5
5
  Author: Pedro Castanheira Costa
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tracecast"
7
- version = "0.2.5"
7
+ version = "0.2.7"
8
8
  description = "LLM observability SDK — framework-agnostic tracing for AI applications"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -1,5 +1,5 @@
1
1
  import pytest
2
- from tracecast.core.cost_calculator import calculate_cost, PRICE_TABLE
2
+ from tracecast.core.cost_calculator import calculate_cost, calculate_audio_cost, PRICE_TABLE
3
3
 
4
4
  @pytest.mark.parametrize("model", [
5
5
  "gpt-4o", "gpt-4o-mini", "gpt-4-turbo",
@@ -151,3 +151,53 @@ def test_modelos_sem_suporte_a_cache_nao_tem_chave_cached():
151
151
  modelos_sem_cache = ["gpt-4-turbo", "gemini-2.5-flash", "llama-4-scout"]
152
152
  for model in modelos_sem_cache:
153
153
  assert "cached" not in PRICE_TABLE[model], f"{model} nao deveria ter preco de cache"
154
+
155
+
156
+ # --- gpt-4.1-nano (bug: ausente da tabela, custo virava $0 silenciosamente) ---
157
+
158
+ def test_gpt_4_1_nano_tem_preco():
159
+ assert "gpt-4.1-nano" in PRICE_TABLE
160
+
161
+
162
+ def test_calcula_custo_gpt_4_1_nano():
163
+ # $0.10 / $0.40 por 1M tokens = 0.00010 / 0.00040 por 1k
164
+ cost = calculate_cost("gpt-4.1-nano", tokens_in=1000, tokens_out=1000)
165
+ assert abs(cost - (0.00010 + 0.00040)) < 1e-9
166
+
167
+
168
+ def test_gpt_4_1_nano_cached_e_25_pct_do_input():
169
+ # mesmo padrao dos irmaos gpt-4.1 / gpt-4.1-mini: cached = 25% do input
170
+ entry = PRICE_TABLE["gpt-4.1-nano"]
171
+ assert abs(entry["cached"] - entry["input"] * 0.25) < 1e-9
172
+
173
+
174
+ # --- embeddings (bug: nunca precificados, custo $0 mesmo se instrumentados) ---
175
+
176
+ @pytest.mark.parametrize("model,expected_input", [
177
+ ("text-embedding-3-small", 0.00002),
178
+ ("text-embedding-3-large", 0.00013),
179
+ ("text-embedding-ada-002", 0.00010),
180
+ ])
181
+ def test_calcula_custo_embeddings(model, expected_input):
182
+ cost = calculate_cost(model, tokens_in=1000, tokens_out=0)
183
+ assert abs(cost - expected_input) < 1e-9
184
+
185
+
186
+ # --- audio (bug: whisper/tts nunca precificados nem instrumentados) ---
187
+
188
+ def test_calculate_audio_cost_whisper_por_minuto():
189
+ cost = calculate_audio_cost("whisper-1", minutes=2.5)
190
+ assert abs(cost - (2.5 * 0.006)) < 1e-9
191
+
192
+
193
+ def test_calculate_audio_cost_tts_por_caractere():
194
+ cost = calculate_audio_cost("gpt-4o-mini-tts", chars=1000)
195
+ assert cost > 0
196
+
197
+
198
+ def test_calculate_audio_cost_modelo_desconhecido_retorna_zero():
199
+ assert calculate_audio_cost("modelo-inexistente", minutes=10) == 0.0
200
+
201
+
202
+ def test_calculate_audio_cost_sem_uso_retorna_zero():
203
+ assert calculate_audio_cost("whisper-1") == 0.0
@@ -108,3 +108,49 @@ def test_index_injects_prefix():
108
108
  resp = client.get("/tc/")
109
109
  assert resp.status_code == 200
110
110
  assert 'window.__TC_PREFIX__ = "/tc"' in resp.text
111
+
112
+
113
+ def test_get_traces_for_metrics_bypasses_max_traces_cap():
114
+ exporter = FakeReadableExporter([_trace(f"t{i}", "p1", i) for i in range(5)])
115
+ reader = TraceReader([exporter], max_traces=2)
116
+ assert len(reader.get_traces_for_metrics()) == 5
117
+
118
+
119
+ def test_api_metrics_bypasses_max_traces_cap():
120
+ exporter = FakeReadableExporter([_trace(f"t{i}", "p1", i) for i in range(5)])
121
+ tracer = Tracer(exporters=[exporter])
122
+ app = FastAPI()
123
+ tracer.mount(app, prefix="/tc", max_traces=2)
124
+ client = TestClient(app)
125
+ resp = client.get("/tc/api/metrics", params={"from": "2020-01-01T00:00:00Z", "to": "2030-01-01T00:00:00Z"})
126
+ assert resp.status_code == 200
127
+ assert resp.json()["total_traces"] == 5
128
+
129
+
130
+ def _trace_named_project(trace_id, project_name, offset_min=0):
131
+ base = datetime(2026, 6, 1, tzinfo=timezone.utc) + timedelta(minutes=offset_min)
132
+ t = Trace(trace_id=trace_id, name="req", started_at=base, project_name=project_name)
133
+ t.finished_at = base + timedelta(seconds=1)
134
+ t._finalize()
135
+ return t
136
+
137
+
138
+ def test_get_filter_options_bypasses_max_traces_cap():
139
+ # "rare-project" is the oldest trace; a small cap sorted by recency would drop it.
140
+ exporter = FakeReadableExporter([
141
+ _trace_named_project("old", "rare-project", offset_min=0),
142
+ *[_trace_named_project(f"t{i}", "common-project", offset_min=10 + i) for i in range(5)],
143
+ ])
144
+ reader = TraceReader([exporter], max_traces=3)
145
+ opts = reader.get_filter_options()
146
+ assert "rare-project" in opts["project_names"]
147
+
148
+
149
+ def test_get_projects_bypasses_max_traces_cap():
150
+ exporter = FakeReadableExporter([
151
+ _trace_named_project("old", "rare-project", offset_min=0),
152
+ *[_trace_named_project(f"t{i}", "common-project", offset_min=10 + i) for i in range(5)],
153
+ ])
154
+ reader = TraceReader([exporter], max_traces=3)
155
+ names = {p["project_name"] for p in reader.get_projects()}
156
+ assert "rare-project" in names
@@ -8,9 +8,14 @@ PRICE_TABLE: Dict[str, Dict[str, float]] = {
8
8
  "gpt-4-turbo": {"input": 0.01000, "output": 0.03000},
9
9
  "gpt-4.1": {"input": 0.00200, "output": 0.00800, "cached": 0.000500},
10
10
  "gpt-4.1-mini": {"input": 0.00040, "output": 0.00160, "cached": 0.000100},
11
+ "gpt-4.1-nano": {"input": 0.00010, "output": 0.00040, "cached": 0.000025},
11
12
  "o3": {"input": 0.00200, "output": 0.00800, "cached": 0.000500},
12
13
  "o4-mini": {"input": 0.00110, "output": 0.00440, "cached": 0.000275},
13
14
 
15
+ "text-embedding-3-small": {"input": 0.00002, "output": 0.0},
16
+ "text-embedding-3-large": {"input": 0.00013, "output": 0.0},
17
+ "text-embedding-ada-002": {"input": 0.00010, "output": 0.0},
18
+
14
19
  "claude-opus-4-6": {"input": 0.00500, "output": 0.02500, "cached": 0.000500},
15
20
  "claude-sonnet-4-6": {"input": 0.00300, "output": 0.01500, "cached": 0.000300},
16
21
  "claude-haiku-4-5": {"input": 0.00100, "output": 0.00500, "cached": 0.000100},
@@ -62,3 +67,24 @@ def calculate_cost(
62
67
  def _prefix_match(model: str, prices: Dict) -> Optional[Dict]:
63
68
  provider = model.split("/")[0] + "/*"
64
69
  return prices.get(provider)
70
+
71
+
72
+ # Audio isn't token-billed: transcription is per-minute of input audio, TTS is
73
+ # per-character of input text. gpt-4o-mini-tts's rate is an approximation
74
+ # (not published on OpenAI's current pricing page as of 2026-07) — verify
75
+ # against an actual invoice before trusting it for reconciliation.
76
+ AUDIO_PRICE_TABLE: Dict[str, Dict[str, float]] = {
77
+ "whisper-1": {"unit": "minute", "price": 0.006},
78
+ "gpt-4o-mini-tts": {"unit": "char", "price": 0.000015},
79
+ "tts-1": {"unit": "char", "price": 0.000015},
80
+ "tts-1-hd": {"unit": "char", "price": 0.00003},
81
+ }
82
+
83
+
84
+ def calculate_audio_cost(model: str, *, minutes: float = 0.0, chars: int = 0) -> float:
85
+ entry = AUDIO_PRICE_TABLE.get(model)
86
+ if not entry:
87
+ return 0.0
88
+ if entry["unit"] == "minute":
89
+ return minutes * entry["price"]
90
+ return chars * entry["price"]
@@ -71,6 +71,22 @@ class TraceReader:
71
71
  )
72
72
  return [_hydrate_trace(r) for r in rows], total
73
73
 
74
+ def get_traces_for_metrics(
75
+ self,
76
+ project_name: Optional[str] = None,
77
+ project_id: Optional[str] = None,
78
+ ) -> List[Trace]:
79
+ """Like get_traces() but bypasses max_traces for queryable exporters,
80
+ so dashboard aggregates reflect the full dataset instead of a capped window."""
81
+ exporter = self._readable()
82
+ if exporter is None:
83
+ return self.get_traces()
84
+ total = exporter.count(project_name=project_name, project_id=project_id)
85
+ if total <= 0:
86
+ return []
87
+ rows = exporter.query(project_name=project_name, project_id=project_id, limit=total, offset=0)
88
+ return [_hydrate_trace(r) for r in rows]
89
+
74
90
  def get_trace(self, trace_id: str) -> Optional[Trace]:
75
91
  exporter = self._readable()
76
92
  if exporter is not None and callable(getattr(exporter, "get", None)):
@@ -89,30 +105,30 @@ class TraceReader:
89
105
  ) -> list:
90
106
  from .aggregator import compute_sessions
91
107
  return compute_sessions(
92
- self.get_traces(),
108
+ self.get_traces_for_metrics(),
93
109
  project_name=project_name,
94
110
  project_id=project_id,
95
111
  user_id=user_id,
96
112
  )
97
113
 
98
114
  def get_session(self, session_id: str) -> List[Trace]:
99
- return [t for t in self.get_traces() if t.session_id == session_id]
115
+ return [t for t in self.get_traces_for_metrics() if t.session_id == session_id]
100
116
 
101
117
  def get_projects(self) -> list:
102
118
  from .aggregator import compute_projects
103
- return compute_projects(self.get_traces())
119
+ return compute_projects(self.get_traces_for_metrics())
104
120
 
105
121
  def get_subprojects(self, project_name: str) -> list:
106
122
  from .aggregator import compute_projects_by_id
107
- traces = [t for t in self.get_traces() if t.project_name == project_name]
123
+ traces = [t for t in self.get_traces_for_metrics() if t.project_name == project_name]
108
124
  return compute_projects_by_id(traces)
109
125
 
110
126
  def get_project(self, project_id: str) -> List[Trace]:
111
- return [t for t in self.get_traces() if t.project_id == project_id]
127
+ return [t for t in self.get_traces_for_metrics() if t.project_id == project_id]
112
128
 
113
129
  def get_filter_options(self) -> dict:
114
130
  from .aggregator import compute_filter_options
115
- return compute_filter_options(self.get_traces())
131
+ return compute_filter_options(self.get_traces_for_metrics())
116
132
 
117
133
  def _read_from(self, exporter) -> List[Trace]:
118
134
  name = type(exporter).__name__
@@ -122,7 +122,7 @@ def _make_router(reader: TraceReader, prefix: str = "") -> "APIRouter":
122
122
  project_name: Optional[str] = Query(None),
123
123
  project_id: Optional[str] = Query(None),
124
124
  ):
125
- traces = reader.get_traces()
125
+ traces = reader.get_traces_for_metrics(project_name=project_name, project_id=project_id)
126
126
  from_parsed = _parse_iso(from_dt)
127
127
  to_parsed = _parse_iso(to_dt)
128
128
  return compute_metrics(