tracecast 0.2.7__tar.gz → 0.2.9__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.7/tracecast.egg-info → tracecast-0.2.9}/PKG-INFO +1 -1
  2. {tracecast-0.2.7 → tracecast-0.2.9}/pyproject.toml +1 -1
  3. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/__init__.py +2 -2
  4. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrument.py +18 -0
  5. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/openai_inst.py +53 -25
  6. {tracecast-0.2.7 → tracecast-0.2.9/tracecast.egg-info}/PKG-INFO +1 -1
  7. {tracecast-0.2.7 → tracecast-0.2.9}/LICENSE +0 -0
  8. {tracecast-0.2.7 → tracecast-0.2.9}/README.md +0 -0
  9. {tracecast-0.2.7 → tracecast-0.2.9}/setup.cfg +0 -0
  10. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_aggregator_graph.py +0 -0
  11. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_context_propagation.py +0 -0
  12. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_cost_calculator.py +0 -0
  13. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_dashboard_mount.py +0 -0
  14. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_dashboard_pushdown.py +0 -0
  15. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_dashboard_sessions.py +0 -0
  16. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_decorators.py +0 -0
  17. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_e2e_langgraph.py +0 -0
  18. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_exporters_db.py +0 -0
  19. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_integration_frameworks.py +0 -0
  20. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_integration_langchain.py +0 -0
  21. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_json_exporter.py +0 -0
  22. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_langchain_callback.py +0 -0
  23. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_llm_wrappers.py +0 -0
  24. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_logging.py +0 -0
  25. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_mongo_exporter.py +0 -0
  26. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_prompt_dashboard.py +0 -0
  27. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_prompts.py +0 -0
  28. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_roundtrip.py +0 -0
  29. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_schema_v2.py +0 -0
  30. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_score.py +0 -0
  31. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_score_dashboard.py +0 -0
  32. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_scoring_api.py +0 -0
  33. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_serve.py +0 -0
  34. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_span.py +0 -0
  35. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_streaming.py +0 -0
  36. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_token_counter.py +0 -0
  37. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_trace.py +0 -0
  38. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_trace_span.py +0 -0
  39. {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_tracer.py +0 -0
  40. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/core/__init__.py +0 -0
  41. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/core/cost_calculator.py +0 -0
  42. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/core/logger.py +0 -0
  43. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/core/scoring.py +0 -0
  44. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/core/token_counter.py +0 -0
  45. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/core/tracer.py +0 -0
  46. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/__init__.py +0 -0
  47. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/aggregator.py +0 -0
  48. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/asgi_middleware.py +0 -0
  49. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/blueprint.py +0 -0
  50. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/eval_reader.py +0 -0
  51. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/prompt_reader.py +0 -0
  52. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/reader.py +0 -0
  53. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/router.py +0 -0
  54. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/score_reader.py +0 -0
  55. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/standalone.py +0 -0
  56. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/static/assets/index-B07hiBI2.js +0 -0
  57. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/static/assets/index-BMIlEmLS.js +0 -0
  58. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/static/assets/index-CY5YOA8F.js +0 -0
  59. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/static/assets/index-DllJJzmE.css +0 -0
  60. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/static/assets/index-Dt0awpJJ.js +0 -0
  61. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/static/index.html +0 -0
  62. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/decorators.py +0 -0
  63. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/__init__.py +0 -0
  64. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/cli.py +0 -0
  65. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/compare.py +0 -0
  66. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/dataset.py +0 -0
  67. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/decorator.py +0 -0
  68. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/judge.py +0 -0
  69. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/metrics.py +0 -0
  70. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/models.py +0 -0
  71. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/online.py +0 -0
  72. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/runner.py +0 -0
  73. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/scorers.py +0 -0
  74. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/__init__.py +0 -0
  75. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/_eval_store.py +0 -0
  76. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/base.py +0 -0
  77. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/dict_exporter.py +0 -0
  78. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/json_file.py +0 -0
  79. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/mongo.py +0 -0
  80. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/postgres.py +0 -0
  81. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/query.py +0 -0
  82. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/__init__.py +0 -0
  83. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/_llamaindex_handler.py +0 -0
  84. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/_streaming.py +0 -0
  85. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/anthropic_inst.py +0 -0
  86. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/base.py +0 -0
  87. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/crewai_inst.py +0 -0
  88. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/gemini_inst.py +0 -0
  89. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/langchain_inst.py +0 -0
  90. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/llamaindex_inst.py +0 -0
  91. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/integrations/__init__.py +0 -0
  92. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/integrations/langchain.py +0 -0
  93. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/integrations/llm.py +0 -0
  94. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/middleware.py +0 -0
  95. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/models/__init__.py +0 -0
  96. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/models/score.py +0 -0
  97. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/models/span.py +0 -0
  98. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/models/trace.py +0 -0
  99. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/prompts/__init__.py +0 -0
  100. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/prompts/client.py +0 -0
  101. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/prompts/models.py +0 -0
  102. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/serve.py +0 -0
  103. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast.egg-info/SOURCES.txt +0 -0
  104. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast.egg-info/dependency_links.txt +0 -0
  105. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast.egg-info/entry_points.txt +0 -0
  106. {tracecast-0.2.7 → tracecast-0.2.9}/tracecast.egg-info/requires.txt +0 -0
  107. {tracecast-0.2.7 → tracecast-0.2.9}/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.7
3
+ Version: 0.2.9
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.7"
7
+ version = "0.2.9"
8
8
  description = "LLM observability SDK — framework-agnostic tracing for AI applications"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -8,7 +8,7 @@ from .decorators import trace_cast, trace_span, set_default_tracer
8
8
  from .core.scoring import score
9
9
  from .prompts import create_prompt, get_prompt, set_label, PromptVersion
10
10
  from .integrations.llm import trace_llm_call, wrap_openai, wrap_anthropic
11
- from .instrument import auto_instrument
11
+ from .instrument import auto_instrument, instrument_openai
12
12
  from .exporters.dict_exporter import DictExporter
13
13
 
14
14
  __all__ = [
@@ -18,5 +18,5 @@ __all__ = [
18
18
  "trace_cast", "trace_span", "set_default_tracer", "score",
19
19
  "create_prompt", "get_prompt", "set_label", "PromptVersion",
20
20
  "trace_llm_call", "wrap_openai", "wrap_anthropic",
21
- "auto_instrument", "DictExporter",
21
+ "auto_instrument", "instrument_openai", "DictExporter",
22
22
  ]
@@ -34,6 +34,24 @@ def auto_instrument(tracer: Optional[Tracer] = None) -> None:
34
34
  _instrumented = True
35
35
 
36
36
 
37
+ def instrument_openai(*, chat: bool = True, embeddings: bool = True, audio: bool = True) -> None:
38
+ """Patch only the requested OpenAI API surfaces, independent of auto_instrument().
39
+
40
+ Use `chat=False` when chat completions are already traced through another
41
+ mechanism (e.g. a manually-wired LangChain callback) and you only want to
42
+ add embeddings/audio coverage — re-patching chat.completions in that case
43
+ would produce duplicate spans for every chat call.
44
+ """
45
+ from .instrumentors.openai_inst import OpenAIInstrumentor
46
+ with _lock:
47
+ inst = _registry.get("openai")
48
+ if inst is None:
49
+ inst = OpenAIInstrumentor()
50
+ _registry["openai"] = inst
51
+ if not inst.is_patched():
52
+ inst.patch(chat=chat, embeddings=embeddings, audio=audio)
53
+
54
+
37
55
  def _reset() -> None:
38
56
  global _instrumented
39
57
  with _lock:
@@ -5,6 +5,22 @@ from typing import Any, Optional
5
5
  from .base import BaseInstrumentor, active_parent_id
6
6
 
7
7
 
8
+ # Only LangChain's own LLM-wrapper packages directly invoke the underlying
9
+ # provider client on their own behalf, triggering LangChain's callback system
10
+ # (which TraceCastCallback hooks into via LangChainInstrumentor). Orchestration
11
+ # packages (langchain_core.tools, langchain.agents, langgraph.*) run *above*
12
+ # a raw client call made by application code (e.g. a @tool function) — that
13
+ # call is invisible to LangChain's callbacks, so it must NOT be deferred here,
14
+ # or it silently disappears from tracing entirely.
15
+ _LANGCHAIN_LLM_WRAPPER_PREFIXES = (
16
+ "langchain_openai.",
17
+ "langchain_community.chat_models",
18
+ "langchain_community.llms",
19
+ "langchain.chat_models",
20
+ "langchain.llms",
21
+ )
22
+
23
+
8
24
  def _handled_by_langchain() -> bool:
9
25
  from ..instrument import _registry
10
26
 
@@ -15,7 +31,7 @@ def _handled_by_langchain() -> bool:
15
31
  depth = 0
16
32
  while frame is not None and depth < 60:
17
33
  module = frame.f_globals.get("__name__", "")
18
- if module.startswith("langchain"):
34
+ if module.startswith(_LANGCHAIN_LLM_WRAPPER_PREFIXES):
19
35
  return True
20
36
  frame = frame.f_back
21
37
  depth += 1
@@ -32,33 +48,44 @@ class OpenAIInstrumentor(BaseInstrumentor):
32
48
  self._original_speech_create: Optional[Any] = None
33
49
  self._patched: bool = False
34
50
 
35
- def patch(self) -> None:
51
+ def patch(self, *, chat: bool = True, embeddings: bool = True, audio: bool = True) -> None:
52
+ """Patch the requested OpenAI API surfaces.
53
+
54
+ `chat=False` lets callers add embeddings/audio coverage without
55
+ re-patching chat.completions — needed when chat completions are
56
+ already traced through another mechanism (e.g. a manually-wired
57
+ LangChain callback), where re-patching would double-count spans.
58
+ """
36
59
  if self._patched:
37
60
  return
38
- import openai.resources.chat.completions as mod
39
61
 
40
- # Sync
41
- self._original_create = mod.Completions.create
42
- self_ref = self
43
- _orig_create = self._original_create
62
+ if chat:
63
+ import openai.resources.chat.completions as mod
64
+
65
+ # Sync
66
+ self._original_create = mod.Completions.create
67
+ self_ref = self
68
+ _orig_create = self._original_create
44
69
 
45
- def patched_create(client_self, *args, **kwargs):
46
- return self_ref._intercept(client_self, args, kwargs, _orig_create)
70
+ def patched_create(client_self, *args, **kwargs):
71
+ return self_ref._intercept(client_self, args, kwargs, _orig_create)
47
72
 
48
- mod.Completions.create = patched_create
73
+ mod.Completions.create = patched_create
49
74
 
50
- # Async
51
- if hasattr(mod, "AsyncCompletions"):
52
- self._original_acreate = mod.AsyncCompletions.create
53
- _orig_acreate = self._original_acreate
75
+ # Async
76
+ if hasattr(mod, "AsyncCompletions"):
77
+ self._original_acreate = mod.AsyncCompletions.create
78
+ _orig_acreate = self._original_acreate
54
79
 
55
- async def patched_acreate(client_self, *args, **kwargs):
56
- return await self_ref._async_intercept(client_self, args, kwargs, _orig_acreate)
80
+ async def patched_acreate(client_self, *args, **kwargs):
81
+ return await self_ref._async_intercept(client_self, args, kwargs, _orig_acreate)
57
82
 
58
- mod.AsyncCompletions.create = patched_acreate
83
+ mod.AsyncCompletions.create = patched_acreate
59
84
 
60
- self._patch_embeddings()
61
- self._patch_audio()
85
+ if embeddings:
86
+ self._patch_embeddings()
87
+ if audio:
88
+ self._patch_audio()
62
89
 
63
90
  self._patched = True
64
91
 
@@ -108,12 +135,13 @@ class OpenAIInstrumentor(BaseInstrumentor):
108
135
  def unpatch(self) -> None:
109
136
  if not self._patched:
110
137
  return
111
- import openai.resources.chat.completions as mod
112
- mod.Completions.create = self._original_create
113
- if self._original_acreate is not None:
114
- mod.AsyncCompletions.create = self._original_acreate
115
- self._original_create = None
116
- self._original_acreate = None
138
+ if self._original_create is not None:
139
+ import openai.resources.chat.completions as mod
140
+ mod.Completions.create = self._original_create
141
+ if self._original_acreate is not None:
142
+ mod.AsyncCompletions.create = self._original_acreate
143
+ self._original_create = None
144
+ self._original_acreate = None
117
145
 
118
146
  if self._original_embeddings_create is not None:
119
147
  import openai.resources.embeddings as emb_mod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tracecast
3
- Version: 0.2.7
3
+ Version: 0.2.9
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