sqlsaber 0.7.0__py3-none-any.whl → 0.8.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.
Potentially problematic release.
This version of sqlsaber might be problematic. Click here for more details.
- sqlsaber/agents/anthropic.py +283 -176
- sqlsaber/agents/base.py +11 -11
- sqlsaber/agents/streaming.py +3 -3
- sqlsaber/cli/auth.py +142 -0
- sqlsaber/cli/commands.py +9 -4
- sqlsaber/cli/completers.py +3 -5
- sqlsaber/cli/database.py +9 -10
- sqlsaber/cli/display.py +5 -7
- sqlsaber/cli/interactive.py +2 -3
- sqlsaber/cli/memory.py +7 -9
- sqlsaber/cli/models.py +1 -2
- sqlsaber/cli/streaming.py +5 -31
- sqlsaber/clients/__init__.py +6 -0
- sqlsaber/clients/anthropic.py +285 -0
- sqlsaber/clients/base.py +31 -0
- sqlsaber/clients/exceptions.py +117 -0
- sqlsaber/clients/models.py +282 -0
- sqlsaber/clients/streaming.py +257 -0
- sqlsaber/config/api_keys.py +2 -3
- sqlsaber/config/auth.py +86 -0
- sqlsaber/config/database.py +20 -20
- sqlsaber/config/oauth_flow.py +274 -0
- sqlsaber/config/oauth_tokens.py +175 -0
- sqlsaber/config/settings.py +34 -23
- sqlsaber/database/connection.py +9 -9
- sqlsaber/database/schema.py +25 -25
- sqlsaber/mcp/mcp.py +3 -4
- sqlsaber/memory/manager.py +3 -5
- sqlsaber/memory/storage.py +7 -8
- sqlsaber/models/events.py +4 -4
- sqlsaber/models/types.py +10 -10
- {sqlsaber-0.7.0.dist-info → sqlsaber-0.8.0.dist-info}/METADATA +1 -1
- sqlsaber-0.8.0.dist-info/RECORD +46 -0
- sqlsaber-0.7.0.dist-info/RECORD +0 -36
- {sqlsaber-0.7.0.dist-info → sqlsaber-0.8.0.dist-info}/WHEEL +0 -0
- {sqlsaber-0.7.0.dist-info → sqlsaber-0.8.0.dist-info}/entry_points.txt +0 -0
- {sqlsaber-0.7.0.dist-info → sqlsaber-0.8.0.dist-info}/licenses/LICENSE +0 -0
sqlsaber/database/connection.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Database connection management."""
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any
|
|
5
5
|
from urllib.parse import urlparse, parse_qs
|
|
6
6
|
import ssl
|
|
7
7
|
from pathlib import Path
|
|
@@ -30,7 +30,7 @@ class BaseDatabaseConnection(ABC):
|
|
|
30
30
|
pass
|
|
31
31
|
|
|
32
32
|
@abstractmethod
|
|
33
|
-
async def execute_query(self, query: str, *args) ->
|
|
33
|
+
async def execute_query(self, query: str, *args) -> list[dict[str, Any]]:
|
|
34
34
|
"""Execute a query and return results as list of dicts.
|
|
35
35
|
|
|
36
36
|
All queries run in a transaction that is rolled back at the end,
|
|
@@ -44,10 +44,10 @@ class PostgreSQLConnection(BaseDatabaseConnection):
|
|
|
44
44
|
|
|
45
45
|
def __init__(self, connection_string: str):
|
|
46
46
|
super().__init__(connection_string)
|
|
47
|
-
self._pool:
|
|
47
|
+
self._pool: asyncpg.Pool | None = None
|
|
48
48
|
self._ssl_context = self._create_ssl_context()
|
|
49
49
|
|
|
50
|
-
def _create_ssl_context(self) ->
|
|
50
|
+
def _create_ssl_context(self) -> ssl.SSLContext | None:
|
|
51
51
|
"""Create SSL context from connection string parameters."""
|
|
52
52
|
parsed = urlparse(self.connection_string)
|
|
53
53
|
if not parsed.query:
|
|
@@ -112,7 +112,7 @@ class PostgreSQLConnection(BaseDatabaseConnection):
|
|
|
112
112
|
await self._pool.close()
|
|
113
113
|
self._pool = None
|
|
114
114
|
|
|
115
|
-
async def execute_query(self, query: str, *args) ->
|
|
115
|
+
async def execute_query(self, query: str, *args) -> list[dict[str, Any]]:
|
|
116
116
|
"""Execute a query and return results as list of dicts.
|
|
117
117
|
|
|
118
118
|
All queries run in a transaction that is rolled back at the end,
|
|
@@ -137,7 +137,7 @@ class MySQLConnection(BaseDatabaseConnection):
|
|
|
137
137
|
|
|
138
138
|
def __init__(self, connection_string: str):
|
|
139
139
|
super().__init__(connection_string)
|
|
140
|
-
self._pool:
|
|
140
|
+
self._pool: aiomysql.Pool | None = None
|
|
141
141
|
self._parse_connection_string()
|
|
142
142
|
|
|
143
143
|
def _parse_connection_string(self):
|
|
@@ -217,7 +217,7 @@ class MySQLConnection(BaseDatabaseConnection):
|
|
|
217
217
|
await self._pool.wait_closed()
|
|
218
218
|
self._pool = None
|
|
219
219
|
|
|
220
|
-
async def execute_query(self, query: str, *args) ->
|
|
220
|
+
async def execute_query(self, query: str, *args) -> list[dict[str, Any]]:
|
|
221
221
|
"""Execute a query and return results as list of dicts.
|
|
222
222
|
|
|
223
223
|
All queries run in a transaction that is rolled back at the end,
|
|
@@ -253,7 +253,7 @@ class SQLiteConnection(BaseDatabaseConnection):
|
|
|
253
253
|
"""SQLite connections are created per query, no persistent pool to close."""
|
|
254
254
|
pass
|
|
255
255
|
|
|
256
|
-
async def execute_query(self, query: str, *args) ->
|
|
256
|
+
async def execute_query(self, query: str, *args) -> list[dict[str, Any]]:
|
|
257
257
|
"""Execute a query and return results as list of dicts.
|
|
258
258
|
|
|
259
259
|
All queries run in a transaction that is rolled back at the end,
|
|
@@ -380,7 +380,7 @@ class CSVConnection(BaseDatabaseConnection):
|
|
|
380
380
|
except Exception as e:
|
|
381
381
|
raise ValueError(f"Error loading CSV file '{self.csv_path}': {str(e)}")
|
|
382
382
|
|
|
383
|
-
async def execute_query(self, query: str, *args) ->
|
|
383
|
+
async def execute_query(self, query: str, *args) -> list[dict[str, Any]]:
|
|
384
384
|
"""Execute a query and return results as list of dicts.
|
|
385
385
|
|
|
386
386
|
All queries run in a transaction that is rolled back at the end,
|
sqlsaber/database/schema.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import time
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
|
-
from typing import Any
|
|
5
|
+
from typing import Any
|
|
6
6
|
|
|
7
7
|
import aiosqlite
|
|
8
8
|
|
|
@@ -21,8 +21,8 @@ class BaseSchemaIntrospector(ABC):
|
|
|
21
21
|
|
|
22
22
|
@abstractmethod
|
|
23
23
|
async def get_tables_info(
|
|
24
|
-
self, connection, table_pattern:
|
|
25
|
-
) ->
|
|
24
|
+
self, connection, table_pattern: str | None = None
|
|
25
|
+
) -> dict[str, Any]:
|
|
26
26
|
"""Get tables information for the specific database type."""
|
|
27
27
|
pass
|
|
28
28
|
|
|
@@ -42,7 +42,7 @@ class BaseSchemaIntrospector(ABC):
|
|
|
42
42
|
pass
|
|
43
43
|
|
|
44
44
|
@abstractmethod
|
|
45
|
-
async def list_tables_info(self, connection) ->
|
|
45
|
+
async def list_tables_info(self, connection) -> dict[str, Any]:
|
|
46
46
|
"""Get list of tables with basic information."""
|
|
47
47
|
pass
|
|
48
48
|
|
|
@@ -51,8 +51,8 @@ class PostgreSQLSchemaIntrospector(BaseSchemaIntrospector):
|
|
|
51
51
|
"""PostgreSQL-specific schema introspection."""
|
|
52
52
|
|
|
53
53
|
async def get_tables_info(
|
|
54
|
-
self, connection, table_pattern:
|
|
55
|
-
) ->
|
|
54
|
+
self, connection, table_pattern: str | None = None
|
|
55
|
+
) -> dict[str, Any]:
|
|
56
56
|
"""Get tables information for PostgreSQL."""
|
|
57
57
|
pool = await connection.get_pool()
|
|
58
58
|
async with pool.acquire() as conn:
|
|
@@ -182,7 +182,7 @@ class PostgreSQLSchemaIntrospector(BaseSchemaIntrospector):
|
|
|
182
182
|
"""
|
|
183
183
|
return await conn.fetch(pk_query)
|
|
184
184
|
|
|
185
|
-
async def list_tables_info(self, connection) ->
|
|
185
|
+
async def list_tables_info(self, connection) -> dict[str, Any]:
|
|
186
186
|
"""Get list of tables with basic information for PostgreSQL."""
|
|
187
187
|
pool = await connection.get_pool()
|
|
188
188
|
async with pool.acquire() as conn:
|
|
@@ -214,8 +214,8 @@ class MySQLSchemaIntrospector(BaseSchemaIntrospector):
|
|
|
214
214
|
"""MySQL-specific schema introspection."""
|
|
215
215
|
|
|
216
216
|
async def get_tables_info(
|
|
217
|
-
self, connection, table_pattern:
|
|
218
|
-
) ->
|
|
217
|
+
self, connection, table_pattern: str | None = None
|
|
218
|
+
) -> dict[str, Any]:
|
|
219
219
|
"""Get tables information for MySQL."""
|
|
220
220
|
pool = await connection.get_pool()
|
|
221
221
|
async with pool.acquire() as conn:
|
|
@@ -353,7 +353,7 @@ class MySQLSchemaIntrospector(BaseSchemaIntrospector):
|
|
|
353
353
|
await cursor.execute(pk_query)
|
|
354
354
|
return await cursor.fetchall()
|
|
355
355
|
|
|
356
|
-
async def list_tables_info(self, connection) ->
|
|
356
|
+
async def list_tables_info(self, connection) -> dict[str, Any]:
|
|
357
357
|
"""Get list of tables with basic information for MySQL."""
|
|
358
358
|
pool = await connection.get_pool()
|
|
359
359
|
async with pool.acquire() as conn:
|
|
@@ -392,8 +392,8 @@ class SQLiteSchemaIntrospector(BaseSchemaIntrospector):
|
|
|
392
392
|
return await cursor.fetchall()
|
|
393
393
|
|
|
394
394
|
async def get_tables_info(
|
|
395
|
-
self, connection, table_pattern:
|
|
396
|
-
) ->
|
|
395
|
+
self, connection, table_pattern: str | None = None
|
|
396
|
+
) -> dict[str, Any]:
|
|
397
397
|
"""Get tables information for SQLite."""
|
|
398
398
|
where_conditions = ["type IN ('table', 'view')", "name NOT LIKE 'sqlite_%'"]
|
|
399
399
|
params = ()
|
|
@@ -496,7 +496,7 @@ class SQLiteSchemaIntrospector(BaseSchemaIntrospector):
|
|
|
496
496
|
|
|
497
497
|
return primary_keys
|
|
498
498
|
|
|
499
|
-
async def list_tables_info(self, connection) ->
|
|
499
|
+
async def list_tables_info(self, connection) -> dict[str, Any]:
|
|
500
500
|
"""Get list of tables with basic information for SQLite."""
|
|
501
501
|
# First get the table names
|
|
502
502
|
tables_query = """
|
|
@@ -548,7 +548,7 @@ class SchemaManager:
|
|
|
548
548
|
def __init__(self, db_connection: BaseDatabaseConnection, cache_ttl: int = 900):
|
|
549
549
|
self.db = db_connection
|
|
550
550
|
self.cache_ttl = cache_ttl # Default 15 minutes
|
|
551
|
-
self._schema_cache:
|
|
551
|
+
self._schema_cache: dict[str, tuple[float, dict[str, Any]]] = {}
|
|
552
552
|
|
|
553
553
|
# Select appropriate introspector based on connection type
|
|
554
554
|
if isinstance(db_connection, PostgreSQLConnection):
|
|
@@ -567,8 +567,8 @@ class SchemaManager:
|
|
|
567
567
|
self._schema_cache.clear()
|
|
568
568
|
|
|
569
569
|
async def get_schema_info(
|
|
570
|
-
self, table_pattern:
|
|
571
|
-
) ->
|
|
570
|
+
self, table_pattern: str | None = None
|
|
571
|
+
) -> dict[str, SchemaInfo]:
|
|
572
572
|
"""Get database schema information, optionally filtered by table pattern.
|
|
573
573
|
|
|
574
574
|
Args:
|
|
@@ -587,7 +587,7 @@ class SchemaManager:
|
|
|
587
587
|
self._schema_cache[cache_key] = (time.time(), schema_info)
|
|
588
588
|
return schema_info
|
|
589
589
|
|
|
590
|
-
def _get_cached_schema(self, cache_key: str) ->
|
|
590
|
+
def _get_cached_schema(self, cache_key: str) -> dict[str, SchemaInfo] | None:
|
|
591
591
|
"""Get schema from cache if available and not expired."""
|
|
592
592
|
if cache_key in self._schema_cache:
|
|
593
593
|
cached_time, cached_data = self._schema_cache[cache_key]
|
|
@@ -596,8 +596,8 @@ class SchemaManager:
|
|
|
596
596
|
return None
|
|
597
597
|
|
|
598
598
|
async def _fetch_schema_from_db(
|
|
599
|
-
self, table_pattern:
|
|
600
|
-
) ->
|
|
599
|
+
self, table_pattern: str | None
|
|
600
|
+
) -> dict[str, SchemaInfo]:
|
|
601
601
|
"""Fetch schema information from database."""
|
|
602
602
|
# Get all schema components
|
|
603
603
|
tables = await self.introspector.get_tables_info(self.db, table_pattern)
|
|
@@ -613,7 +613,7 @@ class SchemaManager:
|
|
|
613
613
|
|
|
614
614
|
return schema_info
|
|
615
615
|
|
|
616
|
-
def _build_table_structure(self, tables: list) ->
|
|
616
|
+
def _build_table_structure(self, tables: list) -> dict[str, dict]:
|
|
617
617
|
"""Build basic table structure from table info."""
|
|
618
618
|
schema_info = {}
|
|
619
619
|
for table in tables:
|
|
@@ -632,7 +632,7 @@ class SchemaManager:
|
|
|
632
632
|
return schema_info
|
|
633
633
|
|
|
634
634
|
def _add_columns_to_schema(
|
|
635
|
-
self, schema_info:
|
|
635
|
+
self, schema_info: dict[str, dict], columns: list
|
|
636
636
|
) -> None:
|
|
637
637
|
"""Add column information to schema."""
|
|
638
638
|
for col in columns:
|
|
@@ -656,7 +656,7 @@ class SchemaManager:
|
|
|
656
656
|
schema_info[full_name]["columns"][col["column_name"]] = col_info
|
|
657
657
|
|
|
658
658
|
def _add_primary_keys_to_schema(
|
|
659
|
-
self, schema_info:
|
|
659
|
+
self, schema_info: dict[str, dict], primary_keys: list
|
|
660
660
|
) -> None:
|
|
661
661
|
"""Add primary key information to schema."""
|
|
662
662
|
for pk in primary_keys:
|
|
@@ -665,7 +665,7 @@ class SchemaManager:
|
|
|
665
665
|
schema_info[full_name]["primary_keys"].append(pk["column_name"])
|
|
666
666
|
|
|
667
667
|
def _add_foreign_keys_to_schema(
|
|
668
|
-
self, schema_info:
|
|
668
|
+
self, schema_info: dict[str, dict], foreign_keys: list
|
|
669
669
|
) -> None:
|
|
670
670
|
"""Add foreign key information to schema."""
|
|
671
671
|
for fk in foreign_keys:
|
|
@@ -681,7 +681,7 @@ class SchemaManager:
|
|
|
681
681
|
}
|
|
682
682
|
)
|
|
683
683
|
|
|
684
|
-
async def list_tables(self) ->
|
|
684
|
+
async def list_tables(self) -> dict[str, Any]:
|
|
685
685
|
"""Get a list of all tables with basic information like row counts."""
|
|
686
686
|
# Check cache first
|
|
687
687
|
cache_key = "list_tables"
|
|
@@ -710,7 +710,7 @@ class SchemaManager:
|
|
|
710
710
|
self._schema_cache[cache_key] = (time.time(), result)
|
|
711
711
|
return result
|
|
712
712
|
|
|
713
|
-
def _get_cached_tables(self, cache_key: str) ->
|
|
713
|
+
def _get_cached_tables(self, cache_key: str) -> dict[str, Any] | None:
|
|
714
714
|
"""Get table list from cache if available and not expired."""
|
|
715
715
|
if cache_key in self._schema_cache:
|
|
716
716
|
cached_time, cached_data = self._schema_cache[cache_key]
|
sqlsaber/mcp/mcp.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""FastMCP server implementation for SQLSaber."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
from fastmcp import FastMCP
|
|
7
6
|
|
|
@@ -32,7 +31,7 @@ mcp = FastMCP(name="SQL Assistant", instructions=INSTRUCTIONS)
|
|
|
32
31
|
config_manager = DatabaseConfigManager()
|
|
33
32
|
|
|
34
33
|
|
|
35
|
-
async def _create_agent_for_database(database_name: str) ->
|
|
34
|
+
async def _create_agent_for_database(database_name: str) -> MCPSQLAgent | None:
|
|
36
35
|
"""Create a MCPSQLAgent for the specified database."""
|
|
37
36
|
try:
|
|
38
37
|
# Look up configured database connection
|
|
@@ -92,7 +91,7 @@ async def list_tables(database: str) -> str:
|
|
|
92
91
|
|
|
93
92
|
|
|
94
93
|
@mcp.tool
|
|
95
|
-
async def introspect_schema(database: str, table_pattern:
|
|
94
|
+
async def introspect_schema(database: str, table_pattern: str | None = None) -> str:
|
|
96
95
|
"""
|
|
97
96
|
Introspect database schema to understand table structures. Use optional pattern to filter tables (e.g., 'public.users', 'user%', '%order%').
|
|
98
97
|
"""
|
|
@@ -112,7 +111,7 @@ async def introspect_schema(database: str, table_pattern: Optional[str] = None)
|
|
|
112
111
|
|
|
113
112
|
|
|
114
113
|
@mcp.tool
|
|
115
|
-
async def execute_sql(database: str, query: str, limit:
|
|
114
|
+
async def execute_sql(database: str, query: str, limit: int | None = 100) -> str:
|
|
116
115
|
"""Execute a SQL query against the specified database."""
|
|
117
116
|
try:
|
|
118
117
|
agent = await _create_agent_for_database(database)
|
sqlsaber/memory/manager.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""Memory manager for handling database-specific context and memories."""
|
|
2
2
|
|
|
3
|
-
from typing import List, Optional
|
|
4
|
-
|
|
5
3
|
from sqlsaber.memory.storage import Memory, MemoryStorage
|
|
6
4
|
|
|
7
5
|
|
|
@@ -15,7 +13,7 @@ class MemoryManager:
|
|
|
15
13
|
"""Add a new memory for the specified database."""
|
|
16
14
|
return self.storage.add_memory(database_name, content)
|
|
17
15
|
|
|
18
|
-
def get_memories(self, database_name: str) ->
|
|
16
|
+
def get_memories(self, database_name: str) -> list[Memory]:
|
|
19
17
|
"""Get all memories for the specified database."""
|
|
20
18
|
return self.storage.get_memories(database_name)
|
|
21
19
|
|
|
@@ -27,7 +25,7 @@ class MemoryManager:
|
|
|
27
25
|
"""Clear all memories for the specified database."""
|
|
28
26
|
return self.storage.clear_memories(database_name)
|
|
29
27
|
|
|
30
|
-
def get_memory_by_id(self, database_name: str, memory_id: str) ->
|
|
28
|
+
def get_memory_by_id(self, database_name: str, memory_id: str) -> Memory | None:
|
|
31
29
|
"""Get a specific memory by ID."""
|
|
32
30
|
return self.storage.get_memory_by_id(database_name, memory_id)
|
|
33
31
|
|
|
@@ -72,6 +70,6 @@ Use this context to better understand the user's needs and provide more relevant
|
|
|
72
70
|
],
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
def list_databases_with_memories(self) ->
|
|
73
|
+
def list_databases_with_memories(self) -> list[str]:
|
|
76
74
|
"""List all databases that have memories."""
|
|
77
75
|
return self.storage.list_databases_with_memories()
|
sqlsaber/memory/storage.py
CHANGED
|
@@ -8,7 +8,6 @@ import time
|
|
|
8
8
|
import uuid
|
|
9
9
|
from dataclasses import dataclass
|
|
10
10
|
from pathlib import Path
|
|
11
|
-
from typing import Dict, List, Optional
|
|
12
11
|
|
|
13
12
|
import platformdirs
|
|
14
13
|
|
|
@@ -21,7 +20,7 @@ class Memory:
|
|
|
21
20
|
content: str
|
|
22
21
|
timestamp: float
|
|
23
22
|
|
|
24
|
-
def to_dict(self) ->
|
|
23
|
+
def to_dict(self) -> dict:
|
|
25
24
|
"""Convert memory to dictionary for JSON serialization."""
|
|
26
25
|
return {
|
|
27
26
|
"id": self.id,
|
|
@@ -30,7 +29,7 @@ class Memory:
|
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
@classmethod
|
|
33
|
-
def from_dict(cls, data:
|
|
32
|
+
def from_dict(cls, data: dict) -> "Memory":
|
|
34
33
|
"""Create Memory from dictionary."""
|
|
35
34
|
return cls(
|
|
36
35
|
id=data["id"],
|
|
@@ -79,7 +78,7 @@ class MemoryStorage:
|
|
|
79
78
|
"""Get the memory file path for a specific database."""
|
|
80
79
|
return self.memory_dir / f"{database_name}.json"
|
|
81
80
|
|
|
82
|
-
def _load_memories(self, database_name: str) ->
|
|
81
|
+
def _load_memories(self, database_name: str) -> list[Memory]:
|
|
83
82
|
"""Load memories for a specific database."""
|
|
84
83
|
memory_file = self._get_memory_file(database_name)
|
|
85
84
|
|
|
@@ -96,7 +95,7 @@ class MemoryStorage:
|
|
|
96
95
|
except (json.JSONDecodeError, IOError, KeyError):
|
|
97
96
|
return []
|
|
98
97
|
|
|
99
|
-
def _save_memories(self, database_name: str, memories:
|
|
98
|
+
def _save_memories(self, database_name: str, memories: list[Memory]) -> None:
|
|
100
99
|
"""Save memories for a specific database."""
|
|
101
100
|
memory_file = self._get_memory_file(database_name)
|
|
102
101
|
|
|
@@ -125,7 +124,7 @@ class MemoryStorage:
|
|
|
125
124
|
|
|
126
125
|
return memory
|
|
127
126
|
|
|
128
|
-
def get_memories(self, database_name: str) ->
|
|
127
|
+
def get_memories(self, database_name: str) -> list[Memory]:
|
|
129
128
|
"""Get all memories for the specified database."""
|
|
130
129
|
return self._load_memories(database_name)
|
|
131
130
|
|
|
@@ -152,7 +151,7 @@ class MemoryStorage:
|
|
|
152
151
|
|
|
153
152
|
return count
|
|
154
153
|
|
|
155
|
-
def get_memory_by_id(self, database_name: str, memory_id: str) ->
|
|
154
|
+
def get_memory_by_id(self, database_name: str, memory_id: str) -> Memory | None:
|
|
156
155
|
"""Get a specific memory by ID."""
|
|
157
156
|
memories = self._load_memories(database_name)
|
|
158
157
|
return next((m for m in memories if m.id == memory_id), None)
|
|
@@ -161,7 +160,7 @@ class MemoryStorage:
|
|
|
161
160
|
"""Check if database has any memories."""
|
|
162
161
|
return len(self._load_memories(database_name)) > 0
|
|
163
162
|
|
|
164
|
-
def list_databases_with_memories(self) ->
|
|
163
|
+
def list_databases_with_memories(self) -> list[str]:
|
|
165
164
|
"""List all databases that have memories."""
|
|
166
165
|
databases = []
|
|
167
166
|
|
sqlsaber/models/events.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Event models for streaming and responses."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class StreamEvent:
|
|
@@ -17,10 +17,10 @@ class SQLResponse:
|
|
|
17
17
|
|
|
18
18
|
def __init__(
|
|
19
19
|
self,
|
|
20
|
-
query:
|
|
20
|
+
query: str | None = None,
|
|
21
21
|
explanation: str = "",
|
|
22
|
-
results:
|
|
23
|
-
error:
|
|
22
|
+
results: list[dict[str, Any]] | None = None,
|
|
23
|
+
error: str | None = None,
|
|
24
24
|
):
|
|
25
25
|
self.query = query
|
|
26
26
|
self.explanation = explanation
|
sqlsaber/models/types.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Type definitions for SQLSaber."""
|
|
2
2
|
|
|
3
|
-
from typing import Any,
|
|
3
|
+
from typing import Any, TypedDict
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class ColumnInfo(TypedDict):
|
|
@@ -8,17 +8,17 @@ class ColumnInfo(TypedDict):
|
|
|
8
8
|
|
|
9
9
|
data_type: str
|
|
10
10
|
nullable: bool
|
|
11
|
-
default:
|
|
12
|
-
max_length:
|
|
13
|
-
precision:
|
|
14
|
-
scale:
|
|
11
|
+
default: str | None
|
|
12
|
+
max_length: int | None
|
|
13
|
+
precision: int | None
|
|
14
|
+
scale: int | None
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class ForeignKeyInfo(TypedDict):
|
|
18
18
|
"""Type definition for foreign key information."""
|
|
19
19
|
|
|
20
20
|
column: str
|
|
21
|
-
references:
|
|
21
|
+
references: dict[str, str] # {"table": "schema.table", "column": "column_name"}
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class SchemaInfo(TypedDict):
|
|
@@ -27,9 +27,9 @@ class SchemaInfo(TypedDict):
|
|
|
27
27
|
schema: str
|
|
28
28
|
name: str
|
|
29
29
|
type: str
|
|
30
|
-
columns:
|
|
31
|
-
primary_keys:
|
|
32
|
-
foreign_keys:
|
|
30
|
+
columns: dict[str, ColumnInfo]
|
|
31
|
+
primary_keys: list[str]
|
|
32
|
+
foreign_keys: list[ForeignKeyInfo]
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
class ToolDefinition(TypedDict):
|
|
@@ -37,4 +37,4 @@ class ToolDefinition(TypedDict):
|
|
|
37
37
|
|
|
38
38
|
name: str
|
|
39
39
|
description: str
|
|
40
|
-
input_schema:
|
|
40
|
+
input_schema: dict[str, Any]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
sqlsaber/__init__.py,sha256=QCFi8xTVMohelfi7zOV1-6oLCcGoiXoOcKQY-HNBCk8,66
|
|
2
|
+
sqlsaber/__main__.py,sha256=RIHxWeWh2QvLfah-2OkhI5IJxojWfy4fXpMnVEJYvxw,78
|
|
3
|
+
sqlsaber/agents/__init__.py,sha256=LWeSeEUE4BhkyAYFF3TE-fx8TtLud3oyEtyB8ojFJgo,167
|
|
4
|
+
sqlsaber/agents/anthropic.py,sha256=CBHneR5NJhu155d0-D1mSGOcTH7kmbXZSLv2mVQotSM,22128
|
|
5
|
+
sqlsaber/agents/base.py,sha256=Cl5ZV4dfgjslOAq8jbrnt5kX-NM_8QmjacWzb0hvbzs,10527
|
|
6
|
+
sqlsaber/agents/mcp.py,sha256=FKtXgDrPZ2-xqUYCw2baI5JzrWekXaC5fjkYW1_Mg50,827
|
|
7
|
+
sqlsaber/agents/streaming.py,sha256=LaSeMTlxuJFRArJVqDly5-_KgcePiCCKPKfMxfB4oGs,521
|
|
8
|
+
sqlsaber/cli/__init__.py,sha256=qVSLVJLLJYzoC6aj6y9MFrzZvAwc4_OgxU9DlkQnZ4M,86
|
|
9
|
+
sqlsaber/cli/auth.py,sha256=tm3f-qIuNS0nQbU2DEI7ezWG092mayNW1GuoiwdV8hI,5047
|
|
10
|
+
sqlsaber/cli/commands.py,sha256=Ob505FV1kfaRKoW_agon4Q82772QmLjxISfvbXGOHE4,5256
|
|
11
|
+
sqlsaber/cli/completers.py,sha256=HsUPjaZweLSeYCWkAcgMl8FylQ1xjWBWYTEL_9F6xfU,6430
|
|
12
|
+
sqlsaber/cli/database.py,sha256=mWpMPcISUokYIiAMU4M_g8YeI-Fz5YU_R3PYs-GigCw,12588
|
|
13
|
+
sqlsaber/cli/display.py,sha256=XcBkjdG7RoM_ijHgv0VWqWleT5CCTm0Hcp1sJoE1FKE,9979
|
|
14
|
+
sqlsaber/cli/interactive.py,sha256=sQQXO8RcbVwxIBArNUlv-8ePhLn3UUdx6zUl44l8tow,7395
|
|
15
|
+
sqlsaber/cli/memory.py,sha256=OFspjaZ2RaYrBdSDVOD-9_6T8NbqedHEn5FztGkLUlc,7621
|
|
16
|
+
sqlsaber/cli/models.py,sha256=7bvIykGPTJu3-3tpPinr44GBkPIQhoeKI3d3Kgn3jOI,7783
|
|
17
|
+
sqlsaber/cli/streaming.py,sha256=WfhFd5ntq2HStpJZwWJ0C5uyXKc3aU14eo8HdjzW1o0,3767
|
|
18
|
+
sqlsaber/clients/__init__.py,sha256=jcMoVsT92U6nQrfotCp1h0ggskJPAcgeYarqQl1qEBg,171
|
|
19
|
+
sqlsaber/clients/anthropic.py,sha256=umRmuzpmJdYO7hO3biAZXO9T_sb6Vv010o6zqn03is8,9947
|
|
20
|
+
sqlsaber/clients/base.py,sha256=RLFJ3NV75Z6keiu9mnh9zrMZK1HwdeUby0e3oeJMtyw,935
|
|
21
|
+
sqlsaber/clients/exceptions.py,sha256=6OoCSxvuA13I3dML2Zngygl9MdaObISh1UHvBB3yUq0,3408
|
|
22
|
+
sqlsaber/clients/models.py,sha256=fOvnkW8NQSdn8Oqfk3-5dP4TylLh7C9wOvuNQYw184A,7016
|
|
23
|
+
sqlsaber/clients/streaming.py,sha256=CwdoocLAyW_GjZm2XcLb33Sa99w5fyb7dU-27FFpePQ,8319
|
|
24
|
+
sqlsaber/config/__init__.py,sha256=olwC45k8Nc61yK0WmPUk7XHdbsZH9HuUAbwnmKe3IgA,100
|
|
25
|
+
sqlsaber/config/api_keys.py,sha256=wnWlYy26AkkevZ1Vln6avYRBDLPRzfrHkj-fPojkxaQ,3624
|
|
26
|
+
sqlsaber/config/auth.py,sha256=b5qB2h1doXyO9Bn8z0CcL8LAR2jF431gGXBGKLgTmtQ,2756
|
|
27
|
+
sqlsaber/config/database.py,sha256=c6q3l4EvoBch1ckYHA70hf6L7fSOY-sItnLCpvJiPrA,11357
|
|
28
|
+
sqlsaber/config/oauth_flow.py,sha256=A3bSXaBLzuAfXV2ZPA94m9NV33c2MyL6M4ii9oEkswQ,10291
|
|
29
|
+
sqlsaber/config/oauth_tokens.py,sha256=C9z35hyx-PvSAYdC1LNf3rg9_wsEIY56hkEczelbad0,6015
|
|
30
|
+
sqlsaber/config/settings.py,sha256=H2NrTaB7Vy5YWhg6k1g94XiQHZq0LZOQEd1ILtx7GHw,4567
|
|
31
|
+
sqlsaber/database/__init__.py,sha256=a_gtKRJnZVO8-fEZI7g3Z8YnGa6Nio-5Y50PgVp07ss,176
|
|
32
|
+
sqlsaber/database/connection.py,sha256=sZVGNMzMwiM11GrsLLPwR8A5ugzJ5O0TCdkrt0KVRuI,15123
|
|
33
|
+
sqlsaber/database/schema.py,sha256=B4emtbaNiqjz6aGBUQYYwARsTMqBilvWSurNg_zKu9U,28600
|
|
34
|
+
sqlsaber/mcp/__init__.py,sha256=COdWq7wauPBp5Ew8tfZItFzbcLDSEkHBJSMhxzy8C9c,112
|
|
35
|
+
sqlsaber/mcp/mcp.py,sha256=YH4crygqb5_Y94nsns6d-26FZCTlDPOh3tf-ghihzDM,4440
|
|
36
|
+
sqlsaber/memory/__init__.py,sha256=GiWkU6f6YYVV0EvvXDmFWe_CxarmDCql05t70MkTEWs,63
|
|
37
|
+
sqlsaber/memory/manager.py,sha256=p3fybMVfH-E4ApT1ZRZUnQIWSk9dkfUPCyfkmA0HALs,2739
|
|
38
|
+
sqlsaber/memory/storage.py,sha256=ne8szLlGj5NELheqLnI7zu21V8YS4rtpYGGC7tOmi-s,5745
|
|
39
|
+
sqlsaber/models/__init__.py,sha256=RJ7p3WtuSwwpFQ1Iw4_DHV2zzCtHqIzsjJzxv8kUjUE,287
|
|
40
|
+
sqlsaber/models/events.py,sha256=89SXKb5GGpH01yTr2kPEBhzp9xv35RFIYuFdAZSIPoE,721
|
|
41
|
+
sqlsaber/models/types.py,sha256=w-zk81V2dtveuteej36_o1fDK3So428j3P2rAejU62U,862
|
|
42
|
+
sqlsaber-0.8.0.dist-info/METADATA,sha256=jksWiXSR2Qy0O2KgeYm6pCY5boEBM1kO6lJuYSHV6_Y,5986
|
|
43
|
+
sqlsaber-0.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
44
|
+
sqlsaber-0.8.0.dist-info/entry_points.txt,sha256=jmFo96Ylm0zIKXJBwhv_P5wQ7SXP9qdaBcnTp8iCEe8,195
|
|
45
|
+
sqlsaber-0.8.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
46
|
+
sqlsaber-0.8.0.dist-info/RECORD,,
|
sqlsaber-0.7.0.dist-info/RECORD
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
sqlsaber/__init__.py,sha256=QCFi8xTVMohelfi7zOV1-6oLCcGoiXoOcKQY-HNBCk8,66
|
|
2
|
-
sqlsaber/__main__.py,sha256=RIHxWeWh2QvLfah-2OkhI5IJxojWfy4fXpMnVEJYvxw,78
|
|
3
|
-
sqlsaber/agents/__init__.py,sha256=LWeSeEUE4BhkyAYFF3TE-fx8TtLud3oyEtyB8ojFJgo,167
|
|
4
|
-
sqlsaber/agents/anthropic.py,sha256=FLVET2HvFmsEuFln9Hu4SaBs-Tnk-GestOgnDnUp3ps,17885
|
|
5
|
-
sqlsaber/agents/base.py,sha256=DAnezHl5RLYoef8XQ-n3KA9PowdrMbQrkjdGKPPnFsI,10570
|
|
6
|
-
sqlsaber/agents/mcp.py,sha256=FKtXgDrPZ2-xqUYCw2baI5JzrWekXaC5fjkYW1_Mg50,827
|
|
7
|
-
sqlsaber/agents/streaming.py,sha256=_EO390-FHUrL1fRCNfibtE9QuJz3LGQygbwG3CB2ViY,533
|
|
8
|
-
sqlsaber/cli/__init__.py,sha256=qVSLVJLLJYzoC6aj6y9MFrzZvAwc4_OgxU9DlkQnZ4M,86
|
|
9
|
-
sqlsaber/cli/commands.py,sha256=Dw24W0jij-8t1lpk99C4PBTgzFSag6vU-FZcjAYGG54,5074
|
|
10
|
-
sqlsaber/cli/completers.py,sha256=JWOCKAm0Prpy_O2QJsf_VbPWfy2lQQh6KutyG8FU4us,6462
|
|
11
|
-
sqlsaber/cli/database.py,sha256=DUfyvNBDp47oFM_VAC_hXHQy_qyE7JbXtowflJpwwH8,12643
|
|
12
|
-
sqlsaber/cli/display.py,sha256=NIBWHUrX_8ZhDu6iW9v4fzx0zncnXa5WdQ9wfTrjKIM,10017
|
|
13
|
-
sqlsaber/cli/interactive.py,sha256=FvgtT45U-yblhbRImKqJ4jgBRNs0u7NhE2PcgoVUaVA,7429
|
|
14
|
-
sqlsaber/cli/memory.py,sha256=LW4ZF2V6Gw6hviUFGZ4ym9ostFCwucgBTIMZ3EANO-I,7671
|
|
15
|
-
sqlsaber/cli/models.py,sha256=3IcXeeU15IQvemSv-V-RQzVytJ3wuQ4YmWk89nTDcSE,7813
|
|
16
|
-
sqlsaber/cli/streaming.py,sha256=DfwygmjEzAh9hZGKjrW9kS1A7MG5W9Ky_kCTzxziODQ,4970
|
|
17
|
-
sqlsaber/config/__init__.py,sha256=olwC45k8Nc61yK0WmPUk7XHdbsZH9HuUAbwnmKe3IgA,100
|
|
18
|
-
sqlsaber/config/api_keys.py,sha256=kLdoExF_My9ojmdhO5Ca7-ZeowsO0v1GVa_QT5jjUPo,3658
|
|
19
|
-
sqlsaber/config/database.py,sha256=vKFOxPjVakjQhj1uoLcfzhS9ZFr6Z2F5b4MmYALQZoA,11421
|
|
20
|
-
sqlsaber/config/settings.py,sha256=zjQ7nS3ybcCb88Ea0tmwJox5-q0ettChZw89ZqRVpX8,3975
|
|
21
|
-
sqlsaber/database/__init__.py,sha256=a_gtKRJnZVO8-fEZI7g3Z8YnGa6Nio-5Y50PgVp07ss,176
|
|
22
|
-
sqlsaber/database/connection.py,sha256=s8GSFZebB8be8sVUr-N0x88-20YfkfljJFRyfoB1gH0,15154
|
|
23
|
-
sqlsaber/database/schema.py,sha256=3CfkyhxgD6SmiUoz7MQPlQLrrA007HOQLnGCvvsdJx0,28647
|
|
24
|
-
sqlsaber/mcp/__init__.py,sha256=COdWq7wauPBp5Ew8tfZItFzbcLDSEkHBJSMhxzy8C9c,112
|
|
25
|
-
sqlsaber/mcp/mcp.py,sha256=ACm1P1TnicjOptQgeLNhXg5xgZf4MYq2kqdfVdj6wh0,4477
|
|
26
|
-
sqlsaber/memory/__init__.py,sha256=GiWkU6f6YYVV0EvvXDmFWe_CxarmDCql05t70MkTEWs,63
|
|
27
|
-
sqlsaber/memory/manager.py,sha256=ML2NEO5Z4Aw36sEI9eOvWVnjl-qT2VOTojViJAj7Seo,2777
|
|
28
|
-
sqlsaber/memory/storage.py,sha256=DvZBsSPaAfk_DqrNEn86uMD-TQsWUI6rQLfNw6PSCB8,5788
|
|
29
|
-
sqlsaber/models/__init__.py,sha256=RJ7p3WtuSwwpFQ1Iw4_DHV2zzCtHqIzsjJzxv8kUjUE,287
|
|
30
|
-
sqlsaber/models/events.py,sha256=q2FackB60J9-7vegYIjzElLwKebIh7nxnV5AFoZc67c,752
|
|
31
|
-
sqlsaber/models/types.py,sha256=3U_30n91EB3IglBTHipwiW4MqmmaA2qfshfraMZyPps,896
|
|
32
|
-
sqlsaber-0.7.0.dist-info/METADATA,sha256=tUV3WHkVZEXissVrKAaOooaZyn7e_NmMV_e-nNaoLVE,5986
|
|
33
|
-
sqlsaber-0.7.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
34
|
-
sqlsaber-0.7.0.dist-info/entry_points.txt,sha256=jmFo96Ylm0zIKXJBwhv_P5wQ7SXP9qdaBcnTp8iCEe8,195
|
|
35
|
-
sqlsaber-0.7.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
36
|
-
sqlsaber-0.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|