ragaai-catalyst 2.1b0__py3-none-any.whl → 2.1b2__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.
- ragaai_catalyst/__init__.py +1 -0
- ragaai_catalyst/dataset.py +1 -4
- ragaai_catalyst/evaluation.py +4 -5
- ragaai_catalyst/guard_executor.py +97 -0
- ragaai_catalyst/guardrails_manager.py +41 -15
- ragaai_catalyst/internal_api_completion.py +1 -1
- ragaai_catalyst/prompt_manager.py +7 -2
- ragaai_catalyst/ragaai_catalyst.py +1 -1
- ragaai_catalyst/synthetic_data_generation.py +7 -0
- ragaai_catalyst/tracers/__init__.py +1 -1
- ragaai_catalyst/tracers/agentic_tracing/__init__.py +3 -0
- ragaai_catalyst/tracers/agentic_tracing/agent_tracer.py +422 -0
- ragaai_catalyst/tracers/agentic_tracing/agentic_tracing.py +198 -0
- ragaai_catalyst/tracers/agentic_tracing/base.py +376 -0
- ragaai_catalyst/tracers/agentic_tracing/data_structure.py +248 -0
- ragaai_catalyst/tracers/agentic_tracing/examples/FinancialAnalysisSystem.ipynb +536 -0
- ragaai_catalyst/tracers/agentic_tracing/examples/GameActivityEventPlanner.ipynb +134 -0
- ragaai_catalyst/tracers/agentic_tracing/examples/TravelPlanner.ipynb +563 -0
- ragaai_catalyst/tracers/agentic_tracing/file_name_tracker.py +46 -0
- ragaai_catalyst/tracers/agentic_tracing/llm_tracer.py +808 -0
- ragaai_catalyst/tracers/agentic_tracing/network_tracer.py +286 -0
- ragaai_catalyst/tracers/agentic_tracing/sample.py +197 -0
- ragaai_catalyst/tracers/agentic_tracing/tool_tracer.py +247 -0
- ragaai_catalyst/tracers/agentic_tracing/unique_decorator.py +165 -0
- ragaai_catalyst/tracers/agentic_tracing/unique_decorator_test.py +172 -0
- ragaai_catalyst/tracers/agentic_tracing/upload_agentic_traces.py +187 -0
- ragaai_catalyst/tracers/agentic_tracing/upload_code.py +115 -0
- ragaai_catalyst/tracers/agentic_tracing/user_interaction_tracer.py +43 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/__init__.py +3 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/api_utils.py +18 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/data_classes.py +61 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/generic.py +32 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/llm_utils.py +177 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/model_costs.json +7823 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/trace_utils.py +74 -0
- ragaai_catalyst/tracers/agentic_tracing/zip_list_of_unique_files.py +184 -0
- ragaai_catalyst/tracers/exporters/raga_exporter.py +1 -7
- ragaai_catalyst/tracers/tracer.py +30 -4
- ragaai_catalyst/tracers/upload_traces.py +127 -0
- ragaai_catalyst-2.1b2.dist-info/METADATA +43 -0
- ragaai_catalyst-2.1b2.dist-info/RECORD +56 -0
- {ragaai_catalyst-2.1b0.dist-info → ragaai_catalyst-2.1b2.dist-info}/WHEEL +1 -1
- ragaai_catalyst-2.1b0.dist-info/METADATA +0 -295
- ragaai_catalyst-2.1b0.dist-info/RECORD +0 -28
- {ragaai_catalyst-2.1b0.dist-info → ragaai_catalyst-2.1b2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,422 @@
|
|
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
|
+
from .unique_decorator import generate_unique_hash_simple
|
8
|
+
|
9
|
+
import contextvars
|
10
|
+
import asyncio
|
11
|
+
from .file_name_tracker import TrackName
|
12
|
+
|
13
|
+
|
14
|
+
class AgentTracerMixin:
|
15
|
+
def __init__(self, *args, **kwargs):
|
16
|
+
super().__init__(*args, **kwargs)
|
17
|
+
self.file_tracker = TrackName()
|
18
|
+
self.current_agent_id = contextvars.ContextVar("agent_id", default=None)
|
19
|
+
self.current_agent_name = contextvars.ContextVar("agent_name", default=None)
|
20
|
+
self.agent_children = contextvars.ContextVar("agent_children", default=[])
|
21
|
+
self.component_network_calls = contextvars.ContextVar("component_network_calls", default={})
|
22
|
+
self.component_user_interaction = contextvars.ContextVar("component_user_interaction", default={})
|
23
|
+
self.gt = None
|
24
|
+
|
25
|
+
|
26
|
+
def trace_agent(self, name: str, agent_type: str = "generic", version: str = "1.0.0", capabilities: List[str] = None):
|
27
|
+
def decorator(target):
|
28
|
+
# Check if target is a class
|
29
|
+
is_class = isinstance(target, type)
|
30
|
+
tracer = self # Store reference to tracer instance
|
31
|
+
top_level_hash_id = generate_unique_hash_simple(target) # Generate hash based on the decorated target code
|
32
|
+
|
33
|
+
|
34
|
+
if is_class:
|
35
|
+
# Store original __init__
|
36
|
+
original_init = target.__init__
|
37
|
+
|
38
|
+
def wrapped_init(self, *args, **kwargs):
|
39
|
+
self.gt = kwargs.get('gt', None) if kwargs else None
|
40
|
+
# Set agent context before initializing
|
41
|
+
component_id = str(uuid.uuid4())
|
42
|
+
hash_id = top_level_hash_id
|
43
|
+
|
44
|
+
# Store the component ID in the instance
|
45
|
+
self._agent_component_id = component_id
|
46
|
+
|
47
|
+
# Get parent agent ID if exists
|
48
|
+
parent_agent_id = tracer.current_agent_id.get()
|
49
|
+
|
50
|
+
# Create agent component
|
51
|
+
agent_component = tracer.create_agent_component(
|
52
|
+
component_id=component_id,
|
53
|
+
hash_id=hash_id,
|
54
|
+
name=name,
|
55
|
+
agent_type=agent_type,
|
56
|
+
version=version,
|
57
|
+
capabilities=capabilities or [],
|
58
|
+
start_time=datetime.now(),
|
59
|
+
end_time=datetime.now(),
|
60
|
+
memory_used=0,
|
61
|
+
input_data=tracer._sanitize_input(args, kwargs),
|
62
|
+
output_data=None,
|
63
|
+
children=[],
|
64
|
+
parent_id=parent_agent_id
|
65
|
+
)
|
66
|
+
|
67
|
+
# Store component for later updates
|
68
|
+
if not hasattr(tracer, '_agent_components'):
|
69
|
+
tracer._agent_components = {}
|
70
|
+
tracer._agent_components[component_id] = agent_component
|
71
|
+
|
72
|
+
# If this is a nested agent, add it to parent's children
|
73
|
+
if parent_agent_id:
|
74
|
+
parent_children = tracer.agent_children.get()
|
75
|
+
parent_children.append(agent_component)
|
76
|
+
tracer.agent_children.set(parent_children)
|
77
|
+
else:
|
78
|
+
# Only add to root components if no parent
|
79
|
+
tracer.add_component(agent_component)
|
80
|
+
|
81
|
+
# Call original __init__ with this agent as current
|
82
|
+
token = tracer.current_agent_id.set(component_id)
|
83
|
+
try:
|
84
|
+
original_init(self, *args, **kwargs)
|
85
|
+
finally:
|
86
|
+
tracer.current_agent_id.reset(token)
|
87
|
+
|
88
|
+
# Wrap all public methods to track execution
|
89
|
+
for attr_name in dir(target):
|
90
|
+
if not attr_name.startswith('_'):
|
91
|
+
attr_value = getattr(target, attr_name)
|
92
|
+
if callable(attr_value):
|
93
|
+
def wrap_method(method):
|
94
|
+
@self.file_tracker.trace_decorator
|
95
|
+
@functools.wraps(method)
|
96
|
+
def wrapped_method(self, *args, **kwargs):
|
97
|
+
self.gt = kwargs.get('gt', None) if kwargs else None
|
98
|
+
# Set this agent as current during method execution
|
99
|
+
token = tracer.current_agent_id.set(self._agent_component_id)
|
100
|
+
|
101
|
+
# Store parent's children before setting new empty list
|
102
|
+
parent_children = tracer.agent_children.get()
|
103
|
+
children_token = tracer.agent_children.set([])
|
104
|
+
|
105
|
+
try:
|
106
|
+
start_time = datetime.now()
|
107
|
+
result = method(self, *args, **kwargs)
|
108
|
+
end_time = datetime.now()
|
109
|
+
|
110
|
+
# Update agent component with method result
|
111
|
+
if hasattr(tracer, '_agent_components'):
|
112
|
+
component = tracer._agent_components.get(self._agent_component_id)
|
113
|
+
if component:
|
114
|
+
component['data']['output'] = tracer._sanitize_output(result)
|
115
|
+
component['data']['input'] = tracer._sanitize_input(args, kwargs)
|
116
|
+
component['start_time'] = start_time.isoformat()
|
117
|
+
component['end_time'] = end_time.isoformat()
|
118
|
+
|
119
|
+
# Get children accumulated during method execution
|
120
|
+
children = tracer.agent_children.get()
|
121
|
+
if children:
|
122
|
+
if 'children' not in component['data']:
|
123
|
+
component['data']['children'] = []
|
124
|
+
component['data']['children'].extend(children)
|
125
|
+
|
126
|
+
# Add this component as a child to parent's children list
|
127
|
+
parent_children.append(component)
|
128
|
+
tracer.agent_children.set(parent_children)
|
129
|
+
return result
|
130
|
+
finally:
|
131
|
+
tracer.current_agent_id.reset(token)
|
132
|
+
tracer.agent_children.reset(children_token)
|
133
|
+
return wrapped_method
|
134
|
+
|
135
|
+
setattr(target, attr_name, wrap_method(attr_value))
|
136
|
+
|
137
|
+
# Replace __init__ with wrapped version
|
138
|
+
target.__init__ = wrapped_init
|
139
|
+
return target
|
140
|
+
else:
|
141
|
+
# For function decorators, use existing sync/async tracing
|
142
|
+
is_async = asyncio.iscoroutinefunction(target)
|
143
|
+
if is_async:
|
144
|
+
async def wrapper(*args, **kwargs):
|
145
|
+
return await self._trace_agent_execution(target, name, agent_type, version, capabilities, top_level_hash_id, *args, **kwargs)
|
146
|
+
return wrapper
|
147
|
+
else:
|
148
|
+
def wrapper(*args, **kwargs):
|
149
|
+
return self._trace_sync_agent_execution(target, name, agent_type, version, capabilities, *args, **kwargs)
|
150
|
+
return wrapper
|
151
|
+
|
152
|
+
return decorator
|
153
|
+
|
154
|
+
def _trace_sync_agent_execution(self, func, name, agent_type, version, capabilities, *args, **kwargs):
|
155
|
+
# Generate a unique hash_id for this execution context
|
156
|
+
hash_id = str(uuid.uuid4())
|
157
|
+
|
158
|
+
"""Synchronous version of agent tracing"""
|
159
|
+
if not self.is_active:
|
160
|
+
return func(*args, **kwargs)
|
161
|
+
|
162
|
+
start_time = datetime.now()
|
163
|
+
start_memory = psutil.Process().memory_info().rss
|
164
|
+
component_id = str(uuid.uuid4())
|
165
|
+
|
166
|
+
# Extract ground truth if present
|
167
|
+
ground_truth = kwargs.pop('gt', None) if kwargs else None
|
168
|
+
|
169
|
+
# Get parent agent ID if exists
|
170
|
+
parent_agent_id = self.current_agent_id.get()
|
171
|
+
|
172
|
+
# Set the current agent context
|
173
|
+
agent_token = self.current_agent_id.set(component_id)
|
174
|
+
agent_name_token = self.current_agent_name.set(name)
|
175
|
+
|
176
|
+
# Initialize empty children list for this agent
|
177
|
+
parent_children = self.agent_children.get()
|
178
|
+
children_token = self.agent_children.set([])
|
179
|
+
|
180
|
+
# Start tracking network calls for this component
|
181
|
+
self.start_component(component_id)
|
182
|
+
|
183
|
+
try:
|
184
|
+
# Execute the agent
|
185
|
+
result = func(*args, **kwargs)
|
186
|
+
|
187
|
+
# Calculate resource usage
|
188
|
+
end_time = datetime.now()
|
189
|
+
end_memory = psutil.Process().memory_info().rss
|
190
|
+
memory_used = max(0, end_memory - start_memory)
|
191
|
+
|
192
|
+
# Get children components collected during execution
|
193
|
+
children = self.agent_children.get()
|
194
|
+
|
195
|
+
# End tracking network calls for this component
|
196
|
+
self.end_component(component_id)
|
197
|
+
|
198
|
+
# Create agent component with children and parent if exists
|
199
|
+
agent_component = self.create_agent_component(
|
200
|
+
component_id=component_id,
|
201
|
+
hash_id=hash_id,
|
202
|
+
name=name,
|
203
|
+
agent_type=agent_type,
|
204
|
+
version=version,
|
205
|
+
capabilities=capabilities or [],
|
206
|
+
start_time=start_time,
|
207
|
+
end_time=end_time,
|
208
|
+
memory_used=memory_used,
|
209
|
+
input_data=self._sanitize_input(args, kwargs),
|
210
|
+
output_data=self._sanitize_output(result),
|
211
|
+
children=children,
|
212
|
+
parent_id=parent_agent_id
|
213
|
+
)
|
214
|
+
|
215
|
+
# Add ground truth to component data if present
|
216
|
+
if ground_truth is not None:
|
217
|
+
agent_component["data"]["gt"] = ground_truth
|
218
|
+
|
219
|
+
# Add this component as a child to parent's children list
|
220
|
+
parent_children.append(agent_component)
|
221
|
+
self.agent_children.set(parent_children)
|
222
|
+
|
223
|
+
# Only add to root components if no parent
|
224
|
+
if not parent_agent_id:
|
225
|
+
self.add_component(agent_component)
|
226
|
+
|
227
|
+
return result
|
228
|
+
finally:
|
229
|
+
self.current_agent_id.reset(agent_token)
|
230
|
+
self.current_agent_name.reset(agent_name_token)
|
231
|
+
self.agent_children.reset(children_token)
|
232
|
+
|
233
|
+
async def _trace_agent_execution(self, func, name, agent_type, version, capabilities, hash_id, *args, **kwargs):
|
234
|
+
"""Asynchronous version of agent tracing"""
|
235
|
+
if not self.is_active:
|
236
|
+
return await func(*args, **kwargs)
|
237
|
+
|
238
|
+
start_time = datetime.now()
|
239
|
+
start_memory = psutil.Process().memory_info().rss
|
240
|
+
component_id = str(uuid.uuid4())
|
241
|
+
|
242
|
+
# Extract ground truth if present
|
243
|
+
ground_truth = kwargs.pop('gt', None) if kwargs else None
|
244
|
+
|
245
|
+
# Get parent agent ID if exists
|
246
|
+
parent_agent_id = self.current_agent_id.get()
|
247
|
+
|
248
|
+
# Set the current agent context
|
249
|
+
agent_token = self.current_agent_id.set(component_id)
|
250
|
+
agent_name_token = self.current_agent_name.set(name)
|
251
|
+
|
252
|
+
# Initialize empty children list for this agent
|
253
|
+
parent_children = self.agent_children.get()
|
254
|
+
children_token = self.agent_children.set([])
|
255
|
+
|
256
|
+
try:
|
257
|
+
# Execute the agent
|
258
|
+
result = await func(*args, **kwargs)
|
259
|
+
|
260
|
+
# Calculate resource usage
|
261
|
+
end_time = datetime.now()
|
262
|
+
end_memory = psutil.Process().memory_info().rss
|
263
|
+
memory_used = max(0, end_memory - start_memory)
|
264
|
+
|
265
|
+
# Get children components collected during execution
|
266
|
+
children = self.agent_children.get()
|
267
|
+
|
268
|
+
# Create agent component with children and parent if exists
|
269
|
+
agent_component = self.create_agent_component(
|
270
|
+
component_id=component_id,
|
271
|
+
hash_id=hash_id,
|
272
|
+
name=name,
|
273
|
+
agent_type=agent_type,
|
274
|
+
version=version,
|
275
|
+
capabilities=capabilities or [],
|
276
|
+
start_time=start_time,
|
277
|
+
end_time=end_time,
|
278
|
+
memory_used=memory_used,
|
279
|
+
input_data=self._sanitize_input(args, kwargs),
|
280
|
+
output_data=self._sanitize_output(result),
|
281
|
+
children=children,
|
282
|
+
parent_id=parent_agent_id
|
283
|
+
)
|
284
|
+
|
285
|
+
# Add ground truth to component data if present
|
286
|
+
if ground_truth is not None:
|
287
|
+
agent_component["data"]["gt"] = ground_truth
|
288
|
+
|
289
|
+
# Add this component as a child to parent's children list
|
290
|
+
parent_children.append(agent_component)
|
291
|
+
self.agent_children.set(parent_children)
|
292
|
+
|
293
|
+
# Only add to root components if no parent
|
294
|
+
if not parent_agent_id:
|
295
|
+
self.add_component(agent_component)
|
296
|
+
|
297
|
+
return result
|
298
|
+
except Exception as e:
|
299
|
+
error_component = {
|
300
|
+
"code": 500,
|
301
|
+
"type": type(e).__name__,
|
302
|
+
"message": str(e),
|
303
|
+
"details": {}
|
304
|
+
}
|
305
|
+
|
306
|
+
# Get children even in case of error
|
307
|
+
children = self.agent_children.get()
|
308
|
+
|
309
|
+
# Set parent_id for all children
|
310
|
+
for child in children:
|
311
|
+
child["parent_id"] = component_id
|
312
|
+
|
313
|
+
# End tracking network calls for this component
|
314
|
+
self.end_component(component_id)
|
315
|
+
|
316
|
+
agent_component = self.create_agent_component(
|
317
|
+
component_id=component_id,
|
318
|
+
hash_id=hash_id,
|
319
|
+
name=name,
|
320
|
+
agent_type=agent_type,
|
321
|
+
version=version,
|
322
|
+
capabilities=capabilities or [],
|
323
|
+
start_time=start_time,
|
324
|
+
end_time=datetime.now(),
|
325
|
+
memory_used=0,
|
326
|
+
input_data=self._sanitize_input(args, kwargs),
|
327
|
+
output_data=None,
|
328
|
+
error=error_component,
|
329
|
+
children=children,
|
330
|
+
parent_id=parent_agent_id # Add parent ID if exists
|
331
|
+
)
|
332
|
+
|
333
|
+
# If this is a nested agent, add it to parent's children
|
334
|
+
if parent_agent_id:
|
335
|
+
parent_component = self._agent_components.get(parent_agent_id)
|
336
|
+
if parent_component:
|
337
|
+
if 'children' not in parent_component['data']:
|
338
|
+
parent_component['data']['children'] = []
|
339
|
+
parent_component['data']['children'].append(agent_component)
|
340
|
+
else:
|
341
|
+
# Only add to root components if no parent
|
342
|
+
self.add_component(agent_component)
|
343
|
+
raise
|
344
|
+
finally:
|
345
|
+
# Reset context variables
|
346
|
+
self.current_agent_id.reset(agent_token)
|
347
|
+
self.current_agent_name.reset(agent_name_token)
|
348
|
+
self.agent_children.reset(children_token)
|
349
|
+
|
350
|
+
def create_agent_component(self, **kwargs):
|
351
|
+
"""Create an agent component according to the data structure"""
|
352
|
+
start_time = kwargs["start_time"]
|
353
|
+
component = {
|
354
|
+
"id": kwargs["component_id"],
|
355
|
+
"hash_id": kwargs["hash_id"],
|
356
|
+
"source_hash_id": None,
|
357
|
+
"type": "agent",
|
358
|
+
"name": kwargs["name"],
|
359
|
+
"start_time": start_time.isoformat(),
|
360
|
+
"end_time": kwargs["end_time"].isoformat(),
|
361
|
+
"error": kwargs.get("error"),
|
362
|
+
"parent_id": kwargs.get("parent_id"),
|
363
|
+
"info": {
|
364
|
+
"agent_type": kwargs["agent_type"],
|
365
|
+
"version": kwargs["version"],
|
366
|
+
"capabilities": kwargs["capabilities"],
|
367
|
+
"memory_used": kwargs["memory_used"]
|
368
|
+
},
|
369
|
+
"data": {
|
370
|
+
"input": kwargs["input_data"],
|
371
|
+
"output": kwargs["output_data"],
|
372
|
+
"children": kwargs.get("children", [])
|
373
|
+
},
|
374
|
+
"network_calls": self.component_network_calls.get(kwargs["component_id"], []),
|
375
|
+
"interactions": self.component_user_interaction.get(kwargs["component_id"], [])
|
376
|
+
}
|
377
|
+
|
378
|
+
if self.gt:
|
379
|
+
component["data"]["gt"] = self.gt
|
380
|
+
|
381
|
+
return component
|
382
|
+
|
383
|
+
def start_component(self, component_id):
|
384
|
+
"""Start tracking network calls for a component"""
|
385
|
+
component_network_calls = self.component_network_calls.get()
|
386
|
+
if component_id not in component_network_calls:
|
387
|
+
component_network_calls[component_id] = []
|
388
|
+
self.component_network_calls.set(component_network_calls)
|
389
|
+
|
390
|
+
def end_component(self, component_id):
|
391
|
+
"""End tracking network calls for a component"""
|
392
|
+
component_network_calls = self.component_network_calls.get()
|
393
|
+
if component_id in component_network_calls:
|
394
|
+
component_network_calls[component_id] = []
|
395
|
+
self.component_network_calls.set(component_network_calls)
|
396
|
+
|
397
|
+
def _sanitize_input(self, args: tuple, kwargs: dict) -> str:
|
398
|
+
"""Convert input arguments to text format.
|
399
|
+
|
400
|
+
Args:
|
401
|
+
args: Input arguments tuple
|
402
|
+
kwargs: Input keyword arguments dict
|
403
|
+
|
404
|
+
Returns:
|
405
|
+
str: Text representation of the input arguments
|
406
|
+
"""
|
407
|
+
def _sanitize_value(value):
|
408
|
+
if isinstance(value, dict):
|
409
|
+
return str({k: _sanitize_value(v) for k, v in value.items()})
|
410
|
+
elif isinstance(value, (list, tuple)):
|
411
|
+
return str([_sanitize_value(item) for item in value])
|
412
|
+
return str(value)
|
413
|
+
|
414
|
+
sanitized_args = [_sanitize_value(arg) for arg in args]
|
415
|
+
sanitized_kwargs = {k: _sanitize_value(v) for k, v in kwargs.items()}
|
416
|
+
return str({"args": sanitized_args, "kwargs": sanitized_kwargs})
|
417
|
+
|
418
|
+
def _sanitize_output(self, output: Any) -> Any:
|
419
|
+
"""Sanitize and format output data"""
|
420
|
+
if isinstance(output, (int, float, bool, str, list, dict)):
|
421
|
+
return output
|
422
|
+
return str(output)
|
@@ -0,0 +1,198 @@
|
|
1
|
+
import contextvars
|
2
|
+
from typing import Optional, Dict
|
3
|
+
import json
|
4
|
+
from datetime import datetime
|
5
|
+
import uuid
|
6
|
+
import os
|
7
|
+
import builtins
|
8
|
+
from pathlib import Path
|
9
|
+
|
10
|
+
from .base import BaseTracer
|
11
|
+
from .llm_tracer import LLMTracerMixin
|
12
|
+
from .tool_tracer import ToolTracerMixin
|
13
|
+
from .agent_tracer import AgentTracerMixin
|
14
|
+
from .network_tracer import NetworkTracer
|
15
|
+
from .user_interaction_tracer import UserInteractionTracer
|
16
|
+
|
17
|
+
from .data_structure import (
|
18
|
+
Trace, Metadata, SystemInfo, OSInfo, EnvironmentInfo,
|
19
|
+
Resources, CPUResource, MemoryResource, DiskResource, NetworkResource,
|
20
|
+
ResourceInfo, MemoryInfo, DiskInfo, NetworkInfo,
|
21
|
+
Component, LLMComponent, AgentComponent, ToolComponent,
|
22
|
+
NetworkCall, Interaction, Error
|
23
|
+
)
|
24
|
+
|
25
|
+
|
26
|
+
from ...ragaai_catalyst import RagaAICatalyst
|
27
|
+
from ..upload_traces import UploadTraces
|
28
|
+
|
29
|
+
class AgenticTracing(BaseTracer, LLMTracerMixin, ToolTracerMixin, AgentTracerMixin):
|
30
|
+
def __init__(self, user_detail, auto_instrument_llm: bool = True):
|
31
|
+
# Initialize all parent classes
|
32
|
+
self.user_interaction_tracer = UserInteractionTracer()
|
33
|
+
LLMTracerMixin.__init__(self)
|
34
|
+
ToolTracerMixin.__init__(self)
|
35
|
+
AgentTracerMixin.__init__(self)
|
36
|
+
|
37
|
+
self.project_name = user_detail["project_name"]
|
38
|
+
self.project_id = user_detail["project_id"]
|
39
|
+
self.dataset_name = user_detail["dataset_name"]
|
40
|
+
self.trace_user_detail = user_detail["trace_user_detail"]
|
41
|
+
self.base_url = f"{RagaAICatalyst.BASE_URL}"
|
42
|
+
self.timeout = 10
|
43
|
+
|
44
|
+
BaseTracer.__init__(self, user_detail)
|
45
|
+
|
46
|
+
self.auto_instrument_llm = auto_instrument_llm
|
47
|
+
self.tools: Dict[str, Tool] = {}
|
48
|
+
self.call_depth = contextvars.ContextVar("call_depth", default=0)
|
49
|
+
self.current_component_id = contextvars.ContextVar("current_component_id", default=None)
|
50
|
+
self.network_tracer = NetworkTracer()
|
51
|
+
self.is_active = False
|
52
|
+
self.current_agent_id = contextvars.ContextVar("current_agent_id", default=None)
|
53
|
+
self.agent_children = contextvars.ContextVar("agent_children", default=[])
|
54
|
+
self.component_network_calls = {} # Store network calls per component
|
55
|
+
self.component_user_interaction = {}
|
56
|
+
|
57
|
+
# Create output directory if it doesn't exist
|
58
|
+
self.output_dir = Path("./traces") # Using default traces directory
|
59
|
+
self.output_dir.mkdir(exist_ok=True)
|
60
|
+
|
61
|
+
def start_component(self, component_id: str):
|
62
|
+
"""Start tracking network calls for a component"""
|
63
|
+
self.component_network_calls[component_id] = []
|
64
|
+
self.network_tracer.network_calls = [] # Reset network calls
|
65
|
+
self.component_user_interaction[component_id] = []
|
66
|
+
self.current_component_id.set(component_id)
|
67
|
+
self.user_interaction_tracer.component_id.set(component_id)
|
68
|
+
|
69
|
+
def end_component(self, component_id: str):
|
70
|
+
"""End tracking network calls for a component"""
|
71
|
+
self.component_network_calls[component_id] = self.network_tracer.network_calls.copy()
|
72
|
+
self.network_tracer.network_calls = [] # Reset for next component
|
73
|
+
self.component_user_interaction[component_id] = self.user_interaction_tracer.interactions.copy()
|
74
|
+
self.user_interaction_tracer.interactions = []
|
75
|
+
|
76
|
+
def start(self):
|
77
|
+
"""Start tracing"""
|
78
|
+
# Setup user interaction tracing
|
79
|
+
self.user_interaction_tracer.project_id.set(self.project_id)
|
80
|
+
self.user_interaction_tracer.trace_id.set(self.trace_id)
|
81
|
+
self.user_interaction_tracer.tracer = self
|
82
|
+
self.user_interaction_tracer.component_id.set(self.current_component_id.get())
|
83
|
+
builtins.print = self.user_interaction_tracer.traced_print
|
84
|
+
builtins.input = self.user_interaction_tracer.traced_input
|
85
|
+
|
86
|
+
# Start base tracer (includes system info and resource monitoring)
|
87
|
+
super().start()
|
88
|
+
self.is_active = True
|
89
|
+
|
90
|
+
# Activate network tracing
|
91
|
+
self.network_tracer.activate_patches()
|
92
|
+
|
93
|
+
# Instrument calls from mixins
|
94
|
+
if self.auto_instrument_llm:
|
95
|
+
self.instrument_llm_calls()
|
96
|
+
|
97
|
+
def stop(self):
|
98
|
+
"""Stop tracing and save results"""
|
99
|
+
if self.is_active:
|
100
|
+
# Restore original print and input functions
|
101
|
+
builtins.print = self.user_interaction_tracer.original_print
|
102
|
+
builtins.input = self.user_interaction_tracer.original_input
|
103
|
+
|
104
|
+
# Calculate final metrics before stopping
|
105
|
+
self._calculate_final_metrics()
|
106
|
+
|
107
|
+
# Deactivate network tracing
|
108
|
+
self.network_tracer.deactivate_patches()
|
109
|
+
|
110
|
+
# Stop base tracer (includes saving to file)
|
111
|
+
super().stop()
|
112
|
+
|
113
|
+
# Cleanup
|
114
|
+
self.unpatch_llm_calls()
|
115
|
+
self.is_active = False
|
116
|
+
|
117
|
+
|
118
|
+
def _calculate_final_metrics(self):
|
119
|
+
"""Calculate total cost and tokens from all components"""
|
120
|
+
total_cost = 0.0
|
121
|
+
total_tokens = 0
|
122
|
+
|
123
|
+
def process_component(component):
|
124
|
+
nonlocal total_cost, total_tokens
|
125
|
+
# Convert component to dict if it's an object
|
126
|
+
comp_dict = component.__dict__ if hasattr(component, '__dict__') else component
|
127
|
+
|
128
|
+
if comp_dict.get('type') == "llm":
|
129
|
+
info = comp_dict.get('info', {})
|
130
|
+
if isinstance(info, dict):
|
131
|
+
# Extract cost
|
132
|
+
cost_info = info.get('cost', {})
|
133
|
+
if isinstance(cost_info, dict):
|
134
|
+
total_cost += cost_info.get('total_cost', 0)
|
135
|
+
|
136
|
+
# Extract tokens
|
137
|
+
token_info = info.get('tokens', {})
|
138
|
+
if isinstance(token_info, dict):
|
139
|
+
total_tokens += token_info.get('total_tokens', 0)
|
140
|
+
else:
|
141
|
+
token_info = info.get('token_usage', {})
|
142
|
+
if isinstance(token_info, dict):
|
143
|
+
total_tokens += token_info.get('total_tokens', 0)
|
144
|
+
|
145
|
+
# Process children if they exist
|
146
|
+
data = comp_dict.get('data', {})
|
147
|
+
if isinstance(data, dict):
|
148
|
+
children = data.get('children', [])
|
149
|
+
if children:
|
150
|
+
for child in children:
|
151
|
+
process_component(child)
|
152
|
+
|
153
|
+
# Process all root components
|
154
|
+
for component in self.components:
|
155
|
+
process_component(component)
|
156
|
+
|
157
|
+
# Update metadata in trace
|
158
|
+
if hasattr(self, 'trace'):
|
159
|
+
if isinstance(self.trace.metadata, dict):
|
160
|
+
self.trace.metadata['total_cost'] = total_cost
|
161
|
+
self.trace.metadata['total_tokens'] = total_tokens
|
162
|
+
else:
|
163
|
+
self.trace.metadata.total_cost = total_cost
|
164
|
+
self.trace.metadata.total_tokens = total_tokens
|
165
|
+
|
166
|
+
def add_component(self, component_data: dict):
|
167
|
+
"""Add a component to the trace data"""
|
168
|
+
# Convert dict to appropriate Component type
|
169
|
+
filtered_data = {k: v for k, v in component_data.items() if k in ["id", "hash_id", "type", "name", "start_time", "end_time", "parent_id", "info", "data", "network_calls", "interactions"]}
|
170
|
+
|
171
|
+
if component_data["type"] == "llm":
|
172
|
+
component = LLMComponent(**filtered_data)
|
173
|
+
elif component_data["type"] == "agent":
|
174
|
+
component = AgentComponent(**filtered_data)
|
175
|
+
elif component_data["type"] == "tool":
|
176
|
+
component = ToolComponent(**filtered_data)
|
177
|
+
else:
|
178
|
+
component = Component(**component_data)
|
179
|
+
|
180
|
+
# Check if there's an active agent context
|
181
|
+
current_agent_id = self.current_agent_id.get()
|
182
|
+
if current_agent_id and component_data["type"] in ["llm", "tool"]:
|
183
|
+
# Add this component as a child of the current agent
|
184
|
+
current_children = self.agent_children.get()
|
185
|
+
current_children.append(component_data)
|
186
|
+
self.agent_children.set(current_children)
|
187
|
+
else:
|
188
|
+
# Add component to the main trace
|
189
|
+
super().add_component(component)
|
190
|
+
|
191
|
+
def __enter__(self):
|
192
|
+
"""Context manager entry"""
|
193
|
+
self.start()
|
194
|
+
return self
|
195
|
+
|
196
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
197
|
+
"""Context manager exit"""
|
198
|
+
self.stop()
|