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.
Files changed (61) hide show
  1. kite/__init__.py +46 -0
  2. kite/ab_testing.py +384 -0
  3. kite/agent.py +556 -0
  4. kite/agents/__init__.py +3 -0
  5. kite/agents/plan_execute.py +191 -0
  6. kite/agents/react_agent.py +509 -0
  7. kite/agents/reflective_agent.py +90 -0
  8. kite/agents/rewoo.py +119 -0
  9. kite/agents/tot.py +151 -0
  10. kite/conversation.py +125 -0
  11. kite/core.py +974 -0
  12. kite/data_loaders.py +111 -0
  13. kite/embedding_providers.py +372 -0
  14. kite/llm_providers.py +1278 -0
  15. kite/memory/__init__.py +6 -0
  16. kite/memory/advanced_rag.py +333 -0
  17. kite/memory/graph_rag.py +719 -0
  18. kite/memory/session_memory.py +423 -0
  19. kite/memory/vector_memory.py +579 -0
  20. kite/monitoring.py +611 -0
  21. kite/observers.py +107 -0
  22. kite/optimization/__init__.py +9 -0
  23. kite/optimization/resource_router.py +80 -0
  24. kite/persistence.py +42 -0
  25. kite/pipeline/__init__.py +5 -0
  26. kite/pipeline/deterministic_pipeline.py +323 -0
  27. kite/pipeline/reactive_pipeline.py +171 -0
  28. kite/pipeline_manager.py +15 -0
  29. kite/routing/__init__.py +6 -0
  30. kite/routing/aggregator_router.py +325 -0
  31. kite/routing/llm_router.py +149 -0
  32. kite/routing/semantic_router.py +228 -0
  33. kite/safety/__init__.py +6 -0
  34. kite/safety/circuit_breaker.py +360 -0
  35. kite/safety/guardrails.py +82 -0
  36. kite/safety/idempotency_manager.py +304 -0
  37. kite/safety/kill_switch.py +75 -0
  38. kite/tool.py +183 -0
  39. kite/tool_registry.py +87 -0
  40. kite/tools/__init__.py +21 -0
  41. kite/tools/code_execution.py +53 -0
  42. kite/tools/contrib/__init__.py +19 -0
  43. kite/tools/contrib/calculator.py +26 -0
  44. kite/tools/contrib/datetime_utils.py +20 -0
  45. kite/tools/contrib/linkedin.py +428 -0
  46. kite/tools/contrib/web_search.py +30 -0
  47. kite/tools/mcp/__init__.py +31 -0
  48. kite/tools/mcp/database_mcp.py +267 -0
  49. kite/tools/mcp/gdrive_mcp_server.py +503 -0
  50. kite/tools/mcp/gmail_mcp_server.py +601 -0
  51. kite/tools/mcp/postgres_mcp_server.py +490 -0
  52. kite/tools/mcp/slack_mcp_server.py +538 -0
  53. kite/tools/mcp/stripe_mcp_server.py +219 -0
  54. kite/tools/search.py +90 -0
  55. kite/tools/system_tools.py +54 -0
  56. kite/tools_manager.py +27 -0
  57. kite_agent-0.1.0.dist-info/METADATA +621 -0
  58. kite_agent-0.1.0.dist-info/RECORD +61 -0
  59. kite_agent-0.1.0.dist-info/WHEEL +5 -0
  60. kite_agent-0.1.0.dist-info/licenses/LICENSE +21 -0
  61. kite_agent-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,503 @@
