ragaai-catalyst 2.1.5b0__py3-none-any.whl → 2.1.5b1__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.
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import json
2
3
  import requests
3
4
  from .utils import response_checker
4
5
  from typing import Union
@@ -271,3 +272,332 @@ class Dataset:
271
272
  except Exception as e:
272
273
  logger.error(f"Error in create_from_csv: {e}")
273
274
  raise
275
+
276
+ def add_rows(self, csv_path, dataset_name):
277
+ """
278
+ Add rows to an existing dataset from a CSV file.
279
+
280
+ Args:
281
+ csv_path (str): Path to the CSV file to be added
282
+ dataset_name (str): Name of the existing dataset to add rows to
283
+
284
+ Raises:
285
+ ValueError: If dataset does not exist or columns are incompatible
286
+ """
287
+ # Get existing dataset columns
288
+ existing_columns = self.get_dataset_columns(dataset_name)
289
+
290
+ # Read the CSV file to check columns
291
+ try:
292
+ import pandas as pd
293
+ df = pd.read_csv(csv_path)
294
+ csv_columns = df.columns.tolist()
295
+ except Exception as e:
296
+ logger.error(f"Failed to read CSV file: {e}")
297
+ raise ValueError(f"Unable to read CSV file: {e}")
298
+
299
+ # Check column compatibility
300
+ for column in existing_columns:
301
+ if column not in csv_columns:
302
+ df[column] = None
303
+
304
+ # Get presigned URL for the CSV
305
+ def get_presignedUrl():
306
+ headers = {
307
+ "Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
308
+ "X-Project-Id": str(self.project_id),
309
+ }
310
+ try:
311
+ response = requests.get(
312
+ f"{Dataset.BASE_URL}/v2/llm/dataset/csv/presigned-url",
313
+ headers=headers,
314
+ timeout=Dataset.TIMEOUT,
315
+ )
316
+ response.raise_for_status()
317
+ return response.json()
318
+ except requests.exceptions.RequestException as e:
319
+ logger.error(f"Failed to get presigned URL: {e}")
320
+ raise
321
+
322
+ try:
323
+ presignedUrl = get_presignedUrl()
324
+ if presignedUrl['success']:
325
+ url = presignedUrl['data']['presignedUrl']
326
+ filename = presignedUrl['data']['fileName']
327
+ else:
328
+ raise ValueError('Unable to fetch presignedUrl')
329
+ except Exception as e:
330
+ logger.error(f"Error in get_presignedUrl: {e}")
331
+ raise
332
+
333
+ # Upload CSV to presigned URL
334
+ def put_csv_to_presignedUrl(url):
335
+ headers = {
336
+ 'Content-Type': 'text/csv',
337
+ 'x-ms-blob-type': 'BlockBlob',
338
+ }
339
+ try:
340
+ with open(csv_path, 'rb') as file:
341
+ response = requests.put(
342
+ url,
343
+ headers=headers,
344
+ data=file,
345
+ timeout=Dataset.TIMEOUT,
346
+ )
347
+ response.raise_for_status()
348
+ return response
349
+ except requests.exceptions.RequestException as e:
350
+ logger.error(f"Failed to put CSV to presigned URL: {e}")
351
+ raise
352
+
353
+ try:
354
+ put_csv_response = put_csv_to_presignedUrl(url)
355
+ if put_csv_response.status_code not in (200, 201):
356
+ raise ValueError('Unable to put csv to the presignedUrl')
357
+ except Exception as e:
358
+ logger.error(f"Error in put_csv_to_presignedUrl: {e}")
359
+ raise
360
+
361
+ # Prepare schema mapping (assuming same mapping as original dataset)
362
+ def generate_schema_mapping(dataset_name):
363
+ headers = {
364
+ 'Content-Type': 'application/json',
365
+ "Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
366
+ "X-Project-Id": str(self.project_id),
367
+ }
368
+ json_data = {
369
+ "size": 12,
370
+ "page": "0",
371
+ "projectId": str(self.project_id),
372
+ "search": ""
373
+ }
374
+ try:
375
+ # First get dataset details
376
+ response = requests.post(
377
+ f"{Dataset.BASE_URL}/v2/llm/dataset",
378
+ headers=headers,
379
+ json=json_data,
380
+ timeout=Dataset.TIMEOUT,
381
+ )
382
+ response.raise_for_status()
383
+ datasets = response.json()["data"]["content"]
384
+ dataset_id = [dataset["id"] for dataset in datasets if dataset["name"]==dataset_name][0]
385
+
386
+ # Get dataset details to extract schema mapping
387
+ response = requests.get(
388
+ f"{Dataset.BASE_URL}/v2/llm/dataset/{dataset_id}?initialCols=0",
389
+ headers=headers,
390
+ timeout=Dataset.TIMEOUT,
391
+ )
392
+ response.raise_for_status()
393
+
394
+ # Extract schema mapping
395
+ schema_mapping = {}
396
+ for col in response.json()["data"]["datasetColumnsResponses"]:
397
+ schema_mapping[col["displayName"]] = {"columnType": col["columnType"]}
398
+
399
+ return schema_mapping
400
+ except requests.exceptions.RequestException as e:
401
+ logger.error(f"Failed to get schema mapping: {e}")
402
+ raise
403
+
404
+ # Upload CSV to elastic
405
+ try:
406
+ schema_mapping = generate_schema_mapping(dataset_name)
407
+
408
+ data = {
409
+ "projectId": str(self.project_id),
410
+ "datasetName": dataset_name,
411
+ "fileName": filename,
412
+ "schemaMapping": schema_mapping,
413
+ "opType": "update", # Use update for adding rows
414
+ "description": "Adding new rows to dataset"
415
+ }
416
+
417
+ headers = {
418
+ 'Content-Type': 'application/json',
419
+ 'Authorization': f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
420
+ "X-Project-Id": str(self.project_id)
421
+ }
422
+
423
+ response = requests.post(
424
+ f"{Dataset.BASE_URL}/v2/llm/dataset/csv",
425
+ headers=headers,
426
+ json=data,
427
+ timeout=Dataset.TIMEOUT,
428
+ )
429
+
430
+ if response.status_code == 400:
431
+ raise ValueError(response.json().get("message", "Failed to add rows"))
432
+
433
+ response.raise_for_status()
434
+
435
+ # Check response
436
+ response_data = response.json()
437
+ if not response_data.get('success', False):
438
+ raise ValueError(response_data.get('message', 'Unknown error occurred'))
439
+
440
+ print(f"Successfully added rows to dataset {dataset_name}")
441
+ return response_data
442
+
443
+ except Exception as e:
444
+ logger.error(f"Error in add_rows_to_dataset: {e}")
445
+ raise
446
+
447
+ def add_columns(self,text_fields,dataset_name, column_name, provider, model,variables={}):
448
+ """
449
+ Add a column to a dataset with dynamically fetched model parameters
450
+
451
+ Args:
452
+ project_id (int): Project ID
453
+ dataset_id (int): Dataset ID
454
+ column_name (str): Name of the new column
455
+ provider (str): Name of the model provider
456
+ model (str): Name of the model
457
+ """
458
+ # First, get model parameters
459
+
460
+ # Validate text_fields input
461
+ if not isinstance(text_fields, list):
462
+ raise ValueError("text_fields must be a list of dictionaries")
463
+
464
+ for field in text_fields:
465
+ if not isinstance(field, dict) or 'role' not in field or 'content' not in field:
466
+ raise ValueError("Each text field must be a dictionary with 'role' and 'content' keys")
467
+
468
+ # First, get the dataset ID
469
+ headers = {
470
+ 'Content-Type': 'application/json',
471
+ "Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
472
+ "X-Project-Id": str(self.project_id),
473
+ }
474
+ json_data = {"size": 12, "page": "0", "projectId": str(self.project_id), "search": ""}
475
+
476
+ try:
477
+ # Get dataset list
478
+ response = requests.post(
479
+ f"{Dataset.BASE_URL}/v2/llm/dataset",
480
+ headers=headers,
481
+ json=json_data,
482
+ timeout=Dataset.TIMEOUT,
483
+ )
484
+ response.raise_for_status()
485
+ datasets = response.json()["data"]["content"]
486
+
487
+ # Find dataset ID
488
+ dataset_id = next((dataset["id"] for dataset in datasets if dataset["name"] == dataset_name), None)
489
+
490
+ if dataset_id is None:
491
+ raise ValueError(f"Dataset {dataset_name} not found")
492
+
493
+
494
+
495
+ parameters_url= f"{Dataset.BASE_URL}/playground/providers/models/parameters/list"
496
+
497
+ headers = {
498
+ 'Content-Type': 'application/json',
499
+ "Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
500
+ "X-Project-Id": str(self.project_id),
501
+ }
502
+
503
+ # Fetch model parameters
504
+ parameters_payload = {
505
+ "providerName": provider,
506
+ "modelName": model
507
+ }
508
+
509
+ # Get model parameters
510
+ params_response = requests.post(
511
+ parameters_url,
512
+ headers=headers,
513
+ json=parameters_payload,
514
+ timeout=30
515
+ )
516
+ params_response.raise_for_status()
517
+
518
+ # Extract parameters
519
+ all_parameters = params_response.json().get('data', [])
520
+
521
+ # Filter and transform parameters for add-column API
522
+ formatted_parameters = []
523
+ for param in all_parameters:
524
+ value = param.get('value')
525
+ param_type = param.get('type')
526
+
527
+ if value is None:
528
+ formatted_param = {
529
+ "name": param.get('name'),
530
+ "value": None, # Pass None if the value is null
531
+ "type": param.get('type')
532
+ }
533
+ else:
534
+ # Improved type handling
535
+ if param_type == "float":
536
+ value = float(value) # Ensure value is converted to float
537
+ elif param_type == "int":
538
+ value = int(value) # Ensure value is converted to int
539
+ elif param_type == "bool":
540
+ value = bool(value) # Ensure value is converted to bool
541
+ elif param_type == "string":
542
+ value = str(value) # Ensure value is converted to string
543
+ else:
544
+ raise ValueError(f"Unsupported parameter type: {param_type}") # Handle unsupported types
545
+
546
+ formatted_param = {
547
+ "name": param.get('name'),
548
+ "value": value,
549
+ "type": param.get('type')
550
+ }
551
+ formatted_parameters.append(formatted_param)
552
+ dataset_id = next((dataset["id"] for dataset in datasets if dataset["name"] == dataset_name), None)
553
+
554
+ # Prepare payload for add column API
555
+ add_column_payload = {
556
+ "rowFilterList": [],
557
+ "columnName": column_name,
558
+ "datasetId": dataset_id,
559
+ "variables": variables,
560
+ "promptTemplate": {
561
+ "textFields": text_fields,
562
+ "modelSpecs": {
563
+ "model": f"{provider}/{model}",
564
+ "parameters": formatted_parameters
565
+ }
566
+ }
567
+ }
568
+ if variables:
569
+ variable_specs = []
570
+ for key, values in variables.items():
571
+ variable_specs.append({
572
+ "name": key,
573
+ "type": "string",
574
+ "schema": values
575
+ })
576
+ add_column_payload["promptTemplate"]["variableSpecs"] = variable_specs
577
+
578
+ # Make API call to add column
579
+ add_column_url = f"{Dataset.BASE_URL}/v2/llm/dataset/add-column"
580
+
581
+ response = requests.post(
582
+ add_column_url,
583
+ headers={
584
+ 'Content-Type': 'application/json',
585
+ 'Authorization': f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
586
+ "X-Project-Id": str(self.project_id)
587
+ },
588
+ json=add_column_payload,
589
+ timeout=30
590
+ )
591
+
592
+ # Check response
593
+ response.raise_for_status()
594
+ response_data = response.json()
595
+
596
+ print("Column added successfully:")
597
+ print(json.dumps(response_data, indent=2))
598
+ return response_data
599
+
600
+ except requests.exceptions.RequestException as e:
601
+ print(f"Error adding column: {e}")
602
+ raise
603
+
@@ -44,6 +44,7 @@ class AgentTracerMixin:
44
44
  # Add auto instrument flags
