openlit 1.34.29__py3-none-any.whl → 1.34.31__py3-none-any.whl
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.
- openlit/__helpers.py +235 -86
- openlit/__init__.py +16 -13
- openlit/_instrumentors.py +2 -1
- openlit/evals/all.py +50 -21
- openlit/evals/bias_detection.py +47 -20
- openlit/evals/hallucination.py +53 -22
- openlit/evals/toxicity.py +50 -21
- openlit/evals/utils.py +54 -30
- openlit/guard/all.py +61 -19
- openlit/guard/prompt_injection.py +34 -14
- openlit/guard/restrict_topic.py +46 -15
- openlit/guard/sensitive_topic.py +34 -14
- openlit/guard/utils.py +58 -22
- openlit/instrumentation/ag2/__init__.py +24 -8
- openlit/instrumentation/ag2/ag2.py +34 -13
- openlit/instrumentation/ag2/async_ag2.py +34 -13
- openlit/instrumentation/ag2/utils.py +133 -30
- openlit/instrumentation/ai21/__init__.py +43 -14
- openlit/instrumentation/ai21/ai21.py +47 -21
- openlit/instrumentation/ai21/async_ai21.py +47 -21
- openlit/instrumentation/ai21/utils.py +299 -78
- openlit/instrumentation/anthropic/__init__.py +21 -4
- openlit/instrumentation/anthropic/anthropic.py +28 -17
- openlit/instrumentation/anthropic/async_anthropic.py +28 -17
- openlit/instrumentation/anthropic/utils.py +145 -35
- openlit/instrumentation/assemblyai/__init__.py +11 -2
- openlit/instrumentation/assemblyai/assemblyai.py +15 -4
- openlit/instrumentation/assemblyai/utils.py +120 -25
- openlit/instrumentation/astra/__init__.py +43 -10
- openlit/instrumentation/astra/astra.py +28 -5
- openlit/instrumentation/astra/async_astra.py +28 -5
- openlit/instrumentation/astra/utils.py +151 -55
- openlit/instrumentation/azure_ai_inference/__init__.py +43 -10
- openlit/instrumentation/azure_ai_inference/async_azure_ai_inference.py +53 -21
- openlit/instrumentation/azure_ai_inference/azure_ai_inference.py +53 -21
- openlit/instrumentation/azure_ai_inference/utils.py +307 -83
- openlit/instrumentation/bedrock/__init__.py +21 -4
- openlit/instrumentation/bedrock/bedrock.py +63 -25
- openlit/instrumentation/bedrock/utils.py +139 -30
- openlit/instrumentation/chroma/__init__.py +89 -16
- openlit/instrumentation/chroma/chroma.py +28 -6
- openlit/instrumentation/chroma/utils.py +167 -51
- openlit/instrumentation/cohere/__init__.py +63 -18
- openlit/instrumentation/cohere/async_cohere.py +63 -24
- openlit/instrumentation/cohere/cohere.py +63 -24
- openlit/instrumentation/cohere/utils.py +286 -73
- openlit/instrumentation/controlflow/__init__.py +35 -9
- openlit/instrumentation/controlflow/controlflow.py +66 -33
- openlit/instrumentation/crawl4ai/__init__.py +25 -10
- openlit/instrumentation/crawl4ai/async_crawl4ai.py +78 -31
- openlit/instrumentation/crawl4ai/crawl4ai.py +78 -31
- openlit/instrumentation/crewai/__init__.py +111 -24
- openlit/instrumentation/crewai/async_crewai.py +114 -0
- openlit/instrumentation/crewai/crewai.py +104 -131
- openlit/instrumentation/crewai/utils.py +615 -0
- openlit/instrumentation/dynamiq/__init__.py +46 -12
- openlit/instrumentation/dynamiq/dynamiq.py +74 -33
- openlit/instrumentation/elevenlabs/__init__.py +23 -4
- openlit/instrumentation/elevenlabs/async_elevenlabs.py +16 -4
- openlit/instrumentation/elevenlabs/elevenlabs.py +16 -4
- openlit/instrumentation/elevenlabs/utils.py +128 -25
- openlit/instrumentation/embedchain/__init__.py +11 -2
- openlit/instrumentation/embedchain/embedchain.py +68 -35
- openlit/instrumentation/firecrawl/__init__.py +24 -7
- openlit/instrumentation/firecrawl/firecrawl.py +46 -20
- openlit/instrumentation/google_ai_studio/__init__.py +45 -10
- openlit/instrumentation/google_ai_studio/async_google_ai_studio.py +67 -44
- openlit/instrumentation/google_ai_studio/google_ai_studio.py +67 -44
- openlit/instrumentation/google_ai_studio/utils.py +180 -67
- openlit/instrumentation/gpt4all/__init__.py +22 -7
- openlit/instrumentation/gpt4all/gpt4all.py +67 -29
- openlit/instrumentation/gpt4all/utils.py +285 -61
- openlit/instrumentation/gpu/__init__.py +128 -47
- openlit/instrumentation/groq/__init__.py +21 -4
- openlit/instrumentation/groq/async_groq.py +33 -21
- openlit/instrumentation/groq/groq.py +33 -21
- openlit/instrumentation/groq/utils.py +192 -55
- openlit/instrumentation/haystack/__init__.py +70 -24
- openlit/instrumentation/haystack/async_haystack.py +28 -6
- openlit/instrumentation/haystack/haystack.py +28 -6
- openlit/instrumentation/haystack/utils.py +196 -74
- openlit/instrumentation/julep/__init__.py +69 -19
- openlit/instrumentation/julep/async_julep.py +53 -27
- openlit/instrumentation/julep/julep.py +53 -28
- openlit/instrumentation/langchain/__init__.py +74 -63
- openlit/instrumentation/langchain/callback_handler.py +1100 -0
- openlit/instrumentation/langchain_community/__init__.py +13 -2
- openlit/instrumentation/langchain_community/async_langchain_community.py +23 -5
- openlit/instrumentation/langchain_community/langchain_community.py +23 -5
- openlit/instrumentation/langchain_community/utils.py +35 -9
- openlit/instrumentation/letta/__init__.py +68 -15
- openlit/instrumentation/letta/letta.py +99 -54
- openlit/instrumentation/litellm/__init__.py +43 -14
- openlit/instrumentation/litellm/async_litellm.py +51 -26
- openlit/instrumentation/litellm/litellm.py +51 -26
- openlit/instrumentation/litellm/utils.py +312 -101
- openlit/instrumentation/llamaindex/__init__.py +267 -90
- openlit/instrumentation/llamaindex/async_llamaindex.py +28 -6
- openlit/instrumentation/llamaindex/llamaindex.py +28 -6
- openlit/instrumentation/llamaindex/utils.py +204 -91
- openlit/instrumentation/mem0/__init__.py +11 -2
- openlit/instrumentation/mem0/mem0.py +50 -29
- openlit/instrumentation/milvus/__init__.py +10 -2
- openlit/instrumentation/milvus/milvus.py +31 -6
- openlit/instrumentation/milvus/utils.py +166 -67
- openlit/instrumentation/mistral/__init__.py +63 -18
- openlit/instrumentation/mistral/async_mistral.py +63 -24
- openlit/instrumentation/mistral/mistral.py +63 -24
- openlit/instrumentation/mistral/utils.py +277 -69
- openlit/instrumentation/multion/__init__.py +69 -19
- openlit/instrumentation/multion/async_multion.py +57 -26
- openlit/instrumentation/multion/multion.py +57 -26
- openlit/instrumentation/ollama/__init__.py +39 -18
- openlit/instrumentation/ollama/async_ollama.py +57 -26
- openlit/instrumentation/ollama/ollama.py +57 -26
- openlit/instrumentation/ollama/utils.py +226 -50
- openlit/instrumentation/openai/__init__.py +156 -32
- openlit/instrumentation/openai/async_openai.py +147 -67
- openlit/instrumentation/openai/openai.py +150 -67
- openlit/instrumentation/openai/utils.py +660 -186
- openlit/instrumentation/openai_agents/__init__.py +6 -2
- openlit/instrumentation/openai_agents/processor.py +409 -537
- openlit/instrumentation/phidata/__init__.py +13 -5
- openlit/instrumentation/phidata/phidata.py +67 -32
- openlit/instrumentation/pinecone/__init__.py +48 -9
- openlit/instrumentation/pinecone/async_pinecone.py +27 -5
- openlit/instrumentation/pinecone/pinecone.py +27 -5
- openlit/instrumentation/pinecone/utils.py +153 -47
- openlit/instrumentation/premai/__init__.py +22 -7
- openlit/instrumentation/premai/premai.py +51 -26
- openlit/instrumentation/premai/utils.py +246 -59
- openlit/instrumentation/pydantic_ai/__init__.py +49 -22
- openlit/instrumentation/pydantic_ai/pydantic_ai.py +69 -16
- openlit/instrumentation/pydantic_ai/utils.py +89 -24
- openlit/instrumentation/qdrant/__init__.py +19 -4
- openlit/instrumentation/qdrant/async_qdrant.py +33 -7
- openlit/instrumentation/qdrant/qdrant.py +33 -7
- openlit/instrumentation/qdrant/utils.py +228 -93
- openlit/instrumentation/reka/__init__.py +23 -10
- openlit/instrumentation/reka/async_reka.py +17 -11
- openlit/instrumentation/reka/reka.py +17 -11
- openlit/instrumentation/reka/utils.py +138 -36
- openlit/instrumentation/together/__init__.py +44 -12
- openlit/instrumentation/together/async_together.py +50 -27
- openlit/instrumentation/together/together.py +50 -27
- openlit/instrumentation/together/utils.py +301 -71
- openlit/instrumentation/transformers/__init__.py +2 -1
- openlit/instrumentation/transformers/transformers.py +13 -3
- openlit/instrumentation/transformers/utils.py +139 -36
- openlit/instrumentation/vertexai/__init__.py +81 -16
- openlit/instrumentation/vertexai/async_vertexai.py +33 -15
- openlit/instrumentation/vertexai/utils.py +123 -27
- openlit/instrumentation/vertexai/vertexai.py +33 -15
- openlit/instrumentation/vllm/__init__.py +12 -5
- openlit/instrumentation/vllm/utils.py +121 -31
- openlit/instrumentation/vllm/vllm.py +16 -10
- openlit/otel/events.py +35 -10
- openlit/otel/metrics.py +32 -24
- openlit/otel/tracing.py +24 -9
- openlit/semcov/__init__.py +101 -7
- {openlit-1.34.29.dist-info → openlit-1.34.31.dist-info}/METADATA +2 -1
- openlit-1.34.31.dist-info/RECORD +166 -0
- openlit/instrumentation/langchain/async_langchain.py +0 -102
- openlit/instrumentation/langchain/langchain.py +0 -102
- openlit/instrumentation/langchain/utils.py +0 -252
- openlit-1.34.29.dist-info/RECORD +0 -166
- {openlit-1.34.29.dist-info → openlit-1.34.31.dist-info}/LICENSE +0 -0
- {openlit-1.34.29.dist-info → openlit-1.34.31.dist-info}/WHEEL +0 -0
@@ -5,15 +5,29 @@ Module for monitoring Julep.
|
|
5
5
|
|
6
6
|
import logging
|
7
7
|
from opentelemetry.trace import SpanKind, Status, StatusCode
|
8
|
-
from opentelemetry.sdk.resources import
|
8
|
+
from opentelemetry.sdk.resources import (
|
9
|
+
SERVICE_NAME,
|
10
|
+
TELEMETRY_SDK_NAME,
|
11
|
+
DEPLOYMENT_ENVIRONMENT,
|
12
|
+
)
|
9
13
|
from openlit.__helpers import handle_exception
|
10
14
|
from openlit.semcov import SemanticConvention
|
11
15
|
|
12
16
|
# Initialize logger for logging potential issues and operations
|
13
17
|
logger = logging.getLogger(__name__)
|
14
18
|
|
15
|
-
|
16
|
-
|
19
|
+
|
20
|
+
def async_wrap_julep(
|
21
|
+
gen_ai_endpoint,
|
22
|
+
version,
|
23
|
+
environment,
|
24
|
+
application_name,
|
25
|
+
tracer,
|
26
|
+
pricing_info,
|
27
|
+
capture_message_content,
|
28
|
+
metrics,
|
29
|
+
disable_metrics,
|
30
|
+
):
|
17
31
|
"""
|
18
32
|
Creates a wrapper around a function call to trace and log its execution metrics.
|
19
33
|
|
@@ -55,44 +69,56 @@ def async_wrap_julep(gen_ai_endpoint, version, environment, application_name,
|
|
55
69
|
errors are handled and logged appropriately.
|
56
70
|
"""
|
57
71
|
|
58
|
-
with tracer.start_as_current_span(
|
72
|
+
with tracer.start_as_current_span(
|
73
|
+
gen_ai_endpoint, kind=SpanKind.CLIENT
|
74
|
+
) as span:
|
59
75
|
response = await wrapped(*args, **kwargs)
|
60
76
|
|
61
77
|
try:
|
62
78
|
span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
|
63
|
-
span.set_attribute(SemanticConvention.GEN_AI_ENDPOINT,
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
span.set_attribute(SERVICE_NAME,
|
70
|
-
|
71
|
-
|
72
|
-
|
79
|
+
span.set_attribute(SemanticConvention.GEN_AI_ENDPOINT, gen_ai_endpoint)
|
80
|
+
span.set_attribute(
|
81
|
+
SemanticConvention.GEN_AI_SYSTEM,
|
82
|
+
SemanticConvention.GEN_AI_SYSTEM_JULEP,
|
83
|
+
)
|
84
|
+
span.set_attribute(DEPLOYMENT_ENVIRONMENT, environment)
|
85
|
+
span.set_attribute(SERVICE_NAME, application_name)
|
86
|
+
span.set_attribute(
|
87
|
+
SemanticConvention.GEN_AI_OPERATION,
|
88
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_AGENT,
|
89
|
+
)
|
73
90
|
|
74
91
|
if gen_ai_endpoint == "julep.agents_create":
|
75
|
-
span.set_attribute(SemanticConvention.GEN_AI_AGENT_ID,
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
span.set_attribute(
|
80
|
-
|
81
|
-
|
82
|
-
|
92
|
+
span.set_attribute(SemanticConvention.GEN_AI_AGENT_ID, response.id)
|
93
|
+
span.set_attribute(
|
94
|
+
SemanticConvention.GEN_AI_AGENT_ROLE, kwargs.get("name", "")
|
95
|
+
)
|
96
|
+
span.set_attribute(
|
97
|
+
SemanticConvention.GEN_AI_REQUEST_MODEL,
|
98
|
+
kwargs.get("model", "gpt-4-turbo"),
|
99
|
+
)
|
100
|
+
span.set_attribute(
|
101
|
+
SemanticConvention.GEN_AI_AGENT_CONTEXT, kwargs.get("about", "")
|
102
|
+
)
|
83
103
|
|
84
104
|
elif gen_ai_endpoint == "julep.task_create":
|
85
|
-
span.set_attribute(
|
86
|
-
|
105
|
+
span.set_attribute(
|
106
|
+
SemanticConvention.GEN_AI_AGENT_TOOLS,
|
107
|
+
str(kwargs.get("tools", "")),
|
108
|
+
)
|
87
109
|
|
88
110
|
elif gen_ai_endpoint == "julep.execution_create":
|
89
|
-
span.set_attribute(
|
90
|
-
|
111
|
+
span.set_attribute(
|
112
|
+
SemanticConvention.GEN_AI_AGENT_TASK_ID,
|
113
|
+
kwargs.get("task_id", ""),
|
114
|
+
)
|
91
115
|
if capture_message_content:
|
92
116
|
span.add_event(
|
93
117
|
name=SemanticConvention.GEN_AI_CONTENT_PROMPT_EVENT,
|
94
118
|
attributes={
|
95
|
-
|
119
|
+
SemanticConvention.GEN_AI_CONTENT_PROMPT: str(
|
120
|
+
kwargs.get("input", "")
|
121
|
+
),
|
96
122
|
},
|
97
123
|
)
|
98
124
|
|
@@ -5,15 +5,29 @@ Module for monitoring Julep.
|
|
5
5
|
|
6
6
|
import logging
|
7
7
|
from opentelemetry.trace import SpanKind, Status, StatusCode
|
8
|
-
from opentelemetry.sdk.resources import
|
8
|
+
from opentelemetry.sdk.resources import (
|
9
|
+
SERVICE_NAME,
|
10
|
+
TELEMETRY_SDK_NAME,
|
11
|
+
DEPLOYMENT_ENVIRONMENT,
|
12
|
+
)
|
9
13
|
from openlit.__helpers import handle_exception
|
10
14
|
from openlit.semcov import SemanticConvention
|
11
15
|
|
12
16
|
# Initialize logger for logging potential issues and operations
|
13
17
|
logger = logging.getLogger(__name__)
|
14
18
|
|
15
|
-
|
16
|
-
|
19
|
+
|
20
|
+
def wrap_julep(
|
21
|
+
gen_ai_endpoint,
|
22
|
+
version,
|
23
|
+
environment,
|
24
|
+
application_name,
|
25
|
+
tracer,
|
26
|
+
pricing_info,
|
27
|
+
capture_message_content,
|
28
|
+
metrics,
|
29
|
+
disable_metrics,
|
30
|
+
):
|
17
31
|
"""
|
18
32
|
Creates a wrapper around a function call to trace and log its execution metrics.
|
19
33
|
|
@@ -55,48 +69,59 @@ def wrap_julep(gen_ai_endpoint, version, environment, application_name,
|
|
55
69
|
errors are handled and logged appropriately.
|
56
70
|
"""
|
57
71
|
|
58
|
-
with tracer.start_as_current_span(
|
72
|
+
with tracer.start_as_current_span(
|
73
|
+
gen_ai_endpoint, kind=SpanKind.CLIENT
|
74
|
+
) as span:
|
59
75
|
response = wrapped(*args, **kwargs)
|
60
76
|
|
61
77
|
try:
|
62
78
|
span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
|
63
|
-
span.set_attribute(SemanticConvention.GEN_AI_ENDPOINT,
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
span.set_attribute(SERVICE_NAME,
|
70
|
-
|
71
|
-
|
72
|
-
|
79
|
+
span.set_attribute(SemanticConvention.GEN_AI_ENDPOINT, gen_ai_endpoint)
|
80
|
+
span.set_attribute(
|
81
|
+
SemanticConvention.GEN_AI_SYSTEM,
|
82
|
+
SemanticConvention.GEN_AI_SYSTEM_JULEP,
|
83
|
+
)
|
84
|
+
span.set_attribute(DEPLOYMENT_ENVIRONMENT, environment)
|
85
|
+
span.set_attribute(SERVICE_NAME, application_name)
|
86
|
+
span.set_attribute(
|
87
|
+
SemanticConvention.GEN_AI_OPERATION,
|
88
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_AGENT,
|
89
|
+
)
|
73
90
|
|
74
91
|
if gen_ai_endpoint == "julep.agents_create":
|
75
|
-
span.set_attribute(SemanticConvention.GEN_AI_AGENT_ID,
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
span.set_attribute(
|
80
|
-
|
81
|
-
|
82
|
-
|
92
|
+
span.set_attribute(SemanticConvention.GEN_AI_AGENT_ID, response.id)
|
93
|
+
span.set_attribute(
|
94
|
+
SemanticConvention.GEN_AI_AGENT_ROLE, kwargs.get("name", "")
|
95
|
+
)
|
96
|
+
span.set_attribute(
|
97
|
+
SemanticConvention.GEN_AI_REQUEST_MODEL,
|
98
|
+
kwargs.get("model", "gpt-4-turbo"),
|
99
|
+
)
|
100
|
+
span.set_attribute(
|
101
|
+
SemanticConvention.GEN_AI_AGENT_CONTEXT, kwargs.get("about", "")
|
102
|
+
)
|
83
103
|
|
84
104
|
elif gen_ai_endpoint == "julep.task_create":
|
85
|
-
span.set_attribute(
|
86
|
-
|
105
|
+
span.set_attribute(
|
106
|
+
SemanticConvention.GEN_AI_AGENT_TOOLS,
|
107
|
+
str(kwargs.get("tools", "")),
|
108
|
+
)
|
87
109
|
|
88
110
|
elif gen_ai_endpoint == "julep.execution_create":
|
89
|
-
span.set_attribute(
|
90
|
-
|
111
|
+
span.set_attribute(
|
112
|
+
SemanticConvention.GEN_AI_AGENT_TASK_ID,
|
113
|
+
kwargs.get("task_id", ""),
|
114
|
+
)
|
91
115
|
if capture_message_content:
|
92
116
|
span.add_event(
|
93
117
|
name=SemanticConvention.GEN_AI_CONTENT_PROMPT_EVENT,
|
94
118
|
attributes={
|
95
|
-
|
119
|
+
SemanticConvention.GEN_AI_CONTENT_PROMPT: str(
|
120
|
+
kwargs.get("input", "")
|
121
|
+
),
|
96
122
|
},
|
97
123
|
)
|
98
124
|
|
99
|
-
|
100
125
|
span.set_status(Status(StatusCode.OK))
|
101
126
|
|
102
127
|
# Return original response
|
@@ -1,68 +1,47 @@
|
|
1
|
-
"""
|
1
|
+
"""
|
2
|
+
OpenLIT LangChain Instrumentation - Callback-Based Hierarchical Spans
|
3
|
+
"""
|
4
|
+
|
2
5
|
from typing import Collection
|
3
6
|
import importlib.metadata
|
4
7
|
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
5
8
|
from wrapt import wrap_function_wrapper
|
6
9
|
|
7
|
-
from openlit.instrumentation.langchain.
|
8
|
-
|
9
|
-
chat
|
10
|
-
)
|
11
|
-
from openlit.instrumentation.langchain.async_langchain import (
|
12
|
-
async_hub,
|
13
|
-
async_chat
|
10
|
+
from openlit.instrumentation.langchain.callback_handler import (
|
11
|
+
OpenLITLangChainCallbackHandler,
|
14
12
|
)
|
15
13
|
|
16
14
|
_instruments = ("langchain >= 0.1.20",)
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
"
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
"object": "BaseChatModel.invoke",
|
40
|
-
"endpoint": "langchain.chat_models",
|
41
|
-
"wrapper": chat,
|
42
|
-
},
|
43
|
-
{
|
44
|
-
"package": "langchain_core.language_models.chat_models",
|
45
|
-
"object": "BaseChatModel.ainvoke",
|
46
|
-
"endpoint": "langchain.chat_models",
|
47
|
-
"wrapper": async_chat,
|
48
|
-
},
|
49
|
-
{
|
50
|
-
"package": "langchain.chains.base",
|
51
|
-
"object": "Chain.invoke",
|
52
|
-
"endpoint": "langchain.chain.invoke",
|
53
|
-
"wrapper": chat,
|
54
|
-
},
|
55
|
-
{
|
56
|
-
"package": "langchain.chains.base",
|
57
|
-
"object": "Chain.ainvoke",
|
58
|
-
"endpoint": "langchain.chain.invoke",
|
59
|
-
"wrapper": async_chat,
|
60
|
-
}
|
61
|
-
]
|
16
|
+
|
17
|
+
class CallbackManagerWrapper: # pylint: disable=too-few-public-methods
|
18
|
+
"""Wrapper to inject OpenLIT callback handler into LangChain's callback system"""
|
19
|
+
|
20
|
+
def __init__(self, callback_handler: OpenLITLangChainCallbackHandler):
|
21
|
+
self.callback_handler = callback_handler
|
22
|
+
|
23
|
+
def __call__(self, wrapped, instance, args, kwargs):
|
24
|
+
"""Inject OpenLIT callback handler when BaseCallbackManager is initialized"""
|
25
|
+
|
26
|
+
# Call original initialization
|
27
|
+
wrapped(*args, **kwargs)
|
28
|
+
|
29
|
+
# Check if our callback handler is already registered
|
30
|
+
for handler in instance.inheritable_handlers:
|
31
|
+
if isinstance(handler, type(self.callback_handler)):
|
32
|
+
break
|
33
|
+
else:
|
34
|
+
# Add our callback handler to the manager
|
35
|
+
instance.add_handler(self.callback_handler, True)
|
36
|
+
|
62
37
|
|
63
38
|
class LangChainInstrumentor(BaseInstrumentor):
|
64
39
|
"""
|
65
|
-
|
40
|
+
OpenLIT LangChain instrumentor using callback-based hierarchical span creation.
|
41
|
+
|
42
|
+
This approach hooks into LangChain's built-in callback system to create
|
43
|
+
proper parent-child span relationships automatically, providing superior
|
44
|
+
observability with OpenLIT's comprehensive business intelligence.
|
66
45
|
"""
|
67
46
|
|
68
47
|
def instrumentation_dependencies(self) -> Collection[str]:
|
@@ -78,17 +57,49 @@ class LangChainInstrumentor(BaseInstrumentor):
|
|
78
57
|
metrics = kwargs.get("metrics_dict")
|
79
58
|
disable_metrics = kwargs.get("disable_metrics")
|
80
59
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
60
|
+
# Create OpenLIT callback handler with all configuration
|
61
|
+
openlit_callback_handler = OpenLITLangChainCallbackHandler(
|
62
|
+
tracer=tracer,
|
63
|
+
version=version,
|
64
|
+
environment=environment,
|
65
|
+
application_name=application_name,
|
66
|
+
pricing_info=pricing_info,
|
67
|
+
capture_message_content=capture_message_content,
|
68
|
+
metrics=metrics,
|
69
|
+
disable_metrics=disable_metrics,
|
70
|
+
)
|
71
|
+
|
72
|
+
# Hook into LangChain's callback system
|
73
|
+
# This automatically provides hierarchical spans for:
|
74
|
+
# - RunnableSequence (workflow spans)
|
75
|
+
# - PromptTemplate (task spans)
|
76
|
+
# - ChatOpenAI (chat spans)
|
77
|
+
# - Tools, Retrievers, etc.
|
78
|
+
try:
|
86
79
|
wrap_function_wrapper(
|
87
|
-
|
88
|
-
|
89
|
-
wrapper(
|
90
|
-
tracer, pricing_info, capture_message_content, metrics, disable_metrics),
|
80
|
+
module="langchain_core.callbacks",
|
81
|
+
name="BaseCallbackManager.__init__",
|
82
|
+
wrapper=CallbackManagerWrapper(openlit_callback_handler),
|
91
83
|
)
|
84
|
+
except Exception:
|
85
|
+
# Graceful degradation if callback system unavailable
|
86
|
+
pass
|
87
|
+
|
88
|
+
# === COMPETITIVE ADVANTAGE SUMMARY ===
|
89
|
+
# Callback-based approach provides:
|
90
|
+
# 1. ✅ Proper span hierarchy (like OpenLLMetry/OpenInference)
|
91
|
+
# 2. ✅ Comprehensive business intelligence (cost, tokens, performance)
|
92
|
+
# 3. ✅ Automatic operation coverage (no manual function wrapping)
|
93
|
+
# 4. ✅ Native LangChain integration (uses built-in callback system)
|
94
|
+
# 5. ✅ Superior observability depth and quality
|
95
|
+
|
96
|
+
# Result: Best of both worlds - hierarchy + business intelligence
|
92
97
|
|
93
98
|
def _uninstrument(self, **kwargs):
|
94
|
-
|
99
|
+
"""Remove instrumentation"""
|
100
|
+
try:
|
101
|
+
from opentelemetry.instrumentation.utils import unwrap
|
102
|
+
|
103
|
+
unwrap("langchain_core.callbacks", "BaseCallbackManager.__init__")
|
104
|
+
except Exception:
|
105
|
+
pass
|