tracecast 0.3.1__tar.gz → 0.3.2__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 (126) hide show
  1. {tracecast-0.3.1/tracecast.egg-info → tracecast-0.3.2}/PKG-INFO +1 -1
  2. {tracecast-0.3.1 → tracecast-0.3.2}/pyproject.toml +1 -1
  3. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/core/export_queue.py +70 -13
  4. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/core/tracer.py +2 -2
  5. {tracecast-0.3.1 → tracecast-0.3.2/tracecast.egg-info}/PKG-INFO +1 -1
  6. {tracecast-0.3.1 → tracecast-0.3.2}/LICENSE +0 -0
  7. {tracecast-0.3.1 → tracecast-0.3.2}/README.md +0 -0
  8. {tracecast-0.3.1 → tracecast-0.3.2}/setup.cfg +0 -0
  9. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_aggregator_graph.py +0 -0
  10. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_async_export_path.py +0 -0
  11. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_context_propagation.py +0 -0
  12. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_cost_calculator.py +0 -0
  13. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_dashboard_mount.py +0 -0
  14. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_dashboard_pushdown.py +0 -0
  15. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_dashboard_sessions.py +0 -0
  16. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_decorators.py +0 -0
  17. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_e2e_langgraph.py +0 -0
  18. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_export_fallback.py +0 -0
  19. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_export_queue.py +0 -0
  20. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_export_retry.py +0 -0
  21. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_export_stats.py +0 -0
  22. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_exporters_db.py +0 -0
  23. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_http_ingest.py +0 -0
  24. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_ingest_auth.py +0 -0
  25. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_integration_frameworks.py +0 -0
  26. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_integration_langchain.py +0 -0
  27. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_json_exporter.py +0 -0
  28. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_langchain_callback.py +0 -0
  29. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_llm_wrappers.py +0 -0
  30. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_logging.py +0 -0
  31. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_metrics_totals.py +0 -0
  32. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_mongo_exporter.py +0 -0
  33. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_prompt_dashboard.py +0 -0
  34. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_prompts.py +0 -0
  35. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_retention.py +0 -0
  36. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_retention_mount.py +0 -0
  37. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_roundtrip.py +0 -0
  38. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_schema_v2.py +0 -0
  39. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_score.py +0 -0
  40. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_score_dashboard.py +0 -0
  41. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_scoring_api.py +0 -0
  42. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_serve.py +0 -0
  43. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_span.py +0 -0
  44. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_span_filter.py +0 -0
  45. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_streaming.py +0 -0
  46. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_token_counter.py +0 -0
  47. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_trace.py +0 -0
  48. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_trace_span.py +0 -0
  49. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_trace_summary.py +0 -0
  50. {tracecast-0.3.1 → tracecast-0.3.2}/tests/test_tracer.py +0 -0
  51. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/__init__.py +0 -0
  52. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/core/__init__.py +0 -0
  53. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/core/cost_calculator.py +0 -0
  54. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/core/export_retry.py +0 -0
  55. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/core/export_stats.py +0 -0
  56. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/core/ingest.py +0 -0
  57. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/core/ingest_auth.py +0 -0
  58. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/core/logger.py +0 -0
  59. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/core/payload.py +0 -0
  60. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/core/scoring.py +0 -0
  61. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/core/span_filter.py +0 -0
  62. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/core/token_counter.py +0 -0
  63. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/core/trace_summary.py +0 -0
  64. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/dashboard/__init__.py +0 -0
  65. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/dashboard/aggregator.py +0 -0
  66. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/dashboard/asgi_middleware.py +0 -0
  67. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/dashboard/blueprint.py +0 -0
  68. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/dashboard/eval_reader.py +0 -0
  69. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/dashboard/prompt_reader.py +0 -0
  70. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/dashboard/reader.py +0 -0
  71. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/dashboard/retention.py +0 -0
  72. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/dashboard/router.py +0 -0
  73. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/dashboard/score_reader.py +0 -0
  74. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/dashboard/standalone.py +0 -0
  75. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/dashboard/static/assets/index-DllJJzmE.css +0 -0
  76. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/dashboard/static/assets/index-WGkVmqjn.js +0 -0
  77. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/dashboard/static/index.html +0 -0
  78. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/decorators.py +0 -0
  79. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/eval/__init__.py +0 -0
  80. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/eval/cli.py +0 -0
  81. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/eval/compare.py +0 -0
  82. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/eval/dataset.py +0 -0
  83. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/eval/decorator.py +0 -0
  84. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/eval/judge.py +0 -0
  85. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/eval/metrics.py +0 -0
  86. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/eval/models.py +0 -0
  87. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/eval/online.py +0 -0
  88. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/eval/runner.py +0 -0
  89. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/eval/scorers.py +0 -0
  90. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/exporters/__init__.py +0 -0
  91. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/exporters/_eval_store.py +0 -0
  92. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/exporters/base.py +0 -0
  93. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/exporters/dict_exporter.py +0 -0
  94. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/exporters/http.py +0 -0
  95. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/exporters/json_file.py +0 -0
  96. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/exporters/mongo.py +0 -0
  97. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/exporters/postgres.py +0 -0
  98. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/exporters/query.py +0 -0
  99. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/instrument.py +0 -0
  100. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/instrumentors/__init__.py +0 -0
  101. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/instrumentors/_llamaindex_handler.py +0 -0
  102. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/instrumentors/_streaming.py +0 -0
  103. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/instrumentors/anthropic_inst.py +0 -0
  104. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/instrumentors/base.py +0 -0
  105. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/instrumentors/crewai_inst.py +0 -0
  106. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/instrumentors/gemini_inst.py +0 -0
  107. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/instrumentors/langchain_inst.py +0 -0
  108. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/instrumentors/llamaindex_inst.py +0 -0
  109. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/instrumentors/openai_inst.py +0 -0
  110. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/integrations/__init__.py +0 -0
  111. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/integrations/langchain.py +0 -0
  112. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/integrations/llm.py +0 -0
  113. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/middleware.py +0 -0
  114. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/models/__init__.py +0 -0
  115. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/models/score.py +0 -0
  116. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/models/span.py +0 -0
  117. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/models/trace.py +0 -0
  118. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/prompts/__init__.py +0 -0
  119. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/prompts/client.py +0 -0
  120. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/prompts/models.py +0 -0
  121. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast/serve.py +0 -0
  122. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast.egg-info/SOURCES.txt +0 -0
  123. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast.egg-info/dependency_links.txt +0 -0
  124. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast.egg-info/entry_points.txt +0 -0
  125. {tracecast-0.3.1 → tracecast-0.3.2}/tracecast.egg-info/requires.txt +0 -0
  126. {tracecast-0.3.1 → tracecast-0.3.2}/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.3.1
