ragaai-catalyst 2.0.7.1__py3-none-any.whl → 2.0.7.2b0__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/evaluation.py +2 -2
- ragaai_catalyst/tracers/agentic_tracing/Untitled-1.json +660 -0
- ragaai_catalyst/tracers/agentic_tracing/__init__.py +3 -0
- ragaai_catalyst/tracers/agentic_tracing/agent_tracer.py +311 -0
- ragaai_catalyst/tracers/agentic_tracing/agentic_tracing.py +212 -0
- ragaai_catalyst/tracers/agentic_tracing/base.py +270 -0
- ragaai_catalyst/tracers/agentic_tracing/data_structure.py +239 -0
- ragaai_catalyst/tracers/agentic_tracing/llm_tracer.py +906 -0
- ragaai_catalyst/tracers/agentic_tracing/network_tracer.py +286 -0
- ragaai_catalyst/tracers/agentic_tracing/sample.py +197 -0
- ragaai_catalyst/tracers/agentic_tracing/tool_tracer.py +235 -0
- ragaai_catalyst/tracers/agentic_tracing/unique_decorator.py +221 -0
- ragaai_catalyst/tracers/agentic_tracing/unique_decorator_test.py +172 -0
- ragaai_catalyst/tracers/agentic_tracing/user_interaction_tracer.py +67 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/__init__.py +3 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/api_utils.py +18 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/data_classes.py +61 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/generic.py +32 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/llm_utils.py +181 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/model_costs.json +5946 -0
- ragaai_catalyst/tracers/agentic_tracing/utils/trace_utils.py +74 -0
- ragaai_catalyst/tracers/tracer.py +26 -4
- ragaai_catalyst/tracers/upload_traces.py +127 -0
- ragaai_catalyst-2.0.7.2b0.dist-info/METADATA +39 -0
- ragaai_catalyst-2.0.7.2b0.dist-info/RECORD +50 -0
- ragaai_catalyst-2.0.7.1.dist-info/METADATA +0 -386
- ragaai_catalyst-2.0.7.1.dist-info/RECORD +0 -29
- {ragaai_catalyst-2.0.7.1.dist-info → ragaai_catalyst-2.0.7.2b0.dist-info}/WHEEL +0 -0
- {ragaai_catalyst-2.0.7.1.dist-info → ragaai_catalyst-2.0.7.2b0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,286 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
import socket
|
3
|
+
from http.client import HTTPConnection, HTTPSConnection
|
4
|
+
import aiohttp
|
5
|
+
import requests
|
6
|
+
import urllib
|
7
|
+
import uuid
|
8
|
+
|
9
|
+
|
10
|
+
class NetworkTracer:
|
11
|
+
def __init__(self):
|
12
|
+
self.network_calls = []
|
13
|
+
self.patches_applied = False # Track whether patches are active
|
14
|
+
# Store original functions for restoration
|
15
|
+
self._original_urlopen = None
|
16
|
+
self._original_requests_request = None
|
17
|
+
self._original_http_request = None
|
18
|
+
self._original_https_request = None
|
19
|
+
self._original_socket_create_connection = None
|
20
|
+
|
21
|
+
def record_call(
|
22
|
+
self,
|
23
|
+
method,
|
24
|
+
url,
|
25
|
+
status_code=None,
|
26
|
+
error=None,
|
27
|
+
start_time=None,
|
28
|
+
end_time=None,
|
29
|
+
request_headers=None,
|
30
|
+
response_headers=None,
|
31
|
+
request_body=None,
|
32
|
+
response_body=None,
|
33
|
+
):
|
34
|
+
duration = (
|
35
|
+
(end_time - start_time).total_seconds() if start_time and end_time else None
|
36
|
+
)
|
37
|
+
|
38
|
+
# Calculate bytes sent/received from headers and body
|
39
|
+
bytes_sent = len(str(request_headers or "")) + len(str(request_body or ""))
|
40
|
+
bytes_received = len(str(response_headers or "")) + len(str(response_body or ""))
|
41
|
+
|
42
|
+
# Extract protocol from URL
|
43
|
+
protocol = "https" if url.startswith("https") else "http"
|
44
|
+
|
45
|
+
self.network_calls.append(
|
46
|
+
{
|
47
|
+
"url": url,
|
48
|
+
"method": method,
|
49
|
+
"status_code": status_code,
|
50
|
+
"response_time": duration,
|
51
|
+
"bytes_sent": bytes_sent,
|
52
|
+
"bytes_received": bytes_received,
|
53
|
+
"protocol": protocol,
|
54
|
+
"connection_id": str(uuid.uuid4()), # Generate unique connection ID
|
55
|
+
"parent_id": None, # Will be set by the component
|
56
|
+
"request": {
|
57
|
+
"headers": request_headers,
|
58
|
+
"body": request_body[:1000] if request_body else None, # Limit to 1000 chars
|
59
|
+
},
|
60
|
+
"response": {
|
61
|
+
"headers": response_headers,
|
62
|
+
"body": response_body[:1000] if response_body else None, # Limit to 1000 chars
|
63
|
+
},
|
64
|
+
"error": str(error) if error else None,
|
65
|
+
}
|
66
|
+
)
|
67
|
+
|
68
|
+
def activate_patches(self):
|
69
|
+
if not self.patches_applied:
|
70
|
+
# Apply monkey patches and store originals
|
71
|
+
self._original_urlopen = monkey_patch_urllib(self)
|
72
|
+
self._original_requests_request = monkey_patch_requests(self)
|
73
|
+
self._original_http_request, self._original_https_request = (
|
74
|
+
monkey_patch_http_client(self)
|
75
|
+
)
|
76
|
+
self._original_socket_create_connection = monkey_patch_socket(self)
|
77
|
+
self.patches_applied = True
|
78
|
+
|
79
|
+
def deactivate_patches(self):
|
80
|
+
if self.patches_applied:
|
81
|
+
# Restore original functions
|
82
|
+
restore_urllib(self._original_urlopen)
|
83
|
+
restore_requests(self._original_requests_request)
|
84
|
+
restore_http_client(
|
85
|
+
self._original_http_request, self._original_https_request
|
86
|
+
)
|
87
|
+
restore_socket(self._original_socket_create_connection)
|
88
|
+
self.network_calls = []
|
89
|
+
self.patches_applied = False
|
90
|
+
|
91
|
+
|
92
|
+
# Define the monkey patch and restore functions
|
93
|
+
def monkey_patch_urllib(network_tracer):
|
94
|
+
from urllib.request import urlopen
|
95
|
+
|
96
|
+
original_urlopen = urlopen
|
97
|
+
|
98
|
+
def patched_urlopen(url, data=None, timeout=None, *args, **kwargs):
|
99
|
+
if isinstance(url, str):
|
100
|
+
method = "GET" if data is None else "POST"
|
101
|
+
url_str = url
|
102
|
+
else:
|
103
|
+
method = url.get_method()
|
104
|
+
url_str = url.full_url
|
105
|
+
|
106
|
+
start_time = datetime.now()
|
107
|
+
try:
|
108
|
+
response = original_urlopen(url, data, timeout, *args, **kwargs)
|
109
|
+
end_time = datetime.now()
|
110
|
+
network_tracer.record_call(
|
111
|
+
method=method,
|
112
|
+
url=url_str,
|
113
|
+
status_code=response.status,
|
114
|
+
start_time=start_time,
|
115
|
+
end_time=end_time,
|
116
|
+
request_headers=dict(response.request.headers),
|
117
|
+
response_headers=dict(response.headers),
|
118
|
+
request_body=data,
|
119
|
+
response_body=response.read().decode("utf-8", errors="ignore"),
|
120
|
+
)
|
121
|
+
return response
|
122
|
+
except Exception as e:
|
123
|
+
end_time = datetime.now()
|
124
|
+
network_tracer.record_call(
|
125
|
+
method=method,
|
126
|
+
url=url_str,
|
127
|
+
error=e,
|
128
|
+
start_time=start_time,
|
129
|
+
end_time=end_time,
|
130
|
+
)
|
131
|
+
raise
|
132
|
+
|
133
|
+
urllib.request.urlopen = patched_urlopen
|
134
|
+
return original_urlopen # Return the original function
|
135
|
+
|
136
|
+
|
137
|
+
def restore_urllib(original_urlopen):
|
138
|
+
urllib.request.urlopen = original_urlopen
|
139
|
+
|
140
|
+
|
141
|
+
def monkey_patch_requests(network_tracer):
|
142
|
+
original_request = requests.Session.request
|
143
|
+
|
144
|
+
def patched_request(self, method, url, *args, **kwargs):
|
145
|
+
start_time = datetime.now()
|
146
|
+
try:
|
147
|
+
response = original_request(self, method, url, *args, **kwargs)
|
148
|
+
end_time = datetime.now()
|
149
|
+
network_tracer.record_call(
|
150
|
+
method=method,
|
151
|
+
url=url,
|
152
|
+
status_code=response.status_code,
|
153
|
+
start_time=start_time,
|
154
|
+
end_time=end_time,
|
155
|
+
request_headers=dict(response.request.headers),
|
156
|
+
response_headers=dict(response.headers),
|
157
|
+
request_body=kwargs.get("data") or kwargs.get("json"),
|
158
|
+
response_body=response.text,
|
159
|
+
)
|
160
|
+
return response
|
161
|
+
except Exception as e:
|
162
|
+
end_time = datetime.now()
|
163
|
+
network_tracer.record_call(
|
164
|
+
method=method,
|
165
|
+
url=url,
|
166
|
+
error=e,
|
167
|
+
start_time=start_time,
|
168
|
+
end_time=end_time,
|
169
|
+
)
|
170
|
+
raise
|
171
|
+
|
172
|
+
requests.Session.request = patched_request
|
173
|
+
return original_request
|
174
|
+
|
175
|
+
|
176
|
+
def restore_requests(original_request):
|
177
|
+
requests.Session.request = original_request
|
178
|
+
|
179
|
+
|
180
|
+
def monkey_patch_http_client(network_tracer):
|
181
|
+
original_http_request = HTTPConnection.request
|
182
|
+
original_https_request = HTTPSConnection.request
|
183
|
+
|
184
|
+
def patched_request(self, method, url, body=None, headers=None, *args, **kwargs):
|
185
|
+
start_time = datetime.now()
|
186
|
+
try:
|
187
|
+
result = (
|
188
|
+
original_http_request(self, method, url, body, headers, *args, **kwargs)
|
189
|
+
if isinstance(self, HTTPConnection)
|
190
|
+
else original_https_request(
|
191
|
+
self, method, url, body, headers, *args, **kwargs
|
192
|
+
)
|
193
|
+
)
|
194
|
+
response = self.getresponse()
|
195
|
+
end_time = datetime.now()
|
196
|
+
network_tracer.record_call(
|
197
|
+
method=method,
|
198
|
+
url=f"{self._http_vsn_str} {self.host}:{self.port}{url}",
|
199
|
+
status_code=response.status,
|
200
|
+
start_time=start_time,
|
201
|
+
end_time=end_time,
|
202
|
+
request_headers=headers,
|
203
|
+
response_headers=dict(response.headers),
|
204
|
+
request_body=body,
|
205
|
+
response_body=response.read().decode("utf-8", errors="ignore"),
|
206
|
+
)
|
207
|
+
return result
|
208
|
+
except Exception as e:
|
209
|
+
end_time = datetime.now()
|
210
|
+
network_tracer.record_call(
|
211
|
+
method=method,
|
212
|
+
url=f"{self._http_vsn_str} {self.host}:{self.port}{url}",
|
213
|
+
error=e,
|
214
|
+
start_time=start_time,
|
215
|
+
end_time=end_time,
|
216
|
+
)
|
217
|
+
raise
|
218
|
+
|
219
|
+
HTTPConnection.request = patched_request
|
220
|
+
HTTPSConnection.request = patched_request
|
221
|
+
return original_http_request, original_https_request
|
222
|
+
|
223
|
+
|
224
|
+
def restore_http_client(original_http_request, original_https_request):
|
225
|
+
HTTPConnection.request = original_http_request
|
226
|
+
HTTPSConnection.request = original_https_request
|
227
|
+
|
228
|
+
|
229
|
+
def monkey_patch_socket(network_tracer):
|
230
|
+
original_create_connection = socket.create_connection
|
231
|
+
|
232
|
+
def patched_create_connection(address, *args, **kwargs):
|
233
|
+
host, port = address
|
234
|
+
start_time = datetime.now()
|
235
|
+
try:
|
236
|
+
result = original_create_connection(address, *args, **kwargs)
|
237
|
+
end_time = datetime.now()
|
238
|
+
network_tracer.record_call(
|
239
|
+
method="CONNECT",
|
240
|
+
url=f"{host}:{port}",
|
241
|
+
start_time=start_time,
|
242
|
+
end_time=end_time,
|
243
|
+
)
|
244
|
+
return result
|
245
|
+
except Exception as e:
|
246
|
+
end_time = datetime.now()
|
247
|
+
network_tracer.record_call(
|
248
|
+
method="CONNECT",
|
249
|
+
url=f"{host}:{port}",
|
250
|
+
error=e,
|
251
|
+
start_time=start_time,
|
252
|
+
end_time=end_time,
|
253
|
+
)
|
254
|
+
raise
|
255
|
+
|
256
|
+
socket.create_connection = patched_create_connection
|
257
|
+
return original_create_connection
|
258
|
+
|
259
|
+
|
260
|
+
def restore_socket(original_create_connection):
|
261
|
+
socket.create_connection = original_create_connection
|
262
|
+
|
263
|
+
|
264
|
+
async def patch_aiohttp_trace_config(network_tracer):
|
265
|
+
async def on_request_start(session, trace_config_ctx, params):
|
266
|
+
trace_config_ctx.start = datetime.now()
|
267
|
+
|
268
|
+
async def on_request_end(session, trace_config_ctx, params):
|
269
|
+
end_time = datetime.now()
|
270
|
+
response = params.response
|
271
|
+
network_tracer.record_call(
|
272
|
+
method=params.method,
|
273
|
+
url=str(params.url),
|
274
|
+
status_code=response.status,
|
275
|
+
start_time=trace_config_ctx.start,
|
276
|
+
end_time=end_time,
|
277
|
+
request_headers=dict(params.headers),
|
278
|
+
response_headers=dict(response.headers),
|
279
|
+
request_body=await params.response.text(),
|
280
|
+
response_body=await response.text(),
|
281
|
+
)
|
282
|
+
|
283
|
+
trace_config = aiohttp.TraceConfig()
|
284
|
+
trace_config.on_request_start.append(on_request_start)
|
285
|
+
trace_config.on_request_end.append(on_request_end)
|
286
|
+
return trace_config
|
@@ -0,0 +1,197 @@
|
|
1
|
+
import os
|
2
|
+
import json
|
3
|
+
from openai import OpenAI
|
4
|
+
import requests
|
5
|
+
from datetime import datetime
|
6
|
+
from dotenv import load_dotenv
|
7
|
+
import sys
|
8
|
+
|
9
|
+
# Load environment variables
|
10
|
+
load_dotenv()
|
11
|
+
|
12
|
+
# Initialize OpenAI client
|
13
|
+
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
14
|
+
|
15
|
+
# Initialize tracer
|
16
|
+
from tracer import Tracer
|
17
|
+
tracer = Tracer(
|
18
|
+
project_name="travel_agent_demo",
|
19
|
+
output_dir="./traces"
|
20
|
+
)
|
21
|
+
|
22
|
+
# Start tracing
|
23
|
+
tracer.start()
|
24
|
+
|
25
|
+
@tracer.trace_tool(
|
26
|
+
name="llm_call",
|
27
|
+
tool_type="llm",
|
28
|
+
version="1.0.0"
|
29
|
+
)
|
30
|
+
def llm_call(prompt, max_tokens=512, model="gpt-3.5-turbo"):
|
31
|
+
response = client.chat.completions.create(
|
32
|
+
model=model,
|
33
|
+
messages=[{"role": "user", "content": prompt}],
|
34
|
+
max_tokens=max_tokens,
|
35
|
+
temperature=0.7,
|
36
|
+
)
|
37
|
+
return response.choices[0].message.content.strip()
|
38
|
+
|
39
|
+
@tracer.trace_tool(
|
40
|
+
name="weather_tool",
|
41
|
+
tool_type="api",
|
42
|
+
version="1.0.0"
|
43
|
+
)
|
44
|
+
def weather_tool(destination):
|
45
|
+
api_key = os.environ.get("OPENWEATHERMAP_API_KEY")
|
46
|
+
base_url = "http://api.openweathermap.org/data/2.5/weather"
|
47
|
+
params = {"q": destination, "appid": api_key, "units": "metric"}
|
48
|
+
|
49
|
+
try:
|
50
|
+
response = requests.get(base_url, params=params)
|
51
|
+
response.raise_for_status()
|
52
|
+
data = response.json()
|
53
|
+
weather_description = data["weather"][0]["description"]
|
54
|
+
temperature = data["main"]["temp"]
|
55
|
+
return f"{weather_description.capitalize()}, {temperature:.1f}°C"
|
56
|
+
except requests.RequestException:
|
57
|
+
return "Weather data not available."
|
58
|
+
|
59
|
+
@tracer.trace_tool(
|
60
|
+
name="currency_converter_tool",
|
61
|
+
tool_type="api",
|
62
|
+
version="1.0.0"
|
63
|
+
)
|
64
|
+
def currency_converter_tool(amount, from_currency, to_currency):
|
65
|
+
api_key = os.environ.get("EXCHANGERATE_API_KEY")
|
66
|
+
base_url = f"https://v6.exchangerate-api.com/v6/{api_key}/pair/{from_currency}/{to_currency}"
|
67
|
+
|
68
|
+
try:
|
69
|
+
response = requests.get(base_url)
|
70
|
+
response.raise_for_status()
|
71
|
+
data = response.json()
|
72
|
+
if data["result"] == "success":
|
73
|
+
rate = data["conversion_rate"]
|
74
|
+
return amount * rate
|
75
|
+
return None
|
76
|
+
except requests.RequestException:
|
77
|
+
return None
|
78
|
+
|
79
|
+
@tracer.trace_tool(
|
80
|
+
name="flight_price_estimator_tool",
|
81
|
+
tool_type="mock",
|
82
|
+
version="1.0.0"
|
83
|
+
)
|
84
|
+
def flight_price_estimator_tool(origin, destination):
|
85
|
+
return f"Estimated price from {origin} to {destination}: $500-$1000"
|
86
|
+
|
87
|
+
@tracer.trace_agent(
|
88
|
+
name="itinerary_agent",
|
89
|
+
agent_type="planner",
|
90
|
+
capabilities=["itinerary_planning", "llm_interaction"]
|
91
|
+
)
|
92
|
+
class ItineraryAgent:
|
93
|
+
def __init__(self, persona="Itinerary Agent"):
|
94
|
+
self.persona = persona
|
95
|
+
|
96
|
+
def plan_itinerary(self, user_preferences, duration=3):
|
97
|
+
itinerary_prompt = f"""
|
98
|
+
You are a travel expert named {self.persona}.
|
99
|
+
Based on the following user preferences, create a {duration}-day travel itinerary.
|
100
|
+
|
101
|
+
User Preferences:
|
102
|
+
{user_preferences}
|
103
|
+
|
104
|
+
Itinerary:
|
105
|
+
"""
|
106
|
+
return llm_call(itinerary_prompt, max_tokens=512)
|
107
|
+
|
108
|
+
@tracer.trace_agent(
|
109
|
+
name="travel_agent",
|
110
|
+
agent_type="orchestrator",
|
111
|
+
capabilities=["preference_extraction", "travel_planning", "information_gathering"]
|
112
|
+
)
|
113
|
+
def travel_agent():
|
114
|
+
print("Welcome to the Personalized Travel Planner!\n")
|
115
|
+
|
116
|
+
# Get user input
|
117
|
+
user_input = "karela, 10 days, $100, nature"
|
118
|
+
|
119
|
+
# Extract preferences
|
120
|
+
preferences_prompt = f"""
|
121
|
+
Extract key travel preferences from the following user input:
|
122
|
+
"{user_input}"
|
123
|
+
|
124
|
+
Please provide the extracted information in this format:
|
125
|
+
Destination:
|
126
|
+
Activities:
|
127
|
+
Budget:
|
128
|
+
Duration (in days):
|
129
|
+
"""
|
130
|
+
extracted_preferences = llm_call(preferences_prompt)
|
131
|
+
print("\nExtracted Preferences:")
|
132
|
+
print(extracted_preferences)
|
133
|
+
|
134
|
+
# Parse extracted preferences
|
135
|
+
preferences = {}
|
136
|
+
for line in extracted_preferences.split("\n"):
|
137
|
+
if ":" in line:
|
138
|
+
key, value = line.split(":", 1)
|
139
|
+
preferences[key.strip()] = value.strip()
|
140
|
+
|
141
|
+
# Validate extracted preferences
|
142
|
+
required_keys = ["Destination", "Activities", "Budget", "Duration (in days)"]
|
143
|
+
if not all(key in preferences for key in required_keys):
|
144
|
+
print("\nCould not extract all required preferences. Please try again.")
|
145
|
+
return
|
146
|
+
|
147
|
+
# Fetch additional information
|
148
|
+
weather = weather_tool(preferences["Destination"])
|
149
|
+
print(f"\nWeather in {preferences['Destination']}: {weather}")
|
150
|
+
|
151
|
+
origin = "delhi"
|
152
|
+
flight_price = flight_price_estimator_tool(origin, preferences["Destination"])
|
153
|
+
print(flight_price)
|
154
|
+
|
155
|
+
# Plan itinerary
|
156
|
+
itinerary_agent = ItineraryAgent()
|
157
|
+
itinerary = itinerary_agent.plan_itinerary(
|
158
|
+
extracted_preferences, int(preferences["Duration (in days)"])
|
159
|
+
)
|
160
|
+
print("\nPlanned Itinerary:")
|
161
|
+
print(itinerary)
|
162
|
+
|
163
|
+
# Currency conversion
|
164
|
+
budget_amount = float(preferences["Budget"].replace("$", "").replace(",", ""))
|
165
|
+
converted_budget = currency_converter_tool(budget_amount, "USD", "INR")
|
166
|
+
if converted_budget:
|
167
|
+
print(f"\nBudget in INR: {converted_budget:.2f} INR")
|
168
|
+
else:
|
169
|
+
print("\nCurrency conversion not available.")
|
170
|
+
|
171
|
+
# Generate travel summary
|
172
|
+
summary_prompt = f"""
|
173
|
+
Summarize the following travel plan:
|
174
|
+
|
175
|
+
Destination: {preferences['Destination']}
|
176
|
+
Activities: {preferences['Activities']}
|
177
|
+
Budget: {preferences['Budget']}
|
178
|
+
Duration: {preferences['Duration (in days)']} days
|
179
|
+
Itinerary: {itinerary}
|
180
|
+
Weather: {weather}
|
181
|
+
Flight Price: {flight_price}
|
182
|
+
|
183
|
+
Travel Summary:
|
184
|
+
"""
|
185
|
+
travel_summary = llm_call(summary_prompt, max_tokens=2048)
|
186
|
+
print("\nTravel Summary:")
|
187
|
+
print(travel_summary)
|
188
|
+
|
189
|
+
def main():
|
190
|
+
try:
|
191
|
+
travel_agent()
|
192
|
+
finally:
|
193
|
+
# Stop tracing and save results
|
194
|
+
tracer.stop()
|
195
|
+
|
196
|
+
if __name__ == "__main__":
|
197
|
+
main()
|