ragaai-catalyst 2.1.5b22__py3-none-any.whl → 2.1.5b24__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.
- ragaai_catalyst/__init__.py +3 -1
- ragaai_catalyst/dataset.py +49 -1
- ragaai_catalyst/redteaming.py +171 -0
- ragaai_catalyst/synthetic_data_generation.py +1 -1
- ragaai_catalyst/tracers/agentic_tracing/tracers/agent_tracer.py +39 -33
- ragaai_catalyst/tracers/agentic_tracing/tracers/base.py +208 -46
- ragaai_catalyst/tracers/agentic_tracing/tracers/llm_tracer.py +235 -62
- ragaai_catalyst/tracers/agentic_tracing/tracers/main_tracer.py +1 -4
- ragaai_catalyst/tracers/agentic_tracing/tracers/tool_tracer.py +5 -0
- ragaai_catalyst/tracers/agentic_tracing/upload/upload_local_metric.py +72 -0
- ragaai_catalyst/tracers/agentic_tracing/upload/upload_trace_metric.py +27 -11
- ragaai_catalyst/tracers/agentic_tracing/utils/llm_utils.py +3 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/span_attributes.py +64 -28
- ragaai_catalyst/tracers/agentic_tracing/utils/unique_decorator.py +3 -1
- ragaai_catalyst/tracers/agentic_tracing/utils/zip_list_of_unique_files.py +40 -21
- ragaai_catalyst/tracers/tracer.py +6 -3
- {ragaai_catalyst-2.1.5b22.dist-info → ragaai_catalyst-2.1.5b24.dist-info}/METADATA +37 -2
- {ragaai_catalyst-2.1.5b22.dist-info → ragaai_catalyst-2.1.5b24.dist-info}/RECORD +21 -19
- {ragaai_catalyst-2.1.5b22.dist-info → ragaai_catalyst-2.1.5b24.dist-info}/LICENSE +0 -0
- {ragaai_catalyst-2.1.5b22.dist-info → ragaai_catalyst-2.1.5b24.dist-info}/WHEEL +0 -0
- {ragaai_catalyst-2.1.5b22.dist-info → ragaai_catalyst-2.1.5b24.dist-info}/top_level.txt +0 -0
@@ -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
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
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
|
-
#
|
506
|
-
|
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
|
-
#
|
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_metrics = BaseTracer.get_formatted_metric(self.span_attributes_dict, self.project_id, name)
|
634
|
+
if formatted_metrics:
|
635
|
+
metrics.extend(formatted_metrics)
|
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
|
530
|
-
"end_time":
|
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
|
-
|
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,77 @@ class LLMTracerMixin:
|
|
552
664
|
"interactions": interactions,
|
553
665
|
}
|
554
666
|
|
555
|
-
|
556
|
-
|
557
|
-
|
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
|
+
try:
|
687
|
+
if isinstance(input_data, dict):
|
688
|
+
messages = input_data.get("kwargs", {}).get("messages", [])
|
689
|
+
elif isinstance(input_data, list):
|
690
|
+
if len(input_data)>0 and isinstance(input_data[0]['content'],ChatResponse):
|
691
|
+
extracted_messages = []
|
692
|
+
|
693
|
+
for item in input_data:
|
694
|
+
chat_response = item.get('content')
|
695
|
+
if hasattr(chat_response, 'message') and hasattr(chat_response.message, 'blocks'):
|
696
|
+
for block in chat_response.message.blocks:
|
697
|
+
if hasattr(block, 'text'):
|
698
|
+
extracted_messages.append(block.text)
|
699
|
+
messages=extracted_messages
|
700
|
+
if isinstance(messages,list):
|
701
|
+
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)]
|
705
|
+
else:
|
706
|
+
messages = input_data
|
707
|
+
elif isinstance(input_data,ChatResponse):
|
708
|
+
messages=input_data['content']
|
709
|
+
else:
|
710
|
+
return ""
|
711
|
+
res=""
|
712
|
+
# try:
|
713
|
+
res="\n".join(msg.get("content", "").strip() for msg in messages if msg.get("content"))
|
714
|
+
except Exception as e:
|
715
|
+
res=str(messages)
|
716
|
+
return res
|
717
|
+
|
718
|
+
def process_content(content):
|
719
|
+
if isinstance(content, str):
|
720
|
+
return content.strip()
|
721
|
+
elif isinstance(content, list):
|
722
|
+
# Handle list of content blocks
|
723
|
+
text_parts = []
|
724
|
+
for block in content:
|
725
|
+
if hasattr(block, 'text'):
|
726
|
+
# Handle TextBlock-like objects
|
727
|
+
text_parts.append(block.text.strip())
|
728
|
+
elif isinstance(block, dict) and 'text' in block:
|
729
|
+
# Handle dictionary with text field
|
730
|
+
text_parts.append(block['text'].strip())
|
731
|
+
return " ".join(text_parts)
|
732
|
+
elif isinstance(content, dict):
|
733
|
+
# Handle dictionary content
|
734
|
+
return content.get('text', '').strip()
|
735
|
+
return ""
|
736
|
+
|
737
|
+
|
568
738
|
def start_component(self, component_id):
|
569
739
|
"""Start tracking network calls for a component"""
|
570
740
|
self.component_network_calls[component_id] = []
|
@@ -582,7 +752,7 @@ class LLMTracerMixin:
|
|
582
752
|
if not self.auto_instrument_llm:
|
583
753
|
return await original_func(*args, **kwargs)
|
584
754
|
|
585
|
-
start_time = datetime.now().astimezone()
|
755
|
+
start_time = datetime.now().astimezone().isoformat()
|
586
756
|
start_memory = psutil.Process().memory_info().rss
|
587
757
|
component_id = str(uuid.uuid4())
|
588
758
|
hash_id = generate_unique_hash(original_func, args, kwargs)
|
@@ -605,7 +775,8 @@ class LLMTracerMixin:
|
|
605
775
|
if stream:
|
606
776
|
prompt_messages = kwargs['messages']
|
607
777
|
# Create response message for streaming case
|
608
|
-
response_message = {"role": "assistant", "content": result} if result else {"role": "assistant",
|
778
|
+
response_message = {"role": "assistant", "content": result} if result else {"role": "assistant",
|
779
|
+
"content": ""}
|
609
780
|
token_usage = num_tokens_from_messages(model_name, prompt_messages, response_message)
|
610
781
|
else:
|
611
782
|
token_usage = extract_token_usage(result)
|
@@ -685,7 +856,7 @@ class LLMTracerMixin:
|
|
685
856
|
if not self.auto_instrument_llm:
|
686
857
|
return original_func(*args, **kwargs)
|
687
858
|
|
688
|
-
start_time = datetime.now().astimezone()
|
859
|
+
start_time = datetime.now().astimezone().isoformat()
|
689
860
|
component_id = str(uuid.uuid4())
|
690
861
|
hash_id = generate_unique_hash(original_func, args, kwargs)
|
691
862
|
|
@@ -707,13 +878,14 @@ class LLMTracerMixin:
|
|
707
878
|
|
708
879
|
# Extract token usage and calculate cost
|
709
880
|
model_name = extract_model_name(args, kwargs, result)
|
710
|
-
|
881
|
+
|
711
882
|
if 'stream' in kwargs:
|
712
883
|
stream = kwargs['stream']
|
713
884
|
if stream:
|
714
885
|
prompt_messages = kwargs['messages']
|
715
886
|
# Create response message for streaming case
|
716
|
-
response_message = {"role": "assistant", "content": result} if result else {"role": "assistant",
|
887
|
+
response_message = {"role": "assistant", "content": result} if result else {"role": "assistant",
|
888
|
+
"content": ""}
|
717
889
|
token_usage = num_tokens_from_messages(model_name, prompt_messages, response_message)
|
718
890
|
else:
|
719
891
|
token_usage = extract_token_usage(result)
|
@@ -786,12 +958,12 @@ class LLMTracerMixin:
|
|
786
958
|
raise
|
787
959
|
|
788
960
|
def trace_llm(
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
961
|
+
self,
|
962
|
+
name: str = None,
|
963
|
+
tags: List[str] = [],
|
964
|
+
metadata: Dict[str, Any] = {},
|
965
|
+
metrics: List[Dict[str, Any]] = [],
|
966
|
+
feedback: Optional[Any] = None,
|
795
967
|
):
|
796
968
|
if name not in self.span_attributes_dict:
|
797
969
|
self.span_attributes_dict[name] = SpanAttributes(name)
|
@@ -817,7 +989,7 @@ class LLMTracerMixin:
|
|
817
989
|
logger.error(f"Validation Error: {e}")
|
818
990
|
except Exception as e:
|
819
991
|
logger.error(f"Error adding metric: {e}")
|
820
|
-
|
992
|
+
|
821
993
|
if feedback:
|
822
994
|
self.span(name).add_feedback(feedback)
|
823
995
|
|
@@ -871,8 +1043,9 @@ class LLMTracerMixin:
|
|
871
1043
|
|
872
1044
|
if error_info:
|
873
1045
|
llm_component["error"] = error_info["error"]
|
874
|
-
|
1046
|
+
|
875
1047
|
self.end_component(component_id)
|
1048
|
+
|
876
1049
|
# metrics
|
877
1050
|
metrics = []
|
878
1051
|
if name in self.span_attributes_dict:
|
@@ -882,7 +1055,7 @@ class LLMTracerMixin:
|
|
882
1055
|
counter = sum(1 for x in self.visited_metrics if x.startswith(base_metric_name))
|
883
1056
|
metric_name = f'{base_metric_name}_{counter}' if counter > 0 else base_metric_name
|
884
1057
|
self.visited_metrics.append(metric_name)
|
885
|
-
metric["name"] = metric_name
|
1058
|
+
metric["name"] = metric_name
|
886
1059
|
metrics.append(metric)
|
887
1060
|
llm_component["metrics"] = metrics
|
888
1061
|
if parent_agent_id:
|
@@ -946,9 +1119,9 @@ class LLMTracerMixin:
|
|
946
1119
|
counter = sum(1 for x in self.visited_metrics if x.startswith(base_metric_name))
|
947
1120
|
metric_name = f'{base_metric_name}_{counter}' if counter > 0 else base_metric_name
|
948
1121
|
self.visited_metrics.append(metric_name)
|
949
|
-
metric["name"] = metric_name
|
1122
|
+
metric["name"] = metric_name
|
950
1123
|
metrics.append(metric)
|
951
|
-
llm_component["metrics"] = metrics
|
1124
|
+
llm_component["metrics"] = metrics
|
952
1125
|
if parent_agent_id:
|
953
1126
|
children = self.agent_children.get()
|
954
1127
|
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"""
|
@@ -364,7 +361,7 @@ class AgenticTracing(
|
|
364
361
|
|
365
362
|
# Check if there's an active agent context
|
366
363
|
current_agent_id = self.current_agent_id.get()
|
367
|
-
if current_agent_id and component_data["type"] in ["llm", "tool"]:
|
364
|
+
if current_agent_id and component_data["type"] in ["llm", "tool", "custom"]:
|
368
365
|
# Add this component as a child of the current agent
|
369
366
|
current_children = self.agent_children.get()
|
370
367
|
current_children.append(component_data)
|
@@ -7,6 +7,7 @@ import functools
|
|
7
7
|
from typing import Optional, Any, Dict, List
|
8
8
|
|
9
9
|
from pydantic import tools
|
10
|
+
from .base import BaseTracer
|
10
11
|
from ..utils.unique_decorator import generate_unique_hash_simple
|
11
12
|
import contextvars
|
12
13
|
import asyncio
|
@@ -483,6 +484,10 @@ class ToolTracerMixin:
|
|
483
484
|
metric["name"] = metric_name
|
484
485
|
metrics.append(metric)
|
485
486
|
|
487
|
+
formatted_metrics = BaseTracer.get_formatted_metric(self.span_attributes_dict, self.project_id, name)
|
488
|
+
if formatted_metrics:
|
489
|
+
metrics.extend(formatted_metrics)
|
490
|
+
|
486
491
|
start_time = kwargs["start_time"]
|
487
492
|
component = {
|
488
493
|
"id": kwargs["component_id"],
|
@@ -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, **kwargs):
|
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": kwargs.get("prompt"),
|
45
|
+
"Response": kwargs.get("response"),
|
46
|
+
"Context": kwargs.get("context"),
|
47
|
+
"ExpectedResponse": kwargs.get("expected_response"),
|
48
|
+
"ExpectedContext": kwargs.get("expected_context"),
|
49
|
+
"Chat": kwargs.get("chat"),
|
50
|
+
"Instructions": kwargs.get("instructions"),
|
51
|
+
"SystemPrompt": kwargs.get("system_prompt"),
|
52
|
+
"Text": kwargs.get("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}")
|