ragaai-catalyst 2.1.5b15__py3-none-any.whl → 2.1.5b17__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.
@@ -45,7 +45,6 @@ class LLMTracerMixin:
45
45
  self.model_costs = load_model_costs()
46
46
  except Exception as e:
47
47
  self.model_costs = {
48
- # TODO: Default cost handling needs to be improved
49
48
  "default": {"input_cost_per_token": 0.0, "output_cost_per_token": 0.0}
50
49
  }
51
50
  self.MAX_PARAMETERS_TO_DISPLAY = 10
@@ -59,7 +58,6 @@ class LLMTracerMixin:
59
58
  self.total_cost = 0.0
60
59
  self.llm_data = {}
61
60
 
62
- # Add auto_instrument options
63
61
  self.auto_instrument_llm = False
64
62
  self.auto_instrument_user_interaction = False
65
63
  self.auto_instrument_file_io = False
@@ -258,6 +256,10 @@ class LLMTracerMixin:
258
256
  self.wrap_method(chat_class, "acomplete")
259
257
 
260
258
  def wrap_openai_client_methods(self, client_class):
259
+ # Skip if OpenAI is not being used (no API key set)
260
+ if not os.getenv("OPENAI_API_KEY"):
261
+ return
262
+
261
263
  original_init = client_class.__init__
262
264
 
263
265
  @functools.wraps(original_init)
@@ -5,6 +5,8 @@ from langchain_core.tools import tool
5
5
  import psutil
6
6
  import functools
7
7
  from typing import Optional, Any, Dict, List
8
+
9
+ from pydantic import tools
8
10
  from ..utils.unique_decorator import generate_unique_hash_simple
9
11
  import contextvars
10
12
  import asyncio
@@ -13,6 +15,7 @@ from ..utils.span_attributes import SpanAttributes
13
15
  import logging
14
16
  import wrapt
15
17
  import time
18
+ import inspect
16
19
 
17
20
  logger = logging.getLogger(__name__)
