ragaai-catalyst 2.1.5b25__py3-none-any.whl → 2.1.5b27__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.
@@ -13,8 +13,12 @@ import traceback
13
13
  import importlib
14
14
  import sys
15
15
  from litellm import model_cost
16
- from llama_index.core.base.llms.types import ChatResponse
16
+ import logging
17
17
 
18
+ try:
19
+ from llama_index.core.base.llms.types import ChatResponse,TextBlock, ChatMessage
20
+ except ImportError:
21
+ logging.warning("Failed to import ChatResponse, TextBlock, ChatMessage. Some features from llamaindex may not work. Please upgrade to the latest version of llama_index or version (>=0.12)")
18
22
  from .base import BaseTracer
19
23
  from ..utils.llm_utils import (
20
24
  extract_model_name,
@@ -30,7 +34,6 @@ from ..utils.llm_utils import (
30
34
  from ..utils.unique_decorator import generate_unique_hash
31
35
  from ..utils.file_name_tracker import TrackName
32
36
  from ..utils.span_attributes import SpanAttributes
33
- import logging
34
37
 
35
38
  logger = logging.getLogger(__name__)
36
39
  logging_level = (
@@ -550,138 +553,132 @@ class LLMTracerMixin:
550
553
  error=None,
551
554
  parameters={},
552
555
  ):
553
- # Update total metrics
554
- self.total_tokens += usage.get("total_tokens", 0)
555
- self.total_cost += cost.get("total_cost", 0)
556
-
557
- network_calls = []
558
- if self.auto_instrument_network:
559
- network_calls = self.component_network_calls.get(component_id, [])
560
-
561
- interactions = []
562
- if self.auto_instrument_user_interaction:
563
- input_output_interactions = []
564
- for interaction in self.component_user_interaction.get(component_id, []):
565
- if interaction["interaction_type"] in ["input", "output"]:
566
- input_output_interactions.append(interaction)
567
- interactions.extend(input_output_interactions)
568
- if self.auto_instrument_file_io:
569
- file_io_interactions = []
570
- for interaction in self.component_user_interaction.get(component_id, []):
571
- if interaction["interaction_type"] in ["file_read", "file_write"]:
572
- file_io_interactions.append(interaction)
573
- interactions.extend(file_io_interactions)
574
-
575
- parameters_to_display = {}
576
- if "run_manager" in parameters:
577
- parameters_obj = parameters["run_manager"]
578
- if hasattr(parameters_obj, "metadata"):
579
- metadata = parameters_obj.metadata
580
- # parameters = {'metadata': metadata}
581
- parameters_to_display.update(metadata)
582
-
583
- # Add only those keys in parameters that are single values and not objects, dict or list
584
- for key, value in parameters.items():
585
- if isinstance(value, (str, int, float, bool)):
586
- parameters_to_display[key] = value
587
-
588
- # Limit the number of parameters to display
589
- parameters_to_display = dict(
590
- list(parameters_to_display.items())[: self.MAX_PARAMETERS_TO_DISPLAY]
591
- )
556
+ try:
557
+ # Update total metrics
558
+ self.total_tokens += usage.get("total_tokens", 0)
559
+ self.total_cost += cost.get("total_cost", 0)
560
+
561
+ network_calls = []
562
+ if self.auto_instrument_network:
563
+ network_calls = self.component_network_calls.get(component_id, [])
564
+
565
+ interactions = []
566
+ if self.auto_instrument_user_interaction:
567
+ input_output_interactions = []
568
+ for interaction in self.component_user_interaction.get(component_id, []):
569
+ if interaction["interaction_type"] in ["input", "output"]:
570
+ input_output_interactions.append(interaction)
571
+ interactions.extend(input_output_interactions)
572
+ if self.auto_instrument_file_io:
573
+ file_io_interactions = []
574
+ for interaction in self.component_user_interaction.get(component_id, []):
575
+ if interaction["interaction_type"] in ["file_read", "file_write"]:
576
+ file_io_interactions.append(interaction)
577
+ interactions.extend(file_io_interactions)
578
+
579
+ parameters_to_display = {}
580
+ if "run_manager" in parameters:
581
+ parameters_obj = parameters["run_manager"]
582
+ if hasattr(parameters_obj, "metadata"):
583
+ metadata = parameters_obj.metadata
584
+ # parameters = {'metadata': metadata}
585
+ parameters_to_display.update(metadata)
586
+
587
+ # Add only those keys in parameters that are single values and not objects, dict or list
588
+ for key, value in parameters.items():
589
+ if isinstance(value, (str, int, float, bool)):
590
+ parameters_to_display[key] = value
591
+
592
+ # Limit the number of parameters to display
593
+ parameters_to_display = dict(
594
+ list(parameters_to_display.items())[: self.MAX_PARAMETERS_TO_DISPLAY]
595
+ )
596
+
597
+ # Set the Context and GT
598
+ span_gt = None
599
+ span_context = None
600
+ if name in self.span_attributes_dict:
601
+ span_gt = self.span_attributes_dict[name].gt
602
+ span_context = self.span_attributes_dict[name].context
603
+
604
+ logger.debug(f"span context {span_context}, span_gt {span_gt}")
605
+
606
+ # Tags
607
+ tags = []
608
+ if name in self.span_attributes_dict:
609
+ tags = self.span_attributes_dict[name].tags or []
610
+
611
+ # Get End Time
612
+ end_time = datetime.now().astimezone().isoformat()
613
+
614
+ # Metrics
615
+ metrics = []
616
+ if name in self.span_attributes_dict:
617
+ raw_metrics = self.span_attributes_dict[name].metrics or []
618
+ for metric in raw_metrics:
619
+ base_metric_name = metric["name"]
620
+ counter = sum(1 for x in self.visited_metrics if x.startswith(base_metric_name))
621
+ metric_name = f'{base_metric_name}_{counter}' if counter > 0 else base_metric_name
622
+ self.visited_metrics.append(metric_name)
623
+ metric["name"] = metric_name
624
+ metrics.append(metric)
625
+
626
+ # TODO TO check i/p and o/p is according or not
627
+ input = input_data["args"] if hasattr(input_data, "args") else input_data
628
+ output = output_data.output_response if output_data else None
629
+ #print("Prompt input:",input)
630
+ prompt = self.convert_to_content(input)
631
+ #print("Prompt Output: ",prompt)
632
+ #print("Response input: ",output)
633
+ response = self.convert_to_content(output)
634
+ #print("Response output: ",response)
635
+
636
+ # TODO: Execute & Add the User requested metrics here
637
+ formatted_metrics = BaseTracer.get_formatted_metric(self.span_attributes_dict, self.project_id, name)
638
+ if formatted_metrics:
639
+ metrics.extend(formatted_metrics)
640
+
641
+ component = {
642
+ "id": component_id,
643
+ "hash_id": hash_id,
644
+ "source_hash_id": None,
645
+ "type": "llm",
646
+ "name": name,
647
+ "start_time": start_time,
648
+ "end_time": end_time,
649
+ "error": error,
650
+ "parent_id": self.current_agent_id.get(),
651
+ "info": {
652
+ "model": llm_type,
653
+ "version": version,
654
+ "memory_used": memory_used,
655
+ "cost": cost,
656
+ "tokens": usage,
657
+ "tags": tags,
658
+ **parameters_to_display,
659
+ },
660
+ "extra_info": parameters,
661
+ "data": {
662
+ "input": input,
663
+ "output": output,
664
+ "memory_used": memory_used,
665
+ },
666
+ "metrics": metrics,
667
+ "network_calls": network_calls,
668
+ "interactions": interactions,
669
+ }
670
+
671
+ # Assign context and gt if available
672
+ component["data"]["gt"] = span_gt
673
+ component["data"]["context"] = span_context
674
+
675
+ # Reset the SpanAttributes context variable
676
+ self.span_attributes_dict[name] = SpanAttributes(name)
677
+
678
+ return component
679
+ except Exception as e:
680
+ raise Exception("Failed to create LLM component")
592
681
 
593
- # Set the Context and GT
594
- span_gt = None
595
- span_context = None
596
- if name in self.span_attributes_dict:
597
- span_gt = self.span_attributes_dict[name].gt
598
- span_context = self.span_attributes_dict[name].context
599
-
600
- logger.debug(f"span context {span_context}, span_gt {span_gt}")
601
-
602
- # Tags
603
- tags = []
604
- if name in self.span_attributes_dict:
605
- tags = self.span_attributes_dict[name].tags or []
606
-
607
- # Get End Time
608
- end_time = datetime.now().astimezone().isoformat()
609
-
610
- # Metrics
611
- metrics = []
612
- if name in self.span_attributes_dict:
613
- raw_metrics = self.span_attributes_dict[name].metrics or []
614
- for metric in raw_metrics:
615
- base_metric_name = metric["name"]
616
- counter = sum(1 for x in self.visited_metrics if x.startswith(base_metric_name))
617
- metric_name = f'{base_metric_name}_{counter}' if counter > 0 else base_metric_name
618
- self.visited_metrics.append(metric_name)
619
- metric["name"] = metric_name
620
- metrics.append(metric)
621
-
622
- # TODO TO check i/p and o/p is according or not
623
- input = input_data["args"] if hasattr(input_data, "args") else input_data
624
- output = output_data.output_response if output_data else None
625
- #print("Prompt input:",input)
626
- prompt = self.convert_to_content(input)
627
- #print("Prompt Output: ",prompt)
628
- #print("Response input: ",output)
629
- response = self.convert_to_content(output)
630
- #print("Response output: ",response)
631
-
632
- # TODO: Execute & Add the User requested metrics here
633
- formatted_metrics = BaseTracer.get_formatted_metric(self.span_attributes_dict, self.project_id, name)
634
- if formatted_metrics:
635
- metrics.extend(formatted_metrics)
636
-
637
- component = {
638
- "id": component_id,
639
- "hash_id": hash_id,
640
- "source_hash_id": None,
641
- "type": "llm",
642
- "name": name,
643
- "start_time": start_time,
644
- "end_time": end_time,
645
- "error": error,
646
- "parent_id": self.current_agent_id.get(),
647
- "info": {
648
- "model": llm_type,
649
- "version": version,
650
- "memory_used": memory_used,
651
- "cost": cost,
652
- "tokens": usage,
653
- "tags": tags,
654
- **parameters_to_display,
655
- },
656
- "extra_info": parameters,
657
- "data": {
658
- "input": input,
659
- "output": output,
660
- "memory_used": memory_used,
661
- },
662
- "metrics": metrics,
663
- "network_calls": network_calls,
664
- "interactions": interactions,
665
- }
666
-
667
- # Assign context and gt if available
668
- component["data"]["gt"] = span_gt
669
- component["data"]["context"] = span_context
670
-
671
- # Reset the SpanAttributes context variable
672
- self.span_attributes_dict[name] = SpanAttributes(name)
673
-
674
- return component
675
-
676
- # def convert_to_content(self, input_data):
677
- # if isinstance(input_data, dict):
678
- # messages = input_data.get("kwargs", {}).get("messages", [])
679
- # elif isinstance(input_data, list):
680
- # messages = input_data
681
- # else:
682
- # return ""
683
- # return "\n".join(process_content(msg.get("content", "")) for msg in messages if msg.get("content"))
684
-
685
682
  def convert_to_content(self, input_data):
686
683
  try:
687
684
  if isinstance(input_data, dict):
@@ -689,7 +686,6 @@ class LLMTracerMixin:
689
686
  elif isinstance(input_data, list):
690
687
  if len(input_data)>0 and isinstance(input_data[0]['content'],ChatResponse):
691
688
  extracted_messages = []
692
-
693
689
  for item in input_data:
694
690
  chat_response = item.get('content')
695
691
  if hasattr(chat_response, 'message') and hasattr(chat_response.message, 'blocks'):
@@ -699,9 +695,10 @@ class LLMTracerMixin:
699
695
  messages=extracted_messages
700
696
  if isinstance(messages,list):
701
697
  return "\n".join(messages)
702
-
703
- #messages=[msg["content"] for msg in input_data if isinstance(msg, dict) and "content" in msg]
704
- #messages = [msg["content"].message for msg in input_data if isinstance(msg, dict) and "content" in msg and isinstance(msg["content"], ChatResponse)]
698
+ elif len(input_data)>0 and isinstance(input_data[0]['content'],TextBlock):
699
+ return " ".join(block.text for item in input_data for block in item['content'] if isinstance(block, TextBlock))
700
+ elif len(input_data)>0 and isinstance(input_data[0]['content'],ChatMessage):
701
+ return " ".join(block.text for block in input_data[0]['content'].blocks if isinstance(block, TextBlock))
705
702
  else:
706
703
  messages = input_data
707
704
  elif isinstance(input_data,ChatResponse):
@@ -709,10 +706,9 @@ class LLMTracerMixin:
709
706
  else:
710
707
  return ""
711
708
  res=""
712
- # try:
713
709
  res="\n".join(msg.get("content", "").strip() for msg in messages if msg.get("content"))
714
710
  except Exception as e:
715
- res=str(messages)
711
+ res=str(input_data)
716
712
  return res
717
713
 
718
714
  def process_content(content):
@@ -965,6 +961,10 @@ class LLMTracerMixin:
965
961
  metrics: List[Dict[str, Any]] = [],
966
962
  feedback: Optional[Any] = None,
967
963
  ):
964
+
965
+ start_memory = psutil.Process().memory_info().rss
966
+ start_time = datetime.now().astimezone().isoformat()
967
+
968
968
  if name not in self.span_attributes_dict:
969
969
  self.span_attributes_dict[name] = SpanAttributes(name)
970
970
  if tags:
@@ -996,7 +996,6 @@ class LLMTracerMixin:
996
996
  self.current_llm_call_name.set(name)
997
997
 
998
998
  def decorator(func):
999
- @self.file_tracker.trace_decorator
1000
999
  @functools.wraps(func)
1001
1000
  async def async_wrapper(*args, **kwargs):
1002
1001
  gt = kwargs.get("gt") if kwargs else None
@@ -1018,14 +1017,34 @@ class LLMTracerMixin:
1018
1017
  result = await func(*args, **kwargs)
1019
1018
  return result
1020
1019
  except Exception as e:
1021
- error_info = {
1022
- "error": {
1023
- "type": type(e).__name__,
1024
- "message": str(e),
1025
- "traceback": traceback.format_exc(),
1026
- "timestamp": datetime.now().astimezone().isoformat(),
1027
- }
1020
+ error_component = {
1021
+ "type": type(e).__name__,
1022
+ "message": str(e),
1023
+ "traceback": traceback.format_exc(),
1024
+ "timestamp": datetime.now().astimezone().isoformat(),
1028
1025
  }
1026
+
1027
+ # End tracking network calls for this component
1028
+ self.end_component(component_id)
1029
+
1030
+ end_memory = psutil.Process().memory_info().rss
1031
+ memory_used = max(0, end_memory - start_memory)
1032
+
1033
+ llm_component = self.create_llm_component(
1034
+ component_id=component_id,
1035
+ hash_id=generate_unique_hash(func, args, kwargs),
1036
+ name=name,
1037
+ llm_type="unknown",
1038
+ version=None,
1039
+ memory_used=memory_used,
1040
+ start_time=start_time,
1041
+ input_data=extract_input_data(args, kwargs, None),
1042
+ output_data=None,
1043
+ error=error_component,
1044
+ )
1045
+ self.llm_data = llm_component
1046
+ self.add_component(llm_component, is_error=True)
1047
+
1029
1048
  raise
1030
1049
  finally:
1031
1050
 
@@ -1070,7 +1089,6 @@ class LLMTracerMixin:
1070
1089
  )
1071
1090
  self.add_component(llm_component)
1072
1091
 
1073
- @self.file_tracker.trace_decorator
1074
1092
  @functools.wraps(func)
1075
1093
  def sync_wrapper(*args, **kwargs):
1076
1094
  gt = kwargs.get("gt") if kwargs else None
@@ -1093,14 +1111,34 @@ class LLMTracerMixin:
1093
1111
  result = func(*args, **kwargs)
1094
1112
  return result
1095
1113
  except Exception as e:
1096
- error_info = {
1097
- "error": {
1098
- "type": type(e).__name__,
1099
- "message": str(e),
1100
- "traceback": traceback.format_exc(),
1101
- "timestamp": datetime.now().astimezone().isoformat(),
1102
- }
1114
+ error_component = {
1115
+ "type": type(e).__name__,
1116
+ "message": str(e),
1117
+ "traceback": traceback.format_exc(),
1118
+ "timestamp": datetime.now().astimezone().isoformat(),
1103
1119
  }
1120
+
1121
+ # End tracking network calls for this component
1122
+ self.end_component(component_id)
1123
+
1124
+ end_memory = psutil.Process().memory_info().rss
1125
+ memory_used = max(0, end_memory - start_memory)
1126
+
1127
+ llm_component = self.create_llm_component(
1128
+ component_id=component_id,
1129
+ hash_id=generate_unique_hash(func, args, kwargs),
1130
+ name=name,
1131
+ llm_type="unknown",
1132
+ version=None,
1133
+ memory_used=memory_used,
1134
+ start_time=start_time,
1135
+ input_data=extract_input_data(args, kwargs, None),
1136
+ output_data=None,
1137
+ error=error_component,
1138
+ )
1139
+ self.llm_data = llm_component
1140
+ self.add_component(llm_component, is_error=True)
1141
+
1104
1142
  raise
1105
1143
  finally:
1106
1144
  llm_component = self.llm_data
@@ -236,6 +236,8 @@ class AgenticTracing(
236
236
  total_cost = 0.0
237
237
  total_tokens = 0
238
238
 
239
+ processed_components = set()
240
+
239
241
  def process_component(component):
240
242
  nonlocal total_cost, total_tokens
241
243
  # Convert component to dict if it's an object
@@ -243,6 +245,11 @@ class AgenticTracing(
243
245
  component.__dict__ if hasattr(component, "__dict__") else component
244
246
  )
245
247
 
248
+ comp_id = comp_dict.get("id") or comp_dict.get("component_id")
249
+ if comp_id in processed_components:
250
+ return # Skip if already processed
251
+ processed_components.add(comp_id)
252
+
246
253
  if comp_dict.get("type") == "llm":
247
254
  info = comp_dict.get("info", {})
248
255
  if isinstance(info, dict):
@@ -372,66 +379,6 @@ class AgenticTracing(
372
379
 
373
380
  # Handle error case
374
381
  if is_error:
375
- # Get the parent component if it exists
376
- parent_id = component_data.get("parent_id")
377
- children = self.agent_children.get()
378
-
379
- # Set parent_id for all children
380
- for child in children:
381
- child["parent_id"] = parent_id
382
-
383
- agent_tracer_mixin = AgentTracerMixin()
384
- agent_tracer_mixin.component_network_calls = self.component_network_calls
385
- agent_tracer_mixin.component_user_interaction = (
386
- self.component_user_interaction
387
- )
388
-
389
- agent_tracer_mixin.span_attributes_dict[self.current_agent_name.get()] = (
390
- SpanAttributes(self.current_agent_name.get())
391
- )
392
-
393
- # Create parent component with error info
394
- parent_component = agent_tracer_mixin.create_agent_component(
395
- component_id=parent_id,
396
- hash_id=str(uuid.uuid4()),
397
- source_hash_id=None,
398
- type="agent",
399
- name=self.current_agent_name.get(),
400
- agent_type=self.agent_type.get(),
401
- version=self.version.get(),
402
- capabilities=self.capabilities.get(),
403
- start_time=self.start_time,
404
- end_time=datetime.now().astimezone().isoformat(),
405
- memory_used=0,
406
- input_data=self.input_data,
407
- output_data=None,
408
- children=children,
409
- parent_id=None, # Add parent ID if exists
410
- )
411
-
412
- filtered_data = {
413
- k: v
414
- for k, v in parent_component.items()
415
- if k
416
- in [
417
- "id",
418
- "hash_id",
419
- "source_hash_id",
420
- "type",
421
- "name",
422
- "start_time",
423
- "end_time",
424
- "parent_id",
425
- "info",
426
- "data",
427
- "network_calls",
428
- "interactions",
429
- "error",
430
- ]
431
- }
432
- parent_agent_component = AgentComponent(**filtered_data)
433
- # Add the parent component to trace and stop tracing
434
- super().add_component(parent_agent_component)
435
382
  self.stop()
436
383
 
437
384
  def __enter__(self):
@@ -255,7 +255,6 @@ class ToolTracerMixin:
255
255
  # Check if the function is async
256
256
  is_async = asyncio.iscoroutinefunction(func)
257
257
 
258
- @self.file_tracker.trace_decorator
259
258
  @functools.wraps(func)
260
259
  async def async_wrapper(*args, **kwargs):
261
260
  async_wrapper.metadata = metadata
@@ -267,7 +266,6 @@ class ToolTracerMixin:
267
266
  func, name, tool_type, version, *args, **kwargs
268
267
  )
269
268
 
270
- @self.file_tracker.trace_decorator
271
269
  @functools.wraps(func)
272
270
  def sync_wrapper(*args, **kwargs):
273
271
  sync_wrapper.metadata = metadata
@@ -309,7 +307,7 @@ class ToolTracerMixin:
309
307
 
310
308
  try:
311
309
  # Execute the tool
312
- result = self.file_tracker.trace_wrapper(func)(*args, **kwargs)
310
+ result = func(*args, **kwargs)
313
311
 
314
312
  # Calculate resource usage
315
313
  end_memory = psutil.Process().memory_info().rss
@@ -359,7 +357,7 @@ class ToolTracerMixin:
359
357
  error=error_component,
360
358
  )
361
359
 
362
- self.add_component(tool_component)
360
+ self.add_component(tool_component, is_error=True)
363
361
 
364
362
  raise
365
363
  finally:
@@ -391,7 +389,7 @@ class ToolTracerMixin:
391
389
  self.start_component(component_id)
392
390
  try:
393
391
  # Execute the tool
394
- result = await self.file_tracker.trace_wrapper(func)(*args, **kwargs)
392
+ result = await func(*args, **kwargs)
395
393
 
396
394
  # Calculate resource usage
397
395
  end_memory = psutil.Process().memory_info().rss
@@ -434,7 +432,7 @@ class ToolTracerMixin:
434
432
  output_data=None,
435
433
  error=error_component,
436
434
  )
437
- self.add_component(tool_component)
435
+ self.add_component(tool_component, is_error=True)
438
436
 
439
437
  raise
440
438
  finally:
@@ -62,4 +62,8 @@ class TrackName:
62
62
 
63
63
  def reset(self):
64
64
  """Reset the file tracker by clearing all tracked files."""
65
- self.files.clear()
65
+ self.files.clear()
66
+
67
+ def trace_main_file(self):
68
+ frame = inspect.stack()[-1]
69
+ self.files.add(frame.filename)