ragaai-catalyst 2.1.5b22__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.
@@ -13,7 +13,9 @@ 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
17
 
18
+ from .base import BaseTracer
17
19
  from ..utils.llm_utils import (
18
20
  extract_model_name,
19
21
  extract_parameters,
@@ -80,7 +82,6 @@ class LLMTracerMixin:
80
82
  def instrument_llm_calls(self):
81
83
  """Enable LLM instrumentation"""
82
84
  self.auto_instrument_llm = True
83
-
84
85
  # Check currently loaded modules
85
86
  if "vertexai" in sys.modules:
86
87
  self.patch_vertex_ai_methods(sys.modules["vertexai"])
@@ -97,28 +98,32 @@ class LLMTracerMixin:
97
98
  self.patch_langchain_google_methods(sys.modules["langchain_google_vertexai"])
98
99
  if "langchain_google_genai" in sys.modules:
99
100
  self.patch_langchain_google_methods(sys.modules["langchain_google_genai"])
101
+
100
102
  if "langchain_openai" in sys.modules:
101
103
  self.patch_langchain_openai_methods(sys.modules["langchain_openai"])
102
104
  if "langchain_anthropic" in sys.modules:
103
105
  self.patch_langchain_anthropic_methods(sys.modules["langchain_anthropic"])
104
106
 
107
+ if "llama_index" in sys.modules:
108
+ self.patch_llama_index_methods(sys.modules["llama_index"])
109
+
105
110
  # Register hooks for future imports with availability checks
106
111
  if self.check_package_available("vertexai"):
107
112
  wrapt.register_post_import_hook(self.patch_vertex_ai_methods, "vertexai")
108
113
  wrapt.register_post_import_hook(
109
114
  self.patch_vertex_ai_methods, "vertexai.generative_models"
110
115
  )
111
-
116
+
112
117
  if self.check_package_available("openai") and self.validate_openai_key():
113
118
  wrapt.register_post_import_hook(self.patch_openai_methods, "openai")
114
119
  wrapt.register_post_import_hook(self.patch_openai_beta_methods, "openai")
115
-
120
+
116
121
  if self.check_package_available("litellm"):
117
122
  wrapt.register_post_import_hook(self.patch_litellm_methods, "litellm")
118
-
123
+
119
124
  if self.check_package_available("anthropic"):
120
125
  wrapt.register_post_import_hook(self.patch_anthropic_methods, "anthropic")
121
-
126
+
122
127
  if self.check_package_available("google.generativeai"):
123
128
  wrapt.register_post_import_hook(
124
129
  self.patch_google_genai_methods, "google.generativeai"
@@ -129,12 +134,16 @@ class LLMTracerMixin:
129
134
  wrapt.register_post_import_hook(
130
135
  self.patch_langchain_google_methods, "langchain_google_vertexai"
131
136
  )
137
+
138
+
139
+ # Add hooks for llama-index
140
+ wrapt.register_post_import_hook(self.patch_llama_index_methods, "llama_index")
132
141
 
133
142
  if self.check_package_available("langchain_google_genai"):
134
143
  wrapt.register_post_import_hook(
135
144
  self.patch_langchain_google_methods, "langchain_google_genai"
136
145
  )
137
-
146
+
138
147
  if self.check_package_available("langchain_openai"):
139
148
  wrapt.register_post_import_hook(
140
149
  self.patch_langchain_openai_methods, "langchain_openai"
@@ -151,11 +160,88 @@ class LLMTracerMixin:
151
160
  def instrument_network_calls(self):
152
161
  """Enable network instrumentation for LLM calls"""
153
162
  self.auto_instrument_network = True
154
-
163
+
155
164
  def instrument_file_io_calls(self):
156
165
  """Enable file IO instrumentation for LLM calls"""
157
166
  self.auto_instrument_file_io = True
158
167
 
168
+ def patch_llama_index_methods(self, module):
169
+ """Patch llama-index LLM methods"""
170
+ try:
171
+ # Handle OpenAI LLM from llama-index
172
+ if hasattr(module, "llms"):
173
+ # OpenAI
174
+ if hasattr(module.llms, "openai"):
175
+ openai_module = module.llms.openai
176
+ if hasattr(openai_module, "OpenAI"):
177
+ llm_class = getattr(openai_module, "OpenAI")
178
+ self.wrap_method(llm_class, "complete")
179
+ self.wrap_method(llm_class, "acomplete")
180
+ self.wrap_method(llm_class, "chat")
181
+ self.wrap_method(llm_class, "achat")
182
+ self.wrap_method(llm_class, "stream_chat")
183
+ # self.wrap_method(llm_class, "stream_achat")
184
+ self.wrap_method(llm_class, "stream_complete")
185
+ # self.wrap_method(llm_class, "stream_acomplete")
186
+
187
+ # Anthropic
188
+ if hasattr(module.llms, "anthropic"):
189
+ anthropic_module = module.llms.anthropic
190
+ if hasattr(anthropic_module, "Anthropic"):
191
+ llm_class = getattr(anthropic_module, "Anthropic")
192
+ self.wrap_method(llm_class, "complete")
193
+ self.wrap_method(llm_class, "acomplete")
194
+ self.wrap_method(llm_class, "chat")
195
+ self.wrap_method(llm_class, "achat")
196
+ self.wrap_method(llm_class, "stream_chat")
197
+ # self.wrap_method(llm_class, "stream_achat")
198
+
199
+ # Azure OpenAI
200
+ if hasattr(module.llms, "azure_openai"):
201
+ azure_module = module.llms.azure_openai
202
+ if hasattr(azure_module, "AzureOpenAI"):
203
+ llm_class = getattr(azure_module, "AzureOpenAI")
204
+ self.wrap_method(llm_class, "complete")
205
+ self.wrap_method(llm_class, "acomplete")
206
+ self.wrap_method(llm_class, "chat")
207
+ self.wrap_method(llm_class, "achat")
208
+ self.wrap_method(llm_class, "stream_chat")
209
+ # self.wrap_method(llm_class, "stream_achat")
210
+
211
+ # LiteLLM
212
+ if hasattr(module.llms, "litellm"):
213
+ litellm_module = module.llms.litellm
214
+ if hasattr(litellm_module, "LiteLLM"):
215
+ llm_class = getattr(litellm_module, "LiteLLM")
216
+ self.wrap_method(llm_class, "complete")
217
+ self.wrap_method(llm_class, "acomplete")
218
+ self.wrap_method(llm_class, "chat")
219
+ self.wrap_method(llm_class, "achat")
220
+
221
+ # Vertex AI
222
+ if hasattr(module.llms, "vertex"):
223
+ vertex_module = module.llms.vertex
224
+ if hasattr(vertex_module, "Vertex"):
225
+ llm_class = getattr(vertex_module, "Vertex")
226
+ self.wrap_method(llm_class, "complete")
227
+ self.wrap_method(llm_class, "acomplete")
228
+ self.wrap_method(llm_class, "chat")
229
+ self.wrap_method(llm_class, "achat")
230
+
231
+ # Gemini
232
+ if hasattr(module.llms, "gemini"):
233
+ gemini_module = module.llms.gemini
234
+ if hasattr(gemini_module, "Gemini"):
235
+ llm_class = getattr(gemini_module, "Gemini")
236
+ self.wrap_method(llm_class, "complete")
237
+ self.wrap_method(llm_class, "acomplete")
238
+ self.wrap_method(llm_class, "chat")
239
+ self.wrap_method(llm_class, "achat")
240
+
241
+ except Exception as e:
242
+ # Log the error but continue execution
243
+ print(f"Warning: Failed to patch llama-index methods: {str(e)}")
244
+
159
245
  def patch_openai_methods(self, module):
160
246
  try:
161
247
  if hasattr(module, "OpenAI"):
@@ -167,12 +253,12 @@ class LLMTracerMixin:
167
253
  except Exception as e:
168
254
  # Log the error but continue execution
169
255
  print(f"Warning: Failed to patch OpenAI methods: {str(e)}")
170
-
256
+
171
257
  def patch_langchain_openai_methods(self, module):
172
258
  try:
173
259
  if hasattr(module, 'ChatOpenAI'):
174
260
  client_class = getattr(module, "ChatOpenAI")
175
-
261
+
176
262
  if hasattr(client_class, "invoke"):
177
263
  self.wrap_langchain_openai_method(client_class, f"{client_class.__name__}.invoke")
178
264
  elif hasattr(client_class, "run"):
@@ -185,7 +271,7 @@ class LLMTracerMixin:
185
271
  except Exception as e:
186
272
  # Log the error but continue execution
187
273
  print(f"Warning: Failed to patch OpenAI methods: {str(e)}")
188
-
274
+
189
275
  def patch_langchain_anthropic_methods(self, module):
190
276
  try:
191
277
  if hasattr(module, 'ChatAnthropic'):
@@ -211,6 +297,7 @@ class LLMTracerMixin:
211
297
  openai.beta.threads.runs.create(...) are automatically traced.
212
298
  """
213
299
  # Make sure openai_module has a 'beta' attribute
300
+ openai_module.api_type = "openai"
214
301
  if not hasattr(openai_module, "beta"):
215
302
  return
216
303
 
@@ -239,7 +326,6 @@ class LLMTracerMixin:
239
326
  if hasattr(runs_obj, method_name):
240
327
  self.wrap_method(runs_obj, method_name)
241
328
 
242
-
243
329
  def patch_anthropic_methods(self, module):
244
330
  if hasattr(module, "Anthropic"):
245
331
  client_class = getattr(module, "Anthropic")
@@ -342,6 +428,7 @@ class LLMTracerMixin:
342
428
  return await self.trace_llm_call(
343
429
  original_create, *args, **kwargs
344
430
  )
431
+
345
432
  client_self.chat.completions.create = wrapped_create
346
433
  else:
347
434
  # Patch sync methods for OpenAI
@@ -353,10 +440,11 @@ class LLMTracerMixin:
353
440
  return self.trace_llm_call_sync(
354
441
  original_create, *args, **kwargs
355
442
  )
443
+
356
444
  client_self.chat.completions.create = wrapped_create
357
445
 
358
446
  setattr(client_class, "__init__", patched_init)
359
-
447
+
360
448
  def wrap_langchain_openai_method(self, client_class, method_name):
361
449
  method = method_name.split(".")[-1]
362
450
  original_init = getattr(client_class, method)
@@ -372,14 +460,14 @@ class LLMTracerMixin:
372
460
  return self.trace_llm_call_sync(original_init, *args, **kwargs)
373
461
 
374
462
  setattr(client_class, method, patched_init)
375
-
463
+
376
464
  def wrap_langchain_anthropic_method(self, client_class, method_name):
377
465
  original_init = getattr(client_class, method_name)
378
466
 
379
467
  @functools.wraps(original_init)
380
468
  def patched_init(*args, **kwargs):
381
469
  is_async = "AsyncChatAnthropic" in client_class.__name__
382
-
470
+
383
471
  if is_async:
384
472
  return self.trace_llm_call(original_init, *args, **kwargs)
385
473
  else:
@@ -447,20 +535,20 @@ class LLMTracerMixin:
447
535
  self.patches.append((obj, method_name, original_method))
448
536
 
449
537
  def create_llm_component(
450
- self,
451
- component_id,
452
- hash_id,
453
- name,
454
- llm_type,
455
- version,
456
- memory_used,
457
- start_time,
458
- input_data,
459
- output_data,
460
- cost={},
461
- usage={},
462
- error=None,
463
- parameters={},
538
+ self,
539
+ component_id,
540
+ hash_id,
541
+ name,
542
+ llm_type,
543
+ version,
544
+ memory_used,
545
+ start_time,
546
+ input_data,
547
+ output_data,
548
+ cost={},
549
+ usage={},
550
+ error=None,
551
+ parameters={},
464
552
  ):
465
553
  # Update total metrics
466
554
  self.total_tokens += usage.get("total_tokens", 0)
@@ -476,7 +564,7 @@ class LLMTracerMixin:
476
564
  for interaction in self.component_user_interaction.get(component_id, []):
477
565
  if interaction["interaction_type"] in ["input", "output"]:
478
566
  input_output_interactions.append(interaction)
479
- interactions.extend(input_output_interactions)
567
+ interactions.extend(input_output_interactions)
480
568
  if self.auto_instrument_file_io:
481
569
  file_io_interactions = []
482
570
  for interaction in self.component_user_interaction.get(component_id, []):
@@ -502,13 +590,24 @@ class LLMTracerMixin:
502
590
  list(parameters_to_display.items())[: self.MAX_PARAMETERS_TO_DISPLAY]
503
591
  )
504
592
 
505
- # Get tags, metrics
506
- # tags
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
507
603
  tags = []
508
604
  if name in self.span_attributes_dict:
509
605
  tags = self.span_attributes_dict[name].tags or []
510
606
 
511
- # metrics
607
+ # Get End Time
608
+ end_time = datetime.now().astimezone().isoformat()
609
+
610
+ # Metrics
512
611
  metrics = []
513
612
  if name in self.span_attributes_dict:
514
613
  raw_metrics = self.span_attributes_dict[name].metrics or []
@@ -517,17 +616,32 @@ class LLMTracerMixin:
517
616
  counter = sum(1 for x in self.visited_metrics if x.startswith(base_metric_name))
518
617
  metric_name = f'{base_metric_name}_{counter}' if counter > 0 else base_metric_name
519
618
  self.visited_metrics.append(metric_name)
520
- metric["name"] = metric_name
619
+ metric["name"] = metric_name
521
620
  metrics.append(metric)
522
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_metric = BaseTracer.get_formatted_metric(self.span_attributes_dict, self.project_id, name, prompt, span_context, response, span_gt)
634
+ if formatted_metric is not None:
635
+ metrics.append(formatted_metric)
636
+
523
637
  component = {
524
638
  "id": component_id,
525
639
  "hash_id": hash_id,
526
640
  "source_hash_id": None,
527
641
  "type": "llm",
528
642
  "name": name,
529
- "start_time": start_time.isoformat(),
530
- "end_time": datetime.now().astimezone().isoformat(),
643
+ "start_time": start_time,
644
+ "end_time": end_time,
531
645
  "error": error,
532
646
  "parent_id": self.current_agent_id.get(),
533
647
  "info": {
@@ -541,10 +655,8 @@ class LLMTracerMixin:
541
655
  },
542
656
  "extra_info": parameters,
543
657
  "data": {
544
- "input": (
545
- input_data["args"] if hasattr(input_data, "args") else input_data
546
- ),
547
- "output": output_data.output_response if output_data else None,
658
+ "input": input,
659
+ "output": output,
548
660
  "memory_used": memory_used,
549
661
  },
550
662
  "metrics": metrics,
@@ -552,19 +664,79 @@ class LLMTracerMixin:
552
664
  "interactions": interactions,
553
665
  }
554
666
 
555
- if name in self.span_attributes_dict:
556
- span_gt = self.span_attributes_dict[name].gt
557
- if span_gt is not None:
558
- component["data"]["gt"] = span_gt
559
- span_context = self.span_attributes_dict[name].context
560
- if span_context:
561
- component["data"]["context"] = span_context
667
+ # Assign context and gt if available
668
+ component["data"]["gt"] = span_gt
669
+ component["data"]["context"] = span_context
562
670
 
563
671
  # Reset the SpanAttributes context variable
564
672
  self.span_attributes_dict[name] = SpanAttributes(name)
565
673
 
566
674
  return component
567
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
+ def convert_to_content(self, input_data):
686
+ if isinstance(input_data, dict):
687
+ messages = input_data.get("kwargs", {}).get("messages", [])
688
+ elif isinstance(input_data, list):
689
+ if len(input_data)>0 and isinstance(input_data[0]['content'],ChatResponse):
690
+ extracted_messages = []
691
+
692
+ for item in input_data:
693
+ chat_response = item.get('content')
694
+ if hasattr(chat_response, 'message') and hasattr(chat_response.message, 'blocks'):
695
+ for block in chat_response.message.blocks:
696
+ if hasattr(block, 'text'):
697
+ extracted_messages.append(block.text)
698
+ messages=extracted_messages
699
+ if isinstance(messages,list):
700
+ return "\n".join(messages)
701
+
702
+ #messages=[msg["content"] for msg in input_data if isinstance(msg, dict) and "content" in msg]
703
+ #messages = [msg["content"].message for msg in input_data if isinstance(msg, dict) and "content" in msg and isinstance(msg["content"], ChatResponse)]
704
+ else:
705
+ messages = input_data
706
+ elif isinstance(input_data,ChatResponse):
707
+ messages=input_data['content']
708
+ else:
709
+ return ""
710
+ res=""
711
+ # try:
712
+ res="\n".join(msg.get("content", "").strip() for msg in messages if msg.get("content"))
713
+ # except Exception as e:
714
+ # print("Exception occured for: ",e)
715
+ # print("Input: ",input_data,"Meeage: ",messages)
716
+ # # import sys
717
+ # # sys.exit()
718
+ return res
719
+
720
+ def process_content(content):
721
+ if isinstance(content, str):
722
+ return content.strip()
723
+ elif isinstance(content, list):
724
+ # Handle list of content blocks
725
+ text_parts = []
726
+ for block in content:
727
+ if hasattr(block, 'text'):
728
+ # Handle TextBlock-like objects
729
+ text_parts.append(block.text.strip())
730
+ elif isinstance(block, dict) and 'text' in block:
731
+ # Handle dictionary with text field
732
+ text_parts.append(block['text'].strip())
733
+ return " ".join(text_parts)
734
+ elif isinstance(content, dict):
735
+ # Handle dictionary content
736
+ return content.get('text', '').strip()
737
+ return ""
738
+
739
+
568
740
  def start_component(self, component_id):
569
741
  """Start tracking network calls for a component"""
570
742
  self.component_network_calls[component_id] = []
@@ -582,7 +754,7 @@ class LLMTracerMixin:
582
754
  if not self.auto_instrument_llm:
583
755
  return await original_func(*args, **kwargs)
584
756
 
585
- start_time = datetime.now().astimezone()
757
+ start_time = datetime.now().astimezone().isoformat()
586
758
  start_memory = psutil.Process().memory_info().rss
587
759
  component_id = str(uuid.uuid4())
588
760
  hash_id = generate_unique_hash(original_func, args, kwargs)
@@ -605,7 +777,8 @@ class LLMTracerMixin:
605
777
  if stream:
606
778
  prompt_messages = kwargs['messages']
607
779
  # Create response message for streaming case
608
- response_message = {"role": "assistant", "content": result} if result else {"role": "assistant", "content": ""}
780
+ response_message = {"role": "assistant", "content": result} if result else {"role": "assistant",
781
+ "content": ""}
609
782
  token_usage = num_tokens_from_messages(model_name, prompt_messages, response_message)
610
783
  else:
611
784
  token_usage = extract_token_usage(result)
@@ -685,7 +858,7 @@ class LLMTracerMixin:
685
858
  if not self.auto_instrument_llm:
686
859
  return original_func(*args, **kwargs)
687
860
 
688
- start_time = datetime.now().astimezone()
861
+ start_time = datetime.now().astimezone().isoformat()
689
862
  component_id = str(uuid.uuid4())
690
863
  hash_id = generate_unique_hash(original_func, args, kwargs)
691
864
 
@@ -707,13 +880,14 @@ class LLMTracerMixin:
707
880
 
708
881
  # Extract token usage and calculate cost
709
882
  model_name = extract_model_name(args, kwargs, result)
710
-
883
+
711
884
  if 'stream' in kwargs:
712
885
  stream = kwargs['stream']
713
886
  if stream:
714
887
  prompt_messages = kwargs['messages']
715
888
  # Create response message for streaming case
716
- response_message = {"role": "assistant", "content": result} if result else {"role": "assistant", "content": ""}
889
+ response_message = {"role": "assistant", "content": result} if result else {"role": "assistant",
890
+ "content": ""}
717
891
  token_usage = num_tokens_from_messages(model_name, prompt_messages, response_message)
718
892
  else:
719
893
  token_usage = extract_token_usage(result)
@@ -786,12 +960,12 @@ class LLMTracerMixin:
786
960
  raise
787
961
 
788
962
  def trace_llm(
789
- self,
790
- name: str = None,
791
- tags: List[str] = [],
792
- metadata: Dict[str, Any] = {},
793
- metrics: List[Dict[str, Any]] = [],
794
- feedback: Optional[Any] = None,
963
+ self,
964
+ name: str = None,
965
+ tags: List[str] = [],
966
+ metadata: Dict[str, Any] = {},
967
+ metrics: List[Dict[str, Any]] = [],
968
+ feedback: Optional[Any] = None,
795
969
  ):
796
970
  if name not in self.span_attributes_dict:
797
971
  self.span_attributes_dict[name] = SpanAttributes(name)
@@ -817,7 +991,7 @@ class LLMTracerMixin:
817
991
  logger.error(f"Validation Error: {e}")
818
992
  except Exception as e:
819
993
  logger.error(f"Error adding metric: {e}")
820
-
994
+
821
995
  if feedback:
822
996
  self.span(name).add_feedback(feedback)
823
997
 
@@ -871,8 +1045,9 @@ class LLMTracerMixin:
871
1045
 
872
1046
  if error_info:
873
1047
  llm_component["error"] = error_info["error"]
874
-
1048
+
875
1049
  self.end_component(component_id)
1050
+
876
1051
  # metrics
877
1052
  metrics = []
878
1053
  if name in self.span_attributes_dict:
@@ -882,7 +1057,7 @@ class LLMTracerMixin:
882
1057
  counter = sum(1 for x in self.visited_metrics if x.startswith(base_metric_name))
883
1058
  metric_name = f'{base_metric_name}_{counter}' if counter > 0 else base_metric_name
884
1059
  self.visited_metrics.append(metric_name)
885
- metric["name"] = metric_name
1060
+ metric["name"] = metric_name
886
1061
  metrics.append(metric)
887
1062
  llm_component["metrics"] = metrics
888
1063
  if parent_agent_id:
@@ -946,9 +1121,9 @@ class LLMTracerMixin:
946
1121
  counter = sum(1 for x in self.visited_metrics if x.startswith(base_metric_name))
947
1122
  metric_name = f'{base_metric_name}_{counter}' if counter > 0 else base_metric_name
948
1123
  self.visited_metrics.append(metric_name)
949
- metric["name"] = metric_name
1124
+ metric["name"] = metric_name
950
1125
  metrics.append(metric)
951
- llm_component["metrics"] = metrics
1126
+ llm_component["metrics"] = metrics
952
1127
  if parent_agent_id:
953
1128
  children = self.agent_children.get()
954
1129
  children.append(llm_component)
@@ -119,9 +119,6 @@ class AgenticTracing(
119
119
  self.component_network_calls = {} # Store network calls per component
120
120
  self.component_user_interaction = {}
121
121
 
122
- # Create output directory if it doesn't exist
123
- self.output_dir = Path("./traces") # Using default traces directory
124
- self.output_dir.mkdir(exist_ok=True)
125
122
 
126
123
  def start_component(self, component_id: str):
127
124
  """Start tracking network calls for a component"""
@@ -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}")