Coverage for src / osiris_cli / database_tools_letta.py: 0%
86 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-27 17:41 +0200
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-27 17:41 +0200
1"""
2Database Tools with Letta Visual Standard
4AI-friendly database tools using the Letta formatting style:
5- Action-result trees with ● and ⎿
6- Summarized outputs (no walls of text)
7- Thinking blocks with ✻
8"""
10from typing import Dict, Any, Optional
11from pathlib import Path
13from .database_manager import db_manager, DatabaseType
14from .letta_formatter import (
15 letta_formatter,
16 action_result,
17 thinking,
18 summarize_query,
19 summarize_schema,
20 LettaGlyphs
21)
22from .logger import get_logger
23from .errors import DatabaseError
25logger = get_logger()
28# Tool definitions for AI (OpenAI function calling format)
29DATABASE_TOOLS_LETTA = [
30 {
31 "type": "function",
32 "function": {
33 "name": "db_connect",
34 "description": "Connect to a database (PostgreSQL, MySQL, SQLite, or MongoDB). Returns connection ID for subsequent operations.",
35 "parameters": {
36 "type": "object",
37 "properties": {
38 "connection_id": {
39 "type": "string",
40 "description": "Unique identifier for this connection"
41 },
42 "db_type": {
43 "type": "string",
44 "enum": ["postgresql", "mysql", "sqlite", "mongodb"],
45 "description": "Database type"
46 },
47 "host": {
48 "type": "string",
49 "description": "Database host (not needed for SQLite)"
50 },
51 "port": {
52 "type": "integer",
53 "description": "Database port (optional, uses defaults)"
54 },
55 "database": {
56 "type": "string",
57 "description": "Database name"
58 },
59 "username": {
60 "type": "string",
61 "description": "Database username"
62 },
63 "password": {
64 "type": "string",
65 "description": "Database password"
66 },
67 "file_path": {
68 "type": "string",
69 "description": "File path for SQLite database"
70 }
71 },
72 "required": ["connection_id", "db_type"]
73 }
74 }
75 },
76 {
77 "type": "function",
78 "function": {
79 "name": "db_query",
80 "description": "Execute SQL query on connected database. Safe mode only allows SELECT queries. Returns results as JSON.",
81 "parameters": {
82 "type": "object",
83 "properties": {
84 "connection_id": {
85 "type": "string",
86 "description": "Connection ID from db_connect"
87 },
88 "query": {
89 "type": "string",
90 "description": "SQL query to execute"
91 },
92 "safe_mode": {
93 "type": "boolean",
94 "description": "If true, only SELECT queries allowed (default: true)"
95 }
96 },
97 "required": ["connection_id", "query"]
98 }
99 }
100 },
101 {
102 "type": "function",
103 "function": {
104 "name": "db_schema",
105 "description": "Get database schema (tables, columns, types). Useful for understanding database structure before querying.",
106 "parameters": {
107 "type": "object",
108 "properties": {
109 "connection_id": {
110 "type": "string",
111 "description": "Connection ID from db_connect"
112 }
113 },
114 "required": ["connection_id"]
115 }
116 }
117 },
118 {
119 "type": "function",
120 "function": {
121 "name": "db_disconnect",
122 "description": "Disconnect from database and clean up resources.",
123 "parameters": {
124 "type": "object",
125 "properties": {
126 "connection_id": {
127 "type": "string",
128 "description": "Connection ID to disconnect"
129 }
130 },
131 "required": ["connection_id"]
132 }
133 }
134 }
135]
138# Tool implementations with Letta formatting
139def tool_db_connect_letta(
140 connection_id: str,
141 db_type: str,
142 **kwargs
143) -> Dict[str, Any]:
144 """Connect to database with Letta visual output"""
146 # Prepare args for display (hide password)
147 display_args = {
148 "connection_id": connection_id,
149 "db_type": db_type
150 }
151 if "host" in kwargs:
152 display_args["host"] = kwargs["host"]
153 if "database" in kwargs:
154 display_args["database"] = kwargs["database"]
155 if "username" in kwargs:
156 display_args["username"] = kwargs["username"]
157 if "password" in kwargs:
158 display_args["password"] = "***"
160 try:
161 # Attempt connection
162 success = db_manager.connect(connection_id, db_type, **kwargs)
164 if success:
165 # Format success message
166 summary = f"Successfully connected to {db_type} database"
167 if kwargs.get('database'):
168 summary += f" ({kwargs['database']})"
170 action_result(
171 "db_connect",
172 display_args,
173 summary,
174 success=True
175 )
177 return {
178 "success": True,
179 "connection_id": connection_id,
180 "message": summary
181 }
182 else:
183 raise DatabaseError(f"Failed to connect to {db_type}")
185 except Exception as e:
186 logger.error(f"Database connection failed: {e}")
188 error_msg = str(e)
189 action_result(
190 "db_connect",
191 display_args,
192 f"Connection failed: {error_msg}",
193 success=False
194 )
196 return {
197 "success": False,
198 "error": error_msg
199 }
202def tool_db_query_letta(
203 connection_id: str,
204 query: str,
205 safe_mode: bool = True
206) -> Dict[str, Any]:
207 """Execute database query with Letta visual output"""
209 # Truncate query for display if too long
210 display_query = query if len(query) < 60 else query[:57] + "..."
212 display_args = {
213 "connection_id": connection_id,
214 "query": display_query
215 }
217 try:
218 # Execute query
219 result = db_manager.execute_query(connection_id, query, safe_mode=safe_mode)
221 # Summarize results
222 summary = summarize_query(result)
224 action_result(
225 "db_query",
226 display_args,
227 summary,
228 success=True
229 )
231 # Return full result for AI to process
232 return result
234 except Exception as e:
235 logger.error(f"Query failed: {e}")
237 error_msg = str(e)
238 action_result(
239 "db_query",
240 display_args,
241 f"Query failed: {error_msg}",
242 success=False
243 )
245 return {
246 "success": False,
247 "error": error_msg
248 }
251def tool_db_schema_letta(connection_id: str) -> Dict[str, Any]:
252 """Get database schema with Letta visual output"""
254 display_args = {"connection_id": connection_id}
256 try:
257 schema = db_manager.get_schema(connection_id)
259 # Summarize schema
260 summary = summarize_schema(schema)
262 action_result(
263 "db_schema",
264 display_args,
265 summary,
266 success=True
267 )
269 return schema
271 except Exception as e:
272 logger.error(f"Schema fetch failed: {e}")
274 error_msg = str(e)
275 action_result(
276 "db_schema",
277 display_args,
278 f"Schema fetch failed: {error_msg}",
279 success=False
280 )
282 return {
283 "success": False,
284 "error": error_msg
285 }
288def tool_db_disconnect_letta(connection_id: str) -> Dict[str, Any]:
289 """Disconnect from database with Letta visual output"""
291 display_args = {"connection_id": connection_id}
293 try:
294 success = db_manager.disconnect(connection_id)
296 if success:
297 action_result(
298 "db_disconnect",
299 display_args,
300 f"Disconnected from {connection_id}",
301 success=True
302 )
304 return {
305 "success": True,
306 "message": f"Disconnected from {connection_id}"
307 }
308 else:
309 action_result(
310 "db_disconnect",
311 display_args,
312 f"Connection {connection_id} not found",
313 success=False
314 )
316 return {
317 "success": False,
318 "error": f"Connection {connection_id} not found"
319 }
321 except Exception as e:
322 error_msg = str(e)
323 action_result(
324 "db_disconnect",
325 display_args,
326 f"Disconnect failed: {error_msg}",
327 success=False
328 )
330 return {
331 "success": False,
332 "error": error_msg
333 }
336# Tool registry for Letta-style tools
337TOOL_HANDLERS_LETTA = {
338 "db_connect": tool_db_connect_letta,
339 "db_query": tool_db_query_letta,
340 "db_schema": tool_db_schema_letta,
341 "db_disconnect": tool_db_disconnect_letta
342}
345# Example workflow demonstrating the Letta visual style
346def example_database_workflow():
347 """
348 Example database workflow showing the Letta visual style.
350 Output will look like:
351 ✻ Thinking...
352 I need to connect to the PostgreSQL database first, then query the users table.
354 ● db_connect(connection_id="mydb", db_type="postgresql", host="localhost")
355 ⎿ Successfully connected to postgresql database (users_db) ✓
357 ● db_schema(connection_id="mydb")
358 ⎿ Schema: 12 tables, 85 columns total ✓
360 ● db_query(connection_id="mydb", query="SELECT COUNT(*) FROM users")
361 ⎿ Found 150 rows with 1 columns ✓
363 ● db_disconnect(connection_id="mydb")
364 ⎿ Disconnected from mydb ✓
365 """
367 # Show reasoning
368 thinking("I need to connect to the PostgreSQL database first, then query the users table to get the count.")
370 # Connect
371 tool_db_connect_letta(
372 connection_id="mydb",
373 db_type="postgresql",
374 host="localhost",
375 port=5432,
376 database="users_db",
377 username="admin",
378 password="secret123"
379 )
381 # Get schema
382 tool_db_schema_letta("mydb")
384 # Query
385 tool_db_query_letta(
386 connection_id="mydb",
387 query="SELECT COUNT(*) FROM users"
388 )
390 # Disconnect
391 tool_db_disconnect_letta("mydb")
394if __name__ == "__main__":
395 # Run example
396 print("\n" + "="*60)
397 print("LETTA VISUAL STANDARD - DATABASE OPERATIONS")
398 print("="*60 + "\n")
400 example_database_workflow()
402 print("\n" + "="*60)
403 print("Status Bar:")
404 print(letta_formatter.format_status_bar())
405 print("="*60 + "\n")