1
+ """
2
+ Google Drive MCP Server Implementation
3
+ Based on Chapter 4: MCP (Model Context Protocol)
4
+
5
+ Allows AI agents to interact with Google Drive.
6
+
7
+ Tools provided:
8
+ - search_files: Search for files
9
+ - read_file: Read file content
10
+ - list_folder: List folder contents
11
+ - get_file_metadata: Get file details
12
+
13
+ Run: python gdrive_mcp_server.py
14
+ """
15
+
16
+ import os
17
+ import json
18
+ from typing import Dict, List, Optional, Any
19
+ from dataclasses import dataclass
20
+ from datetime import datetime
21
+ from dotenv import load_dotenv
22
+
23
+ load_dotenv()
24
+
25
+
26
+ # ============================================================================
27
+ # CONFIGURATION
28
+ # ============================================================================
29
+
30
+ @dataclass
31
+ class GDriveConfig:
32
+ """Configuration for Google Drive MCP server."""
33
+ credentials_path: str = ""
34
+
35
+ # Safety limits
36
+ max_file_size_mb: int = 10
37
+ max_search_results: int = 50
38
+ rate_limit_per_minute: int = 100
39
+
40
+ # Allowed operations
41
+ enable_read: bool = True
42
+ enable_search: bool = True
43
+ enable_list: bool = True
44
+
45
+ # File type restrictions
46
+ allowed_mime_types: Optional[List[str]] = None
47
+
48
+
49
+ # ============================================================================
50
+ # GOOGLE DRIVE MCP SERVER
51
+ # ============================================================================
52
+
53
+ class GoogleDriveMCPServer:
54
+ """
55
+ MCP Server for Google Drive integration.
56
+
57
+ Provides tools for AI agents to search and read Google Drive files.
58
+
59
+ Requires: google-api-python-client, google-auth-httplib2, google-auth-oauthlib
60
+ Install: pip install google-api-python-client google-auth-httplib2 google-auth-oauthlib
61
+
62
+ Setup:
63
+ 1. Enable Google Drive API in Google Cloud Console
64
+ 2. Download credentials.json
65
+ 3. Run authentication flow to get token
66
+
67
+ Example:
68
+ from googleapiclient.discovery import build
69
+ from google.oauth2.credentials import Credentials
70
+
71
+ config = GDriveConfig(credentials_path='~/.gdrive_credentials.json')
72
+ server = GoogleDriveMCPServer(config)
73
+
74
+ # Search files
75
+ results = server.search_files("Q4 strategy")
76
+
77
+ # Read file
78
+ content = server.read_file("file1")
79
+ """
80
+
81
+ def __init__(self, config: GDriveConfig = None, credentials_path: str = "", **kwargs):
82
+ self.config = config or GDriveConfig()
83
+ if credentials_path:
84
+ self.config.credentials_path = credentials_path
85
+
86
+ # Initialize Drive client (requires Google API client)
87
+ try:
88
+ from googleapiclient.discovery import build
89
+ from google.oauth2.credentials import Credentials
90
+ from google.auth.transport.requests import Request
91
+ from google_auth_oauthlib.flow import InstalledAppFlow
92
+ import os
93
+ import pickle
94
+
95
+ SCOPES = ['https://www.googleapis.com/auth/drive.readonly']
96
+
97
+ creds = None
98
+ token_path = os.path.expanduser('~/.gdrive_token.pickle')
99
+
100
+ # Load existing token
101
+ if os.path.exists(token_path):
102
+ with open(token_path, 'rb') as token:
103
+ creds = pickle.load(token)
104
+
105
+ # Refresh or get new credentials
106
+ if not creds or not creds.valid:
107
+ if creds and creds.expired and creds.refresh_token:
108
+ creds.refresh(Request())
109
+ elif self.config.credentials_path and os.path.exists(self.config.credentials_path):
110
+ flow = InstalledAppFlow.from_client_secrets_file(
111
+ self.config.credentials_path, SCOPES)
112
+ creds = flow.run_local_server(port=0)
113
+ else:
114
+ raise Exception("Google Drive credentials not found. Please provide credentials_path")
115
+
116
+ # Save token
117
+ with open(token_path, 'wb') as token:
118
+ pickle.dump(creds, token)
119
+
120
+ self.drive = build('drive', 'v3', credentials=creds)
121
+
122
+ except ImportError:
123
+ raise ImportError(
124
+ "Google API client is required for GoogleDriveMCPServer. "
125
+ "Install with: pip install google-api-python-client google-auth-httplib2 google-auth-oauthlib"
126
+ )
127
+ except Exception as e:
128
+ raise Exception(f"Failed to initialize Google Drive client: {e}")
129
+
130
+
131
+ # Rate limiting
132
+ self.request_count = 0
133
+ self.window_start = datetime.now()
134
+
135
+ print(f"[OK] Google Drive MCP Server initialized")
136
+ print(f" Rate limit: {self.config.rate_limit_per_minute}/min")
137
+ print(f" Max file size: {self.config.max_file_size_mb}MB")
138
+
139
+ def _check_rate_limit(self) -> bool:
140
+ """Check rate limit."""
141
+ now = datetime.now()
142
+
143
+ if (now - self.window_start).seconds >= 60:
144
+ self.request_count = 0
145
+ self.window_start = now
146
+
147
+ if self.request_count >= self.config.rate_limit_per_minute:
148
+ return False
149
+
150
+ self.request_count += 1
151
+ return True
152
+
153
+ def _is_mime_type_allowed(self, mime_type: str) -> bool:
154
+ """Check if MIME type is allowed."""
155
+ if self.config.allowed_mime_types is None:
156
+ return True
157
+
158
+ return mime_type in self.config.allowed_mime_types
159
+
160
+ def search_files(
161
+ self,
162
+ query: str,
163
+ max_results: int = 10
164
+ ) -> Dict[str, Any]:
165
+ """
166
+ Search for files in Google Drive.
167
+
168
+ Args:
169
+ query: Search query
170
+ max_results: Maximum results to return
171
+
172
+ Returns:
173
+ Search results
174
+ """
175
+ print(f"\n Searching Drive: {query}")
176
+
177
+ if not self.config.enable_search:
178
+ return {
179
+ "success": False,
180
+ "error": "Search is disabled"
181
+ }
182
+
183
+ if not self._check_rate_limit():
184
+ return {
185
+ "success": False,
186
+ "error": "Rate limit exceeded"
187
+ }
188
+
189
+ try:
190
+ results = self.drive.files_list(
191
+ q=query,
192
+ pageSize=min(max_results, self.config.max_search_results)
193
+ )
194
+
195
+ files = results.get("files", [])
196
+
197
+ # Filter by allowed MIME types
198
+ filtered_files = []
199
+ for file in files:
200
+ if self._is_mime_type_allowed(file.get("mimeType", "")):
201
+ filtered_files.append({
202
+ "id": file["id"],
203
+ "name": file["name"],
204
+ "mimeType": file["mimeType"],
205
+ "size": file.get("size", 0),
206
+ "modifiedTime": file.get("modifiedTime"),
207
+ "owners": file.get("owners", [])
208
+ })
209
+
210
+ print(f" [OK] Found {len(filtered_files)} files")
211
+
212
+ return {
213
+ "success": True,
214
+ "query": query,
215
+ "files": filtered_files,
216
+ "count": len(filtered_files)
217
+ }
218
+
219
+ except Exception as e:
220
+ print(f" Error: {e}")
221
+ return {
222
+ "success": False,
223
+ "error": str(e)
224
+ }
225
+
226
+ def read_file(self, file_id: str) -> Dict[str, Any]:
227
+ """
228
+ Read file content from Google Drive.
229
+
230
+ Args:
231
+ file_id: Google Drive file ID
232
+
233
+ Returns:
234
+ File content
235
+ """
236
+ print(f"\n Reading file: {file_id}")
237
+
238
+ if not self.config.enable_read:
239
+ return {
240
+ "success": False,
241
+ "error": "Read is disabled"
242
+ }
243
+
244
+ if not self._check_rate_limit():
245
+ return {
246
+ "success": False,
247
+ "error": "Rate limit exceeded"
248
+ }
249
+
250
+ try:
251
+ # Get file metadata first
252
+ metadata = self.drive.files_get(fileId=file_id)
253
+
254
+ # Check MIME type
255
+ if not self._is_mime_type_allowed(metadata.get("mimeType", "")):
256
+ return {
257
+ "success": False,
258
+ "error": f"MIME type not allowed: {metadata.get('mimeType')}"
259
+ }
260
+
261
+ # Check file size
262
+ size_mb = metadata.get("size", 0) / (1024 * 1024)
263
+ if size_mb > self.config.max_file_size_mb:
264
+ return {
265
+ "success": False,
266
+ "error": f"File too large: {size_mb:.1f}MB > {self.config.max_file_size_mb}MB"
267
+ }
268
+
269
+ # Read content
270
+ content = self.drive.files_get_media(fileId=file_id)
271
+
272
+ print(f" [OK] Read {len(content)} characters")
273
+
274
+ return {
275
+ "success": True,
276
+ "file_id": file_id,
277
+ "name": metadata.get("name"),
278
+ "content": content,
279
+ "metadata": {
280
+ "mimeType": metadata.get("mimeType"),
281
+ "size": metadata.get("size"),
282
+ "modifiedTime": metadata.get("modifiedTime"),
283
+ "owners": metadata.get("owners", [])
284
+ }
285
+ }
286
+
287
+ except Exception as e:
288
+ print(f" Error: {e}")
289
+ return {
290
+ "success": False,
291
+ "error": str(e)
292
+ }
293
+
294
+ def get_file_metadata(self, file_id: str) -> Dict[str, Any]:
295
+ """
296
+ Get file metadata without reading content.
297
+
298
+ Args:
299
+ file_id: Google Drive file ID
300
+
301
+ Returns:
302
+ File metadata
303
+ """
304
+ print(f"\n[CHART] Getting metadata: {file_id}")
305
+
306
+ if not self._check_rate_limit():
307
+ return {
308
+ "success": False,
309
+ "error": "Rate limit exceeded"
310
+ }
311
+
312
+ try:
313
+ metadata = self.drive.files_get(fileId=file_id)
314
+
315
+ print(f" [OK] Retrieved metadata for: {metadata.get('name')}")
316
+
317
+ return {
318
+ "success": True,
319
+ "file_id": file_id,
320
+ "metadata": {
321
+ "name": metadata.get("name"),
322
+ "mimeType": metadata.get("mimeType"),
323
+ "size": metadata.get("size"),
324
+ "createdTime": metadata.get("createdTime"),
325
+ "modifiedTime": metadata.get("modifiedTime"),
326
+ "owners": metadata.get("owners", []),
327
+ "parents": metadata.get("parents", [])
328
+ }
329
+ }
330
+
331
+ except Exception as e:
332
+ print(f" Error: {e}")
333
+ return {
334
+ "success": False,
335
+ "error": str(e)
336
+ }
337
+
338
+ def get_tool_definitions(self) -> List[Dict]:
339
+ """Get MCP tool definitions for AI agents."""
340
+ tools = []
341
+
342
+ if self.config.enable_search:
343
+ tools.append({
344
+ "name": "gdrive_search_files",
345
+ "description": "Search for files in Google Drive by keyword or content",
346
+ "input_schema": {
347
+ "type": "object",
348
+ "properties": {
349
+ "query": {
350
+ "type": "string",
351
+ "description": "Search query (keywords, file names, content)"
352
+ },
353
+ "max_results": {
354
+ "type": "integer",
355
+ "description": "Maximum number of results",
356
+ "default": 10
357
+ }
358
+ },
359
+ "required": ["query"]
360
+ }
361
+ })
362
+
363
+ if self.config.enable_read:
364
+ tools.append({
365
+ "name": "gdrive_read_file",
366
+ "description": "Read the content of a Google Drive file",
367
+ "input_schema": {
368
+ "type": "object",
369
+ "properties": {
370
+ "file_id": {
371
+ "type": "string",
372
+ "description": "Google Drive file ID"
373
+ }
374
+ },
375
+ "required": ["file_id"]
376
+ }
377
+ })
378
+
379
+ tools.append({
380
+ "name": "gdrive_get_metadata",
381
+ "description": "Get metadata about a Google Drive file without reading its content",
382
+ "input_schema": {
383
+ "type": "object",
384
+ "properties": {
385
+ "file_id": {
386
+ "type": "string",
387
+ "description": "Google Drive file ID"
388
+ }
389
+ },
390
+ "required": ["file_id"]
391
+ }
392
+ })
393
+
394
+ return tools
395
+
396
+
397
+ # ============================================================================
398
+ # DEMO
399
+ # ============================================================================
400
+
401
+ def demo():
402
+ print("=" * 70)
403
+ print("GOOGLE DRIVE MCP SERVER DEMO")
404
+ print("=" * 70)
405
+ print("\nBased on Chapter 4: Model Context Protocol")
406
+ print("Allows AI agents to search and read Google Drive files\n")
407
+ print("=" * 70)
408
+
409
+ # Initialize server
410
+ config = GDriveConfig(
411
+ max_file_size_mb=10,
412
+ enable_read=True,
413
+ enable_search=True
414
+ )
415
+
416
+ server = GDriveMCPServer(config)
417
+
418
+ # Demo 1: Search files
419
+ print(f"\n{'='*70}")
420
+ print("DEMO 1: Search for files")
421
+ print('='*70)
422
+
423
+ result = server.search_files("Project Zeus")
424
+ if result["success"]:
425
+ print(f"\nFound {result['count']} files matching '{result['query']}':")
426
+ for file in result["files"]:
427
+ print(f"\n {file['name']}")
428
+ print(f" ID: {file['id']}")
429
+ print(f" Type: {file['mimeType']}")
430
+ print(f" Modified: {file.get('modifiedTime', 'N/A')}")
431
+
432
+ # Demo 2: Read file content
433
+ print(f"\n{'='*70}")
434
+ print("DEMO 2: Read file content")
435
+ print('='*70)
436
+
437
+ result = server.read_file("file1")
438
+ if result["success"]:
439
+ print(f"\n File: {result['name']}")
440
+ print(f" Size: {result['metadata']['size']} bytes")
441
+ print(f"\n Content preview:")
442
+ print(" " + " " * 66)
443
+ preview = result['content'][:300] + "..." if len(result['content']) > 300 else result['content']
444
+ for line in preview.split('\n'):
445
+ print(f" {line}")
446
+ print(" " + " " * 66)
447
+
448
+ # Demo 3: Get metadata only
449
+ print(f"\n{'='*70}")
450
+ print("DEMO 3: Get file metadata")
451
+ print('='*70)
452
+
453
+ result = server.get_file_metadata("file2")
454
+ if result["success"]:
455
+ meta = result["metadata"]
456
+ print(f"\n[CHART] Metadata for: {meta['name']}")
457
+ print(f" Type: {meta['mimeType']}")
458
+ print(f" Size: {meta['size']} bytes")
459
+ print(f" Created: {meta['createdTime']}")
460
+ print(f" Modified: {meta['modifiedTime']}")
461
+ print(f" Owners: {', '.join(o['displayName'] for o in meta['owners'])}")
462
+
463
+ # Show tool definitions
464
+ print(f"\n{'='*70}")
465
+ print("MCP TOOL DEFINITIONS")
466
+ print('='*70)
467
+
468
+ tools = server.get_tool_definitions()
469
+ print(f"\nAvailable tools: {len(tools)}")
470
+ for tool in tools:
471
+ print(f"\n {tool['name']}")
472
+ print(f" {tool['description']}")
473
+
474
+ print("\n" + "="*70)
475
+ print("USAGE WITH AI AGENT")
476
+ print("="*70)
477
+ print("""
478
+ # In your agent code:
479
+ from gdrive_mcp_server import GDriveMCPServer, GDriveConfig
480
+
481
+ # Initialize
482
+ server = GDriveMCPServer(GDriveConfig())
483
+
484
+ # Get tools for agent
485
+ tools = server.get_tool_definitions()
486
+
487
+ # When agent calls tool:
488
+ if tool_name == "gdrive_search_files":
489
+ result = server.search_files(args["query"])
490
+ elif tool_name == "gdrive_read_file":
491
+ result = server.read_file(args["file_id"])
492
+ elif tool_name == "gdrive_get_metadata":
493
+ result = server.get_file_metadata(args["file_id"])
494
+
495
+ # Agent can now:
496
+ # - Search: "Find our Q4 strategy document"
497
+ # - Read: "What does the AlphaCorp agreement say about IP?"
498
+ # - Analyze: "Summarize all engineering documents from last month"
499
+ """)
500
+
501
+
502
+ if __name__ == "__main__":
503
+ demo()