ragaai-catalyst 2.1.5b36__py3-none-any.whl → 2.1.5b38__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.
@@ -95,6 +95,9 @@ def process_upload(task_id: str, filepath: str, hash_id: str, zip_path: str,
95
95
  Returns:
96
96
  Dict containing status and any error information
97
97
  """
98
+ # Correct base_url
99
+ base_url = base_url[0] if isinstance(base_url, tuple) else base_url
100
+
98
101
  logger.info(f"Processing upload task {task_id}")
99
102
  result = {
100
103
  "task_id": task_id,
@@ -118,7 +121,7 @@ def process_upload(task_id: str, filepath: str, hash_id: str, zip_path: str,
118
121
 
119
122
  if not IMPORTS_AVAILABLE:
120
123
  logger.warning(f"Test mode: Simulating processing of task {task_id}")
121
- time.sleep(2) # Simulate work
124
+ # time.sleep(2) # Simulate work
122
125
  result["status"] = STATUS_COMPLETED
123
126
  save_task_status(result)
124
127
  return result
@@ -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,145 @@
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
+ # Update the exporter's properties
61
+ self._update_exporter_properties()
62
+
63
+ # Forward the call to the underlying exporter
64
+ return self._exporter.export(spans)
65
+
66
+ def shutdown(self):
67
+ """
68
+ Shutdown the exporter by forwarding to the underlying exporter.
69
+ Before shutting down, update the exporter's properties with the current values.
70
+ """
71
+ # Update the exporter's properties
72
+ self._update_exporter_properties()
73
+
74
+ # Forward the call to the underlying exporter
75
+ return self._exporter.shutdown()
76
+
77
+ def _update_exporter_properties(self):
78
+ """
79
+ Update the underlying exporter's properties with the current values.
80
+ """
81
+ self._exporter.files_to_zip = self._files_to_zip
82
+ self._exporter.project_name = self._project_name
83
+ self._exporter.project_id = self._project_id
84
+ self._exporter.dataset_name = self._dataset_name
85
+ self._exporter.user_details = self._user_details
86
+ self._exporter.base_url = self._base_url
87
+ self._exporter.custom_model_cost = self._custom_model_cost
88
+
89
+ # Getter and setter methods for dynamic properties
90
+
91
+ @property
92
+ def files_to_zip(self):
93
+ return self._files_to_zip
94
+
95
+ @files_to_zip.setter
96
+ def files_to_zip(self, value):
97
+ self._files_to_zip = value
98
+
99
+ @property
100
+ def project_name(self):
101
+ return self._project_name
102
+
103
+ @project_name.setter
104
+ def project_name(self, value):
105
+ self._project_name = value
106
+
107
+ @property
108
+ def project_id(self):
109
+ return self._project_id
110
+
111
+ @project_id.setter
112
+ def project_id(self, value):
113
+ self._project_id = value
114
+
115
+ @property
116
+ def dataset_name(self):
117
+ return self._dataset_name
118
+
119
+ @dataset_name.setter
120
+ def dataset_name(self, value):
121
+ self._dataset_name = value
122
+
123
+ @property
124
+ def user_details(self):
125
+ return self._user_details
126
+
127
+ @user_details.setter
128
+ def user_details(self, value):
129
+ self._user_details = value
130
+
131
+ @property
132
+ def base_url(self):
133
+ return self._base_url
134
+
135
+ @base_url.setter
136
+ def base_url(self, value):
137
+ self._base_url = value
138
+
139
+ @property
140
+ def custom_model_cost(self):
141
+ return self._custom_model_cost
142
+
143
+ @custom_model_cost.setter
144
+ def custom_model_cost(self, value):
145
+ self._custom_model_cost = value
@@ -0,0 +1,121 @@
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
+
39
+ if trace_id not in self.trace_spans:
40
+ self.trace_spans[trace_id] = list()
41
+
42
+ self.trace_spans[trace_id].append(span_json)
43
+
44
+ if span_json["parent_id"] is None:
45
+ trace = self.trace_spans[trace_id]
46
+ self.process_complete_trace(trace, trace_id)
47
+ del self.trace_spans[trace_id]
48
+
49
+ return SpanExportResult.SUCCESS
50
+
51
+ def shutdown(self):
52
+ # Process any remaining traces during shutdown
53
+ for trace_id, spans in self.trace_spans.items():
54
+ self.process_complete_trace(spans, trace_id)
55
+ self.trace_spans.clear()
56
+
57
+ def process_complete_trace(self, spans, trace_id):
58
+ # Convert the trace to ragaai trace format
59
+ try:
60
+ ragaai_trace_details = self.prepare_trace(spans, trace_id)
61
+ except Exception as e:
62
+ print(f"Error converting trace {trace_id}: {e}")
63
+
64
+ # Upload the trace if upload_trace function is provided
65
+ try:
66
+ self.upload_trace(ragaai_trace_details, trace_id)
67
+ except Exception as e:
68
+ print(f"Error uploading trace {trace_id}: {e}")
69
+
70
+ def prepare_trace(self, spans, trace_id):
71
+ try:
72
+ ragaai_trace = convert_json_format(spans, self.custom_model_cost)
73
+ ragaai_trace["workflow"] = []
74
+
75
+ # Add source code hash
76
+ hash_id, zip_path = zip_list_of_unique_files(
77
+ self.files_to_zip, output_dir=self.tmp_dir
78
+ )
79
+
80
+ ragaai_trace["metadata"]["system_info"] = asdict(self.system_monitor.get_system_info())
81
+ ragaai_trace["metadata"]["resources"] = asdict(self.system_monitor.get_resources())
82
+ ragaai_trace["metadata"]["system_info"]["source_code"] = hash_id
83
+
84
+ ragaai_trace["data"][0]["start_time"] = ragaai_trace["start_time"]
85
+ ragaai_trace["data"][0]["end_time"] = ragaai_trace["end_time"]
86
+
87
+ ragaai_trace["project_name"] = self.project_name
88
+
89
+ # Save the trace_json
90
+ trace_file_path = os.path.join(self.tmp_dir, f"{trace_id}.json")
91
+ with open(trace_file_path, "w") as file:
92
+ json.dump(ragaai_trace, file, cls=TracerJSONEncoder, indent=2)
93
+
94
+ return {
95
+ 'trace_file_path': trace_file_path,
96
+ 'code_zip_path': zip_path,
97
+ 'hash_id': hash_id
98
+ }
99
+ except Exception as e:
100
+ logger.error(f"Error converting trace {trace_id}: {str(e)}")
101
+ return None
102
+
103
+ def upload_trace(self, ragaai_trace_details, trace_id):
104
+ filepath = ragaai_trace_details['trace_file_path']
105
+ hash_id = ragaai_trace_details['hash_id']
106
+ zip_path = ragaai_trace_details['code_zip_path']
107
+
108
+
109
+
110
+ self.upload_task_id = submit_upload_task(
111
+ filepath=filepath,
112
+ hash_id=hash_id,
113
+ zip_path=zip_path,
114
+ project_name=self.project_name,
115
+ project_id=self.project_id,
116
+ dataset_name=self.dataset_name,
117
+ user_details=self.user_details,
118
+ base_url=self.base_url
119
+ )
120
+
121
+ logger.info(f"Submitted upload task with ID: {self.upload_task_id}")
@@ -31,13 +31,15 @@ from ragaai_catalyst.tracers.utils import get_unique_key
31
31
  # from ragaai_catalyst.tracers.llamaindex_callback import LlamaIndexTracer
32
32
  from ragaai_catalyst.tracers.llamaindex_instrumentation import LlamaIndexInstrumentationTracer
33
33
  from ragaai_catalyst import RagaAICatalyst
34
- from ragaai_catalyst.tracers.agentic_tracing import AgenticTracing, TrackName
34
+ from ragaai_catalyst.tracers.agentic_tracing import AgenticTracing
35
35
  from ragaai_catalyst.tracers.agentic_tracing.tracers.llm_tracer import LLMTracerMixin
36
+ from ragaai_catalyst.tracers.exporters.ragaai_trace_exporter import RAGATraceExporter
37
+ from ragaai_catalyst.tracers.agentic_tracing.utils.file_name_tracker import TrackName
36
38
 
37
39
  logger = logging.getLogger(__name__)
38
40
 
39
41
  class Tracer(AgenticTracing):
40
- NUM_PROJECTS = 100
42
+ NUM_PROJECTS = 99999
41
43
  TIMEOUT = 10
42
44
  def __init__(
43
45
  self,
@@ -88,7 +90,17 @@ class Tracer(AgenticTracing):
88
90
 
89
91
  # take care of auto_instrumentation
90
92
  if isinstance(auto_instrumentation, bool):
91
- if auto_instrumentation:
93
+ if tracer_type == "agentic/llamaindex":
94
+ auto_instrumentation = {
95
+ "llm": False,
96
+ "tool": False,
97
+ "agent": False,
98
+ "user_interaction": False,
99
+ "file_io": False,
100
+ "network": False,
101
+ "custom": False
102
+ }
103
+ elif auto_instrumentation:
92
104
  auto_instrumentation = {
93
105
  "llm": True,
94
106
  "tool": True,
@@ -127,10 +139,11 @@ class Tracer(AgenticTracing):
127
139
  self.upload_timeout = upload_timeout
128
140
  self.base_url = f"{RagaAICatalyst.BASE_URL}"
129
141
  self.timeout = 30
130
- self.num_projects = 100
142
+ self.num_projects = 99999
131
143
  self.start_time = datetime.datetime.now().astimezone().isoformat()
132
144
  self.model_cost_dict = model_cost
133
145
  self.user_context = "" # Initialize user_context to store context from add_context
146
+ self.file_tracker = TrackName()
134
147
 
135
148
  try:
136
149
  response = requests.get(
@@ -171,7 +184,30 @@ class Tracer(AgenticTracing):
171
184
  elif tracer_type == "llamaindex":
172
185
  self._upload_task = None
173
186
  self.llamaindex_tracer = None
174
-
187
+ elif tracer_type == "agentic/llamaindex":
188
+ from opentelemetry.sdk import trace as trace_sdk
189
+ from opentelemetry.sdk.trace.export import SimpleSpanProcessor
190
+ from openinference.instrumentation.llama_index import LlamaIndexInstrumentor
191
+ from ragaai_catalyst.tracers.exporters.dynamic_trace_exporter import DynamicTraceExporter
192
+
193
+ # Get the code_files
194
+ self.file_tracker.trace_main_file()
195
+ list_of_unique_files = self.file_tracker.get_unique_files()
196
+
197
+ # Create a dynamic exporter that allows property updates
198
+ self.dynamic_exporter = DynamicTraceExporter(
199
+ files_to_zip=list_of_unique_files,
200
+ project_name=self.project_name,
201
+ project_id=self.project_id,
202
+ dataset_name=self.dataset_name,
203
+ user_details=self.user_details,
204
+ base_url=self.base_url,
205
+ custom_model_cost=self.model_custom_cost
206
+ )
207
+
208
+ tracer_provider = trace_sdk.TracerProvider()
209
+ tracer_provider.add_span_processor(SimpleSpanProcessor(self.dynamic_exporter))
210
+ LlamaIndexInstrumentor().instrument(tracer_provider=tracer_provider)
175
211
  else:
176
212
  self._upload_task = None
177
213
  # raise ValueError (f"Currently supported tracer types are 'langchain' and 'llamaindex'.")
@@ -206,30 +242,44 @@ class Tracer(AgenticTracing):
206
242
  "output_cost_per_token": float(cost_config["output_cost_per_million_token"]) /1000000
207
243
  }
208
244
 
209
-
210
-
211
245
  def set_dataset_name(self, dataset_name):
212
246
  """
213
247
  Reinitialize the Tracer with a new dataset name while keeping all other parameters the same.
248
+ If using agentic/llamaindex tracer with dynamic exporter, update the exporter's dataset_name property.
214
249
 
215
250
  Args:
216
251
  dataset_name (str): The new dataset name to set
217
252
  """
218
- # Store current parameters
219
- current_params = {
220
- 'project_name': self.project_name,
221
- 'tracer_type': self.tracer_type,
222
- 'pipeline': self.pipeline,
223
- 'metadata': self.metadata,
224
- 'description': self.description,
225
- 'upload_timeout': self.upload_timeout
226
- }
227
-
228
- # Reinitialize self with new dataset_name and stored parameters
229
- self.__init__(
230
- dataset_name=dataset_name,
231
- **current_params
232
- )
253
+ # If we have a dynamic exporter, update its dataset_name property
254
+ if self.tracer_type == "agentic/llamaindex" and hasattr(self, "dynamic_exporter"):
255
+ # Update the dataset name in the dynamic exporter
256
+ self.dynamic_exporter.dataset_name = dataset_name
257
+ logger.debug(f"Updated dynamic exporter's dataset_name to {dataset_name}")
258
+
259
+ # Update the instance variable
260
+ self.dataset_name = dataset_name
261
+
262
+ # Update user_details with new dataset_name
263
+ self.user_details = self._pass_user_data()
264
+
265
+ # Also update the user_details in the dynamic exporter
266
+ self.dynamic_exporter.user_details = self.user_details
267
+ else:
268
+ # Store current parameters
269
+ current_params = {
270
+ 'project_name': self.project_name,
271
+ 'tracer_type': self.tracer_type,
272
+ 'pipeline': self.pipeline,
273
+ 'metadata': self.metadata,
274
+ 'description': self.description,
275
+ 'upload_timeout': self.upload_timeout
276
+ }
277
+
278
+ # Reinitialize self with new dataset_name and stored parameters
279
+ self.__init__(
280
+ dataset_name=dataset_name,
281
+ **current_params
282
+ )
233
283
 
234
284
  def _improve_metadata(self, metadata, tracer_type):
235
285
  if metadata is None:
@@ -375,7 +425,7 @@ class Tracer(AgenticTracing):
375
425
  project_name=self.project_name,
376
426
  project_id=self.project_id,
377
427
  dataset_name=self.dataset_name,
378
- user_detail=user_detail,
428
+ user_detail=self._pass_user_data(),
379
429
  base_url=self.base_url
380
430
  ).upload_traces(additional_metadata_keys=additional_metadata_dict)
381
431
 
@@ -514,6 +564,50 @@ class Tracer(AgenticTracing):
514
564
  }
515
565
  return user_detail
516
566
 
567
+ def update_dynamic_exporter(self, **kwargs):
568
+ """
569
+ Update the dynamic exporter's properties.
570
+
571
+ Args:
572
+ **kwargs: Keyword arguments to update. Can include any of the following:
573
+ - files_to_zip: List of files to zip
574
+ - project_name: Project name
575
+ - project_id: Project ID
576
+ - dataset_name: Dataset name
577
+ - user_details: User details
578
+ - base_url: Base URL for API
579
+
580
+ Raises:
581
+ AttributeError: If the tracer_type is not 'agentic/llamaindex' or if the dynamic_exporter is not initialized.
582
+ """
583
+ if self.tracer_type != "agentic/llamaindex" or not hasattr(self, "dynamic_exporter"):
584
+ raise AttributeError("Dynamic exporter is only available for 'agentic/llamaindex' tracer type")
585
+
586
+ for key, value in kwargs.items():
587
+ if hasattr(self.dynamic_exporter, key):
588
+ setattr(self.dynamic_exporter, key, value)
589
+ logger.debug(f"Updated dynamic exporter's {key} to {value}")
590
+ else:
591
+ logger.warning(f"Dynamic exporter has no attribute '{key}'")
592
+
593
+ def update_file_list(self):
594
+ """
595
+ Update the file list in the dynamic exporter with the latest tracked files.
596
+ This is useful when new files are added to the project during execution.
597
+
598
+ Raises:
599
+ AttributeError: If the tracer_type is not 'agentic/llamaindex' or if the dynamic_exporter is not initialized.
600
+ """
601
+ if self.tracer_type != "agentic/llamaindex" or not hasattr(self, "dynamic_exporter"):
602
+ raise AttributeError("Dynamic exporter is only available for 'agentic/llamaindex' tracer type")
603
+
604
+ # Get the latest list of unique files
605
+ list_of_unique_files = self.file_tracker.get_unique_files()
606
+
607
+ # Update the dynamic exporter's files_to_zip property
608
+ self.dynamic_exporter.files_to_zip = list_of_unique_files
609
+ logger.debug(f"Updated dynamic exporter's files_to_zip with {len(list_of_unique_files)} files")
610
+
517
611
  def add_context(self, context):
518
612
  """
519
613
  Add context information to the trace. This method is only supported for 'langchain' and 'llamaindex' tracer types.
@@ -0,0 +1,266 @@
1
+ import json
2
+ import sys
3
+ from datetime import datetime
4
+ from typing import final
5
+ import pytz
6
+ import uuid
7
+ from ragaai_catalyst.tracers.agentic_tracing.utils.llm_utils import calculate_llm_cost, get_model_cost
8
+
9
+ def convert_time_format(original_time_str, target_timezone_str="Asia/Kolkata"):
10
+ """
11
+ Converts a UTC time string to a specified timezone format.
12
+
13
+ Args:
14
+ original_time_str (str): The original time string in UTC format (e.g., "2025-02-28T22:05:57.945146Z").
15
+ target_timezone_str (str): The target timezone to convert the time to (default is "Asia/Kolkata").
16
+
17
+ Returns:
18
+ str: The converted time string in the specified timezone format.
19
+ """
20
+ # Parse the original time string into a datetime object
21
+ utc_time = datetime.strptime(original_time_str, "%Y-%m-%dT%H:%M:%S.%fZ")
22
+ # Set the timezone to UTC
23
+ utc_time = utc_time.replace(tzinfo=pytz.UTC)
24
+ # Convert the UTC time to the target timezone
25
+ target_timezone = pytz.timezone(target_timezone_str)
26
+ target_time = utc_time.astimezone(target_timezone)
27
+ # Format the datetime object to the desired string format
28
+ formatted_time = target_time.strftime("%Y-%m-%dT%H:%M:%S.%f%z")
29
+ # Add a colon in the timezone offset for better readability
30
+ formatted_time = formatted_time[:-2] + ':' + formatted_time[-2:]
31
+ return formatted_time
32
+
33
+
34
+ def get_uuid(name):
35
+ """Generate a random UUID (not based on name)."""
36
+ return str(uuid.uuid5(uuid.NAMESPACE_DNS, name))
37
+
38
+ def get_spans(input_trace, custom_model_cost):
39
+ data=[]
40
+ span_type_mapping={"AGENT":"agent","LLM":"llm","TOOL":"tool"}
41
+ span_name_occurrence = {}
42
+ for span in input_trace:
43
+ final_span = {}
44
+ span_type=span_type_mapping.get(span["attributes"]["openinference.span.kind"],"custom")
45
+ final_span["id"] = span["context"]["span_id"]
46
+ if span["name"] not in span_name_occurrence:
47
+ span_name_occurrence[span['name']]=0
48
+ else:
49
+ span_name_occurrence[span['name']]+=1
50
+ final_span["name"] = span["name"]+"."+str(span_name_occurrence[span['name']])
51
+ final_span["hash_id"] = get_uuid(final_span["name"])
52
+ final_span["source_hash_id"] = None
53
+ final_span["type"] = span_type
54
+ final_span["start_time"] = convert_time_format(span['start_time'])
55
+ final_span["end_time"] = convert_time_format(span['end_time'])
56
+ final_span["parent_id"] = span["parent_id"]
57
+ final_span["extra_info"] = None
58
+ '''Handle Error if any'''
59
+ if span["status"]["status_code"].lower() == "error":
60
+ final_span["error"] = span["status"]
61
+ else:
62
+ final_span["error"] = None
63
+ # ToDo: Find final trace format for sending error description
64
+ final_span["metrics"] = []
65
+ final_span["feedback"] = None
66
+ final_span["data"]={}
67
+ final_span["info"]={}
68
+ final_span["metrics"] =[]
69
+ final_span["extra_info"]={}
70
+ if span_type=="agent":
71
+ if "input.value" in span["attributes"]:
72
+ try:
73
+ final_span["data"]["input"] = json.loads(span["attributes"]["input.value"])
74
+ except Exception as e:
75
+ final_span["data"]["input"] = span["attributes"]["input.value"]
76
+ else:
77
+ final_span["data"]["input"] = ""
78
+ if "output.value" in span["attributes"]:
79
+ try:
80
+ final_span["data"]["output"] = json.loads(span["attributes"]["output.value"])
81
+ except Exception as e:
82
+ final_span["data"]["output"] = span["attributes"]["output.value"]
83
+ else:
84
+ final_span["data"]["output"] = ""
85
+
86
+ elif span_type=="tool":
87
+ available_fields = list(span['attributes'].keys())
88
+ tool_fields = [key for key in available_fields if 'tool' in key]
89
+ if "input.value" in span["attributes"]:
90
+ try:
91
+ final_span["data"]["input"] = json.loads(span["attributes"]["input.value"])
92
+ except Exception as e:
93
+ final_span["data"]["input"] = span["attributes"]["input.value"]
94
+ else:
95
+ final_span["data"]["input"] = ""
96
+ if "output.value" in span["attributes"]:
97
+ try:
98
+ final_span["data"]["output"] = json.loads(span["attributes"]["output.value"])
99
+ except Exception as e:
100
+ final_span["data"]["output"] = span["attributes"]["output.value"]
101
+ else:
102
+ final_span["data"]["output"] = ""
103
+ input_data={}
104
+ for key in tool_fields:
105
+ input_data[key] = span['attributes'].get(key, None)
106
+ final_span["info"].update(input_data)
107
+
108
+ elif span_type=="llm":
109
+ available_fields = list(span['attributes'].keys())
110
+ input_fields = [key for key in available_fields if 'input' in key]
111
+ input_data = {}
112
+ for key in input_fields:
113
+ if 'mime_type' not in key:
114
+ try:
115
+ input_data[key] = json.loads(span['attributes'][key])
116
+ except json.JSONDecodeError as e:
117
+ input_data[key] = span['attributes'].get(key, None)
118
+ final_span["data"]["input"] = input_data
119
+
120
+ output_fields = [key for key in available_fields if 'output' in key]
121
+ output_data = {}
122
+ output_data['content'] = {}
123
+ for key in output_fields:
124
+ if 'mime_type' not in key:
125
+ try:
126
+ output_data['content'][key] = json.loads(span['attributes'][key])
127
+ except json.JSONDecodeError as e:
128
+ output_data['content'][key] = span['attributes'].get(key, None)
129
+ final_span["data"]["output"] = [output_data]
130
+
131
+ if "llm.model_name" in span["attributes"]:
132
+ final_span["info"]["model"] = span["attributes"]["llm.model_name"]
133
+ else:
134
+ final_span["info"]["model"] = None
135
+ if "llm.invocation_parameters" in span["attributes"]:
136
+ try:
137
+ final_span["info"].update(**json.loads(span["attributes"]["llm.invocation_parameters"]))
138
+ except json.JSONDecodeError as e:
139
+ print(f"Error in parsing: {e}")
140
+
141
+ try:
142
+ final_span["extra_info"]["llm_parameters"] = json.loads(span["attributes"]["llm.invocation_parameters"])
143
+ except json.JSONDecodeError as e:
144
+ final_span["extra_info"]["llm_parameters"] = span["attributes"]["llm.invocation_parameters"]
145
+ else:
146
+ final_span["extra_info"]["llm_parameters"] = None
147
+
148
+ else:
149
+ if "input.value" in span["attributes"]:
150
+ try:
151
+ final_span["data"]["input"] = json.loads(span["attributes"]["input.value"])
152
+ except Exception as e:
153
+ final_span["data"]["input"] = span["attributes"]["input.value"]
154
+ if "output.value" in span["attributes"]:
155
+ try:
156
+ final_span["data"]["output"] = json.loads(span["attributes"]["output.value"])
157
+ except Exception as e:
158
+ final_span["data"]["output"] = span["attributes"]["output.value"]
159
+
160
+ final_span["info"]["cost"] = {}
161
+ final_span["info"]["token"] = {}
162
+
163
+ if "model" in final_span["info"]:
164
+ model_name = final_span["info"]["model"]
165
+
166
+ model_costs = {
167
+ "default": {"input_cost_per_token": 0.0, "output_cost_per_token": 0.0}
168
+ }
169
+ try:
170
+ model_costs = get_model_cost()
171
+ except Exception as e:
172
+ pass
173
+
174
+ if "resource" in span:
175
+ final_span["info"].update(span["resource"])
176
+ if "llm.token_count.prompt" in span['attributes']:
177
+ final_span["info"]["token"]["prompt_tokens"] = span['attributes']['llm.token_count.prompt']
178
+ if "llm.token_count.completion" in span['attributes']:
179
+ final_span["info"]["token"]["completion_tokens"] = span['attributes']['llm.token_count.completion']
180
+ if "llm.token_count.total" in span['attributes']:
181
+ final_span["info"]["token"]["total_tokens"] = span['attributes']['llm.token_count.total']
182
+
183
+ if "info" in final_span:
184
+ if "token" in final_span["info"]:
185
+ if "prompt_tokens" in final_span["info"]["token"]:
186
+ token_usage = {
187
+ "prompt_tokens": final_span["info"]["token"]["prompt_tokens"],
188
+ "completion_tokens": final_span["info"]["token"]["completion_tokens"],
189
+ "total_tokens": final_span["info"]["token"]["total_tokens"]
190
+ }
191
+ final_span["info"]["cost"] = calculate_llm_cost(token_usage=token_usage, model_name=model_name, model_costs=model_costs, model_custom_cost=custom_model_cost)
192
+ data.append(final_span)
193
+ return data
194
+
195
+ def convert_json_format(input_trace, custom_model_cost):
196
+ """
197
+ Converts a JSON from one format to UI format.
198
+
199
+ Args:
200
+ input_trace (str): The input JSON string.
201
+
202
+ Returns:
203
+ final_trace: The converted JSON, or None if an error occurs.
204
+ """
205
+ final_trace = {
206
+ "id": input_trace[0]["context"]["trace_id"],
207
+ "trace_name": "",
208
+ "project_name": "",
209
+ "start_time": convert_time_format(min(item["start_time"] for item in input_trace)), # Find the minimum start_time of all spans
210
+ "end_time": convert_time_format(max(item["end_time"] for item in input_trace)) # Find the maximum end_time of all spans
211
+ }
212
+ final_trace["metadata"] = {
213
+ "tokens": {
214
+ "prompt_tokens": 0.0,
215
+ "completion_tokens": 0.0,
216
+ "total_tokens": 0.0
217
+ },
218
+ "cost": {
219
+ "input_cost": 0.0,
220
+ "output_cost": 0.0,
221
+ "total_cost": 0.0
222
+ }
223
+ }
224
+ final_trace["replays"]={"source":None}
225
+ final_trace["data"]=[{}]
226
+ final_trace["data"][0]["spans"] = get_spans(input_trace, custom_model_cost)
227
+ final_trace["network_calls"] =[]
228
+ final_trace["interactions"] = []
229
+
230
+ for itr in final_trace["data"][0]["spans"]:
231
+ if itr["type"]=="llm":
232
+ if "token" in itr["info"]:
233
+ if "prompt_tokens" in itr["info"]["token"]:
234
+ final_trace["metadata"]["tokens"]["prompt_tokens"] += itr["info"]["token"]['prompt_tokens']
235
+ final_trace["metadata"]["cost"]["input_cost"] += itr["info"]["cost"]['input_cost']
236
+ if "completion_tokens" in itr["info"]["token"]:
237
+ final_trace["metadata"]["tokens"]["completion_tokens"] += itr["info"]["token"]['completion_tokens']
238
+ final_trace["metadata"]["cost"]["output_cost"] += itr["info"]["cost"]['output_cost']
239
+ if "token" in itr["info"]:
240
+ if "total_tokens" in itr["info"]["token"]:
241
+ final_trace["metadata"]["tokens"]["total_tokens"] += itr["info"]["token"]['total_tokens']
242
+ final_trace["metadata"]["cost"]["total_cost"] += itr["info"]["cost"]['total_cost']
243
+
244
+ # get the total tokens, cost
245
+ final_trace["metadata"]["total_cost"] = final_trace["metadata"]["cost"]["total_cost"]
246
+ final_trace["metadata"]["total_tokens"] = final_trace["metadata"]["tokens"]["total_tokens"]
247
+
248
+ return final_trace
249
+
250
+ if __name__ == "__main__":
251
+ if len(sys.argv) != 3:
252
+ print("Usage: python convert.py <input_openinference_trace_path> <output_trace_path>")
253
+ print("Example: python convert.py sample_openinference_trace/test.json output.json")
254
+ sys.exit(1)
255
+ input_file_path = sys.argv[1]
256
+ output_file_path = sys.argv[2]
257
+ with open(input_file_path,'r') as fin:
258
+ input_trace=[]
259
+ for line in fin:
260
+ data=json.loads(line)
261
+ input_trace.append(data)
262
+ payload = convert_json_format(input_trace)
263
+ print(payload)
264
+ with open(output_file_path,"w") as fout:
265
+ json.dump(payload,fout)
266
+ fout.write("\n")
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ragaai_catalyst
3
- Version: 2.1.5b36
3
+ Version: 2.1.5b38
4
4
  Summary: RAGA AI CATALYST
5
5
  Author-email: Kiran Scaria <kiran.scaria@raga.ai>, Kedar Gaikwad <kedar.gaikwad@raga.ai>, Dushyant Mahajan <dushyant.mahajan@raga.ai>, Siddhartha Kosti <siddhartha.kosti@raga.ai>, Ritika Goel <ritika.goel@raga.ai>, Vijay Chaurasia <vijay.chaurasia@raga.ai>, Tushar Kumar <tushar.kumar@raga.ai>
6
- Requires-Python: <3.13,>=3.9
6
+ Requires-Python: <3.13,>=3.10
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
9
  Requires-Dist: aiohttp>=3.10.2
@@ -38,6 +38,10 @@ Requires-Dist: ipynbname
38
38
  Requires-Dist: tiktoken>=0.7.0
39
39
  Requires-Dist: tomli>=2.0.0
40
40
  Requires-Dist: rich>=13.9.4
41
+ Requires-Dist: openinference-instrumentation-llama-index
42
+ Requires-Dist: opentelemetry-sdk
43
+ Requires-Dist: opentelemetry-exporter-otlp
44
+ Requires-Dist: opentelemetry-proto>=1.12.0
41
45
  Provides-Extra: dev
42
46
  Requires-Dist: pytest; extra == "dev"
43
47
  Requires-Dist: pytest-cov; extra == "dev"
@@ -31,7 +31,7 @@ ragaai_catalyst/tracers/distributed.py,sha256=MwlBwIxCAng-OI-7Ove_rkE1mTLeuW4Jw-
31
31
  ragaai_catalyst/tracers/langchain_callback.py,sha256=KooENtkX0Hp0S_d_1WI3iH3qNVt-ZcnwOKVlydv4dUk,33518
32
32
  ragaai_catalyst/tracers/llamaindex_callback.py,sha256=ZY0BJrrlz-P9Mg2dX-ZkVKG3gSvzwqBtk7JL_05MiYA,14028
33
33
  ragaai_catalyst/tracers/llamaindex_instrumentation.py,sha256=Ys_jLkvVqo12bKgXDmkp4TxJu9HkBATrFE8cIcTYxWw,14329
34
- ragaai_catalyst/tracers/tracer.py,sha256=iO5m4ev0ruXjp-YjRMuN_mhr0Cd499Phz6ZYlfJ1yak,22889
34
+ ragaai_catalyst/tracers/tracer.py,sha256=ZA57OqwDZblU9iPR4Lj5t7gEeqLUmOi_Wa10NxMGQsc,27825
35
35
  ragaai_catalyst/tracers/upload_traces.py,sha256=OKsc-Obf8bJvKBprt3dqj8GQQNkoX3kT_t8TBDi9YDQ,5670
36
36
  ragaai_catalyst/tracers/agentic_tracing/README.md,sha256=X4QwLb7-Jg7GQMIXj-SerZIgDETfw-7VgYlczOR8ZeQ,4508
37
37
  ragaai_catalyst/tracers/agentic_tracing/__init__.py,sha256=yf6SKvOPSpH-9LiKaoLKXwqj5sez8F_5wkOb91yp0oE,260
@@ -54,7 +54,7 @@ ragaai_catalyst/tracers/agentic_tracing/tracers/network_tracer.py,sha256=m8CxYkl
54
54
  ragaai_catalyst/tracers/agentic_tracing/tracers/tool_tracer.py,sha256=xxrliKPfdfbIZRZqMnUewsaTD8_Hv0dbuoBivNZGD4U,21674
55
55
  ragaai_catalyst/tracers/agentic_tracing/tracers/user_interaction_tracer.py,sha256=bhSUhNQCuJXKjgJAXhjKEYjnHMpYN90FSZdR84fNIKU,4614
56
56
  ragaai_catalyst/tracers/agentic_tracing/upload/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
- ragaai_catalyst/tracers/agentic_tracing/upload/trace_uploader.py,sha256=uRuZlo5XFoBK-a9tYI5ckhSRiXCaUf9fsE2Plu_W1XI,12282
57
+ ragaai_catalyst/tracers/agentic_tracing/upload/trace_uploader.py,sha256=Buk0OXjdkku0tuuFzGeqKRtwSeIBe3LpA1oa14qS7v4,12380
58
58
  ragaai_catalyst/tracers/agentic_tracing/upload/upload_agentic_traces.py,sha256=icycLgfA0734xxoM1rTMG_iIrI3iM94th8RQggJ7sSw,8541
59
59
  ragaai_catalyst/tracers/agentic_tracing/upload/upload_code.py,sha256=aw_eHhUYRbR_9IbIkNjYb7NOsmETD3k1p4a6gxaGI7Q,6462
60
60
  ragaai_catalyst/tracers/agentic_tracing/upload/upload_local_metric.py,sha256=m1O8lKpxKwtHofXLW3fTHX5yfqDW5GxoveARlg5cTw4,2571
@@ -73,9 +73,11 @@ ragaai_catalyst/tracers/agentic_tracing/utils/system_monitor.py,sha256=H8WNsk4v_
73
73
  ragaai_catalyst/tracers/agentic_tracing/utils/trace_utils.py,sha256=go7FVnofviATDph-j8sk2juv09CGSRt1Vq4U868Fhd8,2259
74
74
  ragaai_catalyst/tracers/agentic_tracing/utils/unique_decorator.py,sha256=G027toV-Km20JjKrc-Y_PilQ8ABEKrBvvzgLTnqVg7I,5819
75
75
  ragaai_catalyst/tracers/agentic_tracing/utils/zip_list_of_unique_files.py,sha256=4TeCGsFF26249fV6dJHLTZDrRa93SG9oer4rudoF8Y4,19443
76
- ragaai_catalyst/tracers/exporters/__init__.py,sha256=kVA8zp05h3phu4e-iHSlnznp_PzMRczB7LphSsZgUjg,138
76
+ ragaai_catalyst/tracers/exporters/__init__.py,sha256=wQbaqyeIjVZxYprHCKZ9BeiqxeXYBKjzEgP79LWNxCU,293
77
+ ragaai_catalyst/tracers/exporters/dynamic_trace_exporter.py,sha256=dAT2WIvkQLl8RrXdhDwm0NxhxytV6PP4ZViYJ6jlOQg,4553
77
78
  ragaai_catalyst/tracers/exporters/file_span_exporter.py,sha256=RgGteu-NVGprXKkynvyIO5yOjpbtA41R3W_NzCjnkwE,6445
78
79
  ragaai_catalyst/tracers/exporters/raga_exporter.py,sha256=6xvjWXyh8XPkHKSLLmAZUQSvwuyY17ov8pv2VdfI0qA,17875
80
+ ragaai_catalyst/tracers/exporters/ragaai_trace_exporter.py,sha256=OhPnquuOOxoCtx7cGYdnsQJZ8lNnl0alm3A2Wc4Bl2g,4814
79
81
  ragaai_catalyst/tracers/instrumentators/__init__.py,sha256=FgnMQupoRTzmVsG9YKsLQera2Pfs-AluZv8CxwavoyQ,253
80
82
  ragaai_catalyst/tracers/instrumentators/langchain.py,sha256=yMN0qVF0pUVk6R5M1vJoUXezDo1ejs4klCFRlE8x4vE,574
81
83
  ragaai_catalyst/tracers/instrumentators/llamaindex.py,sha256=SMrRlR4xM7k9HK43hakE8rkrWHxMlmtmWD-AX6TeByc,416
@@ -86,9 +88,10 @@ ragaai_catalyst/tracers/utils/convert_llama_instru_callback.py,sha256=8qLo7x4Zsn
86
88
  ragaai_catalyst/tracers/utils/extraction_logic_llama_index.py,sha256=ZhPs0YhVtB82-Pq9o1BvCinKE_WPvVxPTEcZjlJbFYM,2371
87
89
  ragaai_catalyst/tracers/utils/langchain_tracer_extraction_logic.py,sha256=XS2_x2qneqEx9oAighLg-LRiueWcESLwIC2r7eJT-Ww,3117
88
90
  ragaai_catalyst/tracers/utils/model_prices_and_context_window_backup.json,sha256=C3uwkibJ08C9sOX-54kulZYmJlIpZ-SQpfE6HNGrjbM,343502
91
+ ragaai_catalyst/tracers/utils/trace_json_converter.py,sha256=5zeJRD3gY_arfXWGZR4cqAvRhfC_wQUabm_7peiNMdY,12268
89
92
  ragaai_catalyst/tracers/utils/utils.py,sha256=ViygfJ7vZ7U0CTSA1lbxVloHp4NSlmfDzBRNCJuMhis,2374
90
- ragaai_catalyst-2.1.5b36.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
91
- ragaai_catalyst-2.1.5b36.dist-info/METADATA,sha256=zhMfu6uQAQsus_PGtduzzY8JtrbH_ZEaNStdUJs80VQ,21884
92
- ragaai_catalyst-2.1.5b36.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
93
- ragaai_catalyst-2.1.5b36.dist-info/top_level.txt,sha256=HpgsdRgEJMk8nqrU6qdCYk3di7MJkDL0B19lkc7dLfM,16
94
- ragaai_catalyst-2.1.5b36.dist-info/RECORD,,
93
+ ragaai_catalyst-2.1.5b38.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
94
+ ragaai_catalyst-2.1.5b38.dist-info/METADATA,sha256=kz6PkN4Wtpxuq034bzEc6I20VX0hIkzn6ZldzL6ql88,22061
95
+ ragaai_catalyst-2.1.5b38.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
96
+ ragaai_catalyst-2.1.5b38.dist-info/top_level.txt,sha256=HpgsdRgEJMk8nqrU6qdCYk3di7MJkDL0B19lkc7dLfM,16
97
+ ragaai_catalyst-2.1.5b38.dist-info/RECORD,,