ragaai-catalyst 2.1.3b0__py3-none-any.whl → 2.1.4b1__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/tracers/agentic_tracing/data/data_structure.py +36 -10
- ragaai_catalyst/tracers/agentic_tracing/tracers/agent_tracer.py +213 -76
- ragaai_catalyst/tracers/agentic_tracing/tracers/base.py +568 -107
- ragaai_catalyst/tracers/agentic_tracing/tracers/custom_tracer.py +325 -0
- ragaai_catalyst/tracers/agentic_tracing/tracers/llm_tracer.py +207 -81
- ragaai_catalyst/tracers/agentic_tracing/tracers/main_tracer.py +208 -58
- ragaai_catalyst/tracers/agentic_tracing/tracers/network_tracer.py +2 -0
- ragaai_catalyst/tracers/agentic_tracing/tracers/tool_tracer.py +125 -28
- ragaai_catalyst/tracers/agentic_tracing/tracers/user_interaction_tracer.py +86 -0
- ragaai_catalyst/tracers/agentic_tracing/upload/upload_agentic_traces.py +9 -51
- ragaai_catalyst/tracers/agentic_tracing/upload/upload_trace_metric.py +83 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/create_dataset_schema.py +26 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/get_user_trace_metrics.py +28 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/llm_utils.py +45 -15
- ragaai_catalyst/tracers/agentic_tracing/utils/model_costs.json +2476 -2122
- ragaai_catalyst/tracers/agentic_tracing/utils/span_attributes.py +59 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/trace_utils.py +23 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/zip_list_of_unique_files.py +284 -15
- ragaai_catalyst/tracers/tracer.py +80 -8
- ragaai_catalyst-2.1.4b1.dist-info/METADATA +431 -0
- {ragaai_catalyst-2.1.3b0.dist-info → ragaai_catalyst-2.1.4b1.dist-info}/RECORD +23 -18
- ragaai_catalyst-2.1.3b0.dist-info/METADATA +0 -43
- {ragaai_catalyst-2.1.3b0.dist-info → ragaai_catalyst-2.1.4b1.dist-info}/WHEEL +0 -0
- {ragaai_catalyst-2.1.3b0.dist-info → ragaai_catalyst-2.1.4b1.dist-info}/top_level.txt +0 -0
@@ -1,12 +1,22 @@
|
|
1
|
+
import os
|
1
2
|
import uuid
|
2
3
|
from datetime import datetime
|
3
4
|
import psutil
|
4
5
|
import functools
|
5
6
|
from typing import Optional, Any, Dict, List
|
6
|
-
from ..utils.unique_decorator import generate_unique_hash_simple
|
7
|
+
from ..utils.unique_decorator import generate_unique_hash_simple
|
7
8
|
import contextvars
|
8
9
|
import asyncio
|
9
10
|
from ..utils.file_name_tracker import TrackName
|
11
|
+
from ..utils.span_attributes import SpanAttributes
|
12
|
+
import logging
|
13
|
+
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
logging_level = (
|
16
|
+
logger.setLevel(logging.DEBUG)
|
17
|
+
if os.getenv("DEBUG")
|
18
|
+
else logger.setLevel(logging.INFO)
|
19
|
+
)
|
10
20
|
|
11
21
|
|
12
22
|
class ToolTracerMixin:
|
@@ -19,17 +29,65 @@ class ToolTracerMixin:
|
|
19
29
|
self.component_user_interaction = {}
|
20
30
|
self.gt = None
|
21
31
|
|
32
|
+
# add auto_instrument option
|
33
|
+
self.auto_instrument_tool = False
|
34
|
+
self.auto_instrument_user_interaction = False
|
35
|
+
self.auto_instrument_network = False
|
36
|
+
|
37
|
+
# take care of auto_instrument
|
38
|
+
def instrument_tool_calls(self):
|
39
|
+
self.auto_instrument_tool = True
|
40
|
+
|
41
|
+
def instrument_user_interaction_calls(self):
|
42
|
+
self.auto_instrument_user_interaction = True
|
43
|
+
|
44
|
+
def instrument_network_calls(self):
|
45
|
+
self.auto_instrument_network = True
|
46
|
+
|
47
|
+
def trace_tool(
|
48
|
+
self,
|
49
|
+
name: str,
|
50
|
+
tool_type: str = "generic",
|
51
|
+
version: str = "1.0.0",
|
52
|
+
tags: List[str] = [],
|
53
|
+
metadata: Dict[str, Any] = {},
|
54
|
+
metrics: List[Dict[str, Any]] = [],
|
55
|
+
feedback: Optional[Any] = None,
|
56
|
+
):
|
57
|
+
if name not in self.span_attributes_dict:
|
58
|
+
self.span_attributes_dict[name] = SpanAttributes(name)
|
59
|
+
if tags:
|
60
|
+
self.span(name).add_tags(tags)
|
61
|
+
if metadata:
|
62
|
+
self.span(name).add_metadata(metadata)
|
63
|
+
if metrics:
|
64
|
+
if isinstance(metrics, dict):
|
65
|
+
metrics = [metrics]
|
66
|
+
for metric in metrics:
|
67
|
+
try:
|
68
|
+
self.span(name).add_metrics(
|
69
|
+
name=metric["name"],
|
70
|
+
score=metric["score"],
|
71
|
+
reasoning=metric.get("reasoning", ""),
|
72
|
+
cost=metric.get("cost", None),
|
73
|
+
latency=metric.get("latency", None),
|
74
|
+
metadata=metric.get("metadata", {}),
|
75
|
+
config=metric.get("config", {}),
|
76
|
+
)
|
77
|
+
except KeyError as e:
|
78
|
+
logger.error(f"Error adding metric: {e}")
|
79
|
+
if feedback:
|
80
|
+
self.span(name).add_feedback(feedback)
|
22
81
|
|
23
|
-
def trace_tool(self, name: str, tool_type: str = "generic", version: str = "1.0.0"):
|
24
82
|
def decorator(func):
|
25
83
|
# Add metadata attribute to the function
|
26
84
|
metadata = {
|
27
85
|
"name": name,
|
28
86
|
"tool_type": tool_type,
|
29
87
|
"version": version,
|
30
|
-
"is_active":
|
88
|
+
"is_active": self.is_active,
|
31
89
|
}
|
32
|
-
|
90
|
+
|
33
91
|
# Check if the function is async
|
34
92
|
is_async = asyncio.iscoroutinefunction(func)
|
35
93
|
|
@@ -37,7 +95,7 @@ class ToolTracerMixin:
|
|
37
95
|
@functools.wraps(func)
|
38
96
|
async def async_wrapper(*args, **kwargs):
|
39
97
|
async_wrapper.metadata = metadata
|
40
|
-
self.gt = kwargs.get(
|
98
|
+
self.gt = kwargs.get("gt", None) if kwargs else None
|
41
99
|
return await self._trace_tool_execution(
|
42
100
|
func, name, tool_type, version, *args, **kwargs
|
43
101
|
)
|
@@ -46,7 +104,7 @@ class ToolTracerMixin:
|
|
46
104
|
@functools.wraps(func)
|
47
105
|
def sync_wrapper(*args, **kwargs):
|
48
106
|
sync_wrapper.metadata = metadata
|
49
|
-
self.gt = kwargs.get(
|
107
|
+
self.gt = kwargs.get("gt", None) if kwargs else None
|
50
108
|
return self._trace_sync_tool_execution(
|
51
109
|
func, name, tool_type, version, *args, **kwargs
|
52
110
|
)
|
@@ -57,11 +115,16 @@ class ToolTracerMixin:
|
|
57
115
|
|
58
116
|
return decorator
|
59
117
|
|
60
|
-
def _trace_sync_tool_execution(
|
118
|
+
def _trace_sync_tool_execution(
|
119
|
+
self, func, name, tool_type, version, *args, **kwargs
|
120
|
+
):
|
61
121
|
"""Synchronous version of tool tracing"""
|
62
122
|
if not self.is_active:
|
63
123
|
return func(*args, **kwargs)
|
64
124
|
|
125
|
+
if not self.auto_instrument_tool:
|
126
|
+
return func(*args, **kwargs)
|
127
|
+
|
65
128
|
start_time = datetime.now().astimezone()
|
66
129
|
start_memory = psutil.Process().memory_info().rss
|
67
130
|
component_id = str(uuid.uuid4())
|
@@ -91,10 +154,11 @@ class ToolTracerMixin:
|
|
91
154
|
memory_used=memory_used,
|
92
155
|
start_time=start_time,
|
93
156
|
input_data=self._sanitize_input(args, kwargs),
|
94
|
-
output_data=self._sanitize_output(result)
|
157
|
+
output_data=self._sanitize_output(result),
|
95
158
|
)
|
96
159
|
|
97
160
|
self.add_component(tool_component)
|
161
|
+
|
98
162
|
return result
|
99
163
|
|
100
164
|
except Exception as e:
|
@@ -102,12 +166,12 @@ class ToolTracerMixin:
|
|
102
166
|
"code": 500,
|
103
167
|
"type": type(e).__name__,
|
104
168
|
"message": str(e),
|
105
|
-
"details": {}
|
169
|
+
"details": {},
|
106
170
|
}
|
107
|
-
|
171
|
+
|
108
172
|
# End tracking network calls for this component
|
109
173
|
self.end_component(component_id)
|
110
|
-
|
174
|
+
|
111
175
|
tool_component = self.create_tool_component(
|
112
176
|
component_id=component_id,
|
113
177
|
hash_id=hash_id,
|
@@ -118,22 +182,29 @@ class ToolTracerMixin:
|
|
118
182
|
start_time=start_time,
|
119
183
|
input_data=self._sanitize_input(args, kwargs),
|
120
184
|
output_data=None,
|
121
|
-
error=error_component
|
185
|
+
error=error_component,
|
122
186
|
)
|
123
187
|
|
124
188
|
self.add_component(tool_component)
|
189
|
+
|
125
190
|
raise
|
126
191
|
|
127
|
-
async def _trace_tool_execution(
|
192
|
+
async def _trace_tool_execution(
|
193
|
+
self, func, name, tool_type, version, *args, **kwargs
|
194
|
+
):
|
128
195
|
"""Asynchronous version of tool tracing"""
|
129
196
|
if not self.is_active:
|
130
197
|
return await func(*args, **kwargs)
|
131
198
|
|
199
|
+
if not self.auto_instrument_tool:
|
200
|
+
return await func(*args, **kwargs)
|
201
|
+
|
132
202
|
start_time = datetime.now().astimezone()
|
133
203
|
start_memory = psutil.Process().memory_info().rss
|
134
204
|
component_id = str(uuid.uuid4())
|
135
205
|
hash_id = generate_unique_hash_simple(func)
|
136
206
|
|
207
|
+
self.start_component(component_id)
|
137
208
|
try:
|
138
209
|
# Execute the tool
|
139
210
|
result = await func(*args, **kwargs)
|
@@ -141,6 +212,7 @@ class ToolTracerMixin:
|
|
141
212
|
# Calculate resource usage
|
142
213
|
end_memory = psutil.Process().memory_info().rss
|
143
214
|
memory_used = max(0, end_memory - start_memory)
|
215
|
+
self.end_component(component_id)
|
144
216
|
|
145
217
|
# Create tool component
|
146
218
|
tool_component = self.create_tool_component(
|
@@ -152,9 +224,10 @@ class ToolTracerMixin:
|
|
152
224
|
start_time=start_time,
|
153
225
|
memory_used=memory_used,
|
154
226
|
input_data=self._sanitize_input(args, kwargs),
|
155
|
-
output_data=self._sanitize_output(result)
|
227
|
+
output_data=self._sanitize_output(result),
|
156
228
|
)
|
157
229
|
self.add_component(tool_component)
|
230
|
+
|
158
231
|
return result
|
159
232
|
|
160
233
|
except Exception as e:
|
@@ -162,9 +235,9 @@ class ToolTracerMixin:
|
|
162
235
|
"code": 500,
|
163
236
|
"type": type(e).__name__,
|
164
237
|
"message": str(e),
|
165
|
-
"details": {}
|
238
|
+
"details": {},
|
166
239
|
}
|
167
|
-
|
240
|
+
|
168
241
|
tool_component = self.create_tool_component(
|
169
242
|
component_id=component_id,
|
170
243
|
hash_id=hash_id,
|
@@ -175,15 +248,23 @@ class ToolTracerMixin:
|
|
175
248
|
memory_used=0,
|
176
249
|
input_data=self._sanitize_input(args, kwargs),
|
177
250
|
output_data=None,
|
178
|
-
error=error_component
|
251
|
+
error=error_component,
|
179
252
|
)
|
180
253
|
self.add_component(tool_component)
|
254
|
+
|
181
255
|
raise
|
182
256
|
|
183
257
|
def create_tool_component(self, **kwargs):
|
184
|
-
|
185
|
-
|
186
258
|
"""Create a tool component according to the data structure"""
|
259
|
+
network_calls = []
|
260
|
+
if self.auto_instrument_network:
|
261
|
+
network_calls = self.component_network_calls.get(kwargs["component_id"], [])
|
262
|
+
interactions = []
|
263
|
+
if self.auto_instrument_user_interaction:
|
264
|
+
interactions = self.component_user_interaction.get(
|
265
|
+
kwargs["component_id"], []
|
266
|
+
)
|
267
|
+
|
187
268
|
start_time = kwargs["start_time"]
|
188
269
|
component = {
|
189
270
|
"id": kwargs["component_id"],
|
@@ -198,20 +279,25 @@ class ToolTracerMixin:
|
|
198
279
|
"info": {
|
199
280
|
"tool_type": kwargs["tool_type"],
|
200
281
|
"version": kwargs["version"],
|
201
|
-
"memory_used": kwargs["memory_used"]
|
282
|
+
"memory_used": kwargs["memory_used"],
|
283
|
+
"tags": self.span_attributes_dict[kwargs["name"]].tags or [],
|
202
284
|
},
|
203
285
|
"data": {
|
204
286
|
"input": kwargs["input_data"],
|
205
287
|
"output": kwargs["output_data"],
|
206
|
-
"memory_used": kwargs["memory_used"]
|
288
|
+
"memory_used": kwargs["memory_used"],
|
207
289
|
},
|
208
|
-
"
|
209
|
-
"
|
290
|
+
"metrics": self.span_attributes_dict[kwargs["name"]].metrics or [],
|
291
|
+
"network_calls": network_calls,
|
292
|
+
"interactions": interactions,
|
210
293
|
}
|
211
294
|
|
212
|
-
if self.gt:
|
295
|
+
if self.gt:
|
213
296
|
component["data"]["gt"] = self.gt
|
214
297
|
|
298
|
+
# Reset the SpanAttributes context variable
|
299
|
+
self.span_attributes_dict[kwargs["name"]] = SpanAttributes(kwargs["name"])
|
300
|
+
|
215
301
|
return component
|
216
302
|
|
217
303
|
def start_component(self, component_id):
|
@@ -223,15 +309,26 @@ class ToolTracerMixin:
|
|
223
309
|
def _sanitize_input(self, args: tuple, kwargs: dict) -> Dict:
|
224
310
|
"""Sanitize and format input data"""
|
225
311
|
return {
|
226
|
-
"args": [
|
312
|
+
"args": [
|
313
|
+
(
|
314
|
+
str(arg)
|
315
|
+
if not isinstance(arg, (int, float, bool, str, list, dict))
|
316
|
+
else arg
|
317
|
+
)
|
318
|
+
for arg in args
|
319
|
+
],
|
227
320
|
"kwargs": {
|
228
|
-
k:
|
321
|
+
k: (
|
322
|
+
str(v)
|
323
|
+
if not isinstance(v, (int, float, bool, str, list, dict))
|
324
|
+
else v
|
325
|
+
)
|
229
326
|
for k, v in kwargs.items()
|
230
|
-
}
|
327
|
+
},
|
231
328
|
}
|
232
329
|
|
233
330
|
def _sanitize_output(self, output: Any) -> Any:
|
234
331
|
"""Sanitize and format output data"""
|
235
332
|
if isinstance(output, (int, float, bool, str, list, dict)):
|
236
333
|
return output
|
237
|
-
return str(output)
|
334
|
+
return str(output)
|
@@ -3,6 +3,35 @@ from datetime import datetime
|
|
3
3
|
import contextvars
|
4
4
|
import inspect
|
5
5
|
import uuid
|
6
|
+
from typing import Optional, Any
|
7
|
+
|
8
|
+
class TracedFile:
|
9
|
+
def __init__(self, file_obj, file_path: str, tracer):
|
10
|
+
self._file = file_obj
|
11
|
+
self._file_path = file_path
|
12
|
+
self._tracer = tracer
|
13
|
+
|
14
|
+
def write(self, content: str) -> int:
|
15
|
+
self._tracer.trace_file_operation("write", self._file_path, content=content)
|
16
|
+
return self._file.write(content)
|
17
|
+
|
18
|
+
def read(self, size: Optional[int] = None) -> str:
|
19
|
+
content = self._file.read() if size is None else self._file.read(size)
|
20
|
+
self._tracer.trace_file_operation("read", self._file_path, content=content)
|
21
|
+
return content
|
22
|
+
|
23
|
+
def close(self) -> None:
|
24
|
+
return self._file.close()
|
25
|
+
|
26
|
+
def __enter__(self):
|
27
|
+
return self
|
28
|
+
|
29
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
30
|
+
self.close()
|
31
|
+
return None
|
32
|
+
|
33
|
+
def __getattr__(self, name: str) -> Any:
|
34
|
+
return getattr(self._file, name)
|
6
35
|
|
7
36
|
class UserInteractionTracer:
|
8
37
|
def __init__(self, *args, **kwargs):
|
@@ -12,6 +41,7 @@ class UserInteractionTracer:
|
|
12
41
|
self.component_id = contextvars.ContextVar("component_id", default=None)
|
13
42
|
self.original_input = builtins.input
|
14
43
|
self.original_print = builtins.print
|
44
|
+
self.original_open = builtins.open
|
15
45
|
self.interactions = []
|
16
46
|
|
17
47
|
def traced_input(self, prompt=""):
|
@@ -25,6 +55,7 @@ class UserInteractionTracer:
|
|
25
55
|
|
26
56
|
self.interactions.append({
|
27
57
|
"id": str(uuid.uuid4()),
|
58
|
+
"component_id": self.component_id.get(),
|
28
59
|
"interaction_type": "input",
|
29
60
|
"content": content,
|
30
61
|
"timestamp": datetime.now().isoformat()
|
@@ -36,8 +67,63 @@ class UserInteractionTracer:
|
|
36
67
|
|
37
68
|
self.interactions.append({
|
38
69
|
"id": str(uuid.uuid4()),
|
70
|
+
"component_id": self.component_id.get(),
|
39
71
|
"interaction_type": "output",
|
40
72
|
"content": content,
|
41
73
|
"timestamp": datetime.now().isoformat()
|
42
74
|
})
|
43
75
|
return self.original_print(*args, **kwargs)
|
76
|
+
|
77
|
+
def traced_open(self, file: str, mode: str = 'r', *args, **kwargs):
|
78
|
+
# Skip tracing for system and virtual environment paths
|
79
|
+
system_paths = [
|
80
|
+
'site-packages',
|
81
|
+
'dist-packages',
|
82
|
+
'/proc/',
|
83
|
+
'/sys/',
|
84
|
+
'/var/lib/',
|
85
|
+
'/usr/lib/',
|
86
|
+
'/System/Library'
|
87
|
+
]
|
88
|
+
|
89
|
+
file_str = str(file)
|
90
|
+
if any(path in file_str for path in system_paths):
|
91
|
+
return self.original_open(file, mode, *args, **kwargs)
|
92
|
+
|
93
|
+
file_obj = self.original_open(file, mode, *args, **kwargs)
|
94
|
+
return TracedFile(file_obj, file, self)
|
95
|
+
|
96
|
+
def trace_file_operation(self, operation: str, file_path: str, **kwargs):
|
97
|
+
interaction_type = f"file_{operation}"
|
98
|
+
|
99
|
+
# Check for existing interaction with same file_path and operation
|
100
|
+
for existing in reversed(self.interactions):
|
101
|
+
if (existing.get("file_path") == file_path and
|
102
|
+
existing.get("interaction_type") == interaction_type):
|
103
|
+
# Merge content if it exists
|
104
|
+
if "content" in kwargs and "content" in existing:
|
105
|
+
existing["content"] += kwargs["content"]
|
106
|
+
return
|
107
|
+
break
|
108
|
+
|
109
|
+
# If no matching interaction found or couldn't merge, create new one
|
110
|
+
interaction = {
|
111
|
+
"id": str(uuid.uuid4()),
|
112
|
+
"component_id": self.component_id.get(),
|
113
|
+
"interaction_type": interaction_type,
|
114
|
+
"file_path": file_path,
|
115
|
+
"timestamp": datetime.now().isoformat()
|
116
|
+
}
|
117
|
+
interaction.update(kwargs)
|
118
|
+
self.interactions.append(interaction)
|
119
|
+
|
120
|
+
def __enter__(self):
|
121
|
+
builtins.input = self.traced_input
|
122
|
+
builtins.print = self.traced_print
|
123
|
+
builtins.open = self.traced_open
|
124
|
+
return self
|
125
|
+
|
126
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
127
|
+
builtins.input = self.original_input
|
128
|
+
builtins.print = self.original_print
|
129
|
+
builtins.open = self.original_open
|
@@ -18,50 +18,7 @@ class UploadAgenticTraces:
|
|
18
18
|
self.dataset_name = dataset_name
|
19
19
|
self.user_detail = user_detail
|
20
20
|
self.base_url = base_url
|
21
|
-
self.timeout =
|
22
|
-
|
23
|
-
def _create_dataset_schema_with_trace(self):
|
24
|
-
SCHEMA_MAPPING_NEW = {
|
25
|
-
"trace_id": {"columnType": "traceId"},
|
26
|
-
"trace_uri": {"columnType": "traceUri"},
|
27
|
-
"prompt": {"columnType": "prompt"},
|
28
|
-
"response":{"columnType": "response"},
|
29
|
-
"context": {"columnType": "context"},
|
30
|
-
"llm_model": {"columnType":"pipeline"},
|
31
|
-
"recorded_on": {"columnType": "metadata"},
|
32
|
-
"embed_model": {"columnType":"pipeline"},
|
33
|
-
"log_source": {"columnType": "metadata"},
|
34
|
-
"vector_store":{"columnType":"pipeline"},
|
35
|
-
"feedback": {"columnType":"feedBack"}
|
36
|
-
}
|
37
|
-
def make_request():
|
38
|
-
headers = {
|
39
|
-
"Content-Type": "application/json",
|
40
|
-
"Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
|
41
|
-
"X-Project-Name": self.project_name,
|
42
|
-
}
|
43
|
-
payload = json.dumps({
|
44
|
-
"datasetName": self.dataset_name,
|
45
|
-
# "schemaMapping": SCHEMA_MAPPING_NEW,
|
46
|
-
"traceFolderUrl": None,
|
47
|
-
})
|
48
|
-
response = requests.request("POST",
|
49
|
-
f"{self.base_url}/v1/llm/dataset/logs",
|
50
|
-
headers=headers,
|
51
|
-
data=payload,
|
52
|
-
timeout=self.timeout
|
53
|
-
)
|
54
|
-
|
55
|
-
return response
|
56
|
-
|
57
|
-
response = make_request()
|
58
|
-
|
59
|
-
if response.status_code == 401:
|
60
|
-
# get_token() # Fetch a new token and set it in the environment
|
61
|
-
response = make_request() # Retry the request
|
62
|
-
if response.status_code != 200:
|
63
|
-
return response.status_code
|
64
|
-
return response.status_code
|
21
|
+
self.timeout = 30
|
65
22
|
|
66
23
|
|
67
24
|
def _get_presigned_url(self):
|
@@ -181,10 +138,11 @@ class UploadAgenticTraces:
|
|
181
138
|
return None
|
182
139
|
|
183
140
|
def upload_agentic_traces(self):
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
141
|
+
try:
|
142
|
+
presignedUrl = self._get_presigned_url()
|
143
|
+
if presignedUrl is None:
|
144
|
+
return
|
145
|
+
self._put_presigned_url(presignedUrl, self.json_file_path)
|
146
|
+
self.insert_traces(presignedUrl)
|
147
|
+
except Exception as e:
|
148
|
+
print(f"Error while uploading agentic traces: {e}")
|
@@ -0,0 +1,83 @@
|
|
1
|
+
import requests
|
2
|
+
import os
|
3
|
+
import json
|
4
|
+
from ....ragaai_catalyst import RagaAICatalyst
|
5
|
+
from ..utils.get_user_trace_metrics import get_user_trace_metrics
|
6
|
+
|
7
|
+
def upload_trace_metric(json_file_path, dataset_name, project_name):
|
8
|
+
try:
|
9
|
+
with open(json_file_path, "r") as f:
|
10
|
+
traces = json.load(f)
|
11
|
+
metrics = get_trace_metrics_from_trace(traces)
|
12
|
+
metrics = _change_metrics_format_for_payload(metrics)
|
13
|
+
|
14
|
+
user_trace_metrics = get_user_trace_metrics(project_name, dataset_name)
|
15
|
+
if user_trace_metrics:
|
16
|
+
user_trace_metrics_list = [metric["displayName"] for metric in user_trace_metrics]
|
17
|
+
|
18
|
+
if user_trace_metrics:
|
19
|
+
for metric in metrics:
|
20
|
+
if metric["displayName"] in user_trace_metrics_list:
|
21
|
+
metricConfig = next((user_metric["metricConfig"] for user_metric in user_trace_metrics if user_metric["displayName"] == metric["displayName"]), None)
|
22
|
+
if not metricConfig or metricConfig.get("Metric Source", {}).get("value") != "user":
|
23
|
+
raise ValueError(f"Metrics {metric['displayName']} already exist in dataset {dataset_name} of project {project_name}.")
|
24
|
+
|
25
|
+
headers = {
|
26
|
+
"Content-Type": "application/json",
|
27
|
+
"Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
|
28
|
+
"X-Project-Name": project_name,
|
29
|
+
}
|
30
|
+
payload = json.dumps({
|
31
|
+
"datasetName": dataset_name,
|
32
|
+
"metrics": metrics
|
33
|
+
})
|
34
|
+
response = requests.request("POST",
|
35
|
+
f"{RagaAICatalyst.BASE_URL}/v1/llm/trace/metrics",
|
36
|
+
headers=headers,
|
37
|
+
data=payload,
|
38
|
+
timeout=10)
|
39
|
+
if response.status_code != 200:
|
40
|
+
raise ValueError(f"Error inserting agentic trace metrics")
|
41
|
+
except requests.exceptions.RequestException as e:
|
42
|
+
raise ValueError(f"Error submitting traces: {e}")
|
43
|
+
return None
|
44
|
+
|
45
|
+
return response
|
46
|
+
|
47
|
+
|
48
|
+
def _get_children_metrics_of_agent(children_traces):
|
49
|
+
metrics = []
|
50
|
+
for span in children_traces:
|
51
|
+
metrics.extend(span.get("metrics", []))
|
52
|
+
if span["type"] != "agent":
|
53
|
+
metric = span.get("metrics", [])
|
54
|
+
if metric:
|
55
|
+
metrics.extend(metric)
|
56
|
+
else:
|
57
|
+
metrics.extend(_get_children_metrics_of_agent(span["data"]["children"]))
|
58
|
+
return metrics
|
59
|
+
|
60
|
+
def get_trace_metrics_from_trace(traces):
|
61
|
+
metrics = []
|
62
|
+
for span in traces["data"][0]["spans"]:
|
63
|
+
if span["type"] == "agent":
|
64
|
+
children_metric = _get_children_metrics_of_agent(span["data"]["children"])
|
65
|
+
if children_metric:
|
66
|
+
metrics.extend(children_metric)
|
67
|
+
else:
|
68
|
+
metric = span.get("metrics", [])
|
69
|
+
if metric:
|
70
|
+
metrics.extend(metric)
|
71
|
+
return metrics
|
72
|
+
|
73
|
+
def _change_metrics_format_for_payload(metrics):
|
74
|
+
formatted_metrics = []
|
75
|
+
for metric in metrics:
|
76
|
+
if any(m["name"] == metric["name"] for m in formatted_metrics):
|
77
|
+
continue
|
78
|
+
formatted_metrics.append({
|
79
|
+
"name": metric["name"],
|
80
|
+
"displayName": metric["name"],
|
81
|
+
"config": {"source": "user"},
|
82
|
+
})
|
83
|
+
return formatted_metrics
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import os
|
2
|
+
import json
|
3
|
+
import re
|
4
|
+
import requests
|
5
|
+
from ragaai_catalyst.tracers.agentic_tracing.tracers.base import RagaAICatalyst
|
6
|
+
|
7
|
+
def create_dataset_schema_with_trace(project_name, dataset_name):
|
8
|
+
def make_request():
|
9
|
+
headers = {
|
10
|
+
"Content-Type": "application/json",
|
11
|
+
"Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
|
12
|
+
"X-Project-Name": project_name,
|
13
|
+
}
|
14
|
+
payload = json.dumps({
|
15
|
+
"datasetName": dataset_name,
|
16
|
+
"traceFolderUrl": None,
|
17
|
+
})
|
18
|
+
response = requests.request("POST",
|
19
|
+
f"{RagaAICatalyst.BASE_URL}/v1/llm/dataset/logs",
|
20
|
+
headers=headers,
|
21
|
+
data=payload,
|
22
|
+
timeout=10
|
23
|
+
)
|
24
|
+
return response
|
25
|
+
response = make_request()
|
26
|
+
return response
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import requests
|
2
|
+
import os
|
3
|
+
from ....ragaai_catalyst import RagaAICatalyst
|
4
|
+
from ....dataset import Dataset
|
5
|
+
|
6
|
+
def get_user_trace_metrics(project_name, dataset_name):
|
7
|
+
try:
|
8
|
+
list_datasets = Dataset(project_name=project_name).list_datasets()
|
9
|
+
if not list_datasets:
|
10
|
+
return []
|
11
|
+
elif dataset_name not in list_datasets:
|
12
|
+
return []
|
13
|
+
else:
|
14
|
+
headers = {
|
15
|
+
"Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
|
16
|
+
"X-Project-Name": project_name,
|
17
|
+
}
|
18
|
+
response = requests.request("GET",
|
19
|
+
f"{RagaAICatalyst.BASE_URL}/v1/llm/trace/metrics?datasetName={dataset_name}",
|
20
|
+
headers=headers, timeout=10)
|
21
|
+
if response.status_code != 200:
|
22
|
+
print(f"Error fetching traces metrics: {response.json()['message']}")
|
23
|
+
return None
|
24
|
+
|
25
|
+
return response.json()["data"]["columns"]
|
26
|
+
except Exception as e:
|
27
|
+
print(f"Error fetching traces metrics: {e}")
|
28
|
+
return None
|