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.
- {tracecast-0.2.7/tracecast.egg-info → tracecast-0.2.9}/PKG-INFO +1 -1
- {tracecast-0.2.7 → tracecast-0.2.9}/pyproject.toml +1 -1
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/__init__.py +2 -2
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrument.py +18 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/openai_inst.py +53 -25
- {tracecast-0.2.7 → tracecast-0.2.9/tracecast.egg-info}/PKG-INFO +1 -1
- {tracecast-0.2.7 → tracecast-0.2.9}/LICENSE +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/README.md +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/setup.cfg +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_aggregator_graph.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_context_propagation.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_cost_calculator.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_dashboard_mount.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_dashboard_pushdown.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_dashboard_sessions.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_decorators.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_e2e_langgraph.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_exporters_db.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_integration_frameworks.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_integration_langchain.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_json_exporter.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_langchain_callback.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_llm_wrappers.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_logging.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_mongo_exporter.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_prompt_dashboard.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_prompts.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_roundtrip.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_schema_v2.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_score.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_score_dashboard.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_scoring_api.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_serve.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_span.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_streaming.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_token_counter.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_trace.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_trace_span.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tests/test_tracer.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/core/__init__.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/core/cost_calculator.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/core/logger.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/core/scoring.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/core/token_counter.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/core/tracer.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/__init__.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/aggregator.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/asgi_middleware.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/blueprint.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/eval_reader.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/prompt_reader.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/reader.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/router.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/score_reader.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/standalone.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/static/assets/index-B07hiBI2.js +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/static/assets/index-BMIlEmLS.js +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/static/assets/index-CY5YOA8F.js +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/static/assets/index-DllJJzmE.css +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/static/assets/index-Dt0awpJJ.js +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/dashboard/static/index.html +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/decorators.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/__init__.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/cli.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/compare.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/dataset.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/decorator.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/judge.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/metrics.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/models.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/online.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/runner.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/eval/scorers.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/__init__.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/_eval_store.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/base.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/dict_exporter.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/json_file.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/mongo.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/postgres.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/exporters/query.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/__init__.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/_llamaindex_handler.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/_streaming.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/anthropic_inst.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/base.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/crewai_inst.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/gemini_inst.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/langchain_inst.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/instrumentors/llamaindex_inst.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/integrations/__init__.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/integrations/langchain.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/integrations/llm.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/middleware.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/models/__init__.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/models/score.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/models/span.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/models/trace.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/prompts/__init__.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/prompts/client.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/prompts/models.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast/serve.py +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast.egg-info/SOURCES.txt +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast.egg-info/dependency_links.txt +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast.egg-info/entry_points.txt +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast.egg-info/requires.txt +0 -0
- {tracecast-0.2.7 → tracecast-0.2.9}/tracecast.egg-info/top_level.txt +0 -0
|
@@ -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(
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
46
|
-
|
|
70
|
+
def patched_create(client_self, *args, **kwargs):
|
|
71
|
+
return self_ref._intercept(client_self, args, kwargs, _orig_create)
|
|
47
72
|
|
|
48
|
-
|
|
73
|
+
mod.Completions.create = patched_create
|
|
49
74
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
75
|
+
# Async
|
|
76
|
+
if hasattr(mod, "AsyncCompletions"):
|
|
77
|
+
self._original_acreate = mod.AsyncCompletions.create
|
|
78
|
+
_orig_acreate = self._original_acreate
|
|
54
79
|
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
83
|
+
mod.AsyncCompletions.create = patched_acreate
|
|
59
84
|
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|