ragaai-catalyst 2.1.5b21__py3-none-any.whl → 2.1.5b23__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.
Files changed (27) hide show
  1. ragaai_catalyst/__init__.py +3 -1
  2. ragaai_catalyst/dataset.py +49 -1
  3. ragaai_catalyst/redteaming.py +171 -0
  4. ragaai_catalyst/synthetic_data_generation.py +40 -7
  5. ragaai_catalyst/tracers/agentic_tracing/tracers/agent_tracer.py +57 -46
  6. ragaai_catalyst/tracers/agentic_tracing/tracers/base.py +218 -47
  7. ragaai_catalyst/tracers/agentic_tracing/tracers/custom_tracer.py +17 -7
  8. ragaai_catalyst/tracers/agentic_tracing/tracers/llm_tracer.py +327 -62
  9. ragaai_catalyst/tracers/agentic_tracing/tracers/main_tracer.py +0 -3
  10. ragaai_catalyst/tracers/agentic_tracing/tracers/tool_tracer.py +17 -6
  11. ragaai_catalyst/tracers/agentic_tracing/upload/upload_local_metric.py +72 -0
  12. ragaai_catalyst/tracers/agentic_tracing/upload/upload_trace_metric.py +32 -15
  13. ragaai_catalyst/tracers/agentic_tracing/utils/file_name_tracker.py +21 -2
  14. ragaai_catalyst/tracers/agentic_tracing/utils/llm_utils.py +33 -11
  15. ragaai_catalyst/tracers/agentic_tracing/utils/model_costs.json +1204 -484
  16. ragaai_catalyst/tracers/agentic_tracing/utils/span_attributes.py +79 -10
  17. ragaai_catalyst/tracers/agentic_tracing/utils/trace_utils.py +0 -32
  18. ragaai_catalyst/tracers/agentic_tracing/utils/unique_decorator.py +3 -1
  19. ragaai_catalyst/tracers/agentic_tracing/utils/zip_list_of_unique_files.py +40 -21
  20. ragaai_catalyst/tracers/distributed.py +7 -3
  21. ragaai_catalyst/tracers/tracer.py +9 -9
  22. ragaai_catalyst/tracers/utils/langchain_tracer_extraction_logic.py +0 -1
  23. {ragaai_catalyst-2.1.5b21.dist-info → ragaai_catalyst-2.1.5b23.dist-info}/METADATA +37 -2
  24. {ragaai_catalyst-2.1.5b21.dist-info → ragaai_catalyst-2.1.5b23.dist-info}/RECORD +27 -25
  25. {ragaai_catalyst-2.1.5b21.dist-info → ragaai_catalyst-2.1.5b23.dist-info}/LICENSE +0 -0
  26. {ragaai_catalyst-2.1.5b21.dist-info → ragaai_catalyst-2.1.5b23.dist-info}/WHEEL +0 -0
  27. {ragaai_catalyst-2.1.5b21.dist-info → ragaai_catalyst-2.1.5b23.dist-info}/top_level.txt +0 -0
@@ -258,7 +258,10 @@ class ToolTracerMixin:
258
258
  @functools.wraps(func)
259
259
  async def async_wrapper(*args, **kwargs):
260
260
  async_wrapper.metadata = metadata
261
- self.gt = kwargs.get("gt", None) if kwargs else None
261
+ gt = kwargs.get("gt") if kwargs else None
262
+ if gt is not None:
263
+ span = self.span(name)
264
+ span.add_gt(gt)
262
265
  return await self._trace_tool_execution(
263
266
  func, name, tool_type, version, *args, **kwargs
264
267
  )
@@ -267,7 +270,10 @@ class ToolTracerMixin:
267
270
  @functools.wraps(func)
268
271
  def sync_wrapper(*args, **kwargs):
269
272
  sync_wrapper.metadata = metadata
270
- self.gt = kwargs.get("gt", None) if kwargs else None
273
+ gt = kwargs.get("gt") if kwargs else None
274
+ if gt is not None:
275
+ span = self.span(name)
276
+ span.add_gt(gt)
271
277
  return self._trace_sync_tool_execution(
272
278
  func, name, tool_type, version, *args, **kwargs
273
279
  )
@@ -302,7 +308,7 @@ class ToolTracerMixin:
302
308
 
303
309
  try:
304
310
  # Execute the tool
