kite-agent 0.1.0__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.
- kite/__init__.py +46 -0
- kite/ab_testing.py +384 -0
- kite/agent.py +556 -0
- kite/agents/__init__.py +3 -0
- kite/agents/plan_execute.py +191 -0
- kite/agents/react_agent.py +509 -0
- kite/agents/reflective_agent.py +90 -0
- kite/agents/rewoo.py +119 -0
- kite/agents/tot.py +151 -0
- kite/conversation.py +125 -0
- kite/core.py +974 -0
- kite/data_loaders.py +111 -0
- kite/embedding_providers.py +372 -0
- kite/llm_providers.py +1278 -0
- kite/memory/__init__.py +6 -0
- kite/memory/advanced_rag.py +333 -0
- kite/memory/graph_rag.py +719 -0
- kite/memory/session_memory.py +423 -0
- kite/memory/vector_memory.py +579 -0
- kite/monitoring.py +611 -0
- kite/observers.py +107 -0
- kite/optimization/__init__.py +9 -0
- kite/optimization/resource_router.py +80 -0
- kite/persistence.py +42 -0
- kite/pipeline/__init__.py +5 -0
- kite/pipeline/deterministic_pipeline.py +323 -0
- kite/pipeline/reactive_pipeline.py +171 -0
- kite/pipeline_manager.py +15 -0
- kite/routing/__init__.py +6 -0
- kite/routing/aggregator_router.py +325 -0
- kite/routing/llm_router.py +149 -0
- kite/routing/semantic_router.py +228 -0
- kite/safety/__init__.py +6 -0
- kite/safety/circuit_breaker.py +360 -0
- kite/safety/guardrails.py +82 -0
- kite/safety/idempotency_manager.py +304 -0
- kite/safety/kill_switch.py +75 -0
- kite/tool.py +183 -0
- kite/tool_registry.py +87 -0
- kite/tools/__init__.py +21 -0
- kite/tools/code_execution.py +53 -0
- kite/tools/contrib/__init__.py +19 -0
- kite/tools/contrib/calculator.py +26 -0
- kite/tools/contrib/datetime_utils.py +20 -0
- kite/tools/contrib/linkedin.py +428 -0
- kite/tools/contrib/web_search.py +30 -0
- kite/tools/mcp/__init__.py +31 -0
- kite/tools/mcp/database_mcp.py +267 -0
- kite/tools/mcp/gdrive_mcp_server.py +503 -0
- kite/tools/mcp/gmail_mcp_server.py +601 -0
- kite/tools/mcp/postgres_mcp_server.py +490 -0
- kite/tools/mcp/slack_mcp_server.py +538 -0
- kite/tools/mcp/stripe_mcp_server.py +219 -0
- kite/tools/search.py +90 -0
- kite/tools/system_tools.py +54 -0
- kite/tools_manager.py +27 -0
- kite_agent-0.1.0.dist-info/METADATA +621 -0
- kite_agent-0.1.0.dist-info/RECORD +61 -0
- kite_agent-0.1.0.dist-info/WHEEL +5 -0
- kite_agent-0.1.0.dist-info/licenses/LICENSE +21 -0
- kite_agent-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Database Connectors
|
|
3
|
+
Provides Model Context Protocol (MCP) inspired tools for database interaction.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import json
|
|
8
|
+
from typing import Dict, List, Any, Optional
|
|
9
|
+
import sqlite3
|
|
10
|
+
import psycopg2
|
|
11
|
+
import mysql.connector
|
|
12
|
+
import redis
|
|
13
|
+
import asyncio
|
|
14
|
+
from functools import wraps
|
|
15
|
+
|
|
16
|
+
# New Production Connectors
|
|
17
|
+
try:
|
|
18
|
+
from neo4j import GraphDatabase
|
|
19
|
+
except ImportError:
|
|
20
|
+
GraphDatabase = None
|
|
21
|
+
try:
|
|
22
|
+
from cassandra.cluster import Cluster
|
|
23
|
+
from cassandra.auth import PlainTextAuthProvider
|
|
24
|
+
except Exception:
|
|
25
|
+
Cluster = None
|
|
26
|
+
try:
|
|
27
|
+
import boto3
|
|
28
|
+
except ImportError:
|
|
29
|
+
boto3 = None
|
|
30
|
+
try:
|
|
31
|
+
from google.cloud import firestore
|
|
32
|
+
except ImportError:
|
|
33
|
+
firestore = None
|
|
34
|
+
|
|
35
|
+
def safe_execute(timeout_seconds: int = 30):
|
|
36
|
+
"""Decorator for safe tool execution with timeouts and error handling."""
|
|
37
|
+
def decorator(func):
|
|
38
|
+
@wraps(func)
|
|
39
|
+
async def wrapper(*args, **kwargs):
|
|
40
|
+
try:
|
|
41
|
+
# Handle both sync and async functions
|
|
42
|
+
if asyncio.iscoroutinefunction(func):
|
|
43
|
+
return await asyncio.wait_for(func(*args, **kwargs), timeout=timeout_seconds)
|
|
44
|
+
else:
|
|
45
|
+
# For sync functions in an async environment
|
|
46
|
+
loop = asyncio.get_event_loop()
|
|
47
|
+
return await asyncio.wait_for(
|
|
48
|
+
loop.run_in_executor(None, lambda: func(*args, **kwargs)),
|
|
49
|
+
timeout=timeout_seconds
|
|
50
|
+
)
|
|
51
|
+
except asyncio.TimeoutError:
|
|
52
|
+
return {"error": f"Database operation timed out after {timeout_seconds}s"}
|
|
53
|
+
except Exception as e:
|
|
54
|
+
return {"error": f"Database error: {str(e)}"}
|
|
55
|
+
return wrapper
|
|
56
|
+
return decorator
|
|
57
|
+
|
|
58
|
+
class DatabaseMCP:
|
|
59
|
+
"""
|
|
60
|
+
MCP-style interface for various databases.
|
|
61
|
+
Implements tools for query and schema discovery.
|
|
62
|
+
"""
|
|
63
|
+
def __init__(self, config: Optional[Dict] = None):
|
|
64
|
+
self.config = config or {}
|
|
65
|
+
self.conns = {}
|
|
66
|
+
|
|
67
|
+
def _get_pg_conn(self):
|
|
68
|
+
if 'postgres' not in self.conns:
|
|
69
|
+
self.conns['postgres'] = psycopg2.connect(
|
|
70
|
+
dsn=self.config.get('postgres_dsn', os.getenv('POSTGRES_DSN'))
|
|
71
|
+
)
|
|
72
|
+
return self.conns['postgres']
|
|
73
|
+
|
|
74
|
+
def _get_mysql_conn(self):
|
|
75
|
+
if 'mysql' not in self.conns:
|
|
76
|
+
self.conns['mysql'] = mysql.connector.connect(
|
|
77
|
+
host=self.config.get('mysql_host', os.getenv('MYSQL_HOST')),
|
|
78
|
+
user=self.config.get('mysql_user', os.getenv('MYSQL_USER')),
|
|
79
|
+
password=self.config.get('mysql_password', os.getenv('MYSQL_PASSWORD')),
|
|
80
|
+
database=self.config.get('mysql_db', os.getenv('MYSQL_DB'))
|
|
81
|
+
)
|
|
82
|
+
return self.conns['mysql']
|
|
83
|
+
|
|
84
|
+
def _get_sqlite_conn(self):
|
|
85
|
+
if 'sqlite' not in self.conns:
|
|
86
|
+
self.conns['sqlite'] = sqlite3.connect(
|
|
87
|
+
self.config.get('sqlite_db', os.getenv('SQLITE_DB', 'kite.db')),
|
|
88
|
+
check_same_thread=False
|
|
89
|
+
)
|
|
90
|
+
return self.conns['sqlite']
|
|
91
|
+
|
|
92
|
+
def _get_redis_conn(self):
|
|
93
|
+
if 'redis' not in self.conns:
|
|
94
|
+
self.conns['redis'] = redis.Redis(
|
|
95
|
+
host=self.config.get('redis_host', os.getenv('REDIS_HOST', 'localhost')),
|
|
96
|
+
port=self.config.get('redis_port', os.getenv('REDIS_PORT', 6379)),
|
|
97
|
+
db=self.config.get('redis_db', 0),
|
|
98
|
+
decode_responses=True,
|
|
99
|
+
socket_timeout=5,
|
|
100
|
+
retry_on_timeout=True
|
|
101
|
+
)
|
|
102
|
+
return self.conns['redis']
|
|
103
|
+
|
|
104
|
+
def _get_neo4j_conn(self):
|
|
105
|
+
if 'neo4j' not in self.conns:
|
|
106
|
+
if GraphDatabase is None: raise ImportError("neo4j not installed")
|
|
107
|
+
self.conns['neo4j'] = GraphDatabase.driver(
|
|
108
|
+
self.config.get('neo4j_uri', os.getenv('NEO4J_URI')),
|
|
109
|
+
auth=(self.config.get('neo4j_user', os.getenv('NEO4J_USER')),
|
|
110
|
+
self.config.get('neo4j_password', os.getenv('NEO4J_PASSWORD')))
|
|
111
|
+
)
|
|
112
|
+
return self.conns['neo4j']
|
|
113
|
+
|
|
114
|
+
def _get_cassandra_conn(self):
|
|
115
|
+
if 'cassandra' not in self.conns:
|
|
116
|
+
if Cluster is None: raise ImportError("cassandra-driver not installed")
|
|
117
|
+
auth_provider = PlainTextAuthProvider(
|
|
118
|
+
username=self.config.get('cassandra_user', os.getenv('CASSANDRA_USER')),
|
|
119
|
+
password=self.config.get('cassandra_password', os.getenv('CASSANDRA_PASSWORD'))
|
|
120
|
+
)
|
|
121
|
+
self.conns['cassandra'] = Cluster(
|
|
122
|
+
[self.config.get('cassandra_host', os.getenv('CASSANDRA_HOST', 'localhost'))],
|
|
123
|
+
auth_provider=auth_provider
|
|
124
|
+
).connect()
|
|
125
|
+
return self.conns['cassandra']
|
|
126
|
+
|
|
127
|
+
def _get_dynamodb_resource(self):
|
|
128
|
+
if 'dynamodb' not in self.conns:
|
|
129
|
+
if boto3 is None: raise ImportError("boto3 not installed")
|
|
130
|
+
self.conns['dynamodb'] = boto3.resource(
|
|
131
|
+
'dynamodb',
|
|
132
|
+
region_name=self.config.get('aws_region', os.getenv('AWS_REGION', 'us-east-1'))
|
|
133
|
+
)
|
|
134
|
+
return self.conns['dynamodb']
|
|
135
|
+
|
|
136
|
+
def _get_firestore_client(self):
|
|
137
|
+
if 'firestore' not in self.conns:
|
|
138
|
+
if firestore is None: raise ImportError("google-cloud-firestore not installed")
|
|
139
|
+
self.conns['firestore'] = firestore.Client()
|
|
140
|
+
return self.conns['firestore']
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@safe_execute(timeout_seconds=30)
|
|
144
|
+
def query_postgres(self, sql: str, params: Optional[tuple] = None) -> List[Dict]:
|
|
145
|
+
"""Execute a PostgreSQL query and return results."""
|
|
146
|
+
conn = self._get_pg_conn()
|
|
147
|
+
with conn.cursor() as cur:
|
|
148
|
+
cur.execute(sql, params)
|
|
149
|
+
columns = [desc[0] for desc in cur.description]
|
|
150
|
+
return [dict(zip(columns, row)) for row in cur.fetchall()]
|
|
151
|
+
|
|
152
|
+
@safe_execute(timeout_seconds=30)
|
|
153
|
+
def query_sqlite(self, sql: str, params: Optional[tuple] = None) -> List[Dict]:
|
|
154
|
+
"""Execute a SQLite query and return results."""
|
|
155
|
+
conn = self._get_sqlite_conn()
|
|
156
|
+
conn.row_factory = sqlite3.Row
|
|
157
|
+
cur = conn.cursor()
|
|
158
|
+
cur.execute(sql, params or ())
|
|
159
|
+
return [dict(row) for row in cur.fetchall()]
|
|
160
|
+
|
|
161
|
+
@safe_execute(timeout_seconds=30)
|
|
162
|
+
def query_mysql(self, sql: str, params: Optional[tuple] = None) -> List[Dict]:
|
|
163
|
+
"""Execute a MySQL query and return results."""
|
|
164
|
+
conn = self._get_mysql_conn()
|
|
165
|
+
cur = conn.cursor(dictionary=True)
|
|
166
|
+
cur.execute(sql, params)
|
|
167
|
+
return cur.fetchall()
|
|
168
|
+
|
|
169
|
+
@safe_execute(timeout_seconds=30)
|
|
170
|
+
async def query_mongo(self, collection: str, query: Dict, limit: int = 10) -> List[Dict]:
|
|
171
|
+
"""Query a MongoDB collection."""
|
|
172
|
+
from motor.motor_asyncio import AsyncIOMotorClient
|
|
173
|
+
client = AsyncIOMotorClient(self.config.get('mongo_uri', os.getenv('MONGO_URI')))
|
|
174
|
+
db_name = self.config.get('mongo_db', os.getenv('MONGO_DB'))
|
|
175
|
+
results = await client[db_name][collection].find(query).to_list(length=limit)
|
|
176
|
+
return results
|
|
177
|
+
|
|
178
|
+
@safe_execute(timeout_seconds=30)
|
|
179
|
+
def query_neo4j(self, cypher: str, params: Optional[Dict] = None) -> List[Dict]:
|
|
180
|
+
"""Execute a Cypher query on Neo4j."""
|
|
181
|
+
driver = self._get_neo4j_conn()
|
|
182
|
+
with driver.session() as session:
|
|
183
|
+
result = session.run(cypher, params or {})
|
|
184
|
+
return [dict(record) for record in result]
|
|
185
|
+
|
|
186
|
+
@safe_execute(timeout_seconds=30)
|
|
187
|
+
def query_cassandra(self, cql: str, params: Optional[tuple] = None) -> List[Dict]:
|
|
188
|
+
"""Execute a CQL query on Cassandra."""
|
|
189
|
+
session = self._get_cassandra_conn()
|
|
190
|
+
rows = session.execute(cql, params or ())
|
|
191
|
+
return [dict(row._asdict()) for row in rows]
|
|
192
|
+
|
|
193
|
+
@safe_execute(timeout_seconds=30)
|
|
194
|
+
def query_dynamodb(self, table_name: str, key: Dict) -> Dict:
|
|
195
|
+
"""Get an item from DynamoDB."""
|
|
196
|
+
table = self._get_dynamodb_resource().Table(table_name)
|
|
197
|
+
response = table.get_item(Key=key)
|
|
198
|
+
return response.get('Item', {})
|
|
199
|
+
|
|
200
|
+
@safe_execute(timeout_seconds=30)
|
|
201
|
+
def query_firestore(self, collection: str, document: str) -> Dict:
|
|
202
|
+
"""Get a document from Firestore."""
|
|
203
|
+
client = self._get_firestore_client()
|
|
204
|
+
doc_ref = client.collection(collection).document(document)
|
|
205
|
+
doc = doc_ref.get()
|
|
206
|
+
return doc.to_dict() if doc.exists else {}
|
|
207
|
+
|
|
208
|
+
def redis_get(self, key: str) -> Optional[str]:
|
|
209
|
+
"""Get value from Redis."""
|
|
210
|
+
return self._get_redis_conn().get(key)
|
|
211
|
+
|
|
212
|
+
def redis_set(self, key: str, value: str, ex: int = 3600):
|
|
213
|
+
"""Set value in Redis with TTL."""
|
|
214
|
+
self._get_redis_conn().set(key, value, ex=ex)
|
|
215
|
+
|
|
216
|
+
def get_tools(self) -> List[Dict]:
|
|
217
|
+
"""Expose methods as Kite tools."""
|
|
218
|
+
return [
|
|
219
|
+
{
|
|
220
|
+
"name": "query_postgres",
|
|
221
|
+
"description": "Execute Read-only SQL queries on PostgreSQL",
|
|
222
|
+
"parameters": {"sql": "string"},
|
|
223
|
+
"func": self.query_postgres
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"name": "query_mysql",
|
|
227
|
+
"description": "Execute Read-only SQL queries on MySQL",
|
|
228
|
+
"parameters": {"sql": "string"},
|
|
229
|
+
"func": self.query_mysql
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"name": "redis_get",
|
|
233
|
+
"description": "Retrieve data from Redis operational cache",
|
|
234
|
+
"parameters": {"key": "string"},
|
|
235
|
+
"func": self.redis_get
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"name": "query_sqlite",
|
|
239
|
+
"description": "Execute Read-only SQL queries on local SQLite database",
|
|
240
|
+
"parameters": {"sql": "string"},
|
|
241
|
+
"func": self.query_sqlite
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"name": "query_neo4j",
|
|
245
|
+
"description": "Execute Cypher queries on Neo4j graph database",
|
|
246
|
+
"parameters": {"cypher": "string"},
|
|
247
|
+
"func": self.query_neo4j
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"name": "query_cassandra",
|
|
251
|
+
"description": "Execute CQL queries on Cassandra database",
|
|
252
|
+
"parameters": {"cql": "string"},
|
|
253
|
+
"func": self.query_cassandra
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"name": "query_dynamodb",
|
|
257
|
+
"description": "Get item from DynamoDB table",
|
|
258
|
+
"parameters": {"table_name": "string", "key": "object"},
|
|
259
|
+
"func": self.query_dynamodb
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"name": "query_firestore",
|
|
263
|
+
"description": "Get document from Firestore collection",
|
|
264
|
+
"parameters": {"collection": "string", "document": "string"},
|
|
265
|
+
"func": self.query_firestore
|
|
266
|
+
}
|
|
267
|
+
]
|