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.
Files changed (168) hide show
  1. openlit/__helpers.py +235 -86
  2. openlit/__init__.py +16 -13
  3. openlit/_instrumentors.py +2 -1
  4. openlit/evals/all.py +50 -21
  5. openlit/evals/bias_detection.py +47 -20
  6. openlit/evals/hallucination.py +53 -22
  7. openlit/evals/toxicity.py +50 -21
  8. openlit/evals/utils.py +54 -30
  9. openlit/guard/all.py +61 -19
  10. openlit/guard/prompt_injection.py +34 -14
  11. openlit/guard/restrict_topic.py +46 -15
  12. openlit/guard/sensitive_topic.py +34 -14
  13. openlit/guard/utils.py +58 -22
  14. openlit/instrumentation/ag2/__init__.py +24 -8
  15. openlit/instrumentation/ag2/ag2.py +34 -13
  16. openlit/instrumentation/ag2/async_ag2.py +34 -13
  17. openlit/instrumentation/ag2/utils.py +133 -30
  18. openlit/instrumentation/ai21/__init__.py +43 -14
  19. openlit/instrumentation/ai21/ai21.py +47 -21
  20. openlit/instrumentation/ai21/async_ai21.py +47 -21
  21. openlit/instrumentation/ai21/utils.py +299 -78
  22. openlit/instrumentation/anthropic/__init__.py +21 -4
  23. openlit/instrumentation/anthropic/anthropic.py +28 -17
  24. openlit/instrumentation/anthropic/async_anthropic.py +28 -17
  25. openlit/instrumentation/anthropic/utils.py +145 -35
  26. openlit/instrumentation/assemblyai/__init__.py +11 -2
  27. openlit/instrumentation/assemblyai/assemblyai.py +15 -4
  28. openlit/instrumentation/assemblyai/utils.py +120 -25
  29. openlit/instrumentation/astra/__init__.py +43 -10
  30. openlit/instrumentation/astra/astra.py +28 -5
  31. openlit/instrumentation/astra/async_astra.py +28 -5
  32. openlit/instrumentation/astra/utils.py +151 -55
  33. openlit/instrumentation/azure_ai_inference/__init__.py +43 -10
  34. openlit/instrumentation/azure_ai_inference/async_azure_ai_inference.py +53 -21
  35. openlit/instrumentation/azure_ai_inference/azure_ai_inference.py +53 -21
  36. openlit/instrumentation/azure_ai_inference/utils.py +307 -83
  37. openlit/instrumentation/bedrock/__init__.py +21 -4
  38. openlit/instrumentation/bedrock/bedrock.py +63 -25
  39. openlit/instrumentation/bedrock/utils.py +139 -30
  40. openlit/instrumentation/chroma/__init__.py +89 -16
  41. openlit/instrumentation/chroma/chroma.py +28 -6
  42. openlit/instrumentation/chroma/utils.py +167 -51
  43. openlit/instrumentation/cohere/__init__.py +63 -18
  44. openlit/instrumentation/cohere/async_cohere.py +63 -24
  45. openlit/instrumentation/cohere/cohere.py +63 -24
  46. openlit/instrumentation/cohere/utils.py +286 -73
  47. openlit/instrumentation/controlflow/__init__.py +35 -9
  48. openlit/instrumentation/controlflow/controlflow.py +66 -33
  49. openlit/instrumentation/crawl4ai/__init__.py +25 -10
  50. openlit/instrumentation/crawl4ai/async_crawl4ai.py +78 -31
  51. openlit/instrumentation/crawl4ai/crawl4ai.py +78 -31
  52. openlit/instrumentation/crewai/__init__.py +111 -24
  53. openlit/instrumentation/crewai/async_crewai.py +114 -0
  54. openlit/instrumentation/crewai/crewai.py +104 -131
  55. openlit/instrumentation/crewai/utils.py +615 -0
  56. openlit/instrumentation/dynamiq/__init__.py +46 -12
  57. openlit/instrumentation/dynamiq/dynamiq.py +74 -33
  58. openlit/instrumentation/elevenlabs/__init__.py +23 -4
  59. openlit/instrumentation/elevenlabs/async_elevenlabs.py +16 -4
  60. openlit/instrumentation/elevenlabs/elevenlabs.py +16 -4
  61. openlit/instrumentation/elevenlabs/utils.py +128 -25
  62. openlit/instrumentation/embedchain/__init__.py +11 -2
  63. openlit/instrumentation/embedchain/embedchain.py +68 -35
  64. openlit/instrumentation/firecrawl/__init__.py +24 -7
  65. openlit/instrumentation/firecrawl/firecrawl.py +46 -20
  66. openlit/instrumentation/google_ai_studio/__init__.py +45 -10
  67. openlit/instrumentation/google_ai_studio/async_google_ai_studio.py +67 -44
  68. openlit/instrumentation/google_ai_studio/google_ai_studio.py +67 -44
  69. openlit/instrumentation/google_ai_studio/utils.py +180 -67
  70. openlit/instrumentation/gpt4all/__init__.py +22 -7
  71. openlit/instrumentation/gpt4all/gpt4all.py +67 -29
  72. openlit/instrumentation/gpt4all/utils.py +285 -61
  73. openlit/instrumentation/gpu/__init__.py +128 -47
  74. openlit/instrumentation/groq/__init__.py +21 -4
  75. openlit/instrumentation/groq/async_groq.py +33 -21
  76. openlit/instrumentation/groq/groq.py +33 -21
  77. openlit/instrumentation/groq/utils.py +192 -55
  78. openlit/instrumentation/haystack/__init__.py +70 -24
  79. openlit/instrumentation/haystack/async_haystack.py +28 -6
  80. openlit/instrumentation/haystack/haystack.py +28 -6
  81. openlit/instrumentation/haystack/utils.py +196 -74
  82. openlit/instrumentation/julep/__init__.py +69 -19
  83. openlit/instrumentation/julep/async_julep.py +53 -27
  84. openlit/instrumentation/julep/julep.py +53 -28
  85. openlit/instrumentation/langchain/__init__.py +74 -63
  86. openlit/instrumentation/langchain/callback_handler.py +1100 -0
  87. openlit/instrumentation/langchain_community/__init__.py +13 -2
  88. openlit/instrumentation/langchain_community/async_langchain_community.py +23 -5
  89. openlit/instrumentation/langchain_community/langchain_community.py +23 -5
  90. openlit/instrumentation/langchain_community/utils.py +35 -9
  91. openlit/instrumentation/letta/__init__.py +68 -15
  92. openlit/instrumentation/letta/letta.py +99 -54
  93. openlit/instrumentation/litellm/__init__.py +43 -14
  94. openlit/instrumentation/litellm/async_litellm.py +51 -26
  95. openlit/instrumentation/litellm/litellm.py +51 -26
  96. openlit/instrumentation/litellm/utils.py +312 -101
  97. openlit/instrumentation/llamaindex/__init__.py +267 -90
  98. openlit/instrumentation/llamaindex/async_llamaindex.py +28 -6
  99. openlit/instrumentation/llamaindex/llamaindex.py +28 -6
  100. openlit/instrumentation/llamaindex/utils.py +204 -91
  101. openlit/instrumentation/mem0/__init__.py +11 -2
  102. openlit/instrumentation/mem0/mem0.py +50 -29
  103. openlit/instrumentation/milvus/__init__.py +10 -2
  104. openlit/instrumentation/milvus/milvus.py +31 -6
  105. openlit/instrumentation/milvus/utils.py +166 -67
  106. openlit/instrumentation/mistral/__init__.py +63 -18
  107. openlit/instrumentation/mistral/async_mistral.py +63 -24
  108. openlit/instrumentation/mistral/mistral.py +63 -24
  109. openlit/instrumentation/mistral/utils.py +277 -69
  110. openlit/instrumentation/multion/__init__.py +69 -19
  111. openlit/instrumentation/multion/async_multion.py +57 -26
  112. openlit/instrumentation/multion/multion.py +57 -26
  113. openlit/instrumentation/ollama/__init__.py +39 -18
  114. openlit/instrumentation/ollama/async_ollama.py +57 -26
  115. openlit/instrumentation/ollama/ollama.py +57 -26
  116. openlit/instrumentation/ollama/utils.py +226 -50
  117. openlit/instrumentation/openai/__init__.py +156 -32
  118. openlit/instrumentation/openai/async_openai.py +147 -67
  119. openlit/instrumentation/openai/openai.py +150 -67
  120. openlit/instrumentation/openai/utils.py +660 -186
  121. openlit/instrumentation/openai_agents/__init__.py +6 -2
  122. openlit/instrumentation/openai_agents/processor.py +409 -537
  123. openlit/instrumentation/phidata/__init__.py +13 -5
  124. openlit/instrumentation/phidata/phidata.py +67 -32
  125. openlit/instrumentation/pinecone/__init__.py +48 -9
  126. openlit/instrumentation/pinecone/async_pinecone.py +27 -5
  127. openlit/instrumentation/pinecone/pinecone.py +27 -5
  128. openlit/instrumentation/pinecone/utils.py +153 -47
  129. openlit/instrumentation/premai/__init__.py +22 -7
  130. openlit/instrumentation/premai/premai.py +51 -26
  131. openlit/instrumentation/premai/utils.py +246 -59
  132. openlit/instrumentation/pydantic_ai/__init__.py +49 -22
  133. openlit/instrumentation/pydantic_ai/pydantic_ai.py +69 -16
  134. openlit/instrumentation/pydantic_ai/utils.py +89 -24
  135. openlit/instrumentation/qdrant/__init__.py +19 -4
  136. openlit/instrumentation/qdrant/async_qdrant.py +33 -7
  137. openlit/instrumentation/qdrant/qdrant.py +33 -7
  138. openlit/instrumentation/qdrant/utils.py +228 -93
  139. openlit/instrumentation/reka/__init__.py +23 -10
  140. openlit/instrumentation/reka/async_reka.py +17 -11
  141. openlit/instrumentation/reka/reka.py +17 -11
  142. openlit/instrumentation/reka/utils.py +138 -36
  143. openlit/instrumentation/together/__init__.py +44 -12
  144. openlit/instrumentation/together/async_together.py +50 -27
  145. openlit/instrumentation/together/together.py +50 -27
  146. openlit/instrumentation/together/utils.py +301 -71
  147. openlit/instrumentation/transformers/__init__.py +2 -1
  148. openlit/instrumentation/transformers/transformers.py +13 -3
  149. openlit/instrumentation/transformers/utils.py +139 -36
  150. openlit/instrumentation/vertexai/__init__.py +81 -16
  151. openlit/instrumentation/vertexai/async_vertexai.py +33 -15
  152. openlit/instrumentation/vertexai/utils.py +123 -27
  153. openlit/instrumentation/vertexai/vertexai.py +33 -15
  154. openlit/instrumentation/vllm/__init__.py +12 -5
  155. openlit/instrumentation/vllm/utils.py +121 -31
  156. openlit/instrumentation/vllm/vllm.py +16 -10
  157. openlit/otel/events.py +35 -10
  158. openlit/otel/metrics.py +32 -24
  159. openlit/otel/tracing.py +24 -9
  160. openlit/semcov/__init__.py +101 -7
  161. {openlit-1.34.29.dist-info → openlit-1.34.31.dist-info}/METADATA +2 -1
  162. openlit-1.34.31.dist-info/RECORD +166 -0
  163. openlit/instrumentation/langchain/async_langchain.py +0 -102
  164. openlit/instrumentation/langchain/langchain.py +0 -102
  165. openlit/instrumentation/langchain/utils.py +0 -252
  166. openlit-1.34.29.dist-info/RECORD +0 -166
  167. {openlit-1.34.29.dist-info → openlit-1.34.31.dist-info}/LICENSE +0 -0
  168. {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 SERVICE_NAME, TELEMETRY_SDK_NAME, DEPLOYMENT_ENVIRONMENT
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
- def async_wrap_julep(gen_ai_endpoint, version, environment, application_name,
16
- tracer, pricing_info, capture_message_content, metrics, disable_metrics):
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(gen_ai_endpoint, kind= SpanKind.CLIENT) as 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
- gen_ai_endpoint)
65
- span.set_attribute(SemanticConvention.GEN_AI_SYSTEM,
66
- SemanticConvention.GEN_AI_SYSTEM_JULEP)
67
- span.set_attribute(DEPLOYMENT_ENVIRONMENT,
68
- environment)
69
- span.set_attribute(SERVICE_NAME,
70
- application_name)
71
- span.set_attribute(SemanticConvention.GEN_AI_OPERATION,
72
- SemanticConvention.GEN_AI_OPERATION_TYPE_AGENT)
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
- response.id)
77
- span.set_attribute(SemanticConvention.GEN_AI_AGENT_ROLE,
78
- kwargs.get("name", ""))
79
- span.set_attribute(SemanticConvention.GEN_AI_REQUEST_MODEL,
80
- kwargs.get("model", "gpt-4-turbo"))
81
- span.set_attribute(SemanticConvention.GEN_AI_AGENT_CONTEXT,
82
- kwargs.get("about", ""))
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(SemanticConvention.GEN_AI_AGENT_TOOLS,
86
- str(kwargs.get("tools", "")))
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(SemanticConvention.GEN_AI_AGENT_TASK_ID,
90
- kwargs.get("task_id", ""))
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
- SemanticConvention.GEN_AI_CONTENT_PROMPT:str(kwargs.get("input", "")),
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 SERVICE_NAME, TELEMETRY_SDK_NAME, DEPLOYMENT_ENVIRONMENT
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
- def wrap_julep(gen_ai_endpoint, version, environment, application_name,
16
- tracer, pricing_info, capture_message_content, metrics, disable_metrics):
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(gen_ai_endpoint, kind= SpanKind.CLIENT) as 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
- gen_ai_endpoint)
65
- span.set_attribute(SemanticConvention.GEN_AI_SYSTEM,
66
- SemanticConvention.GEN_AI_SYSTEM_JULEP)
67
- span.set_attribute(DEPLOYMENT_ENVIRONMENT,
68
- environment)
69
- span.set_attribute(SERVICE_NAME,
70
- application_name)
71
- span.set_attribute(SemanticConvention.GEN_AI_OPERATION,
72
- SemanticConvention.GEN_AI_OPERATION_TYPE_AGENT)
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
- response.id)
77
- span.set_attribute(SemanticConvention.GEN_AI_AGENT_ROLE,
78
- kwargs.get("name", ""))
79
- span.set_attribute(SemanticConvention.GEN_AI_REQUEST_MODEL,
80
- kwargs.get("model", "gpt-4-turbo"))
81
- span.set_attribute(SemanticConvention.GEN_AI_AGENT_CONTEXT,
82
- kwargs.get("about", ""))
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(SemanticConvention.GEN_AI_AGENT_TOOLS,
86
- str(kwargs.get("tools", "")))
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(SemanticConvention.GEN_AI_AGENT_TASK_ID,
90
- kwargs.get("task_id", ""))
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
- SemanticConvention.GEN_AI_CONTENT_PROMPT:str(kwargs.get("input", "")),
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
- """Initializer of Auto Instrumentation of LangChain Functions"""
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.langchain import (
8
- hub,
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
- WRAPPED_METHODS = [
19
- {
20
- "package": "langchain.hub",
21
- "object": "pull",
22
- "endpoint": "langchain.retrieve.prompt",
23
- "wrapper": hub,
24
- },
25
- {
26
- "package": "langchain_core.language_models.llms",
27
- "object": "BaseLLM.invoke",
28
- "endpoint": "langchain.llm",
29
- "wrapper": chat,
30
- },
31
- {
32
- "package": "langchain_core.language_models.llms",
33
- "object": "BaseLLM.ainvoke",
34
- "endpoint": "langchain.llm",
35
- "wrapper": async_chat,
36
- },
37
- {
38
- "package": "langchain_core.language_models.chat_models",
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
- An instrumentor for LangChain client library.
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
- for wrapped_method in WRAPPED_METHODS:
82
- wrap_package = wrapped_method.get("package")
83
- wrap_object = wrapped_method.get("object")
84
- gen_ai_endpoint = wrapped_method.get("endpoint")
85
- wrapper = wrapped_method.get("wrapper")
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
- wrap_package,
88
- wrap_object,
89
- wrapper(gen_ai_endpoint, version, environment, application_name,
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
- pass
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