305
- result = func(*args, **kwargs)
311
+ result = self.file_tracker.trace_wrapper(func)(*args, **kwargs)
306
312
 
307
313
  # Calculate resource usage
308
314
  end_memory = psutil.Process().memory_info().rss
@@ -384,7 +390,7 @@ class ToolTracerMixin:
384
390
  self.start_component(component_id)
385
391
  try:
386
392
  # Execute the tool
387
- result = await func(*args, **kwargs)
393
+ result = await self.file_tracker.trace_wrapper(func)(*args, **kwargs)
388
394
 
389
395
  # Calculate resource usage
390
396
  end_memory = psutil.Process().memory_info().rss
@@ -504,8 +510,13 @@ class ToolTracerMixin:
504
510
  "interactions": interactions,
505
511
  }
506
512
 
507
- if self.gt:
508
- component["data"]["gt"] = self.gt
513
+ if name in self.span_attributes_dict:
514
+ span_gt = self.span_attributes_dict[name].gt
515
+ if span_gt is not None:
516
+ component["data"]["gt"] = span_gt
517
+ span_context = self.span_attributes_dict[name].context
518
+ if span_context:
519
+ component["data"]["context"] = span_context
509
520
 
510
521
  # Reset the SpanAttributes context variable
511
522
  self.span_attributes_dict[kwargs["name"]] = SpanAttributes(kwargs["name"])
@@ -0,0 +1,72 @@
1
+ import logging
2
+ import os
3
+ import requests
4
+
5
+ from ragaai_catalyst import RagaAICatalyst
6
+
7
+ logger = logging.getLogger(__name__)
8
+ logging_level = (
9
+ logger.setLevel(logging.DEBUG)
10
+ if os.getenv("DEBUG")
11
+ else logger.setLevel(logging.INFO)
12
+ )
13
+
14
+ def calculate_metric(project_id, metric_name, model, provider, prompt, response, context, expected_response=None):
15
+ user_id = "1"
16
+ org_domain = "raga"
17
+
18
+ headers = {
19
+ "Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
20
+ "X-Project-Id": str(project_id), # TODO to change it to project_id
21
+ "Content-Type": "application/json"
22
+ }
23
+
24
+ payload = {
25
+ "data": [
26
+ {
27
+ "metric_name": metric_name,
28
+ "metric_config": {
29
+ "threshold": {
30
+ "isEditable": True,
31
+ "lte": 0.3
32
+ },
33
+ "model": model,
34
+ "orgDomain": org_domain,
35
+ "provider": provider,
36
+ "user_id": user_id,
37
+ "job_id": 1,
38
+ "metric_name": metric_name,
39
+ "request_id": 1
40
+ },
41
+ "trace_object": {
42
+ "Data": {
43
+ "DocId": "doc-1",
44
+ "Prompt": prompt,
45
+ "Response": response,
46
+ "Context": context,
47
+ "ExpectedResponse": "",
48
+ "ExpectedContext": expected_response,
49
+ "Chat": "",
50
+ "Instructions": "",
51
+ "SystemPrompt": "",
52
+ "Text": ""
53
+ },
54
+ "claims": {},
55
+ "last_computed_metrics": {
56
+ metric_name: {
57
+ }
58
+ }
59
+ }
60
+ }
61
+ ]
62
+ }
63
+
64
+ try:
65
+ BASE_URL = RagaAICatalyst.BASE_URL
66
+ response = requests.post(f"{BASE_URL}/v1/llm/calculate-metric", headers=headers, json=payload, timeout=30)
67
+ logger.debug(f"Metric calculation response status {response.status_code}")
68
+ response.raise_for_status()
69
+ return response.json()
70
+ except requests.exceptions.RequestException as e:
71
+ logger.debug(f"Error in calculate-metric api: {e}, payload: {payload}")
72
+ raise Exception(f"Error in calculate-metric: {e}")
@@ -1,27 +1,39 @@
1
+ import logging
2
+
1
3
  import requests
2
4
  import os
3
5
  import json
4
6
  from ....ragaai_catalyst import RagaAICatalyst
5
7
  from ..utils.get_user_trace_metrics import get_user_trace_metrics
6
8
 
9
+ logger = logging.getLogger(__name__)
10
+ logging_level = (
11
+ logger.setLevel(logging.DEBUG)
12
+ if os.getenv("DEBUG")
13
+ else logger.setLevel(logging.INFO)
14
+ )
15
+
16
+
7
17
  def upload_trace_metric(json_file_path, dataset_name, project_name):
