ragaai-catalyst 2.1.4b0__py3-none-any.whl → 2.1.4b2__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 (23) hide show
  1. ragaai_catalyst/tracers/agentic_tracing/data/data_structure.py +36 -10
  2. ragaai_catalyst/tracers/agentic_tracing/tracers/agent_tracer.py +226 -76
  3. ragaai_catalyst/tracers/agentic_tracing/tracers/base.py +568 -107
  4. ragaai_catalyst/tracers/agentic_tracing/tracers/custom_tracer.py +325 -0
  5. ragaai_catalyst/tracers/agentic_tracing/tracers/llm_tracer.py +218 -81
  6. ragaai_catalyst/tracers/agentic_tracing/tracers/main_tracer.py +210 -58
  7. ragaai_catalyst/tracers/agentic_tracing/tracers/network_tracer.py +2 -0
  8. ragaai_catalyst/tracers/agentic_tracing/tracers/tool_tracer.py +137 -28
  9. ragaai_catalyst/tracers/agentic_tracing/tracers/user_interaction_tracer.py +86 -0
  10. ragaai_catalyst/tracers/agentic_tracing/upload/upload_agentic_traces.py +9 -51
  11. ragaai_catalyst/tracers/agentic_tracing/upload/upload_trace_metric.py +83 -0
  12. ragaai_catalyst/tracers/agentic_tracing/utils/create_dataset_schema.py +26 -0
  13. ragaai_catalyst/tracers/agentic_tracing/utils/get_user_trace_metrics.py +28 -0
  14. ragaai_catalyst/tracers/agentic_tracing/utils/llm_utils.py +45 -15
  15. ragaai_catalyst/tracers/agentic_tracing/utils/model_costs.json +2476 -2122
  16. ragaai_catalyst/tracers/agentic_tracing/utils/span_attributes.py +59 -0
  17. ragaai_catalyst/tracers/agentic_tracing/utils/trace_utils.py +23 -0
  18. ragaai_catalyst/tracers/agentic_tracing/utils/zip_list_of_unique_files.py +284 -15
  19. ragaai_catalyst/tracers/tracer.py +77 -8
  20. {ragaai_catalyst-2.1.4b0.dist-info → ragaai_catalyst-2.1.4b2.dist-info}/METADATA +2 -1
  21. {ragaai_catalyst-2.1.4b0.dist-info → ragaai_catalyst-2.1.4b2.dist-info}/RECORD +23 -18
  22. {ragaai_catalyst-2.1.4b0.dist-info → ragaai_catalyst-2.1.4b2.dist-info}/WHEEL +0 -0
  23. {ragaai_catalyst-2.1.4b0.dist-info → ragaai_catalyst-2.1.4b2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,325 @@
1
+ import sys
2
+ import uuid
3
+ import psutil
4
+ import threading
5
+ from datetime import datetime
6
+ import functools
7
+ from typing import Optional, Any, Dict, List
8
+ from ..utils.unique_decorator import generate_unique_hash_simple, mydecorator
9
+ import contextvars
10
+ import asyncio
11
+ from ..utils.file_name_tracker import TrackName
12
+
13
+
14
+ class CustomTracerMixin:
15
+ def __init__(self, *args, **kwargs):
16
+ super().__init__(*args, **kwargs)
17
+ self.file_tracker = TrackName()
18
+ self.current_custom_name = contextvars.ContextVar("custom_name", default=None)
19
+ self.current_custom_id = contextvars.ContextVar("custom_id", default=None)
20
+ self.component_network_calls = {}
21
+ self.component_user_interaction = {}
22
+ self.gt = None
23
+
24
+ # Add auto instrument flags
25
+ self.auto_instrument_custom = False
26
+ self.auto_instrument_user_interaction = False
27
+ self.auto_instrument_network = False
28
+
29
+ def trace_custom(self, name: str = None, custom_type: str = "generic", version: str = "1.0.0", trace_variables: bool = True):
30
+ def decorator(func):
31
+ # Add metadata attribute to the function
32
+ metadata = {
33
+ "name": name or func.__name__,
34
+ "custom_type": custom_type,
35
+ "version": version,
36
+ "trace_variables": trace_variables,
37
+ "is_active": True
38
+ }
39
+
40
+ # Check if the function is async
41
+ is_async = asyncio.iscoroutinefunction(func)
42
+
43
+ @self.file_tracker.trace_decorator
44
+ @functools.wraps(func)
45
+ async def async_wrapper(*args, **kwargs):
46
+ async_wrapper.metadata = metadata
47
+ self.gt = kwargs.get('gt', None) if kwargs else None
48
+ return await self._trace_custom_execution(
49
+ func, name or func.__name__, custom_type, version, trace_variables, *args, **kwargs
50
+ )
51
+
52
+ @self.file_tracker.trace_decorator
53
+ @functools.wraps(func)
54
+ def sync_wrapper(*args, **kwargs):
55
+ sync_wrapper.metadata = metadata
56
+ self.gt = kwargs.get('gt', None) if kwargs else None
57
+ return self._trace_sync_custom_execution(
58
+ func, name or func.__name__, custom_type, version, trace_variables, *args, **kwargs
59
+ )
60
+
61
+ wrapper = async_wrapper if is_async else sync_wrapper
62
+ wrapper.metadata = metadata
63
+ return wrapper
64
+
65
+ return decorator
66
+
67
+ def _trace_sync_custom_execution(self, func, name, custom_type, version, trace_variables, *args, **kwargs):
68
+ """Synchronous version of custom tracing"""
69
+ if not self.is_active or not self.auto_instrument_custom:
70
+ return func(*args, **kwargs)
71
+
72
+ start_time = datetime.now().astimezone()
73
+ start_memory = psutil.Process().memory_info().rss
74
+ component_id = str(uuid.uuid4())
75
+ hash_id = generate_unique_hash_simple(func)
76
+ variable_traces = []
77
+
78
+ # Set up variable tracing if enabled
79
+ if trace_variables:
80
+ def trace_variables_func(frame, event, arg):
81
+ if event == 'line' and frame.f_code == func.__code__:
82
+ try:
83
+ locals_dict = {k: v for k, v in frame.f_locals.items()
84
+ if not k.startswith('__') and isinstance(v, (int, float, bool, str, list, dict, tuple, set))}
85
+ if locals_dict:
86
+ variable_traces.append({
87
+ 'variables': locals_dict,
88
+ 'timestamp': datetime.now().astimezone().isoformat()
89
+ })
90
+ except:
91
+ pass
92
+ return trace_variables_func
93
+
94
+ sys.settrace(trace_variables_func)
95
+
96
+ # Start tracking network calls for this component
97
+ self.start_component(component_id)
98
+
99
+ try:
100
+ # Execute the function
101
+ result = func(*args, **kwargs)
102
+
103
+ # Calculate resource usage
104
+ end_time = datetime.now().astimezone()
105
+ end_memory = psutil.Process().memory_info().rss
106
+ memory_used = max(0, end_memory - start_memory)
107
+
108
+ # End tracking network calls for this component
109
+ self.end_component(component_id)
110
+
111
+ # Create custom component
112
+ custom_component = self.create_custom_component(
113
+ component_id=component_id,
114
+ hash_id=hash_id,
115
+ name=name,
116
+ custom_type=custom_type,
117
+ version=version,
118
+ memory_used=memory_used,
119
+ start_time=start_time,
120
+ end_time=end_time,
121
+ variable_traces=variable_traces,
122
+ input_data=self._sanitize_input(args, kwargs),
123
+ output_data=self._sanitize_output(result)
124
+ )
125
+
126
+ self.add_component(custom_component)
127
+ return result
128
+
129
+ except Exception as e:
130
+ error_component = {
131
+ "code": 500,
132
+ "type": type(e).__name__,
133
+ "message": str(e),
134
+ "details": {}
135
+ }
136
+
137
+ # End tracking network calls for this component
138
+ self.end_component(component_id)
139
+
140
+ end_time = datetime.now().astimezone()
141
+
142
+ custom_component = self.create_custom_component(
143
+ component_id=component_id,
144
+ hash_id=hash_id,
145
+ name=name,
146
+ custom_type=custom_type,
147
+ version=version,
148
+ memory_used=0,
149
+ start_time=start_time,
150
+ end_time=end_time,
151
+ variable_traces=variable_traces,
152
+ input_data=self._sanitize_input(args, kwargs),
153
+ output_data=None,
154
+ error=error_component
155
+ )
156
+
157
+ self.add_component(custom_component)
158
+ raise
159
+ finally:
160
+ if trace_variables:
161
+ sys.settrace(None)
162
+
163
+ async def _trace_custom_execution(self, func, name, custom_type, version, trace_variables, *args, **kwargs):
164
+ """Asynchronous version of custom tracing"""
165
+ if not self.is_active or not self.auto_instrument_custom:
166
+ return await func(*args, **kwargs)
167
+
168
+ start_time = datetime.now().astimezone()
169
+ start_memory = psutil.Process().memory_info().rss
170
+ component_id = str(uuid.uuid4())
171
+ hash_id = generate_unique_hash_simple(func)
172
+ variable_traces = []
173
+
174
+ # Set up variable tracing if enabled
175
+ if trace_variables:
176
+ def trace_variables_func(frame, event, arg):
177
+ if event == 'line' and frame.f_code == func.__code__:
178
+ try:
179
+ locals_dict = {k: v for k, v in frame.f_locals.items()
180
+ if not k.startswith('__') and isinstance(v, (int, float, bool, str, list, dict, tuple, set))}
181
+ if locals_dict:
182
+ variable_traces.append({
183
+ 'variables': locals_dict,
184
+ 'timestamp': datetime.now().astimezone().isoformat()
185
+ })
186
+ except:
187
+ pass
188
+ return trace_variables_func
189
+
190
+ sys.settrace(trace_variables_func)
191
+
192
+ try:
193
+ # Execute the function
194
+ result = await func(*args, **kwargs)
195
+
196
+ # Calculate resource usage
197
+ end_time = datetime.now().astimezone()
198
+ end_memory = psutil.Process().memory_info().rss
199
+ memory_used = max(0, end_memory - start_memory)
200
+
201
+ # Create custom component
202
+ custom_component = self.create_custom_component(
203
+ component_id=component_id,
204
+ hash_id=hash_id,
205
+ name=name,
206
+ custom_type=custom_type,
207
+ version=version,
208
+ start_time=start_time,
209
+ end_time=end_time,
210
+ memory_used=memory_used,
211
+ variable_traces=variable_traces,
212
+ input_data=self._sanitize_input(args, kwargs),
213
+ output_data=self._sanitize_output(result)
214
+ )
215
+ self.add_component(custom_component)
216
+ return result
217
+
218
+ except Exception as e:
219
+ error_component = {
220
+ "code": 500,
221
+ "type": type(e).__name__,
222
+ "message": str(e),
223
+ "details": {}
224
+ }
225
+
226
+ end_time = datetime.now().astimezone()
227
+
228
+ custom_component = self.create_custom_component(
229
+ component_id=component_id,
230
+ hash_id=hash_id,
231
+ name=name,
232
+ custom_type=custom_type,
233
+ version=version,
234
+ start_time=start_time,
235
+ end_time=end_time,
236
+ memory_used=0,
237
+ variable_traces=variable_traces,
238
+ input_data=self._sanitize_input(args, kwargs),
239
+ output_data=None,
240
+ error=error_component
241
+ )
242
+ self.add_component(custom_component)
243
+ raise
244
+ finally:
245
+ if trace_variables:
246
+ sys.settrace(None)
247
+
248
+ def create_custom_component(self, **kwargs):
249
+ """Create a custom component according to the data structure"""
250
+ start_time = kwargs["start_time"]
251
+
252
+ network_calls = []
253
+ if self.auto_instrument_network:
254
+ network_calls = self.component_network_calls.get(kwargs["component_id"], [])
255
+
256
+ interactions = []
257
+ if self.auto_instrument_user_interaction:
258
+ interactions = self.component_user_interaction.get(kwargs["component_id"], [])
259
+
260
+ component = {
261
+ "id": kwargs["component_id"],
262
+ "hash_id": kwargs["hash_id"],
263
+ "source_hash_id": None,
264
+ "type": "custom",
265
+ "name": kwargs["name"],
266
+ "start_time": start_time.isoformat(),
267
+ "end_time": kwargs["end_time"].isoformat(),
268
+ "error": kwargs.get("error"),
269
+ "parent_id": self.current_agent_id.get() if hasattr(self, 'current_agent_id') else None,
270
+ "info": {
271
+ "custom_type": kwargs["custom_type"],
272
+ "version": kwargs["version"],
273
+ "memory_used": kwargs["memory_used"]
274
+ },
275
+ "data": {
276
+ "input": kwargs["input_data"],
277
+ "output": kwargs["output_data"],
278
+ "memory_used": kwargs["memory_used"],
279
+ "variable_traces": kwargs.get("variable_traces", [])
280
+ },
281
+ "network_calls": network_calls,
282
+ "interactions": interactions
283
+ }
284
+
285
+ if self.gt:
286
+ component["data"]["gt"] = self.gt
287
+
288
+ return component
289
+
290
+ def start_component(self, component_id):
291
+ """Start tracking network calls for a component"""
292
+ self.component_network_calls[component_id] = []
293
+
294
+ def end_component(self, component_id):
295
+ """End tracking network calls for a component"""
296
+ pass
297
+
298
+ def _sanitize_input(self, args: tuple, kwargs: dict) -> Dict:
299
+ """Sanitize and format input data"""
300
+ return {
301
+ "args": [str(arg) if not isinstance(arg, (int, float, bool, str, list, dict)) else arg for arg in args],
302
+ "kwargs": {
303
+ k: str(v) if not isinstance(v, (int, float, bool, str, list, dict)) else v
304
+ for k, v in kwargs.items()
305
+ }
306
+ }
307
+
308
+ def _sanitize_output(self, output: Any) -> Any:
309
+ """Sanitize and format output data"""
310
+ if isinstance(output, (int, float, bool, str, list, dict)):
311
+ return output
312
+ return str(output)
313
+
314
+ # Auto instrumentation methods
315
+ def instrument_custom_calls(self):
316
+ """Enable auto-instrumentation for custom calls"""
317
+ self.auto_instrument_custom = True
318
+
319
+ def instrument_user_interaction_calls(self):
320
+ """Enable auto-instrumentation for user interaction calls"""
321
+ self.auto_instrument_user_interaction = True
322
+
323
+ def instrument_network_calls(self):
324
+ """Enable auto-instrumentation for network calls"""
325
+ self.auto_instrument_network = True