45
45
  self.auto_instrument_agent = False
46
46
  self.auto_instrument_user_interaction = False
47
+ self.auto_instrument_file_io = False
47
48
  self.auto_instrument_network = False
48
49
 
49
50
  def trace_agent(
@@ -512,10 +513,22 @@ class AgentTracerMixin:
512
513
  network_calls = self.component_network_calls.get(kwargs["component_id"], [])
513
514
  interactions = []
514
515
  if self.auto_instrument_user_interaction:
515
- interactions = self.component_user_interaction.get(
516
- kwargs["component_id"], []
517
- )
518
- start_time = kwargs["start_time"]
516
+ input_output_interactions = []
517
+ for interaction in self.component_user_interaction.get(kwargs["component_id"], []):
518
+ if interaction["interaction_type"] in ["input", "output"]:
519
+ input_output_interactions.append(interaction)
520
+ interactions.extend(input_output_interactions)
521
+ if self.auto_instrument_file_io:
522
+ file_io_interactions = []
523
+ for interaction in self.component_user_interaction.get(kwargs["component_id"], []):
524
+ if interaction["interaction_type"] in ["file_read", "file_write"]:
525
+ file_io_interactions.append(interaction)
526
+ interactions.extend(file_io_interactions)
527
+
528
+ # Get start time
529
+ start_time = None
530
+ if "start_time" in kwargs:
531
+ start_time = kwargs["start_time"]
519
532
 
520
533
  # Get tags, metrics
521
534
  name = kwargs["name"]
@@ -621,3 +634,6 @@ class AgentTracerMixin:
621
634
 
622
635
  def instrument_network_calls(self):
623
636
  self.auto_instrument_network = True
637
+
638
+ def instrument_file_io_calls(self):
639
+ self.auto_instrument_file_io = True
@@ -695,11 +695,12 @@ class BaseTracer:
695
695
  # Process additional interactions and network calls
696
696
  if "interactions" in child:
697
697
  for interaction in child["interactions"]:
698
- interaction["id"] = str(interaction_id)
699
- interaction["span_id"] = child.get("id")
700
- interaction["error"] = None
701
- interactions.append(interaction)
702
- interaction_id += 1
698
+ if interaction!=[]:
699
+ interaction["id"] = str(interaction_id)
700
+ interaction["span_id"] = child.get("id")
701
+ interaction["error"] = None
702
+ interactions.append(interaction)
703
+ interaction_id += 1
703
704
 
704
705
  if "network_calls" in child:
705
706
  for child_network_call in child["network_calls"]:
@@ -876,15 +877,16 @@ class BaseTracer:
876
877
  # Process interactions from span.data if they exist
877
878
  if span.interactions:
878
879
  for span_interaction in span.interactions:
879
- interaction = {}
880
- interaction["id"] = str(interaction_id)
881
- interaction["span_id"] = span.id
882
- interaction["interaction_type"] = span_interaction.type
883
- interaction["content"] = span_interaction.content
884
- interaction["timestamp"] = span_interaction.timestamp
885
- interaction["error"] = span.error
886
- interactions.append(interaction)
887
- interaction_id += 1
880
+ if span_interaction != []:
881
+ interaction = {}
882
+ interaction["id"] = str(interaction_id)
883
+ interaction["span_id"] = span.id
884
+ interaction["interaction_type"] = span_interaction.type
885
+ interaction["content"] = span_interaction.content
886
+ interaction["timestamp"] = span_interaction.timestamp
887
+ interaction["error"] = span.error
888
+ interactions.append(interaction)
889
+ interaction_id += 1
888
890
 
889
891
  if span.network_calls:
890
892
  for span_network_call in span.network_calls:
@@ -25,6 +25,7 @@ class CustomTracerMixin:
25
25
  self.auto_instrument_custom = False
26
26
  self.auto_instrument_user_interaction = False
27
27
  self.auto_instrument_network = False
28
+ self.auto_instrument_file_io = False
28
29
 
29
30
  def trace_custom(self, name: str = None, custom_type: str = "generic", version: str = "1.0.0", trace_variables: bool = True):
30
31
  def decorator(func):
@@ -246,8 +247,18 @@ class CustomTracerMixin:
246
247
 
247
248
  interactions = []
248
249
  if self.auto_instrument_user_interaction:
249
- interactions = self.component_user_interaction.get(kwargs["component_id"], [])
250
-
250
+ input_output_interactions = []
251
+ for interaction in self.component_user_interaction.get(kwargs["component_id"], []):
252
+ if interaction["interaction_type"] in ["input", "output"]:
253
+ input_output_interactions.append(interaction)
254
+ interactions.extend(input_output_interactions)
255
+ if self.auto_instrument_file_io:
256
+ file_io_interactions = []
257
+ for interaction in self.component_user_interaction.get(kwargs["component_id"], []):
258
+ if interaction["interaction_type"] in ["file_read", "file_write"]:
259
+ file_io_interactions.append(interaction)
260
+ interactions.extend(file_io_interactions)
261
+
251
262
  component = {
252
263
  "id": kwargs["component_id"],
253
264
  "hash_id": kwargs["hash_id"],
@@ -314,3 +325,7 @@ class CustomTracerMixin:
314
325
  def instrument_network_calls(self):
315
326
  """Enable auto-instrumentation for network calls"""
316
327
  self.auto_instrument_network = True
328
+
329
+ def instrument_file_io_calls(self):
330
+ """Enable auto-instrumentation for file IO calls"""
331
+ self.auto_instrument_file_io = True
@@ -61,6 +61,7 @@ class LLMTracerMixin:
61
61
  # Add auto_instrument options
62
62
  self.auto_instrument_llm = False
63
63
  self.auto_instrument_user_interaction = False
64
+ self.auto_instrument_file_io = False
64
65
  self.auto_instrument_network = False
65
66
 
66
67
  def instrument_llm_calls(self):
@@ -117,6 +118,10 @@ class LLMTracerMixin:
117
118
  def instrument_network_calls(self):
118
119
  """Enable network instrumentation for LLM calls"""
119
120
  self.auto_instrument_network = True
121
+
122
+ def instrument_file_io_calls(self):
123
+ """Enable file IO instrumentation for LLM calls"""
124
+ self.auto_instrument_file_io = True
120
125
 
121
126
  def patch_openai_methods(self, module):
122
127
  try:
@@ -334,7 +339,17 @@ class LLMTracerMixin:
334
339
 
335
340
  interactions = []
336
341
  if self.auto_instrument_user_interaction:
337
- interactions = self.component_user_interaction.get(component_id, [])
342
+ input_output_interactions = []
343
+ for interaction in self.component_user_interaction.get(component_id, []):
344
+ if interaction["interaction_type"] in ["input", "output"]:
345
+ input_output_interactions.append(interaction)
346
+ interactions.extend(input_output_interactions)
347
+ if self.auto_instrument_file_io:
348
+ file_io_interactions = []
349
+ for interaction in self.component_user_interaction.get(component_id, []):
350
+ if interaction["interaction_type"] in ["file_read", "file_write"]:
351
+ file_io_interactions.append(interaction)
352
+ interactions.extend(file_io_interactions)
338
353
 
339
354
  parameters_to_display = {}
340
355
  if "run_manager" in parameters:
@@ -84,27 +84,23 @@ class AgenticTracing(
84
84
  self.auto_instrument_custom = True
85
85
  else:
86
86
  # Set global active state
87
- self.is_active = (
88
- any(auto_instrumentation.values())
89
- if isinstance(auto_instrumentation, dict)
90
- else bool(auto_instrumentation)
91
- )
87
+ self.is_active = True
92
88
 
93
89
  # Set individual components
94
90
  if isinstance(auto_instrumentation, dict):
95
- self.auto_instrument_llm = auto_instrumentation.get("llm", False)
96
- self.auto_instrument_tool = auto_instrumentation.get("tool", False)
97
- self.auto_instrument_agent = auto_instrumentation.get("agent", False)
91
+ self.auto_instrument_llm = auto_instrumentation.get("llm", True)
92
+ self.auto_instrument_tool = auto_instrumentation.get("tool", True)
93
+ self.auto_instrument_agent = auto_instrumentation.get("agent", True)
98
94
  self.auto_instrument_user_interaction = auto_instrumentation.get(
99
- "user_interaction", False
95
+ "user_interaction", True
100
96
  )
101
97
  self.auto_instrument_file_io = auto_instrumentation.get(
102
- "file_io", False
98
+ "file_io", True
103
99
  )
104
100
  self.auto_instrument_network = auto_instrumentation.get(
105
- "network", False
101
+ "network", True
106
102
  )
107
- self.auto_instrument_custom = auto_instrumentation.get("custom", False)
103
+ self.auto_instrument_custom = auto_instrumentation.get("custom", True)
108
104
  else:
109
105
  # If boolean provided, apply to all components
110
106
  self.auto_instrument_llm = bool(auto_instrumentation)
@@ -170,9 +166,6 @@ class AgenticTracing(
170
166
  self.user_interaction_tracer.trace_id.set(self.trace_id)
171
167
  self.user_interaction_tracer.tracer = self
172
168
  self.user_interaction_tracer.component_id.set(self.current_component_id.get())
173
- builtins.print = self.user_interaction_tracer.traced_print
174
- builtins.input = self.user_interaction_tracer.traced_input
175
- builtins.open = self.user_interaction_tracer.traced_open
176
169
 
177
170
  # Start base tracer (includes system info and resource monitoring)
178
171
  super().start()
@@ -194,11 +187,12 @@ class AgenticTracing(
194
187
  self.instrument_custom_calls()
195
188
 
196
189
  if self.auto_instrument_user_interaction:
197
-
198
190
  ToolTracerMixin.instrument_user_interaction_calls(self)
199
191
  LLMTracerMixin.instrument_user_interaction_calls(self)
200
192
  AgentTracerMixin.instrument_user_interaction_calls(self)
201
193
  CustomTracerMixin.instrument_user_interaction_calls(self)
194
+ builtins.print = self.user_interaction_tracer.traced_print
195
+ builtins.input = self.user_interaction_tracer.traced_input
202
196
 
203
197
  if self.auto_instrument_network:
204
198
  ToolTracerMixin.instrument_network_calls(self)
@@ -206,9 +200,12 @@ class AgenticTracing(
206
200
  AgentTracerMixin.instrument_network_calls(self)
207
201
  CustomTracerMixin.instrument_network_calls(self)
208
202
 
209
- # These will be implemented later
210
- # if self.auto_instrument_file_io:
211
- # self.instrument_file_io_calls()
203
+ if self.auto_instrument_file_io:
204
+ ToolTracerMixin.instrument_file_io_calls(self)
205
+ LLMTracerMixin.instrument_file_io_calls(self)
206
+ AgentTracerMixin.instrument_file_io_calls(self)
207
+ CustomTracerMixin.instrument_file_io_calls(self)
208
+ builtins.open = self.user_interaction_tracer.traced_open
212
209
 
213
210
  def stop(self):
214
211
  """Stop tracing and save results"""
@@ -32,6 +32,7 @@ class ToolTracerMixin:
32
32
  # add auto_instrument option
33
33
  self.auto_instrument_tool = False
34
34
  self.auto_instrument_user_interaction = False
35
+ self.auto_instrument_file_io = False
35
36
  self.auto_instrument_network = False
36
37
 
37
38
  # take care of auto_instrument
@@ -40,6 +41,9 @@ class ToolTracerMixin:
40
41
 
41
42
  def instrument_user_interaction_calls(self):
42
43
  self.auto_instrument_user_interaction = True
44
+
45
+ def instrument_file_io_calls(self):
46
+ self.auto_instrument_file_io = True
43
47
 
44
48
  def instrument_network_calls(self):
45
49
  self.auto_instrument_network = True
@@ -133,6 +137,10 @@ class ToolTracerMixin:
133
137
  component_id = str(uuid.uuid4())
134
138
  hash_id = generate_unique_hash_simple(func)
135
139
 
140
+ # Set current tool name and store the token
141
+ name_token = self.current_tool_name.set(name)
142
+ id_token = self.current_tool_id.set(component_id)
143
+
136
144
  # Start tracking network calls for this component
137
145
  self.start_component(component_id)
138
146
 
@@ -191,6 +199,12 @@ class ToolTracerMixin:
191
199
  self.add_component(tool_component)
192
200
 
193
201
  raise
202
+ finally:
203
+ # Reset the tool name and id context
204
+ if name_token:
205
+ self.current_tool_name.reset(name_token)
206
+ if id_token:
207
+ self.current_tool_id.reset(id_token)
194
208
 
195
209
  async def _trace_tool_execution(
196
210
  self, func, name, tool_type, version, *args, **kwargs
@@ -207,6 +221,10 @@ class ToolTracerMixin:
207
221
  component_id = str(uuid.uuid4())
208
222
  hash_id = generate_unique_hash_simple(func)
209
223
 
224
+ # Set current tool name and store the token
225
+ name_token = self.current_tool_name.set(name)
226
+ id_token = self.current_tool_id.set(component_id)
227
+
210
228
  self.start_component(component_id)
211
229
  try:
212
230
  # Execute the tool
@@ -256,6 +274,12 @@ class ToolTracerMixin:
256
274
  self.add_component(tool_component)
257
275
 
258
276
  raise
277
+ finally:
278
+ # Reset the tool name and id context
279
+ if name_token:
280
+ self.current_tool_name.reset(name_token)
281
+ if id_token:
282
+ self.current_tool_id.reset(id_token)
259
283
 
260
284
  def create_tool_component(self, **kwargs):
261
285
  """Create a tool component according to the data structure"""
@@ -264,9 +288,19 @@ class ToolTracerMixin:
264
288
  network_calls = self.component_network_calls.get(kwargs["component_id"], [])
265
289
  interactions = []
266
290
  if self.auto_instrument_user_interaction:
267
- interactions = self.component_user_interaction.get(
268
- kwargs["component_id"], []
269
- )
291
+ input_output_interactions = []
292
+ for interaction in self.component_user_interaction.get(kwargs["component_id"], []):
293
+ if interaction["interaction_type"] in ["input", "output"]:
294
+ input_output_interactions.append(interaction)
295
+ if input_output_interactions!=[]:
296
+ interactions.extend(input_output_interactions)
297
+ if self.auto_instrument_file_io:
298
+ file_io_interactions = []
299
+ for interaction in self.component_user_interaction.get(kwargs["component_id"], []):
300
+ if interaction["interaction_type"] in ["file_read", "file_write"]:
301
+ file_io_interactions.append(interaction)
302
+ if file_io_interactions!=[]:
303
+ interactions.extend(file_io_interactions)
270
304
 
271
305
  # Get tags, metrics
272
306
  name = kwargs["name"]
@@ -2063,7 +2063,9 @@
2063
2063
  "input_cost_per_token": 5.9e-07,
2064
2064
  "output_cost_per_token": 7.9e-07,
2065
2065
  "litellm_provider": "groq",
2066
- "mode": "chat"
2066
+ "mode": "chat",
2067
+ "supports_function_calling": true,
2068
+ "supports_response_schema": true
2067
2069
  },
2068
2070
  "groq/llama-3.3-70b-specdec": {
2069
2071
  "max_tokens": 8192,
@@ -4605,6 +4607,20 @@
4605
4607
  "litellm_provider": "replicate",
4606
4608
  "mode": "chat"
4607
4609
  },
4610
+ "openrouter/deepseek/deepseek-r1": {
4611
+ "max_tokens": 8192,
4612
+ "max_input_tokens": 64000,
4613
+ "max_output_tokens": 8192,
4614
+ "input_cost_per_token": 5.5e-07,
4615
+ "input_cost_per_token_cache_hit": 1.4e-07,
4616
+ "output_cost_per_token": 2.19e-06,
4617
+ "litellm_provider": "openrouter",
4618
+ "mode": "chat",
4619
+ "supports_function_calling": true,
4620
+ "supports_assistant_prefill": true,
4621
+ "supports_tool_choice": true,
4622
+ "supports_prompt_caching": true
4623
+ },
4608
4624
  "openrouter/deepseek/deepseek-chat": {
4609
4625
  "max_tokens": 8192,
4610
4626
  "max_input_tokens": 66000,
@@ -191,6 +191,7 @@ def trace_tool(name: str = None, tool_type: str = "generic", version: str = "1.0
191
191
  """Decorator for tracing tool functions."""
192
192
  def decorator(func):
193
193
  is_async = asyncio.iscoroutinefunction(func)
194
+ span_name = name or func.__name__
194
195
 
195
196
  @wraps(func)
196
197
  async def async_wrapper(*args, **kwargs):
@@ -198,15 +199,23 @@ def trace_tool(name: str = None, tool_type: str = "generic", version: str = "1.0
198
199
  if not tracer:
199
200
  return await func(*args, **kwargs)
200
201
 
201
- # Use async tool tracing
202
- return await tracer._trace_tool_execution(
203
- func,
204
- name or func.__name__,
205
- tool_type,
206
- version,
207
- *args,
208
- **kwargs
209
- )
202
+ # Set current tool name and store the token
203
+ name_token = tracer.current_tool_name.set(span_name)
204
+
205
+ try:
206
+ # Use async tool tracing
207
+ return await tracer._trace_tool_execution(
208
+ func,
209
+ span_name,
210
+ tool_type,
211
+ version,
212
+ *args,
213
+ **kwargs
214
+ )
215
+ finally:
216
+ # Reset using the stored token
217
+ if name_token:
218
+ tracer.current_tool_name.reset(name_token)
210
219
 
211
220
  @wraps(func)
212
221
  def sync_wrapper(*args, **kwargs):
@@ -214,15 +223,23 @@ def trace_tool(name: str = None, tool_type: str = "generic", version: str = "1.0
214
223
  if not tracer:
215
224
  return func(*args, **kwargs)
216
225
 
217
- # Use synchronous tool tracing
218
- return tracer._trace_sync_tool_execution(
219
- func,
220
- name or func.__name__,
221
- tool_type,
222
- version,
223
- *args,
224
- **kwargs
225
- )
226
+ # Set current tool name and store the token
227
+ name_token = tracer.current_tool_name.set(span_name)
228
+
229
+ try:
230
+ # Use synchronous tool tracing
231
+ return tracer._trace_sync_tool_execution(
232
+ func,
233
+ span_name,
234
+ tool_type,
235
+ version,
236
+ *args,
237
+ **kwargs
238
+ )
239
+ finally:
240
+ # Reset using the stored token
241
+ if name_token:
242
+ tracer.current_tool_name.reset(name_token)
226
243
 
227
244
  return async_wrapper if is_async else sync_wrapper
228
245
  return decorator
@@ -278,7 +295,17 @@ def current_span():
278
295
  if not tracer:
279
296
  return None
280
297
 
281
- # Get the current agent name from context
298
+ # First check for LLM context
299
+ llm_name = tracer.current_llm_call_name.get()
300
+ if llm_name:
301
+ return tracer.span(llm_name)
302
+
303
+ # Then check for tool context
304
+ tool_name = tracer.current_tool_name.get()
305
+ if tool_name:
306
+ return tracer.span(tool_name)
307
+
308
+ # Finally fall back to agent context
282
309
  agent_name = tracer.current_agent_name.get()
283
310
  if not agent_name:
284
311
  raise ValueError("No active span found. Make sure you're calling this within a traced function.")
@@ -98,10 +98,10 @@ class Tracer(AgenticTracing):
98
98
  "custom": False
99
99
  }
100
100
  elif isinstance(auto_instrumentation, dict):
101
- auto_instrumentation = {k: v for k, v in auto_instrumentation.items() if v}
101
+ auto_instrumentation = {k: v for k, v in auto_instrumentation.items()}
102
102
  for key in ["llm", "tool", "agent", "user_interaction", "file_io", "network", "custom"]:
103
103
  if key not in auto_instrumentation:
104
- auto_instrumentation[key] = False
104
+ auto_instrumentation[key] = True
105
105
 
106
106
  super().__init__(user_detail=user_detail, auto_instrumentation=auto_instrumentation)
107
107
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ragaai_catalyst
3
- Version: 2.1.5b0
3
+ Version: 2.1.5b1
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>
6
6
  Requires-Python: <3.13,>=3.9
@@ -1,6 +1,6 @@
1
1
  ragaai_catalyst/__init__.py,sha256=tvESV8UuVtth14E89wQxgf0WvQZSApWfDeLiIdmMhkE,791
2
2
  ragaai_catalyst/_version.py,sha256=JKt9KaVNOMVeGs8ojO6LvIZr7ZkMzNN-gCcvryy4x8E,460
3
- ragaai_catalyst/dataset.py,sha256=aTRvZicAXmrC0KdmmsoJH_rtEJrxbqYmf1P806c1Wg0,10521
3
+ ragaai_catalyst/dataset.py,sha256=Mc0L28lO4f7zD8DraQMHWzuizJf8Q0Z-DxNx3pS4OFo,23409
4
4
  ragaai_catalyst/evaluation.py,sha256=34H2bYZNSrcu0jMQgDZw1OLVbQU80PaVLo2avju8POM,20311
5
5
  ragaai_catalyst/experiment.py,sha256=8yQo1phCHlpnJ-4CqCaIbLXg_1ZlAuLGI9kqGBl-OTE,18859
6
6
  ragaai_catalyst/guard_executor.py,sha256=llPbE3DyVtrybojXknzBZj8-dtUrGBQwi9-ZiPJxGRo,3762
@@ -12,9 +12,9 @@ ragaai_catalyst/ragaai_catalyst.py,sha256=FdqMzwuQLqS2-3JJDsTQ8uh2itllOxfPrRUjb8
12
12
  ragaai_catalyst/synthetic_data_generation.py,sha256=uDV9tNwto2xSkWg5XHXUvjErW-4P34CTrxaJpRfezyA,19250
13
13
  ragaai_catalyst/utils.py,sha256=TlhEFwLyRU690HvANbyoRycR3nQ67lxVUQoUOfTPYQ0,3772
14
14
  ragaai_catalyst/tracers/__init__.py,sha256=LfgTes-nHpazssbGKnn8kyLZNr49kIPrlkrqqoTFTfc,301
15
- ragaai_catalyst/tracers/distributed.py,sha256=_KO7swUDoPErbfahxNSRpxQk22rstQItfPGP8e4l7E4,9334
15
+ ragaai_catalyst/tracers/distributed.py,sha256=AIRvS5Ur4jbFDXsUkYuCTmtGoHHx3LOG4n5tWOh610U,10330
16
16
  ragaai_catalyst/tracers/llamaindex_callback.py,sha256=ZY0BJrrlz-P9Mg2dX-ZkVKG3gSvzwqBtk7JL_05MiYA,14028
17
- ragaai_catalyst/tracers/tracer.py,sha256=UX-01NYWcH2y4UW1W287Cn-jy760rgaFqu8llJbeMdg,15654
17
+ ragaai_catalyst/tracers/tracer.py,sha256=S_ANRm5zSMvQiUyQTRwyUepFci_T3AN26wAOXoURfyc,15648
18
18
  ragaai_catalyst/tracers/upload_traces.py,sha256=mT5rverNUL5Rcal9VR5_c75wHBAUrm2pvYetTZqP3ok,4796
19
19
  ragaai_catalyst/tracers/agentic_tracing/README.md,sha256=X4QwLb7-Jg7GQMIXj-SerZIgDETfw-7VgYlczOR8ZeQ,4508
20
20
  ragaai_catalyst/tracers/agentic_tracing/__init__.py,sha256=yf6SKvOPSpH-9LiKaoLKXwqj5sez8F_5wkOb91yp0oE,260
@@ -27,14 +27,14 @@ ragaai_catalyst/tracers/agentic_tracing/tests/__init__.py,sha256=47DEQpj8HBSa-_T
27
27
  ragaai_catalyst/tracers/agentic_tracing/tests/ai_travel_agent.py,sha256=S4rCcKzU_5SB62BYEbNn_1VbbTdG4396N8rdZ3ZNGcE,5654
28
28
  ragaai_catalyst/tracers/agentic_tracing/tests/unique_decorator_test.py,sha256=Xk1cLzs-2A3dgyBwRRnCWs7Eubki40FVonwd433hPN8,4805
29
29
  ragaai_catalyst/tracers/agentic_tracing/tracers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
- ragaai_catalyst/tracers/agentic_tracing/tracers/agent_tracer.py,sha256=aLiq5nPie5TT61QYtvAtvErsxjPFYiUxjayn5aCX1_k,25543
31
- ragaai_catalyst/tracers/agentic_tracing/tracers/base.py,sha256=XZH5qi8eMIHG_qjedRHa3lGYp0NiAtCUxN9P_uW_NMo,39981
32
- ragaai_catalyst/tracers/agentic_tracing/tracers/custom_tracer.py,sha256=uay8lU7T-CKsVu8KvWX31qfMqufK9S3Ive7XKo2Ksmk,12252
30
+ ragaai_catalyst/tracers/agentic_tracing/tracers/agent_tracer.py,sha256=--wvhOJ-J2433WPatIS3wx6VFeCUIcgRT5_ZjGQDv2c,26364
31
+ ragaai_catalyst/tracers/agentic_tracing/tracers/base.py,sha256=RFHbmzFdkl1zV4ZnM6VOAnCeTMqSKmgdH8VuWsrBwf4,40120
32
+ ragaai_catalyst/tracers/agentic_tracing/tracers/custom_tracer.py,sha256=l3x3uFO5ov93I7UUrUX1M06WVGy2ug2jEZ1G7o315z4,13075
33
33
  ragaai_catalyst/tracers/agentic_tracing/tracers/langgraph_tracer.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
- ragaai_catalyst/tracers/agentic_tracing/tracers/llm_tracer.py,sha256=hU97LWEdqFL-tnIb_Qlk6VJdS6yVPH0QVzVzg1yuCB0,29341
35
- ragaai_catalyst/tracers/agentic_tracing/tracers/main_tracer.py,sha256=9K1swf0IOEEzgpkO2A_k_Yxk6ybLRXDcVoQAzm-_fWs,15889
34
+ ragaai_catalyst/tracers/agentic_tracing/tracers/llm_tracer.py,sha256=fuSVks5-OchQLXqN6T18CtDjE8ufrCcksckcC3WW-_k,30150
35
+ ragaai_catalyst/tracers/agentic_tracing/tracers/main_tracer.py,sha256=6hsg-Yw11v4qeELI1CWrdX8BXf-wJrTF5smBI5prgoo,15873
36
36
  ragaai_catalyst/tracers/agentic_tracing/tracers/network_tracer.py,sha256=m8CxYkl7iMiFya_lNwN1ykBc3Pmo-2pR_2HmpptwHWQ,10352
37
- ragaai_catalyst/tracers/agentic_tracing/tracers/tool_tracer.py,sha256=4h6epgRHPVRfTrCznwc6KPK6xevwBaZwE-q0Zj-4Kkk,12160
37
+ ragaai_catalyst/tracers/agentic_tracing/tracers/tool_tracer.py,sha256=4rWL7fIJE5wN0nwh6fMWyh3OrrenZHJkNzyQXikyzQI,13771
38
38
  ragaai_catalyst/tracers/agentic_tracing/tracers/user_interaction_tracer.py,sha256=bhSUhNQCuJXKjgJAXhjKEYjnHMpYN90FSZdR84fNIKU,4614
39
39
  ragaai_catalyst/tracers/agentic_tracing/upload/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
40
  ragaai_catalyst/tracers/agentic_tracing/upload/upload_agentic_traces.py,sha256=1MDKXAAPzOEdxFKWWQrRgrmM3kz--DGXSywGXQmR3lQ,6041
@@ -47,7 +47,7 @@ ragaai_catalyst/tracers/agentic_tracing/utils/file_name_tracker.py,sha256=515NND
47
47
  ragaai_catalyst/tracers/agentic_tracing/utils/generic.py,sha256=WwXT01xmp8MSr7KinuDCSK9a1ifpLcT7ajFkvYviG_A,1190
48
48
  ragaai_catalyst/tracers/agentic_tracing/utils/get_user_trace_metrics.py,sha256=vPZ4dn4EHFW0kqd1GyRpsYXbfrRrd0DXCmh-pzsDBNE,1109
49
49
  ragaai_catalyst/tracers/agentic_tracing/utils/llm_utils.py,sha256=wlXCuaRe81s-7FWdJ_MquXFGRZZfNrZxLIIxl-Ohbqk,15541
50
- ragaai_catalyst/tracers/agentic_tracing/utils/model_costs.json,sha256=weznWpRd6N5X-O8lkzVA6nd5LSYJKa62s3GZL51NQpU,293833
50
+ ragaai_catalyst/tracers/agentic_tracing/utils/model_costs.json,sha256=GXV1s349reRMpYF_EkK-b6peSb4SY-17WnlkvpuQ4sM,294430
51
51
  ragaai_catalyst/tracers/agentic_tracing/utils/span_attributes.py,sha256=MqeRNGxzeuh9qTK0NbYMftl9V9Z0V7gMgBoHkrXP56k,1592
52
52
  ragaai_catalyst/tracers/agentic_tracing/utils/trace_utils.py,sha256=RciiDdo2riibEoM8X0FKHaXi78y3bWwNkV8U0leqigk,3508
53
53
  ragaai_catalyst/tracers/agentic_tracing/utils/unique_decorator.py,sha256=DQHjcEuqEKsNSWaNs7SoOaq50yK4Jsl966S7mBnV-zA,5723
@@ -61,8 +61,8 @@ ragaai_catalyst/tracers/instrumentators/llamaindex.py,sha256=SMrRlR4xM7k9HK43hak
61
61
  ragaai_catalyst/tracers/instrumentators/openai.py,sha256=14R4KW9wQCR1xysLfsP_nxS7cqXrTPoD8En4MBAaZUU,379
62
62
  ragaai_catalyst/tracers/utils/__init__.py,sha256=KeMaZtYaTojilpLv65qH08QmpYclfpacDA0U3wg6Ybw,64
63
63
  ragaai_catalyst/tracers/utils/utils.py,sha256=ViygfJ7vZ7U0CTSA1lbxVloHp4NSlmfDzBRNCJuMhis,2374
64
- ragaai_catalyst-2.1.5b0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
65
- ragaai_catalyst-2.1.5b0.dist-info/METADATA,sha256=R0r0eV49WTeIO0O1NlkFk99eIbAPnvqf0urcA-GpF48,12764
66
- ragaai_catalyst-2.1.5b0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
67
- ragaai_catalyst-2.1.5b0.dist-info/top_level.txt,sha256=HpgsdRgEJMk8nqrU6qdCYk3di7MJkDL0B19lkc7dLfM,16
68
- ragaai_catalyst-2.1.5b0.dist-info/RECORD,,
64
+ ragaai_catalyst-2.1.5b1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
65
+ ragaai_catalyst-2.1.5b1.dist-info/METADATA,sha256=raEM7cStptlPTu7ph3OyOU7UanQ3_7-T9dQbFXlFMbs,12764
66
+ ragaai_catalyst-2.1.5b1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
67
+ ragaai_catalyst-2.1.5b1.dist-info/top_level.txt,sha256=HpgsdRgEJMk8nqrU6qdCYk3di7MJkDL0B19lkc7dLfM,16
68
+ ragaai_catalyst-2.1.5b1.dist-info/RECORD,,