8
18
  try:
9
19
  with open(json_file_path, "r") as f:
10
20
  traces = json.load(f)
21
+
11
22
  metrics = get_trace_metrics_from_trace(traces)
12
23
  metrics = _change_metrics_format_for_payload(metrics)
13
24
 
14
25
  user_trace_metrics = get_user_trace_metrics(project_name, dataset_name)
15
26
  if user_trace_metrics:
16
27
  user_trace_metrics_list = [metric["displayName"] for metric in user_trace_metrics]
17
-
28
+
18
29
  if user_trace_metrics:
19
30
  for metric in metrics:
20
31
  if metric["displayName"] in user_trace_metrics_list:
21
- metricConfig = next((user_metric["metricConfig"] for user_metric in user_trace_metrics if user_metric["displayName"] == metric["displayName"]), None)
32
+ metricConfig = next((user_metric["metricConfig"] for user_metric in user_trace_metrics if
33
+ user_metric["displayName"] == metric["displayName"]), None)
22
34
  if not metricConfig or metricConfig.get("Metric Source", {}).get("value") != "user":
23
- raise ValueError(f"Metrics {metric['displayName']} already exist in dataset {dataset_name} of project {project_name}.")
24
-
35
+ raise ValueError(
36
+ f"Metrics {metric['displayName']} already exist in dataset {dataset_name} of project {project_name}.")
25
37
  headers = {
26
38
  "Content-Type": "application/json",
27
39
  "Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
@@ -31,9 +43,9 @@ def upload_trace_metric(json_file_path, dataset_name, project_name):
31
43
  "datasetName": dataset_name,
32
44
  "metrics": metrics
33
45
  })