3
+ Version: 0.3.2
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.3.1"
7
+ version = "0.3.2"
8
8
  description = "LLM observability SDK — framework-agnostic tracing for AI applications"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -112,11 +112,28 @@ class _FlushMarker:
112
112
  self.event = threading.Event()
113
113
 
114
114
 
115
+ def default_overflow_queue_size(main_maxsize: int) -> int:
116
+ """Secondary buffer for disk-spool handoff — still bounded, never blocks request path."""
117
+ raw = os.environ.get("TRACECAST_EXPORT_OVERFLOW")
118
+ if raw is not None and str(raw).strip() != "":
119
+ try:
120
+ return max(1, int(raw))
121
+ except ValueError:
122
+ pass
123
+ # At least as large as main queue (and ≥256) so brief export stalls
124
+ # hand off to spool thread without dropping; hard cap for RAM.
125
+ return max(1, min(5000, max(main_maxsize, 256)))
126
+
127
+
115
128
  class ExportWorker:
116
- """Bounded in-memory queue + dedicated drain thread + optional JSONL spool.
129
+ """Bounded export buffer with **hard latency guarantee on request path**.
117
130
 
118
- Request path only does ``put_nowait`` (or spool append). Serialize/export
119
- always runs on the worker thread — never on the ASGI event loop.
131
+ Request / ASGI path does **only** ``queue.put_nowait``:
132
+ 1) main export queue
133
+ 2) if full and spool enabled → overflow queue (still put_nowait)
134
+ 3) if overflow full → drop counter (never block the user message)
135
+
136
+ Serialize, disk spool I/O, HTTP/Mongo all run on daemon worker threads.
120
137
  """
121
138
 
122
139
  def __init__(
@@ -130,6 +147,7 @@ class ExportWorker:
130
147
  on_drop: Optional[Callable[[Any], None]] = None,
131
148
  spool_path: Optional[str] = None,
132
149
  spool_poll: float = 1.0,
150
+ overflow_maxsize: Optional[int] = None,
133
151
  ):
134
152
  if maxsize < 1:
135
153
  raise ValueError("export queue maxsize must be >= 1")
@@ -154,6 +172,16 @@ class ExportWorker:
154
172
  self._spool_poll = max(0.2, float(spool_poll))
155
173
  self._spool_stop = threading.Event()
156
174
  self._spool_thread: Optional[threading.Thread] = None
175
+ # Overflow: hold Trace refs briefly until spool thread writes JSONL.
176
+ # Never serialize/disk on the calling (request) thread.
177
+ osize = (
178
+ overflow_maxsize
179
+ if overflow_maxsize is not None
180
+ else default_overflow_queue_size(maxsize)
181
+ )
182
+ self._overflow: Optional[queue.Queue] = (
183
+ queue.Queue(maxsize=max(1, osize)) if self._spool_path else None
184
+ )
157
185
  if self._spool_path:
158
186
  Path(self._spool_path).parent.mkdir(parents=True, exist_ok=True)
159
187
 
@@ -190,25 +218,35 @@ class ExportWorker:
190
218
  self._atexit_registered = True
191
219
 
192
220
  def enqueue(self, item: Any) -> bool:
193
- """Non-blocking. True if accepted to memory or durable spool."""
221
+ """Non-blocking. Never waits on disk, network, or locks held by exporters.
222
+
223
+ Latency budget on caller: a few microseconds for put_nowait.
224
+ """
194
225
  self.start()
195
226
  try:
196
227
  self._q.put_nowait(item)
197
228
  return True
198
229
  except queue.Full:
199
- if self._append_spool(item):
230
+ pass
231
+ # Main full → overflow handoff for async spool (still non-blocking).
232
+ if self._overflow is not None:
233
+ try:
234
+ self._overflow.put_nowait(item)
200
235
  self._spooled += 1
201
236
  return True
202
- self._dropped += 1
203
- self._log_drop(item)
204
- if self._on_drop is not None:
205
- try:
206
- self._on_drop(item)
207
- except Exception:
208
- pass
209
- return False
237
+ except queue.Full:
238
+ pass
239
+ self._dropped += 1
240
+ self._log_drop(item)
241
+ if self._on_drop is not None:
242
+ try:
243
+ self._on_drop(item)
244
+ except Exception:
245
+ pass
246
+ return False
210
247
 
211
248
  def _append_spool(self, item: Any) -> bool:
249
+ """Disk write — **worker thread only**, never request path."""
212
250
  if not self._spool_path:
213
251
  return False
214
252
  try:
@@ -221,8 +259,25 @@ class ExportWorker:
221
259
  _logger.warning("TraceCast: export spool write failed: %s", exc)
222
260
  return False
223
261
 
262
+ def _flush_overflow_to_spool(self) -> None:
263
+ if self._overflow is None:
264
+ return
265
+ while True:
266
+ try:
267
+ item = self._overflow.get_nowait()
268
+ except queue.Empty:
269
+ break
270
+ if not self._append_spool(item):
271
+ # Last resort: try main queue again; else count drop.
272
+ try:
273
+ self._q.put_nowait(item)
274
+ except queue.Full:
275
+ self._dropped += 1
276
+ self._log_drop(item)
277
+
224
278
  def _spool_loop(self) -> None:
225
279
  while not self._spool_stop.wait(self._spool_poll):
280
+ self._flush_overflow_to_spool()
226
281
  self._drain_spool_once()
227
282
 
228
283
  def _drain_spool_once(self) -> None:
@@ -277,6 +332,7 @@ class ExportWorker:
277
332
  def flush(self, timeout: Optional[float] = None) -> bool:
278
333
  if not self._started:
279
334
  return True
335
+ self._flush_overflow_to_spool()
280
336
  self._drain_spool_once()
281
337
  marker = _FlushMarker()
282
338
  deadline = None if timeout is None else time.monotonic() + timeout
@@ -302,6 +358,7 @@ class ExportWorker:
302
358
  self._spool_stop.set()
303
359
  if self._spool_thread is not None:
304
360
  self._spool_thread.join(timeout=min(2.0, timeout))
361
+ self._flush_overflow_to_spool()
305
362
  self._drain_spool_once()
306
363
  deadline = time.monotonic() + timeout
307
364
  while True:
@@ -190,8 +190,8 @@ class Tracer:
190
190
  return
191
191
  if self._export_worker is not None:
192
192
  self._schedule_online_eval(trace)
193
- # Enqueue live Trace — serialize happens on export worker thread.
194
- # Request path stays O(1) put_nowait (or spool append).
193
+ # Enqueue live Trace — serialize/HTTP/disk only on worker threads.
194
+ # Request path: put_nowait only (never blocks user message latency).
195
195
  self._export_worker.enqueue(trace)
196
196
  return
197
197
  self._export(trace)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tracecast
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: LLM observability SDK — framework-agnostic tracing for AI applications
5
5
  Author: Pedro Castanheira Costa
6
6
  License: MIT
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes