rakam-systems-agent 0.1.1rc7__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.
- rakam_systems_agent/__init__.py +35 -0
- rakam_systems_agent/components/__init__.py +26 -0
- rakam_systems_agent/components/base_agent.py +358 -0
- rakam_systems_agent/components/chat_history/__init__.py +10 -0
- rakam_systems_agent/components/chat_history/json_chat_history.py +372 -0
- rakam_systems_agent/components/chat_history/postgres_chat_history.py +668 -0
- rakam_systems_agent/components/chat_history/sql_chat_history.py +446 -0
- rakam_systems_agent/components/llm_gateway/README.md +505 -0
- rakam_systems_agent/components/llm_gateway/__init__.py +16 -0
- rakam_systems_agent/components/llm_gateway/gateway_factory.py +313 -0
- rakam_systems_agent/components/llm_gateway/mistral_gateway.py +287 -0
- rakam_systems_agent/components/llm_gateway/openai_gateway.py +295 -0
- rakam_systems_agent/components/tools/LLM_GATEWAY_TOOLS_README.md +533 -0
- rakam_systems_agent/components/tools/__init__.py +46 -0
- rakam_systems_agent/components/tools/example_tools.py +431 -0
- rakam_systems_agent/components/tools/llm_gateway_tools.py +605 -0
- rakam_systems_agent/components/tools/search_tool.py +14 -0
- rakam_systems_agent/server/README.md +375 -0
- rakam_systems_agent/server/__init__.py +12 -0
- rakam_systems_agent/server/mcp_server_agent.py +127 -0
- rakam_systems_agent-0.1.1rc7.dist-info/METADATA +367 -0
- rakam_systems_agent-0.1.1rc7.dist-info/RECORD +23 -0
- rakam_systems_agent-0.1.1rc7.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example tools demonstrating both direct and MCP invocation patterns.
|
|
3
|
+
These tools can be used for testing and as templates for custom tools.
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
import asyncio
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Dict, List, Any, Optional
|
|
9
|
+
from rakam_systems_core.ai_core.interfaces.tool import ToolComponent
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# === Simple Direct Tools ===
|
|
13
|
+
|
|
14
|
+
async def get_current_weather(location: str, units: str = "celsius") -> Dict[str, Any]:
|
|
15
|
+
"""
|
|
16
|
+
Get the current weather for a location (mock implementation).
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
location: City name or location
|
|
20
|
+
units: Temperature units (celsius or fahrenheit)
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Dictionary with weather information
|
|
24
|
+
"""
|
|
25
|
+
await asyncio.sleep(0.5) # Simulate API call
|
|
26
|
+
return {
|
|
27
|
+
"location": location,
|
|
28
|
+
"temperature": 22 if units == "celsius" else 72,
|
|
29
|
+
"units": units,
|
|
30
|
+
"condition": "sunny",
|
|
31
|
+
"humidity": 45,
|
|
32
|
+
"timestamp": datetime.now().isoformat(),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async def calculate_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
|
|
37
|
+
"""
|
|
38
|
+
Calculate distance between two geographic coordinates (mock implementation).
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
lat1: Latitude of first point
|
|
42
|
+
lon1: Longitude of first point
|
|
43
|
+
lat2: Latitude of second point
|
|
44
|
+
lon2: Longitude of second point
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Distance in kilometers
|
|
48
|
+
"""
|
|
49
|
+
await asyncio.sleep(0.3)
|
|
50
|
+
# Simplified calculation (not accurate, just for demo)
|
|
51
|
+
import math
|
|
52
|
+
dlat = lat2 - lat1
|
|
53
|
+
dlon = lon2 - lon1
|
|
54
|
+
distance = math.sqrt(dlat**2 + dlon**2) * 111 # Rough km conversion
|
|
55
|
+
return round(distance, 2)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def format_currency(amount: float, currency: str = "USD") -> str:
|
|
59
|
+
"""
|
|
60
|
+
Format a number as currency.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
amount: Amount to format
|
|
64
|
+
currency: Currency code (USD, EUR, GBP, etc.)
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Formatted currency string
|
|
68
|
+
"""
|
|
69
|
+
symbols = {
|
|
70
|
+
"USD": "$",
|
|
71
|
+
"EUR": "€",
|
|
72
|
+
"GBP": "£",
|
|
73
|
+
"JPY": "¥",
|
|
74
|
+
}
|
|
75
|
+
symbol = symbols.get(currency, currency + " ")
|
|
76
|
+
return f"{symbol}{amount:,.2f}"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
async def translate_text(text: str, target_language: str = "en") -> Dict[str, str]:
|
|
80
|
+
"""
|
|
81
|
+
Translate text to target language (mock implementation).
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
text: Text to translate
|
|
85
|
+
target_language: Target language code (en, es, fr, de, etc.)
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Dictionary with original and translated text
|
|
89
|
+
"""
|
|
90
|
+
await asyncio.sleep(0.7)
|
|
91
|
+
# Mock translation - just returns the same text with a note
|
|
92
|
+
translations = {
|
|
93
|
+
"en": text,
|
|
94
|
+
"es": f"[ES] {text}",
|
|
95
|
+
"fr": f"[FR] {text}",
|
|
96
|
+
"de": f"[DE] {text}",
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
"original": text,
|
|
100
|
+
"translated": translations.get(target_language, f"[{target_language}] {text}"),
|
|
101
|
+
"target_language": target_language,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def analyze_sentiment(text: str) -> Dict[str, Any]:
|
|
106
|
+
"""
|
|
107
|
+
Analyze sentiment of text (mock implementation).
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
text: Text to analyze
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Dictionary with sentiment analysis results
|
|
114
|
+
"""
|
|
115
|
+
# Very simple mock sentiment analysis
|
|
116
|
+
positive_words = ["good", "great", "excellent",
|
|
117
|
+
"amazing", "wonderful", "happy"]
|
|
118
|
+
negative_words = ["bad", "terrible", "awful", "horrible", "sad", "angry"]
|
|
119
|
+
|
|
120
|
+
text_lower = text.lower()
|
|
121
|
+
positive_count = sum(1 for word in positive_words if word in text_lower)
|
|
122
|
+
negative_count = sum(1 for word in negative_words if word in text_lower)
|
|
123
|
+
|
|
124
|
+
if positive_count > negative_count:
|
|
125
|
+
sentiment = "positive"
|
|
126
|
+
score = 0.7
|
|
127
|
+
elif negative_count > positive_count:
|
|
128
|
+
sentiment = "negative"
|
|
129
|
+
score = 0.3
|
|
130
|
+
else:
|
|
131
|
+
sentiment = "neutral"
|
|
132
|
+
score = 0.5
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
"text": text,
|
|
136
|
+
"sentiment": sentiment,
|
|
137
|
+
"score": score,
|
|
138
|
+
"positive_indicators": positive_count,
|
|
139
|
+
"negative_indicators": negative_count,
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# === Tool Components ===
|
|
144
|
+
|
|
145
|
+
class WebSearchTool(ToolComponent):
|
|
146
|
+
"""
|
|
147
|
+
Web search tool component (mock implementation).
|
|
148
|
+
Can be used directly or exposed via MCP.
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
def __init__(self, name: str = "web_search", config: Optional[Dict] = None):
|
|
152
|
+
super().__init__(
|
|
153
|
+
name=name,
|
|
154
|
+
config=config,
|
|
155
|
+
description="Search the web for information",
|
|
156
|
+
json_schema={
|
|
157
|
+
"type": "object",
|
|
158
|
+
"properties": {
|
|
159
|
+
"query": {
|
|
160
|
+
"type": "string",
|
|
161
|
+
"description": "Search query"
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"required": ["query"],
|
|
165
|
+
"additionalProperties": False,
|
|
166
|
+
}
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
def run(self, query: str) -> Dict[str, Any]:
|
|
170
|
+
"""
|
|
171
|
+
Perform a web search (mock implementation).
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
query: Search query
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Dictionary with search results
|
|
178
|
+
"""
|
|
179
|
+
# Mock search results
|
|
180
|
+
return {
|
|
181
|
+
"query": query,
|
|
182
|
+
"results": [
|
|
183
|
+
{
|
|
184
|
+
"title": f"Result 1 for '{query}'",
|
|
185
|
+
"url": "https://example.com/1",
|
|
186
|
+
"snippet": f"This is a mock result for the query: {query}",
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"title": f"Result 2 for '{query}'",
|
|
190
|
+
"url": "https://example.com/2",
|
|
191
|
+
"snippet": f"Another mock result about {query}",
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
"total_results": 2,
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async def arun(self, query: str) -> Dict[str, Any]:
|
|
198
|
+
"""Async version of run."""
|
|
199
|
+
await asyncio.sleep(0.8) # Simulate network delay
|
|
200
|
+
return self.run(query)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class DatabaseQueryTool(ToolComponent):
|
|
204
|
+
"""
|
|
205
|
+
Database query tool component (mock implementation).
|
|
206
|
+
Demonstrates a tool that could be exposed via MCP for security.
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
def __init__(self, name: str = "database_query", config: Optional[Dict] = None):
|
|
210
|
+
super().__init__(
|
|
211
|
+
name=name,
|
|
212
|
+
config=config,
|
|
213
|
+
description="Query the database",
|
|
214
|
+
json_schema={
|
|
215
|
+
"type": "object",
|
|
216
|
+
"properties": {
|
|
217
|
+
"query": {
|
|
218
|
+
"type": "string",
|
|
219
|
+
"description": "Database query string"
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
"required": ["query"],
|
|
223
|
+
"additionalProperties": False,
|
|
224
|
+
}
|
|
225
|
+
)
|
|
226
|
+
self._mock_db = {
|
|
227
|
+
"users": [
|
|
228
|
+
{"id": 1, "name": "Alice", "email": "alice@example.com"},
|
|
229
|
+
{"id": 2, "name": "Bob", "email": "bob@example.com"},
|
|
230
|
+
],
|
|
231
|
+
"products": [
|
|
232
|
+
{"id": 1, "name": "Widget", "price": 19.99},
|
|
233
|
+
{"id": 2, "name": "Gadget", "price": 29.99},
|
|
234
|
+
],
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
def run(self, query: str) -> Dict[str, Any]:
|
|
238
|
+
"""
|
|
239
|
+
Execute a database query (mock implementation).
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
query: Query string (simplified, e.g., "SELECT * FROM users")
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
Dictionary with query results
|
|
246
|
+
"""
|
|
247
|
+
# Very simple mock query parser
|
|
248
|
+
query_lower = query.lower()
|
|
249
|
+
|
|
250
|
+
if "users" in query_lower:
|
|
251
|
+
table = "users"
|
|
252
|
+
results = self._mock_db["users"]
|
|
253
|
+
elif "products" in query_lower:
|
|
254
|
+
table = "products"
|
|
255
|
+
results = self._mock_db["products"]
|
|
256
|
+
else:
|
|
257
|
+
table = "unknown"
|
|
258
|
+
results = []
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
"query": query,
|
|
262
|
+
"table": table,
|
|
263
|
+
"results": results,
|
|
264
|
+
"count": len(results),
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async def arun(self, query: str) -> Dict[str, Any]:
|
|
268
|
+
"""Async version of run."""
|
|
269
|
+
await asyncio.sleep(0.4) # Simulate database query time
|
|
270
|
+
return self.run(query)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class FileProcessorTool(ToolComponent):
|
|
274
|
+
"""
|
|
275
|
+
File processor tool component (mock implementation).
|
|
276
|
+
Demonstrates a tool that processes files.
|
|
277
|
+
"""
|
|
278
|
+
|
|
279
|
+
def __init__(self, name: str = "file_processor", config: Optional[Dict] = None):
|
|
280
|
+
super().__init__(
|
|
281
|
+
name=name,
|
|
282
|
+
config=config,
|
|
283
|
+
description="Process and analyze files",
|
|
284
|
+
json_schema={
|
|
285
|
+
"type": "object",
|
|
286
|
+
"properties": {
|
|
287
|
+
"query": {
|
|
288
|
+
"type": "string",
|
|
289
|
+
"description": "File path or processing command"
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
"required": ["query"],
|
|
293
|
+
"additionalProperties": False,
|
|
294
|
+
}
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
def run(self, query: str) -> Dict[str, Any]:
|
|
298
|
+
"""
|
|
299
|
+
Process a file (mock implementation).
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
query: File path or processing command
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Dictionary with processing results
|
|
306
|
+
"""
|
|
307
|
+
return {
|
|
308
|
+
"file": query,
|
|
309
|
+
"status": "processed",
|
|
310
|
+
"lines": 42,
|
|
311
|
+
"words": 256,
|
|
312
|
+
"characters": 1543,
|
|
313
|
+
"type": "text/plain",
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async def arun(self, query: str) -> Dict[str, Any]:
|
|
317
|
+
"""Async version of run."""
|
|
318
|
+
await asyncio.sleep(0.6) # Simulate file processing
|
|
319
|
+
return self.run(query)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
# === Helper Functions for Tool Registration ===
|
|
323
|
+
|
|
324
|
+
def get_all_example_tools() -> List[Dict[str, Any]]:
|
|
325
|
+
"""
|
|
326
|
+
Get configuration for all example tools.
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
List of tool configuration dictionaries
|
|
330
|
+
"""
|
|
331
|
+
return [
|
|
332
|
+
{
|
|
333
|
+
"name": "get_current_weather",
|
|
334
|
+
"function": get_current_weather,
|
|
335
|
+
"description": "Get the current weather for a location",
|
|
336
|
+
"json_schema": {
|
|
337
|
+
"type": "object",
|
|
338
|
+
"properties": {
|
|
339
|
+
"location": {
|
|
340
|
+
"type": "string",
|
|
341
|
+
"description": "City name or location"
|
|
342
|
+
},
|
|
343
|
+
"units": {
|
|
344
|
+
"type": "string",
|
|
345
|
+
"description": "Temperature units (celsius or fahrenheit)",
|
|
346
|
+
"enum": ["celsius", "fahrenheit"],
|
|
347
|
+
"default": "celsius"
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
"required": ["location"],
|
|
351
|
+
"additionalProperties": False,
|
|
352
|
+
},
|
|
353
|
+
"category": "utility",
|
|
354
|
+
"tags": ["weather", "external"],
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
"name": "calculate_distance",
|
|
358
|
+
"function": calculate_distance,
|
|
359
|
+
"description": "Calculate distance between two geographic coordinates",
|
|
360
|
+
"json_schema": {
|
|
361
|
+
"type": "object",
|
|
362
|
+
"properties": {
|
|
363
|
+
"lat1": {"type": "number", "description": "Latitude of first point"},
|
|
364
|
+
"lon1": {"type": "number", "description": "Longitude of first point"},
|
|
365
|
+
"lat2": {"type": "number", "description": "Latitude of second point"},
|
|
366
|
+
"lon2": {"type": "number", "description": "Longitude of second point"},
|
|
367
|
+
},
|
|
368
|
+
"required": ["lat1", "lon1", "lat2", "lon2"],
|
|
369
|
+
"additionalProperties": False,
|
|
370
|
+
},
|
|
371
|
+
"category": "math",
|
|
372
|
+
"tags": ["geography", "calculation"],
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
"name": "format_currency",
|
|
376
|
+
"function": format_currency,
|
|
377
|
+
"description": "Format a number as currency",
|
|
378
|
+
"json_schema": {
|
|
379
|
+
"type": "object",
|
|
380
|
+
"properties": {
|
|
381
|
+
"amount": {"type": "number", "description": "Amount to format"},
|
|
382
|
+
"currency": {
|
|
383
|
+
"type": "string",
|
|
384
|
+
"description": "Currency code",
|
|
385
|
+
"enum": ["USD", "EUR", "GBP", "JPY"],
|
|
386
|
+
"default": "USD"
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
"required": ["amount"],
|
|
390
|
+
"additionalProperties": False,
|
|
391
|
+
},
|
|
392
|
+
"category": "utility",
|
|
393
|
+
"tags": ["formatting", "currency"],
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
"name": "translate_text",
|
|
397
|
+
"function": translate_text,
|
|
398
|
+
"description": "Translate text to target language",
|
|
399
|
+
"json_schema": {
|
|
400
|
+
"type": "object",
|
|
401
|
+
"properties": {
|
|
402
|
+
"text": {"type": "string", "description": "Text to translate"},
|
|
403
|
+
"target_language": {
|
|
404
|
+
"type": "string",
|
|
405
|
+
"description": "Target language code",
|
|
406
|
+
"enum": ["en", "es", "fr", "de"],
|
|
407
|
+
"default": "en"
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
"required": ["text"],
|
|
411
|
+
"additionalProperties": False,
|
|
412
|
+
},
|
|
413
|
+
"category": "nlp",
|
|
414
|
+
"tags": ["translation", "language"],
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
"name": "analyze_sentiment",
|
|
418
|
+
"function": analyze_sentiment,
|
|
419
|
+
"description": "Analyze sentiment of text",
|
|
420
|
+
"json_schema": {
|
|
421
|
+
"type": "object",
|
|
422
|
+
"properties": {
|
|
423
|
+
"text": {"type": "string", "description": "Text to analyze"},
|
|
424
|
+
},
|
|
425
|
+
"required": ["text"],
|
|
426
|
+
"additionalProperties": False,
|
|
427
|
+
},
|
|
428
|
+
"category": "nlp",
|
|
429
|
+
"tags": ["sentiment", "analysis"],
|
|
430
|
+
},
|
|
431
|
+
]
|