34
- response = requests.request("POST",
35
- f"{RagaAICatalyst.BASE_URL}/v1/llm/trace/metrics",
36
- headers=headers,
46
+ response = requests.request("POST",
47
+ f"{RagaAICatalyst.BASE_URL}/v1/llm/trace/metrics",
48
+ headers=headers,
37
49
  data=payload,
38
50
  timeout=10)
39
51
  if response.status_code != 200:
@@ -68,23 +80,28 @@ def get_trace_metrics_from_trace(traces):
68
80
  # get span level metrics
69
81
  for span in traces["data"][0]["spans"]:
70
82
  if span["type"] == "agent":
83
+ # Add children metrics of agent
71
84
  children_metric = _get_children_metrics_of_agent(span["data"]["children"])
72
85
  if children_metric:
73
86
  metrics.extend(children_metric)
74
- else:
75
- metric = span.get("metrics", [])
76
- if metric:
77
- metrics.extend(metric)
87
+
88
+ metric = span.get("metrics", [])
89
+ if metric:
90
+ metrics.extend(metric)
78
91
  return metrics
79
92
 
93
+
80
94
  def _change_metrics_format_for_payload(metrics):
81
95
  formatted_metrics = []
82
96
  for metric in metrics:
83
- if any(m["name"] == metric["name"] for m in formatted_metrics):
97
+ if any(m["name"] == metric.get("displayName") or m['name'] == metric.get("name") for m in formatted_metrics):
84
98
  continue
99
+ metric_display_name = metric["name"]
100
+ if metric.get("displayName"):
101
+ metric_display_name = metric['displayName']
85
102
  formatted_metrics.append({
86
- "name": metric["name"],
87
- "displayName": metric["name"],
103
+ "name": metric_display_name,
104
+ "displayName": metric_display_name,
88
105
  "config": {"source": "user"},
89
106
  })
90
- return formatted_metrics
107
+ return formatted_metrics
@@ -8,13 +8,32 @@ class TrackName:
8
8
  def trace_decorator(self, func):
9
9
  @wraps(func)
10
10
  def wrapper(*args, **kwargs):
11
- file_name = self._get_file_name()
11
+ file_name = self._get_decorated_file_name()
12
12
  self.files.add(file_name)
13
13
 
14
14
  return func(*args, **kwargs)
15
15
  return wrapper
16
+
17
+ def trace_wrapper(self, func):
18
+ @wraps(func)
19
+ def wrapper(*args, **kwargs):
20
+ file_name = self._get_wrapped_file_name()
21
+ self.files.add(file_name)
22
+ return func(*args, **kwargs)
23
+ return wrapper
24
+
25
+ def _get_wrapped_file_name(self):
26
+ try:
27
+ from IPython import get_ipython
28
+ if 'IPKernelApp' in get_ipython().config:
29
+ return self._get_notebook_name()
30
+ except Exception:
31
+ pass
32
+
33
+ frame = inspect.stack()[4]
34
+ return frame.filename
16
35
 
17
- def _get_file_name(self):
36
+ def _get_decorated_file_name(self):
18
37
  # Check if running in a Jupyter notebook
19
38
  try:
20
39
  from IPython import get_ipython
@@ -2,9 +2,9 @@ from ..data.data_structure import LLMCall
2
2
  from .trace_utils import (
3
3
  calculate_cost,
4
4
  convert_usage_to_dict,
5
- load_model_costs,
6
5
  )
7
6
  from importlib import resources
7
+ from litellm import model_cost
8
8
  import json
9
9
  import os
10
10
  import asyncio
@@ -38,7 +38,13 @@ def extract_model_name(args, kwargs, result):
38
38
  metadata = manager.metadata
39
39
  model_name = metadata.get('ls_model_name', None)
40
40
  if model_name:
41
- model = model_name
41
+ model = model_name
42
+
43
+ if not model:
44
+ if 'to_dict' in dir(result):
45
+ result = result.to_dict()
46
+ if 'model_version' in result:
47
+ model = result['model_version']
42
48
 
43
49
 
44
50
  # Normalize Google model names
@@ -50,11 +56,6 @@ def extract_model_name(args, kwargs, result):
50
56
  return "gemini-1.5-pro"
51
57
  if "gemini-pro" in model:
52
58
  return "gemini-pro"
53
-
54
- if 'to_dict' in dir(result):
55
- result = result.to_dict()
56
- if 'model_version' in result:
57
- model = result['model_version']
58
59
 
59
60
  return model or "default"
60
61
 
@@ -70,6 +71,9 @@ def extract_parameters(kwargs):
70
71
  # Remove messages key in parameters (OpenAI message)
71
72
  if 'messages' in parameters:
72
73
  del parameters['messages']
74
+
75
+ if 'run_manager' in parameters:
76
+ del parameters['run_manager']
73
77
 
74
78
  if 'generation_config' in parameters:
75
79
  generation_config = parameters['generation_config']
@@ -268,10 +272,21 @@ def num_tokens_from_messages(model="gpt-4o-mini-2024-07-18", prompt_messages=Non
268
272
  }
269
273
 
270
274
  def extract_input_data(args, kwargs, result):
271
- """Extract input data from function call"""
275
+ """Sanitize and format input data, including handling of nested lists and dictionaries."""
276
+
277
+ def sanitize_value(value):
278
+ if isinstance(value, (int, float, bool, str)):
279
+ return value
280
+ elif isinstance(value, list):
281
+ return [sanitize_value(item) for item in value]
282
+ elif isinstance(value, dict):
283
+ return {key: sanitize_value(val) for key, val in value.items()}
284
+ else:
285
+ return str(value) # Convert non-standard types to string
286
+
272
287
  return {
273
- 'args': args,
274
- 'kwargs': kwargs
288
+ "args": [sanitize_value(arg) for arg in args],
289
+ "kwargs": {key: sanitize_value(val) for key, val in kwargs.items()},
275
290
  }
276
291
 
277
292
 
@@ -370,6 +385,13 @@ def extract_llm_output(result):
370
385
  })
371
386
  return OutputResponse(output)
372
387
 
388
+ # Handle AIMessage Format
389
+ if hasattr(result, "content"):
390
+ return OutputResponse([{
391
+ "content": result.content,
392
+ "role": getattr(result, "role", "assistant")
393
+ }])
394
+
373
395
  # Handle Vertex AI format
374
396
  # format1
375
397
  if hasattr(result, "text"):
@@ -517,7 +539,7 @@ def extract_llm_data(args, kwargs, result):
517
539
  token_usage = extract_token_usage(result)
518
540
 
519
541
  # Load model costs
520
- model_costs = load_model_costs()
542
+ model_costs = model_cost
521
543
 
522
544
  # Calculate cost
523
545
  cost = calculate_llm_cost(token_usage, model_name, model_costs)