18
21
  logging_level = (
@@ -38,6 +41,8 @@ class ToolTracerMixin:
38
41
  self.auto_instrument_file_io = False
39
42
  self.auto_instrument_network = False
40
43
  self._instrumented_tools = set() # Track which tools we've instrumented
44
+ self._method_usage = {} # Track which methods are actually used
45
+ self._active_tool_calls = set() # Track active tool calls to prevent duplicates
41
46
 
42
47
  # take care of auto_instrument
43
48
  def instrument_tool_calls(self):
@@ -48,71 +53,45 @@ class ToolTracerMixin:
48
53
  import sys
49
54
 
50
55
  if "langchain_community.tools" in sys.modules:
51
- self.patch_langchain_community_tools(sys.modules["langchain_community.tools"])
56
+ self.patch_langchain_tools(sys.modules["langchain_community.tools"])
52
57
 
53
58
  if "langchain.tools" in sys.modules:
54
- self.patch_langchain_community_tools(sys.modules["langchain.tools"])
59
+ self.patch_langchain_tools(sys.modules["langchain.tools"])
55
60
 
56
61
  if "langchain_core.tools" in sys.modules:
57
62
  self.patch_langchain_core_tools(sys.modules["langchain_core.tools"])
58
63
 
59
64
  # Register hooks for future imports
60
65
  wrapt.register_post_import_hook(
61
- self.patch_langchain_community_tools, "langchain_community.tools"
66
+ self.patch_langchain_tools, "langchain_community.tools"
62
67
  )
63
68
  wrapt.register_post_import_hook(
64
- self.patch_langchain_community_tools, "langchain.tools"
69
+ self.patch_langchain_tools, "langchain.tools"
65
70
  )
66
-
67
71
  wrapt.register_post_import_hook(
68
- self.patch_langchain_core_tools, "langchain_core.tools"
72
+ self.patch_langchain_core_tools, "langchain_core.tools"
69
73
  )
70
74
 
71
75
  def patch_langchain_core_tools(self, module):
72
- """Patch langchain core tools by wrapping @tool decorated functions"""
76
+ """Patch langchain tool methods"""
73
77
  from langchain_core.tools import BaseTool, StructuredTool, Tool
74
-
75
- # Patch the tool decorator
76
- original_tool = module.tool
77
78
 
78
- def wrapped_tool(*args, **kwargs):
79
- # Get the original decorated function
80
- decorated = original_tool(*args, **kwargs)
81
-
82
- def wrapper(func):
83
- tool_instance = decorated(func)
84
- # Wrap the tool's run/arun methods
85
- if hasattr(tool_instance, 'run'):
86
- self.wrap_tool_method(tool_instance.__class__, 'run')
87
- if hasattr(tool_instance, 'arun'):
88
- self.wrap_tool_method(tool_instance.__class__, 'arun')
89
- if hasattr(tool_instance, 'invoke'):
90
- self.wrap_tool_method(tool_instance.__class__, 'invoke')
91
- if hasattr(tool_instance, 'ainvoke'):
92
- self.wrap_tool_method(tool_instance.__class__, 'ainvoke')
93
- return tool_instance
94
-
95
- return wrapper
96
-
97
- # Replace the original decorator
98
- module.tool = wrapped_tool
79
+ # Process tool classes in order of inheritance (base class first)
80
+ tool_classes = [BaseTool] # Start with base class
81
+ # Add derived classes that don't inherit from already processed classes
82
+ for tool_class in [StructuredTool, Tool]:
83
+ if not any(issubclass(tool_class, processed) for processed in tool_classes):
84
+ tool_classes.append(tool_class)
99
85
 
100
- # Patch base tool classes
101
- for tool_class in [BaseTool, StructuredTool, Tool]:
86
+ for tool_class in tool_classes:
102
87
  if tool_class in self._instrumented_tools:
103
88
  continue
104
- if hasattr(tool_class, 'run'):
105
- self.wrap_tool_method(tool_class, f'{tool_class.__name__}.run')
106
- if hasattr(tool_class, 'arun'):
107
- self.wrap_tool_method(tool_class, f'{tool_class.__name__}.arun')
108
- if hasattr(tool_class, 'invoke'):
109
- self.wrap_tool_method(tool_class, f'{tool_class.__name__}.invoke')
110
- if hasattr(tool_class, 'ainvoke'):
111
- self.wrap_tool_method(tool_class, f'{tool_class.__name__}.ainvoke')
89
+ # Create proxy instead of directly wrapping methods
90
+ self.ToolMethodProxy(self, tool_class, tool_class.__name__)
112
91
  self._instrumented_tools.add(tool_class)
113
-
114
- def patch_langchain_community_tools(self, module):
115
- """Patch langchain-community tool methods"""
92
+
93
+ def patch_langchain_tools(self, module):
94
+ """Patch langchain tool methods"""
116
95
  for directory in dir(module):
117
96
  dir_class = getattr(module, directory)
118
97
  tools = getattr(dir_class, "__all__", None)
@@ -124,35 +103,97 @@ class ToolTracerMixin:
124
103
  if tool_class in self._instrumented_tools:
125
104
  continue
126
105
 
127
- # Prefer invoke/ainvoke over run/arun
128
- if hasattr(tool_class, "invoke"):
129
- self.wrap_tool_method(tool_class, f"{tool}.invoke")
130
- elif hasattr(tool_class, "run"): # Only wrap run if invoke doesn't exist
131
- self.wrap_tool_method(tool_class, f"{tool}.run")
132
-
133
- if hasattr(tool_class, "ainvoke"):
134
- self.wrap_tool_method(tool_class, f"{tool}.ainvoke")
135
- elif hasattr(tool_class, "arun"): # Only wrap arun if ainvoke doesn't exist
136
- self.wrap_tool_method(tool_class, f"{tool}.arun")
137
-
106
+ # Create proxy instead of directly wrapping methods
107
+ self.ToolMethodProxy(self, tool_class, tool)
138
108
  self._instrumented_tools.add(tool_class)
139
109
 
140
- def wrap_tool_method(self, obj, method_name):
141
- """Wrap a method with tracing functionality"""
142
- method_name = method_name.split(".")[-1]
143
- tool_name = obj.__name__.split(".")[0]
144
- original_method = getattr(obj, method_name)
145
-
146
- @functools.wraps(original_method)
147
- def wrapper(*args, **kwargs):
148
- name = tool_name
149
- tool_type = "langchain"
150
- version = None
151
- if asyncio.iscoroutinefunction(original_method):
152
- return self._trace_tool_execution(original_method, name, tool_type, version, *args, **kwargs)
153
- return self._trace_sync_tool_execution(original_method, name, tool_type, version, *args, **kwargs)
110
+ class ToolMethodProxy:
111
+ def __init__(self, tracer, tool_class, tool_name):
112
+ self.tracer = tracer
113
+ self.tool_class = tool_class
114
+ self.tool_name = tool_name
115
+ self._original_methods = {}
116
+ self._wrapped = False
117
+
118
+ # Store original methods
119
+ for method in ['run', 'arun', 'invoke', 'ainvoke']:
120
+ if hasattr(tool_class, method):
121
+ self._original_methods[method] = getattr(tool_class, method)
122
+ setattr(tool_class, method, self._create_proxy_method(method))
123
+
124
+ def _create_proxy_method(self, method_name):
125
+ original_method = self._original_methods[method_name]
154
126
 
155
- setattr(obj, method_name, wrapper)
127
+ async def async_proxy_method(*args, **kwargs):
128
+ if not self._wrapped:
129
+ self._cleanup_proxy()
130
+ self.tracer._wrap_specific_method(self.tool_class, method_name, self.tool_name)
131
+ self._wrapped = True
132
+ # Get the now-wrapped method
133
+ wrapped_method = getattr(self.tool_class, method_name)
134
+ return await wrapped_method(*args, **kwargs)
135
+
136
+ def sync_proxy_method(*args, **kwargs):
137
+ if not self._wrapped:
138
+ self._cleanup_proxy()
139
+ self.tracer._wrap_specific_method(self.tool_class, method_name, self.tool_name)
140
+ self._wrapped = True
141
+ # Get the now-wrapped method
142
+ wrapped_method = getattr(self.tool_class, method_name)
143
+ return wrapped_method(*args, **kwargs)
144
+
145
+ # Use appropriate proxy based on whether original method is async
146
+ proxy_method = async_proxy_method if asyncio.iscoroutinefunction(original_method) else sync_proxy_method
147
+ proxy_method.__name__ = method_name
148
+ return proxy_method
149
+
150
+ def _cleanup_proxy(self):
151
+ # Restore all original methods except the one that was called
152
+ for method, original in self._original_methods.items():
153
+ if not self._wrapped:
154
+ setattr(self.tool_class, method, original)
155
+
156
+ def _wrap_specific_method(self, tool_class, method_name, tool_name):
157
+ """Wrap only the specific method that is being used"""
158
+ original_method = getattr(tool_class, method_name)
159
+
160
+ async def async_wrapper(*args, **kwargs):
161
+ tool_call_id = kwargs.get('tool_call_id', None)
162
+ if tool_call_id and tool_call_id in self._active_tool_calls:
163
+ # Skip tracing if this tool call is already being traced
164
+ return await original_method(*args, **kwargs)
165
+
166
+ if tool_call_id:
167
+ self._active_tool_calls.add(tool_call_id)
168
+ try:
169
+ name = tool_name
170
+ tool_type = "langchain"
171
+ version = None
172
+ return await self._trace_tool_execution(original_method, name, tool_type, version, *args, **kwargs)
173
+ finally:
174
+ if tool_call_id:
175
+ self._active_tool_calls.remove(tool_call_id)
176
+
177
+ def sync_wrapper(*args, **kwargs):
178
+ tool_call_id = kwargs.get('tool_call_id', None)
179
+ if tool_call_id and tool_call_id in self._active_tool_calls:
180
+ # Skip tracing if this tool call is already being traced
181
+ return original_method(*args, **kwargs)
182
+
183
+ if tool_call_id:
184
+ self._active_tool_calls.add(tool_call_id)
185
+ try:
186
+ name = tool_name
187
+ tool_type = "langchain"
188
+ version = None
189
+ return self._trace_sync_tool_execution(original_method, name, tool_type, version, *args, **kwargs)
190
+ finally:
191
+ if tool_call_id:
192
+ self._active_tool_calls.remove(tool_call_id)
193
+
194
+ wrapper = async_wrapper if asyncio.iscoroutinefunction(original_method) else sync_wrapper
195
+ wrapper.__name__ = method_name
196
+ setattr(tool_class, method_name, wrapper)
156
197
 
157
198
  def instrument_user_interaction_calls(self):
158
199
  self.auto_instrument_user_interaction = True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ragaai_catalyst
3
- Version: 2.1.5b15
3
+ Version: 2.1.5b17
4
4
  Summary: RAGA AI CATALYST
5
5
  Author-email: Kiran Scaria <kiran.scaria@raga.ai>, Kedar Gaikwad <kedar.gaikwad@raga.ai>, Dushyant Mahajan <dushyant.mahajan@raga.ai>, Siddhartha Kosti <siddhartha.kosti@raga.ai>, Ritika Goel <ritika.goel@raga.ai>, Vijay Chaurasia <vijay.chaurasia@raga.ai>
6
6
  Requires-Python: <3.13,>=3.9
@@ -32,10 +32,10 @@ ragaai_catalyst/tracers/agentic_tracing/tracers/agent_tracer.py,sha256=8d6YovuWi
32
32
  ragaai_catalyst/tracers/agentic_tracing/tracers/base.py,sha256=88rX7OkOGEyVNECUrc4bYqODyulXve_-99d9ku5hBeQ,37373
33
33
  ragaai_catalyst/tracers/agentic_tracing/tracers/custom_tracer.py,sha256=OHet_Cphyrsq2CP4WiooTsWSgg3Rc1n8QsOl1s2vqdY,13480
34
34
  ragaai_catalyst/tracers/agentic_tracing/tracers/langgraph_tracer.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- ragaai_catalyst/tracers/agentic_tracing/tracers/llm_tracer.py,sha256=NklCHbkG_ITlJ0zxGm-QHdk6it5xeu5gUHnK18YuLGo,34499
35
+ ragaai_catalyst/tracers/agentic_tracing/tracers/llm_tracer.py,sha256=ySoUbj7NPveV1g-G4E4D8uOH_4Ad93qVu9Z2aAZHH1o,34518
36
36
  ragaai_catalyst/tracers/agentic_tracing/tracers/main_tracer.py,sha256=JEwvFV6KdYcg8zt2qUTKmmPvrkEft0YNAyUYK6FWF7c,18335
37
37
  ragaai_catalyst/tracers/agentic_tracing/tracers/network_tracer.py,sha256=m8CxYkl7iMiFya_lNwN1ykBc3Pmo-2pR_2HmpptwHWQ,10352
38
- ragaai_catalyst/tracers/agentic_tracing/tracers/tool_tracer.py,sha256=n03vRN5Lwp--B_dt2ZGt2WWXYRIymTqCVrdg-Hf5rd4,19119
38
+ ragaai_catalyst/tracers/agentic_tracing/tracers/tool_tracer.py,sha256=nmgkE3jRlTpq_uk6FYw0hTv2VgT8ejxvvBRL7gso39I,21037
39
39
  ragaai_catalyst/tracers/agentic_tracing/tracers/user_interaction_tracer.py,sha256=bhSUhNQCuJXKjgJAXhjKEYjnHMpYN90FSZdR84fNIKU,4614
40
40
  ragaai_catalyst/tracers/agentic_tracing/upload/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
41
  ragaai_catalyst/tracers/agentic_tracing/upload/upload_agentic_traces.py,sha256=1MDKXAAPzOEdxFKWWQrRgrmM3kz--DGXSywGXQmR3lQ,6041
@@ -66,8 +66,8 @@ ragaai_catalyst/tracers/utils/__init__.py,sha256=KeMaZtYaTojilpLv65qH08QmpYclfpa
66
66
  ragaai_catalyst/tracers/utils/convert_langchain_callbacks_output.py,sha256=ofrNrxf2b1hpjDh_zeaxiYq86azn1MF3kW8-ViYPEg0,1641
67
67
  ragaai_catalyst/tracers/utils/langchain_tracer_extraction_logic.py,sha256=qK67fdUBz5Xr99ajqXbYf1ueKS1V3a3_XR0zCcN4iGI,3061
68
68
  ragaai_catalyst/tracers/utils/utils.py,sha256=ViygfJ7vZ7U0CTSA1lbxVloHp4NSlmfDzBRNCJuMhis,2374
69
- ragaai_catalyst-2.1.5b15.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
70
- ragaai_catalyst-2.1.5b15.dist-info/METADATA,sha256=_mdTGHCMe9WtrUydgSdzhnmvRZYzN6pfcxnWkpV5QHs,12796
71
- ragaai_catalyst-2.1.5b15.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
72
- ragaai_catalyst-2.1.5b15.dist-info/top_level.txt,sha256=HpgsdRgEJMk8nqrU6qdCYk3di7MJkDL0B19lkc7dLfM,16
73
- ragaai_catalyst-2.1.5b15.dist-info/RECORD,,
69
+ ragaai_catalyst-2.1.5b17.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
70
+ ragaai_catalyst-2.1.5b17.dist-info/METADATA,sha256=ZAT6uQwGxjeBf-2Nzdd5qgi0obRJTSad3fOlmbs_iGE,12796
71
+ ragaai_catalyst-2.1.5b17.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
72
+ ragaai_catalyst-2.1.5b17.dist-info/top_level.txt,sha256=HpgsdRgEJMk8nqrU6qdCYk3di7MJkDL0B19lkc7dLfM,16
73
+ ragaai_catalyst-2.1.5b17.dist-info/RECORD,,