ragaai-catalyst 2.0.7__py3-none-any.whl → 2.0.7.2b1__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 (30) hide show
  1. ragaai_catalyst/evaluation.py +2 -2
  2. ragaai_catalyst/tracers/agentic_tracing/Untitled-1.json +660 -0
  3. ragaai_catalyst/tracers/agentic_tracing/__init__.py +3 -0
  4. ragaai_catalyst/tracers/agentic_tracing/agent_tracer.py +311 -0
  5. ragaai_catalyst/tracers/agentic_tracing/agentic_tracing.py +212 -0
  6. ragaai_catalyst/tracers/agentic_tracing/base.py +270 -0
  7. ragaai_catalyst/tracers/agentic_tracing/data_structure.py +239 -0
  8. ragaai_catalyst/tracers/agentic_tracing/llm_tracer.py +906 -0
  9. ragaai_catalyst/tracers/agentic_tracing/network_tracer.py +286 -0
  10. ragaai_catalyst/tracers/agentic_tracing/sample.py +197 -0
  11. ragaai_catalyst/tracers/agentic_tracing/tool_tracer.py +235 -0
  12. ragaai_catalyst/tracers/agentic_tracing/unique_decorator.py +221 -0
  13. ragaai_catalyst/tracers/agentic_tracing/unique_decorator_test.py +172 -0
  14. ragaai_catalyst/tracers/agentic_tracing/user_interaction_tracer.py +67 -0
  15. ragaai_catalyst/tracers/agentic_tracing/utils/__init__.py +3 -0
  16. ragaai_catalyst/tracers/agentic_tracing/utils/api_utils.py +18 -0
  17. ragaai_catalyst/tracers/agentic_tracing/utils/data_classes.py +61 -0
  18. ragaai_catalyst/tracers/agentic_tracing/utils/generic.py +32 -0
  19. ragaai_catalyst/tracers/agentic_tracing/utils/llm_utils.py +181 -0
  20. ragaai_catalyst/tracers/agentic_tracing/utils/model_costs.json +5946 -0
  21. ragaai_catalyst/tracers/agentic_tracing/utils/trace_utils.py +74 -0
  22. ragaai_catalyst/tracers/llamaindex_callback.py +60 -56
  23. ragaai_catalyst/tracers/tracer.py +26 -4
  24. ragaai_catalyst/tracers/upload_traces.py +138 -0
  25. ragaai_catalyst-2.0.7.2b1.dist-info/METADATA +39 -0
  26. ragaai_catalyst-2.0.7.2b1.dist-info/RECORD +50 -0
  27. ragaai_catalyst-2.0.7.dist-info/METADATA +0 -386
  28. ragaai_catalyst-2.0.7.dist-info/RECORD +0 -29
  29. {ragaai_catalyst-2.0.7.dist-info → ragaai_catalyst-2.0.7.2b1.dist-info}/WHEEL +0 -0
  30. {ragaai_catalyst-2.0.7.dist-info → ragaai_catalyst-2.0.7.2b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,3 @@
1
+ from .agentic_tracing import AgenticTracing
2
+
3
+ __all__ = ['AgenticTracing']
@@ -0,0 +1,311 @@
1
+ import functools
2
+ import uuid
3
+ from datetime import datetime
4
+ import psutil
5
+ from typing import Optional, Any, Dict, List
6
+ from .unique_decorator import mydecorator
7
+
8
+ import contextvars
9
+ import asyncio
10
+
11
+ class AgentTracerMixin:
12
+ def __init__(self, *args, **kwargs):
13
+ super().__init__(*args, **kwargs)
14
+ self.current_agent_id = contextvars.ContextVar("agent_id", default=None)
15
+ self.current_agent_name = contextvars.ContextVar("agent_name", default=None)
16
+ self.agent_children = contextvars.ContextVar("agent_children", default=[])
17
+ self.component_network_calls = contextvars.ContextVar("component_network_calls", default={})
18
+ self._trace_sync_agent_execution = mydecorator(self._trace_sync_agent_execution)
19
+ self._trace_agent_execution = mydecorator(self._trace_agent_execution)
20
+
21
+
22
+ def trace_agent(self, name: str, agent_type: str = "generic", version: str = "1.0.0", capabilities: List[str] = None):
23
+ def decorator(func):
24
+ # Check if the function is async
25
+ is_async = asyncio.iscoroutinefunction(func)
26
+
27
+ @functools.wraps(func)
28
+ async def async_wrapper(*args, **kwargs):
29
+ return await self._trace_agent_execution(
30
+ func, name, agent_type, version, capabilities, *args, **kwargs
31
+ )
32
+
33
+ @functools.wraps(func)
34
+ def sync_wrapper(*args, **kwargs):
35
+ return self._trace_sync_agent_execution(
36
+ func, name, agent_type, version, capabilities, *args, **kwargs
37
+ )
38
+
39
+ return async_wrapper if is_async else sync_wrapper
40
+
41
+ return decorator
42
+
43
+ def _trace_sync_agent_execution(self, func, name, agent_type, version, capabilities, *args, **kwargs):
44
+ """Synchronous version of agent tracing"""
45
+ if not self.is_active:
46
+ return func(*args, **kwargs)
47
+
48
+ start_time = datetime.now()
49
+ start_memory = psutil.Process().memory_info().rss
50
+ component_id = str(uuid.uuid4())
51
+ hash_id = self._trace_sync_agent_execution.hash_id
52
+
53
+ # Initialize empty children list for this agent
54
+ children_token = self.agent_children.set([])
55
+
56
+ # Set the current agent context
57
+ agent_token = self.current_agent_id.set(component_id)
58
+ agent_name_token = self.current_agent_name.set(name)
59
+
60
+ # Start tracking network calls for this component
61
+ self.start_component(component_id)
62
+
63
+ try:
64
+ # Execute the agent
65
+ result = func(*args, **kwargs)
66
+
67
+ # Calculate resource usage
68
+ end_time = datetime.now()
69
+ end_memory = psutil.Process().memory_info().rss
70
+ memory_used = max(0, end_memory - start_memory)
71
+
72
+ # Get children components collected during execution
73
+ children = self.agent_children.get()
74
+
75
+ # Set parent_id for all children
76
+ for child in children:
77
+ child["parent_id"] = component_id
78
+
79
+ # End tracking network calls for this component
80
+ self.end_component(component_id)
81
+
82
+ # Create agent component with children
83
+ agent_component = self.create_agent_component(
84
+ component_id=component_id,
85
+ hash_id=hash_id,
86
+ name=name,
87
+ agent_type=agent_type,
88
+ version=version,
89
+ capabilities=capabilities or [],
90
+ start_time=start_time,
91
+ end_time=end_time,
92
+ memory_used=memory_used,
93
+ input_data=self._sanitize_input(args, kwargs),
94
+ output_data=self._sanitize_output(result),
95
+ children=children
96
+ )
97
+
98
+ self.add_component(agent_component)
99
+ return result
100
+
101
+ except Exception as e:
102
+ error_component = {
103
+ "code": 500,
104
+ "type": type(e).__name__,
105
+ "message": str(e),
106
+ "details": {}
107
+ }
108
+
109
+ # Get children even in case of error
110
+ children = self.agent_children.get()
111
+
112
+ # Set parent_id for all children
113
+ for child in children:
114
+ child["parent_id"] = component_id
115
+
116
+ # End tracking network calls for this component
117
+ self.end_component(component_id)
118
+
119
+ agent_component = self.create_agent_component(
120
+ component_id=component_id,
121
+ hash_id=hash_id,
122
+ name=name,
123
+ agent_type=agent_type,
124
+ version=version,
125
+ capabilities=capabilities or [],
126
+ start_time=start_time,
127
+ end_time=datetime.now(),
128
+ memory_used=0,
129
+ input_data=self._sanitize_input(args, kwargs),
130
+ output_data=None,
131
+ error=error_component,
132
+ children=children
133
+ )
134
+
135
+ self.add_component(agent_component)
136
+ raise
137
+ finally:
138
+ # Reset context variables
139
+ self.current_agent_id.reset(agent_token)
140
+ self.current_agent_name.reset(agent_name_token)
141
+ self.agent_children.reset(children_token)
142
+
143
+ async def _trace_agent_execution(self, func, name, agent_type, version, capabilities, *args, **kwargs):
144
+ """Asynchronous version of agent tracing"""
145
+ if not self.is_active:
146
+ return await func(*args, **kwargs)
147
+
148
+ start_time = datetime.now()
149
+ start_memory = psutil.Process().memory_info().rss
150
+ component_id = str(uuid.uuid4())
151
+ hash_id = self._trace_agent_execution.hash_id
152
+
153
+ # Initialize empty children list for this agent
154
+ children_token = self.agent_children.set([])
155
+
156
+ # Set the current agent context
157
+ agent_token = self.current_agent_id.set(component_id)
158
+ agent_name_token = self.current_agent_name.set(name)
159
+
160
+ # Start tracking network calls for this component
161
+ self.start_component(component_id)
162
+
163
+ try:
164
+ # Execute the agent
165
+ result = await func(*args, **kwargs)
166
+
167
+ # Calculate resource usage
168
+ end_time = datetime.now()
169
+ end_memory = psutil.Process().memory_info().rss
170
+ memory_used = max(0, end_memory - start_memory)
171
+
172
+ # Get children components collected during execution
173
+ children = self.agent_children.get()
174
+
175
+ # Set parent_id for all children
176
+ for child in children:
177
+ child["parent_id"] = component_id
178
+
179
+ # End tracking network calls for this component
180
+ self.end_component(component_id)
181
+
182
+ # Create agent component with children
183
+ agent_component = self.create_agent_component(
184
+ component_id=component_id,
185
+ hash_id=hash_id,
186
+ name=name,
187
+ agent_type=agent_type,
188
+ version=version,
189
+ capabilities=capabilities or [],
190
+ start_time=start_time,
191
+ end_time=end_time,
192
+ memory_used=memory_used,
193
+ input_data=self._sanitize_input(args, kwargs),
194
+ output_data=self._sanitize_output(result),
195
+ children=children
196
+ )
197
+
198
+ self.add_component(agent_component)
199
+ return result
200
+
201
+ except Exception as e:
202
+ error_component = {
203
+ "code": 500,
204
+ "type": type(e).__name__,
205
+ "message": str(e),
206
+ "details": {}
207
+ }
208
+
209
+ # Get children even in case of error
210
+ children = self.agent_children.get()
211
+
212
+ # Set parent_id for all children
213
+ for child in children:
214
+ child["parent_id"] = component_id
215
+
216
+ # End tracking network calls for this component
217
+ self.end_component(component_id)
218
+
219
+ agent_component = self.create_agent_component(
220
+ component_id=component_id,
221
+ hash_id=hash_id,
222
+ name=name,
223
+ agent_type=agent_type,
224
+ version=version,
225
+ capabilities=capabilities or [],
226
+ start_time=start_time,
227
+ end_time=datetime.now(),
228
+ memory_used=0,
229
+ input_data=self._sanitize_input(args, kwargs),
230
+ output_data=None,
231
+ error=error_component,
232
+ children=children
233
+ )
234
+
235
+ self.add_component(agent_component)
236
+ raise
237
+ finally:
238
+ # Reset context variables
239
+ self.current_agent_id.reset(agent_token)
240
+ self.current_agent_name.reset(agent_name_token)
241
+ self.agent_children.reset(children_token)
242
+
243
+ def create_agent_component(self, **kwargs):
244
+ """Create an agent component according to the data structure"""
245
+ start_time = kwargs["start_time"]
246
+ component = {
247
+ "id": kwargs["component_id"],
248
+ "hash_id": kwargs["hash_id"],
249
+ "source_hash_id": None,
250
+ "type": "agent",
251
+ "name": kwargs["name"],
252
+ "start_time": start_time.isoformat(),
253
+ "end_time": kwargs["end_time"].isoformat(),
254
+ "error": kwargs.get("error"),
255
+ "parent_id": None,
256
+ "info": {
257
+ "agent_type": kwargs["agent_type"],
258
+ "version": kwargs["version"],
259
+ "capabilities": kwargs["capabilities"],
260
+ "memory_used": kwargs["memory_used"]
261
+ },
262
+ "data": {
263
+ "input": kwargs["input_data"],
264
+ "output": kwargs["output_data"],
265
+ "children": kwargs.get("children", [])
266
+ },
267
+ "network_calls": self.component_network_calls.get(kwargs["component_id"], []),
268
+ "interactions": [{
269
+ "id": f"int_{uuid.uuid4()}",
270
+ "interaction_type": "input",
271
+ "timestamp": start_time.isoformat(),
272
+ "content": kwargs["input_data"]
273
+ }, {
274
+ "id": f"int_{uuid.uuid4()}",
275
+ "interaction_type": "output",
276
+ "timestamp": kwargs["end_time"].isoformat(),
277
+ "content": kwargs["output_data"]
278
+ }]
279
+ }
280
+
281
+ return component
282
+
283
+ def start_component(self, component_id):
284
+ """Start tracking network calls for a component"""
285
+ component_network_calls = self.component_network_calls.get()
286
+ if component_id not in component_network_calls:
287
+ component_network_calls[component_id] = []
288
+ self.component_network_calls.set(component_network_calls)
289
+
290
+ def end_component(self, component_id):
291
+ """End tracking network calls for a component"""
292
+ component_network_calls = self.component_network_calls.get()
293
+ if component_id in component_network_calls:
294
+ component_network_calls[component_id] = []
295
+ self.component_network_calls.set(component_network_calls)
296
+
297
+ def _sanitize_input(self, args: tuple, kwargs: dict) -> Dict:
298
+ """Sanitize and format input data"""
299
+ return {
300
+ "args": [str(arg) if not isinstance(arg, (int, float, bool, str, list, dict)) else arg for arg in args],
301
+ "kwargs": {
302
+ k: str(v) if not isinstance(v, (int, float, bool, str, list, dict)) else v
303
+ for k, v in kwargs.items()
304
+ }
305
+ }
306
+
307
+ def _sanitize_output(self, output: Any) -> Any:
308
+ """Sanitize and format output data"""
309
+ if isinstance(output, (int, float, bool, str, list, dict)):
310
+ return output
311
+ return str(output)
@@ -0,0 +1,212 @@
1
+ import contextvars
2
+ from typing import Optional, Dict
3
+ import json
4
+ from datetime import datetime
5
+ import uuid
6
+ import os
7
+ from pathlib import Path
8
+
9
+ from .base import BaseTracer
10
+ from .llm_tracer import LLMTracerMixin
11
+ from .tool_tracer import ToolTracerMixin
12
+ from .agent_tracer import AgentTracerMixin
13
+ from .network_tracer import NetworkTracer
14
+
15
+ from .data_structure import (
16
+ Trace, Metadata, SystemInfo, OSInfo, EnvironmentInfo,
17
+ Resources, CPUResource, MemoryResource, DiskResource, NetworkResource,
18
+ ResourceInfo, MemoryInfo, DiskInfo, NetworkInfo,
19
+ Component, LLMComponent, AgentComponent, ToolComponent,
20
+ NetworkCall, Interaction, Error
21
+ )
22
+
23
+
24
+ from ...ragaai_catalyst import RagaAICatalyst
25
+ from ..upload_traces import UploadTraces
26
+
27
+ class AgenticTracing(BaseTracer, LLMTracerMixin, ToolTracerMixin, AgentTracerMixin):
28
+ def __init__(self, user_detail, auto_instrument_llm: bool = True):
29
+ # Initialize all parent classes
30
+ LLMTracerMixin.__init__(self)
31
+ ToolTracerMixin.__init__(self)
32
+ AgentTracerMixin.__init__(self)
33
+
34
+ self.project_name = user_detail["project_name"]
35
+ self.project_id = user_detail["project_id"]
36
+ self.dataset_name = user_detail["dataset_name"]
37
+ self.trace_user_detail = user_detail["trace_user_detail"]
38
+ self.base_url = f"{RagaAICatalyst.BASE_URL}"
39
+ self.timeout = 10
40
+
41
+ BaseTracer.__init__(self, user_detail)
42
+
43
+ self.auto_instrument_llm = auto_instrument_llm
44
+ self.tools: Dict[str, Tool] = {}
45
+ self.call_depth = contextvars.ContextVar("call_depth", default=0)
46
+ self.network_tracer = NetworkTracer()
47
+ self.is_active = False
48
+ self.current_agent_id = contextvars.ContextVar("current_agent_id", default=None)
49
+ self.agent_children = contextvars.ContextVar("agent_children", default=[])
50
+ self.component_network_calls = {} # Store network calls per component
51
+
52
+ # Create output directory if it doesn't exist
53
+ self.output_dir = Path("./traces") # Using default traces directory
54
+ self.output_dir.mkdir(exist_ok=True)
55
+
56
+ def start_component(self, component_id: str):
57
+ """Start tracking network calls for a component"""
58
+ self.component_network_calls[component_id] = []
59
+ self.network_tracer.network_calls = [] # Reset network calls
60
+
61
+ def end_component(self, component_id: str):
62
+ """End tracking network calls for a component"""
63
+ self.component_network_calls[component_id] = self.network_tracer.network_calls.copy()
64
+ self.network_tracer.network_calls = [] # Reset for next component
65
+
66
+ def start(self):
67
+ """Start tracing"""
68
+ # Start base tracer (includes system info and resource monitoring)
69
+ super().start()
70
+ self.is_active = True
71
+
72
+ # Activate network tracing
73
+ self.network_tracer.activate_patches()
74
+
75
+ # Instrument calls from mixins
76
+ if self.auto_instrument_llm:
77
+ self.instrument_llm_calls()
78
+
79
+ def stop(self):
80
+ """Stop tracing and save results"""
81
+ if self.is_active:
82
+ # Calculate final metrics before stopping
83
+ self._calculate_final_metrics()
84
+
85
+ # Deactivate network tracing
86
+ self.network_tracer.deactivate_patches()
87
+
88
+ # Stop base tracer (includes saving to file)
89
+ super().stop()
90
+
91
+ # Cleanup
92
+ self.unpatch_llm_calls()
93
+ self.is_active = False
94
+
95
+
96
+ def _calculate_final_metrics(self):
97
+ """Calculate total cost and tokens from all components"""
98
+ total_cost = 0.0
99
+ total_tokens = 0
100
+
101
+ def process_component(component):
102
+ nonlocal total_cost, total_tokens
103
+ # Convert component to dict if it's an object
104
+ comp_dict = component.__dict__ if hasattr(component, '__dict__') else component
105
+
106
+ if comp_dict.get('type') == "llm":
107
+ info = comp_dict.get('info', {})
108
+ if isinstance(info, dict):
109
+ # Extract cost
110
+ cost_info = info.get('cost', {})
111
+ if isinstance(cost_info, dict):
112
+ total_cost += cost_info.get('total_cost', 0)
113
+
114
+ # Extract tokens
115
+ token_info = info.get('tokens', {})
116
+ if isinstance(token_info, dict):
117
+ total_tokens += token_info.get('total_tokens', 0)
118
+ else:
119
+ token_info = info.get('token_usage', {})
120
+ if isinstance(token_info, dict):
121
+ total_tokens += token_info.get('total_tokens', 0)
122
+
123
+ # Process children if they exist
124
+ data = comp_dict.get('data', {})
125
+ if isinstance(data, dict):
126
+ children = data.get('children', [])
127
+ if children:
128
+ for child in children:
129
+ process_component(child)
130
+
131
+ # Process all root components
132
+ for component in self.components:
133
+ process_component(component)
134
+
135
+ # Update metadata in trace
136
+ if hasattr(self, 'trace'):
137
+ if isinstance(self.trace.metadata, dict):
138
+ self.trace.metadata['total_cost'] = total_cost
139
+ self.trace.metadata['total_tokens'] = total_tokens
140
+ else:
141
+ self.trace.metadata.total_cost = total_cost
142
+ self.trace.metadata.total_tokens = total_tokens
143
+
144
+ def add_component(self, component_data: dict):
145
+ """Add a component to the trace data"""
146
+ component_id = component_data["id"]
147
+
148
+ # Convert dict to appropriate Component type
149
+ if component_data["type"] == "llm":
150
+ component = LLMComponent(**component_data)
151
+ # Add network calls specific to this LLM component
152
+ component_data["network_calls"] = self.component_network_calls.get(component_id, [])
153
+ # Add user interaction for LLM calls
154
+ component_data["interactions"] = [{
155
+ "id": f"int_{uuid.uuid4()}",
156
+ "interaction_type": "input",
157
+ "content": component_data["data"]["input"]
158
+ }, {
159
+ "id": f"int_{uuid.uuid4()}",
160
+ "interaction_type": "output",
161
+ "content": component_data["data"]["output"]
162
+ }]
163
+ elif component_data["type"] == "agent":
164
+ component = AgentComponent(**component_data)
165
+ # Add network calls specific to this agent component
166
+ component_data["network_calls"] = self.component_network_calls.get(component_id, [])
167
+ # Add user interaction for agent
168
+ component_data["interactions"] = [{
169
+ "id": f"int_{uuid.uuid4()}",
170
+ "interaction_type": "input",
171
+ "content": component_data["data"]["input"]
172
+ }, {
173
+ "id": f"int_{uuid.uuid4()}",
174
+ "interaction_type": "output",
175
+ "content": component_data["data"]["output"]
176
+ }]
177
+ elif component_data["type"] == "tool":
178
+ component = ToolComponent(**component_data)
179
+ # Add network calls specific to this tool component
180
+ component_data["network_calls"] = self.component_network_calls.get(component_id, [])
181
+ # Add user interaction for tool
182
+ component_data["interactions"] = [{
183
+ "id": f"int_{uuid.uuid4()}",
184
+ "interaction_type": "input",
185
+ "content": component_data["data"]["input"]
186
+ }, {
187
+ "id": f"int_{uuid.uuid4()}",
188
+ "interaction_type": "output",
189
+ "content": component_data["data"]["output"]
190
+ }]
191
+ else:
192
+ component = Component(**component_data)
193
+
194
+ # Check if there's an active agent context
195
+ current_agent_id = self.current_agent_id.get()
196
+ if current_agent_id and component_data["type"] in ["llm", "tool"]:
197
+ # Add this component as a child of the current agent
198
+ current_children = self.agent_children.get()
199
+ current_children.append(component_data)
200
+ self.agent_children.set(current_children)
201
+ else:
202
+ # Add component to the main trace
203
+ super().add_component(component)
204
+
205
+ def __enter__(self):
206
+ """Context manager entry"""
207
+ self.start()
208
+ return self
209
+
210
+ def __exit__(self, exc_type, exc_value, traceback):
211
+ """Context manager exit"""
212
+ self.stop()