ragaai-catalyst 2.1.4.1b0__py3-none-any.whl → 2.1.5__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 +23 -2
- ragaai_catalyst/dataset.py +462 -1
- ragaai_catalyst/evaluation.py +76 -7
- ragaai_catalyst/ragaai_catalyst.py +52 -10
- ragaai_catalyst/redteaming/__init__.py +7 -0
- ragaai_catalyst/redteaming/config/detectors.toml +13 -0
- ragaai_catalyst/redteaming/data_generator/scenario_generator.py +95 -0
- ragaai_catalyst/redteaming/data_generator/test_case_generator.py +120 -0
- ragaai_catalyst/redteaming/evaluator.py +125 -0
- ragaai_catalyst/redteaming/llm_generator.py +136 -0
- ragaai_catalyst/redteaming/llm_generator_old.py +83 -0
- ragaai_catalyst/redteaming/red_teaming.py +331 -0
- ragaai_catalyst/redteaming/requirements.txt +4 -0
- ragaai_catalyst/redteaming/tests/grok.ipynb +97 -0
- ragaai_catalyst/redteaming/tests/stereotype.ipynb +2258 -0
- ragaai_catalyst/redteaming/upload_result.py +38 -0
- ragaai_catalyst/redteaming/utils/issue_description.py +114 -0
- ragaai_catalyst/redteaming/utils/rt.png +0 -0
- ragaai_catalyst/redteaming_old.py +171 -0
- ragaai_catalyst/synthetic_data_generation.py +400 -22
- ragaai_catalyst/tracers/__init__.py +17 -1
- ragaai_catalyst/tracers/agentic_tracing/data/data_structure.py +4 -2
- ragaai_catalyst/tracers/agentic_tracing/tracers/agent_tracer.py +212 -148
- ragaai_catalyst/tracers/agentic_tracing/tracers/base.py +657 -247
- ragaai_catalyst/tracers/agentic_tracing/tracers/custom_tracer.py +50 -19
- ragaai_catalyst/tracers/agentic_tracing/tracers/llm_tracer.py +588 -177
- ragaai_catalyst/tracers/agentic_tracing/tracers/main_tracer.py +99 -100
- ragaai_catalyst/tracers/agentic_tracing/tracers/network_tracer.py +3 -3
- ragaai_catalyst/tracers/agentic_tracing/tracers/tool_tracer.py +230 -29
- ragaai_catalyst/tracers/agentic_tracing/upload/trace_uploader.py +358 -0
- ragaai_catalyst/tracers/agentic_tracing/upload/upload_agentic_traces.py +75 -20
- ragaai_catalyst/tracers/agentic_tracing/upload/upload_code.py +55 -11
- ragaai_catalyst/tracers/agentic_tracing/upload/upload_local_metric.py +74 -0
- ragaai_catalyst/tracers/agentic_tracing/upload/upload_trace_metric.py +47 -16
- ragaai_catalyst/tracers/agentic_tracing/utils/create_dataset_schema.py +4 -2
- ragaai_catalyst/tracers/agentic_tracing/utils/file_name_tracker.py +26 -3
- ragaai_catalyst/tracers/agentic_tracing/utils/llm_utils.py +182 -17
- ragaai_catalyst/tracers/agentic_tracing/utils/model_costs.json +1233 -497
- ragaai_catalyst/tracers/agentic_tracing/utils/span_attributes.py +81 -10
- ragaai_catalyst/tracers/agentic_tracing/utils/supported_llm_provider.toml +34 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/system_monitor.py +215 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/trace_utils.py +0 -32
- ragaai_catalyst/tracers/agentic_tracing/utils/unique_decorator.py +3 -1
- ragaai_catalyst/tracers/agentic_tracing/utils/zip_list_of_unique_files.py +73 -47
- ragaai_catalyst/tracers/distributed.py +300 -0
- ragaai_catalyst/tracers/exporters/__init__.py +3 -1
- ragaai_catalyst/tracers/exporters/dynamic_trace_exporter.py +160 -0
- ragaai_catalyst/tracers/exporters/ragaai_trace_exporter.py +129 -0
- ragaai_catalyst/tracers/langchain_callback.py +809 -0
- ragaai_catalyst/tracers/llamaindex_instrumentation.py +424 -0
- ragaai_catalyst/tracers/tracer.py +301 -55
- ragaai_catalyst/tracers/upload_traces.py +24 -7
- ragaai_catalyst/tracers/utils/convert_langchain_callbacks_output.py +61 -0
- ragaai_catalyst/tracers/utils/convert_llama_instru_callback.py +69 -0
- ragaai_catalyst/tracers/utils/extraction_logic_llama_index.py +74 -0
- ragaai_catalyst/tracers/utils/langchain_tracer_extraction_logic.py +82 -0
- ragaai_catalyst/tracers/utils/model_prices_and_context_window_backup.json +9365 -0
- ragaai_catalyst/tracers/utils/trace_json_converter.py +269 -0
- {ragaai_catalyst-2.1.4.1b0.dist-info → ragaai_catalyst-2.1.5.dist-info}/METADATA +367 -45
- ragaai_catalyst-2.1.5.dist-info/RECORD +97 -0
- {ragaai_catalyst-2.1.4.1b0.dist-info → ragaai_catalyst-2.1.5.dist-info}/WHEEL +1 -1
- ragaai_catalyst-2.1.4.1b0.dist-info/RECORD +0 -67
- {ragaai_catalyst-2.1.4.1b0.dist-info → ragaai_catalyst-2.1.5.dist-info}/LICENSE +0 -0
- {ragaai_catalyst-2.1.4.1b0.dist-info → ragaai_catalyst-2.1.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,300 @@
|
|
1
|
+
"""
|
2
|
+
Distributed tracing functionality for RagaAI Catalyst.
|
3
|
+
Provides simplified initialization and decorator-based tracing.
|
4
|
+
"""
|
5
|
+
|
6
|
+
import os
|
7
|
+
import threading
|
8
|
+
from typing import Optional, Dict, Any, List
|
9
|
+
from functools import wraps
|
10
|
+
from contextlib import contextmanager
|
11
|
+
import uuid
|
12
|
+
from .agentic_tracing.utils.unique_decorator import generate_unique_hash_simple
|
13
|
+
from datetime import datetime
|
14
|
+
import asyncio
|
15
|
+
|
16
|
+
from .tracer import Tracer
|
17
|
+
from ..ragaai_catalyst import RagaAICatalyst
|
18
|
+
|
19
|
+
# Global state
|
20
|
+
_global_tracer: Optional[Tracer] = None
|
21
|
+
_global_catalyst: Optional[RagaAICatalyst] = None
|
22
|
+
_tracer_lock = threading.Lock()
|
23
|
+
_active_spans = threading.local()
|
24
|
+
|
25
|
+
def get_current_tracer() -> Optional[Tracer]:
|
26
|
+
"""Get the current global tracer instance."""
|
27
|
+
return _global_tracer
|
28
|
+
|
29
|
+
def get_current_catalyst() -> Optional[RagaAICatalyst]:
|
30
|
+
"""Get the current global catalyst instance."""
|
31
|
+
return _global_catalyst
|
32
|
+
|
33
|
+
def init_tracing(
|
34
|
+
project_name: str = None,
|
35
|
+
dataset_name: str = None,
|
36
|
+
access_key: str = None,
|
37
|
+
secret_key: str = None,
|
38
|
+
base_url: str = None,
|
39
|
+
tracer: Tracer = None,
|
40
|
+
catalyst: RagaAICatalyst = None,
|
41
|
+
**kwargs
|
42
|
+
) -> None:
|
43
|
+
"""Initialize distributed tracing.
|
44
|
+
|
45
|
+
Args:
|
46
|
+
project_name: Project name for new tracer
|
47
|
+
dataset_name: Dataset name for new tracer
|
48
|
+
access_key: RagaAI Catalyst access key
|
49
|
+
secret_key: RagaAI Catalyst secret key
|
50
|
+
base_url: RagaAI Catalyst API base URL
|
51
|
+
tracer: Existing Tracer instance
|
52
|
+
catalyst: Existing RagaAICatalyst instance
|
53
|
+
**kwargs: Additional tracer parameters
|
54
|
+
"""
|
55
|
+
global _global_tracer, _global_catalyst
|
56
|
+
|
57
|
+
with _tracer_lock:
|
58
|
+
if tracer and catalyst:
|
59
|
+
if isinstance(tracer, Tracer) and isinstance(catalyst, RagaAICatalyst):
|
60
|
+
_global_tracer = tracer
|
61
|
+
_global_catalyst = catalyst
|
62
|
+
else:
|
63
|
+
raise ValueError("Both Tracer and Catalyst objects must be instances of Tracer and RagaAICatalyst, respectively.")
|
64
|
+
else:
|
65
|
+
raise ValueError("Both Tracer and Catalyst objects must be provided.")
|
66
|
+
|
67
|
+
|
68
|
+
def trace_agent(name: str = None, agent_type: str = "generic", version: str = "1.0.0", **kwargs):
|
69
|
+
"""Decorator for tracing agent functions."""
|
70
|
+
def decorator(func):
|
71
|
+
is_async = asyncio.iscoroutinefunction(func)
|
72
|
+
span_name = name or func.__name__
|
73
|
+
# Generate hash based on the decorated function
|
74
|
+
top_level_hash_id = generate_unique_hash_simple(func)
|
75
|
+
|
76
|
+
@wraps(func)
|
77
|
+
async def async_wrapper(*args, **kwargs):
|
78
|
+
tracer = get_current_tracer()
|
79
|
+
if not tracer:
|
80
|
+
return await func(*args, **kwargs)
|
81
|
+
|
82
|
+
# Set current agent name and store the token
|
83
|
+
name_token = tracer.current_agent_name.set(span_name)
|
84
|
+
|
85
|
+
try:
|
86
|
+
# Use async agent tracing
|
87
|
+
return await tracer._trace_agent_execution(
|
88
|
+
func,
|
89
|
+
span_name,
|
90
|
+
agent_type,
|
91
|
+
version,
|
92
|
+
None, # capabilities
|
93
|
+
top_level_hash_id,
|
94
|
+
*args,
|
95
|
+
**kwargs
|
96
|
+
)
|
97
|
+
finally:
|
98
|
+
# Reset using the stored token
|
99
|
+
if name_token:
|
100
|
+
tracer.current_agent_name.reset(name_token)
|
101
|
+
|
102
|
+
@wraps(func)
|
103
|
+
def sync_wrapper(*args, **kwargs):
|
104
|
+
tracer = get_current_tracer()
|
105
|
+
if not tracer:
|
106
|
+
return func(*args, **kwargs)
|
107
|
+
|
108
|
+
# Set current agent name and store the token
|
109
|
+
name_token = tracer.current_agent_name.set(span_name)
|
110
|
+
|
111
|
+
try:
|
112
|
+
# Use synchronous agent tracing
|
113
|
+
return tracer._trace_sync_agent_execution(
|
114
|
+
func,
|
115
|
+
span_name,
|
116
|
+
agent_type,
|
117
|
+
version,
|
118
|
+
None, # capabilities
|
119
|
+
top_level_hash_id,
|
120
|
+
*args,
|
121
|
+
**kwargs
|
122
|
+
)
|
123
|
+
finally:
|
124
|
+
# Reset using the stored token
|
125
|
+
if name_token:
|
126
|
+
tracer.current_agent_name.reset(name_token)
|
127
|
+
|
128
|
+
return async_wrapper if is_async else sync_wrapper
|
129
|
+
return decorator
|
130
|
+
|
131
|
+
def trace_llm(name: str = None, model: str = None, **kwargs):
|
132
|
+
"""Decorator for tracing LLM calls."""
|
133
|
+
def decorator(func):
|
134
|
+
is_async = asyncio.iscoroutinefunction(func)
|
135
|
+
span_name = name or func.__name__
|
136
|
+
|
137
|
+
@wraps(func)
|
138
|
+
async def async_wrapper(*args, **kwargs):
|
139
|
+
tracer = get_current_tracer()
|
140
|
+
if not tracer:
|
141
|
+
return await func(*args, **kwargs)
|
142
|
+
|
143
|
+
# Set current LLM name and store the token
|
144
|
+
name_token = tracer.current_llm_call_name.set(span_name)
|
145
|
+
|
146
|
+
try:
|
147
|
+
# Just execute the function within the current span
|
148
|
+
result = await func(*args, **kwargs)
|
149
|
+
return result
|
150
|
+
finally:
|
151
|
+
# Reset using the stored token
|
152
|
+
if name_token:
|
153
|
+
tracer.current_llm_call_name.reset(name_token)
|
154
|
+
|
155
|
+
@wraps(func)
|
156
|
+
def sync_wrapper(*args, **kwargs):
|
157
|
+
tracer = get_current_tracer()
|
158
|
+
if not tracer:
|
159
|
+
return func(*args, **kwargs)
|
160
|
+
|
161
|
+
# Set current LLM name and store the token
|
162
|
+
name_token = tracer.current_llm_call_name.set(span_name)
|
163
|
+
|
164
|
+
try:
|
165
|
+
# Just execute the function within the current span
|
166
|
+
result = func(*args, **kwargs)
|
167
|
+
return result
|
168
|
+
finally:
|
169
|
+
# Reset using the stored token
|
170
|
+
if name_token:
|
171
|
+
tracer.current_llm_call_name.reset(name_token)
|
172
|
+
|
173
|
+
return async_wrapper if is_async else sync_wrapper
|
174
|
+
return decorator
|
175
|
+
|
176
|
+
|
177
|
+
def trace_tool(name: str = None, tool_type: str = "generic", version: str = "1.0.0", **kwargs):
|
178
|
+
"""Decorator for tracing tool functions."""
|
179
|
+
def decorator(func):
|
180
|
+
is_async = asyncio.iscoroutinefunction(func)
|
181
|
+
span_name = name or func.__name__
|
182
|
+
|
183
|
+
@wraps(func)
|
184
|
+
async def async_wrapper(*args, **kwargs):
|
185
|
+
tracer = get_current_tracer()
|
186
|
+
if not tracer:
|
187
|
+
return await func(*args, **kwargs)
|
188
|
+
|
189
|
+
# Set current tool name and store the token
|
190
|
+
name_token = tracer.current_tool_name.set(span_name)
|
191
|
+
|
192
|
+
try:
|
193
|
+
# Use async tool tracing
|
194
|
+
return await tracer._trace_tool_execution(
|
195
|
+
func,
|
196
|
+
span_name,
|
197
|
+
tool_type,
|
198
|
+
version,
|
199
|
+
*args,
|
200
|
+
**kwargs
|
201
|
+
)
|
202
|
+
finally:
|
203
|
+
# Reset using the stored token
|
204
|
+
if name_token:
|
205
|
+
tracer.current_tool_name.reset(name_token)
|
206
|
+
|
207
|
+
@wraps(func)
|
208
|
+
def sync_wrapper(*args, **kwargs):
|
209
|
+
tracer = get_current_tracer()
|
210
|
+
if not tracer:
|
211
|
+
return func(*args, **kwargs)
|
212
|
+
|
213
|
+
# Set current tool name and store the token
|
214
|
+
name_token = tracer.current_tool_name.set(span_name)
|
215
|
+
|
216
|
+
try:
|
217
|
+
# Use synchronous tool tracing
|
218
|
+
return tracer._trace_sync_tool_execution(
|
219
|
+
func,
|
220
|
+
span_name,
|
221
|
+
tool_type,
|
222
|
+
version,
|
223
|
+
*args,
|
224
|
+
**kwargs
|
225
|
+
)
|
226
|
+
finally:
|
227
|
+
# Reset using the stored token
|
228
|
+
if name_token:
|
229
|
+
tracer.current_tool_name.reset(name_token)
|
230
|
+
|
231
|
+
return async_wrapper if is_async else sync_wrapper
|
232
|
+
return decorator
|
233
|
+
|
234
|
+
|
235
|
+
|
236
|
+
def trace_custom(name: str = None, custom_type: str = "generic", version: str = "1.0.0", trace_variables: bool = False, **kwargs):
|
237
|
+
"""Decorator for tracing custom functions."""
|
238
|
+
def decorator(func):
|
239
|
+
is_async = asyncio.iscoroutinefunction(func)
|
240
|
+
|
241
|
+
@wraps(func)
|
242
|
+
async def async_wrapper(*args, **kwargs):
|
243
|
+
tracer = get_current_tracer()
|
244
|
+
if not tracer:
|
245
|
+
return await func(*args, **kwargs)
|
246
|
+
|
247
|
+
# Use async tool tracing
|
248
|
+
return await tracer._trace_custom_execution(
|
249
|
+
func,
|
250
|
+
name or func.__name__,
|
251
|
+
custom_type,
|
252
|
+
version,
|
253
|
+
trace_variables,
|
254
|
+
*args,
|
255
|
+
**kwargs
|
256
|
+
)
|
257
|
+
|
258
|
+
@wraps(func)
|
259
|
+
def sync_wrapper(*args, **kwargs):
|
260
|
+
tracer = get_current_tracer()
|
261
|
+
if not tracer:
|
262
|
+
return func(*args, **kwargs)
|
263
|
+
|
264
|
+
# Use synchronous tool tracing
|
265
|
+
return tracer._trace_sync_custom_execution(
|
266
|
+
func,
|
267
|
+
name or func.__name__,
|
268
|
+
custom_type,
|
269
|
+
version,
|
270
|
+
trace_variables,
|
271
|
+
*args,
|
272
|
+
**kwargs
|
273
|
+
)
|
274
|
+
|
275
|
+
return async_wrapper if is_async else sync_wrapper
|
276
|
+
return decorator
|
277
|
+
|
278
|
+
|
279
|
+
def current_span():
|
280
|
+
"""Get the current active span for adding metrics."""
|
281
|
+
tracer = get_current_tracer()
|
282
|
+
if not tracer:
|
283
|
+
return None
|
284
|
+
|
285
|
+
# First check for LLM context
|
286
|
+
llm_name = tracer.current_llm_call_name.get()
|
287
|
+
if llm_name:
|
288
|
+
return tracer.span(llm_name)
|
289
|
+
|
290
|
+
# Then check for tool context
|
291
|
+
tool_name = tracer.current_tool_name.get()
|
292
|
+
if tool_name:
|
293
|
+
return tracer.span(tool_name)
|
294
|
+
|
295
|
+
# Finally fall back to agent context
|
296
|
+
agent_name = tracer.current_agent_name.get()
|
297
|
+
if not agent_name:
|
298
|
+
raise ValueError("No active span found. Make sure you're calling this within a traced function.")
|
299
|
+
|
300
|
+
return tracer.span(agent_name)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
from .file_span_exporter import FileSpanExporter
|
2
2
|
from .raga_exporter import RagaExporter
|
3
|
+
from .ragaai_trace_exporter import RAGATraceExporter
|
4
|
+
from .dynamic_trace_exporter import DynamicTraceExporter
|
3
5
|
|
4
6
|
|
5
|
-
__all__ = ["FileSpanExporter", "RagaExporter"]
|
7
|
+
__all__ = ["FileSpanExporter", "RagaExporter", "RAGATraceExporter", "DynamicTraceExporter"]
|
@@ -0,0 +1,160 @@
|
|
1
|
+
"""
|
2
|
+
Dynamic Trace Exporter - A wrapper for RAGATraceExporter that allows dynamic updates to properties.
|
3
|
+
"""
|
4
|
+
import logging
|
5
|
+
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
|
6
|
+
from ragaai_catalyst.tracers.exporters.ragaai_trace_exporter import RAGATraceExporter
|
7
|
+
|
8
|
+
logger = logging.getLogger("RagaAICatalyst")
|
9
|
+
|
10
|
+
class DynamicTraceExporter(SpanExporter):
|
11
|
+
"""
|
12
|
+
A wrapper around RAGATraceExporter that allows dynamic updates to properties.
|
13
|
+
This exporter forwards all calls to the underlying RAGATraceExporter but allows
|
14
|
+
certain properties to be updated dynamically during execution.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def __init__(self, files_to_zip, project_name, project_id, dataset_name, user_details, base_url, custom_model_cost):
|
18
|
+
"""
|
19
|
+
Initialize the DynamicTraceExporter.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
files_to_zip: List of files to zip
|
23
|
+
project_name: Project name
|
24
|
+
project_id: Project ID
|
25
|
+
dataset_name: Dataset name
|
26
|
+
user_details: User details
|
27
|
+
base_url: Base URL for API
|
28
|
+
"""
|
29
|
+
self._exporter = RAGATraceExporter(
|
30
|
+
files_to_zip=files_to_zip,
|
31
|
+
project_name=project_name,
|
32
|
+
project_id=project_id,
|
33
|
+
dataset_name=dataset_name,
|
34
|
+
user_details=user_details,
|
35
|
+
base_url=base_url,
|
36
|
+
custom_model_cost=custom_model_cost
|
37
|
+
)
|
38
|
+
|
39
|
+
# Store the initial values
|
40
|
+
self._files_to_zip = files_to_zip
|
41
|
+
self._project_name = project_name
|
42
|
+
self._project_id = project_id
|
43
|
+
self._dataset_name = dataset_name
|
44
|
+
self._user_details = user_details
|
45
|
+
self._base_url = base_url
|
46
|
+
self._custom_model_cost = custom_model_cost
|
47
|
+
|
48
|
+
|
49
|
+
def export(self, spans):
|
50
|
+
"""
|
51
|
+
Export spans by forwarding to the underlying exporter.
|
52
|
+
Before exporting, update the exporter's properties with the current values.
|
53
|
+
|
54
|
+
Args:
|
55
|
+
spans: Spans to export
|
56
|
+
|
57
|
+
Returns:
|
58
|
+
SpanExportResult: Result of the export operation
|
59
|
+
"""
|
60
|
+
try:
|
61
|
+
# Update the exporter's properties
|
62
|
+
self._update_exporter_properties()
|
63
|
+
except Exception as e:
|
64
|
+
raise Exception(f"Error updating exporter properties: {e}")
|
65
|
+
|
66
|
+
try:
|
67
|
+
# Forward the call to the underlying exporter
|
68
|
+
result = self._exporter.export(spans)
|
69
|
+
return result
|
70
|
+
except Exception as e:
|
71
|
+
raise Exception(f"Error exporting trace: {e}")
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
def shutdown(self):
|
76
|
+
"""
|
77
|
+
Shutdown the exporter by forwarding to the underlying exporter.
|
78
|
+
Before shutting down, update the exporter's properties with the current values.
|
79
|
+
"""
|
80
|
+
try:
|
81
|
+
# Update the exporter's properties
|
82
|
+
self._update_exporter_properties()
|
83
|
+
except Exception as e:
|
84
|
+
raise Exception(f"Error updating exporter properties: {e}")
|
85
|
+
|
86
|
+
try:
|
87
|
+
# Forward the call to the underlying exporter
|
88
|
+
return self._exporter.shutdown()
|
89
|
+
except Exception as e:
|
90
|
+
raise Exception(f"Error shutting down exporter: {e}")
|
91
|
+
|
92
|
+
def _update_exporter_properties(self):
|
93
|
+
"""
|
94
|
+
Update the underlying exporter's properties with the current values.
|
95
|
+
"""
|
96
|
+
self._exporter.files_to_zip = self._files_to_zip
|
97
|
+
self._exporter.project_name = self._project_name
|
98
|
+
self._exporter.project_id = self._project_id
|
99
|
+
self._exporter.dataset_name = self._dataset_name
|
100
|
+
self._exporter.user_details = self._user_details
|
101
|
+
self._exporter.base_url = self._base_url
|
102
|
+
self._exporter.custom_model_cost = self._custom_model_cost
|
103
|
+
|
104
|
+
# Getter and setter methods for dynamic properties
|
105
|
+
|
106
|
+
@property
|
107
|
+
def files_to_zip(self):
|
108
|
+
return self._files_to_zip
|
109
|
+
|
110
|
+
@files_to_zip.setter
|
111
|
+
def files_to_zip(self, value):
|
112
|
+
self._files_to_zip = value
|
113
|
+
|
114
|
+
@property
|
115
|
+
def project_name(self):
|
116
|
+
return self._project_name
|
117
|
+
|
118
|
+
@project_name.setter
|
119
|
+
def project_name(self, value):
|
120
|
+
self._project_name = value
|
121
|
+
|
122
|
+
@property
|
123
|
+
def project_id(self):
|
124
|
+
return self._project_id
|
125
|
+
|
126
|
+
@project_id.setter
|
127
|
+
def project_id(self, value):
|
128
|
+
self._project_id = value
|
129
|
+
|
130
|
+
@property
|
131
|
+
def dataset_name(self):
|
132
|
+
return self._dataset_name
|
133
|
+
|
134
|
+
@dataset_name.setter
|
135
|
+
def dataset_name(self, value):
|
136
|
+
self._dataset_name = value
|
137
|
+
|
138
|
+
@property
|
139
|
+
def user_details(self):
|
140
|
+
return self._user_details
|
141
|
+
|
142
|
+
@user_details.setter
|
143
|
+
def user_details(self, value):
|
144
|
+
self._user_details = value
|
145
|
+
|
146
|
+
@property
|
147
|
+
def base_url(self):
|
148
|
+
return self._base_url
|
149
|
+
|
150
|
+
@base_url.setter
|
151
|
+
def base_url(self, value):
|
152
|
+
self._base_url = value
|
153
|
+
|
154
|
+
@property
|
155
|
+
def custom_model_cost(self):
|
156
|
+
return self._custom_model_cost
|
157
|
+
|
158
|
+
@custom_model_cost.setter
|
159
|
+
def custom_model_cost(self, value):
|
160
|
+
self._custom_model_cost = value
|
@@ -0,0 +1,129 @@
|
|
1
|
+
import os
|
2
|
+
import json
|
3
|
+
import tempfile
|
4
|
+
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
|
5
|
+
import logging
|
6
|
+
from datetime import datetime
|
7
|
+
from dataclasses import asdict
|
8
|
+
from ragaai_catalyst.tracers.utils.trace_json_converter import convert_json_format
|
9
|
+
from ragaai_catalyst.tracers.agentic_tracing.tracers.base import TracerJSONEncoder
|
10
|
+
from ragaai_catalyst.tracers.agentic_tracing.utils.system_monitor import SystemMonitor
|
11
|
+
from ragaai_catalyst.tracers.agentic_tracing.upload.trace_uploader import submit_upload_task
|
12
|
+
from ragaai_catalyst.tracers.agentic_tracing.utils.zip_list_of_unique_files import zip_list_of_unique_files
|
13
|
+
|
14
|
+
|
15
|
+
logger = logging.getLogger("RagaAICatalyst")
|
16
|
+
logging_level = (
|
17
|
+
logger.setLevel(logging.DEBUG) if os.getenv("DEBUG") == "1" else logging.INFO
|
18
|
+
)
|
19
|
+
|
20
|
+
|
21
|
+
class RAGATraceExporter(SpanExporter):
|
22
|
+
def __init__(self, files_to_zip, project_name, project_id, dataset_name, user_details, base_url, custom_model_cost):
|
23
|
+
self.trace_spans = dict()
|
24
|
+
self.tmp_dir = tempfile.gettempdir()
|
25
|
+
self.files_to_zip = files_to_zip
|
26
|
+
self.project_name = project_name
|
27
|
+
self.project_id = project_id
|
28
|
+
self.dataset_name = dataset_name
|
29
|
+
self.user_details = user_details
|
30
|
+
self.base_url = base_url
|
31
|
+
self.custom_model_cost = custom_model_cost
|
32
|
+
self.system_monitor = SystemMonitor(dataset_name)
|
33
|
+
|
34
|
+
def export(self, spans):
|
35
|
+
for span in spans:
|
36
|
+
span_json = json.loads(span.to_json())
|
37
|
+
trace_id = span_json.get("context").get("trace_id")
|
38
|
+
if trace_id is None:
|
39
|
+
raise Exception("Trace ID is None")
|
40
|
+
|
41
|
+
if trace_id not in self.trace_spans:
|
42
|
+
self.trace_spans[trace_id] = list()
|
43
|
+
|
44
|
+
self.trace_spans[trace_id].append(span_json)
|
45
|
+
|
46
|
+
if span_json["parent_id"] is None:
|
47
|
+
trace = self.trace_spans[trace_id]
|
48
|
+
try:
|
49
|
+
self.process_complete_trace(trace, trace_id)
|
50
|
+
except Exception as e:
|
51
|
+
raise Exception(f"Error processing complete trace: {e}")
|
52
|
+
try:
|
53
|
+
del self.trace_spans[trace_id]
|
54
|
+
except Exception as e:
|
55
|
+
raise Exception(f"Error deleting trace: {e}")
|
56
|
+
|
57
|
+
return SpanExportResult.SUCCESS
|
58
|
+
|
59
|
+
def shutdown(self):
|
60
|
+
# Process any remaining traces during shutdown
|
61
|
+
for trace_id, spans in self.trace_spans.items():
|
62
|
+
self.process_complete_trace(spans, trace_id)
|
63
|
+
self.trace_spans.clear()
|
64
|
+
|
65
|
+
def process_complete_trace(self, spans, trace_id):
|
66
|
+
# Convert the trace to ragaai trace format
|
67
|
+
try:
|
68
|
+
ragaai_trace_details = self.prepare_trace(spans, trace_id)
|
69
|
+
except Exception as e:
|
70
|
+
print(f"Error converting trace {trace_id}: {e}")
|
71
|
+
|
72
|
+
# Upload the trace if upload_trace function is provided
|
73
|
+
try:
|
74
|
+
self.upload_trace(ragaai_trace_details, trace_id)
|
75
|
+
except Exception as e:
|
76
|
+
print(f"Error uploading trace {trace_id}: {e}")
|
77
|
+
|
78
|
+
def prepare_trace(self, spans, trace_id):
|
79
|
+
try:
|
80
|
+
ragaai_trace = convert_json_format(spans, self.custom_model_cost)
|
81
|
+
ragaai_trace["workflow"] = []
|
82
|
+
|
83
|
+
# Add source code hash
|
84
|
+
hash_id, zip_path = zip_list_of_unique_files(
|
85
|
+
self.files_to_zip, output_dir=self.tmp_dir
|
86
|
+
)
|
87
|
+
|
88
|
+
ragaai_trace["metadata"]["system_info"] = asdict(self.system_monitor.get_system_info())
|
89
|
+
ragaai_trace["metadata"]["resources"] = asdict(self.system_monitor.get_resources())
|
90
|
+
ragaai_trace["metadata"]["system_info"]["source_code"] = hash_id
|
91
|
+
|
92
|
+
ragaai_trace["data"][0]["start_time"] = ragaai_trace["start_time"]
|
93
|
+
ragaai_trace["data"][0]["end_time"] = ragaai_trace["end_time"]
|
94
|
+
|
95
|
+
ragaai_trace["project_name"] = self.project_name
|
96
|
+
|
97
|
+
# Save the trace_json
|
98
|
+
trace_file_path = os.path.join(self.tmp_dir, f"{trace_id}.json")
|
99
|
+
with open(trace_file_path, "w") as file:
|
100
|
+
json.dump(ragaai_trace, file, cls=TracerJSONEncoder, indent=2)
|
101
|
+
|
102
|
+
return {
|
103
|
+
'trace_file_path': trace_file_path,
|
104
|
+
'code_zip_path': zip_path,
|
105
|
+
'hash_id': hash_id
|
106
|
+
}
|
107
|
+
except Exception as e:
|
108
|
+
logger.error(f"Error converting trace {trace_id}: {str(e)}")
|
109
|
+
return None
|
110
|
+
|
111
|
+
def upload_trace(self, ragaai_trace_details, trace_id):
|
112
|
+
filepath = ragaai_trace_details['trace_file_path']
|
113
|
+
hash_id = ragaai_trace_details['hash_id']
|
114
|
+
zip_path = ragaai_trace_details['code_zip_path']
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
self.upload_task_id = submit_upload_task(
|
119
|
+
filepath=filepath,
|
120
|
+
hash_id=hash_id,
|
121
|
+
zip_path=zip_path,
|
122
|
+
project_name=self.project_name,
|
123
|
+
project_id=self.project_id,
|
124
|
+
dataset_name=self.dataset_name,
|
125
|
+
user_details=self.user_details,
|
126
|
+
base_url=self.base_url
|
127
|
+
)
|
128
|
+
|
129
|
+
logger.info(f"Submitted upload task with ID: {self.upload_task_id}")
|