sqlsaber 0.25.0__py3-none-any.whl → 0.26.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/__init__.py +2 -2
- sqlsaber/agents/base.py +1 -1
- sqlsaber/agents/mcp.py +1 -1
- sqlsaber/agents/pydantic_ai_agent.py +207 -135
- sqlsaber/cli/commands.py +11 -28
- sqlsaber/cli/completers.py +2 -0
- sqlsaber/cli/database.py +1 -1
- sqlsaber/cli/display.py +29 -9
- sqlsaber/cli/interactive.py +22 -15
- sqlsaber/cli/streaming.py +15 -17
- sqlsaber/cli/threads.py +10 -6
- sqlsaber/config/settings.py +25 -2
- sqlsaber/database/__init__.py +55 -1
- sqlsaber/database/base.py +124 -0
- sqlsaber/database/csv.py +133 -0
- sqlsaber/database/duckdb.py +313 -0
- sqlsaber/database/mysql.py +345 -0
- sqlsaber/database/postgresql.py +328 -0
- sqlsaber/database/schema.py +66 -963
- sqlsaber/database/sqlite.py +258 -0
- sqlsaber/mcp/mcp.py +1 -1
- sqlsaber/tools/sql_tools.py +1 -1
- {sqlsaber-0.25.0.dist-info → sqlsaber-0.26.0.dist-info}/METADATA +43 -9
- sqlsaber-0.26.0.dist-info/RECORD +52 -0
- sqlsaber/database/connection.py +0 -535
- sqlsaber-0.25.0.dist-info/RECORD +0 -47
- {sqlsaber-0.25.0.dist-info → sqlsaber-0.26.0.dist-info}/WHEEL +0 -0
- {sqlsaber-0.25.0.dist-info → sqlsaber-0.26.0.dist-info}/entry_points.txt +0 -0
- {sqlsaber-0.25.0.dist-info → sqlsaber-0.26.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""SQLite database connection and schema introspection."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import aiosqlite
|
|
7
|
+
|
|
8
|
+
from .base import (
|
|
9
|
+
DEFAULT_QUERY_TIMEOUT,
|
|
10
|
+
BaseDatabaseConnection,
|
|
11
|
+
BaseSchemaIntrospector,
|
|
12
|
+
QueryTimeoutError,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SQLiteConnection(BaseDatabaseConnection):
|
|
17
|
+
"""SQLite database connection using aiosqlite."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, connection_string: str):
|
|
20
|
+
super().__init__(connection_string)
|
|
21
|
+
# Extract database path from sqlite:///path format
|
|
22
|
+
self.database_path = connection_string.replace("sqlite:///", "")
|
|
23
|
+
|
|
24
|
+
async def get_pool(self):
|
|
25
|
+
"""SQLite doesn't use connection pooling, return database path."""
|
|
26
|
+
return self.database_path
|
|
27
|
+
|
|
28
|
+
async def close(self):
|
|
29
|
+
"""SQLite connections are created per query, no persistent pool to close."""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
async def execute_query(
|
|
33
|
+
self, query: str, *args, timeout: float | None = None
|
|
34
|
+
) -> list[dict[str, Any]]:
|
|
35
|
+
"""Execute a query and return results as list of dicts.
|
|
36
|
+
|
|
37
|
+
All queries run in a transaction that is rolled back at the end,
|
|
38
|
+
ensuring no changes are persisted to the database.
|
|
39
|
+
"""
|
|
40
|
+
effective_timeout = timeout or DEFAULT_QUERY_TIMEOUT
|
|
41
|
+
|
|
42
|
+
async with aiosqlite.connect(self.database_path) as conn:
|
|
43
|
+
# Enable row factory for dict-like access
|
|
44
|
+
conn.row_factory = aiosqlite.Row
|
|
45
|
+
|
|
46
|
+
# Start transaction
|
|
47
|
+
await conn.execute("BEGIN")
|
|
48
|
+
try:
|
|
49
|
+
# Execute query with client-side timeout (SQLite has no server-side timeout)
|
|
50
|
+
if effective_timeout:
|
|
51
|
+
cursor = await asyncio.wait_for(
|
|
52
|
+
conn.execute(query, args if args else ()),
|
|
53
|
+
timeout=effective_timeout,
|
|
54
|
+
)
|
|
55
|
+
rows = await asyncio.wait_for(
|
|
56
|
+
cursor.fetchall(), timeout=effective_timeout
|
|
57
|
+
)
|
|
58
|
+
else:
|
|
59
|
+
cursor = await conn.execute(query, args if args else ())
|
|
60
|
+
rows = await cursor.fetchall()
|
|
61
|
+
|
|
62
|
+
return [dict(row) for row in rows]
|
|
63
|
+
except asyncio.TimeoutError as exc:
|
|
64
|
+
raise QueryTimeoutError(effective_timeout or 0) from exc
|
|
65
|
+
finally:
|
|
66
|
+
# Always rollback to ensure no changes are committed
|
|
67
|
+
await conn.rollback()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class SQLiteSchemaIntrospector(BaseSchemaIntrospector):
|
|
71
|
+
"""SQLite-specific schema introspection."""
|
|
72
|
+
|
|
73
|
+
async def _execute_query(self, connection, query: str, params=()) -> list:
|
|
74
|
+
"""Helper method to execute queries on both SQLite and CSV connections."""
|
|
75
|
+
# Handle both SQLite and CSV connections
|
|
76
|
+
if hasattr(connection, "database_path"):
|
|
77
|
+
# Regular SQLite connection
|
|
78
|
+
async with aiosqlite.connect(connection.database_path) as conn:
|
|
79
|
+
conn.row_factory = aiosqlite.Row
|
|
80
|
+
cursor = await conn.execute(query, params)
|
|
81
|
+
return await cursor.fetchall()
|
|
82
|
+
else:
|
|
83
|
+
# CSV connection - use the existing connection
|
|
84
|
+
conn = await connection.get_pool()
|
|
85
|
+
cursor = await conn.execute(query, params)
|
|
86
|
+
return await cursor.fetchall()
|
|
87
|
+
|
|
88
|
+
async def get_tables_info(
|
|
89
|
+
self, connection, table_pattern: str | None = None
|
|
90
|
+
) -> dict[str, Any]:
|
|
91
|
+
"""Get tables information for SQLite."""
|
|
92
|
+
where_conditions = ["type IN ('table', 'view')", "name NOT LIKE 'sqlite_%'"]
|
|
93
|
+
params = ()
|
|
94
|
+
|
|
95
|
+
if table_pattern:
|
|
96
|
+
where_conditions.append("name LIKE ?")
|
|
97
|
+
params = (table_pattern,)
|
|
98
|
+
|
|
99
|
+
query = f"""
|
|
100
|
+
SELECT
|
|
101
|
+
'main' as table_schema,
|
|
102
|
+
name as table_name,
|
|
103
|
+
type as table_type
|
|
104
|
+
FROM sqlite_master
|
|
105
|
+
WHERE {" AND ".join(where_conditions)}
|
|
106
|
+
ORDER BY name;
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
return await self._execute_query(connection, query, params)
|
|
110
|
+
|
|
111
|
+
async def get_columns_info(self, connection, tables: list) -> list:
|
|
112
|
+
"""Get columns information for SQLite."""
|
|
113
|
+
if not tables:
|
|
114
|
+
return []
|
|
115
|
+
|
|
116
|
+
columns = []
|
|
117
|
+
for table in tables:
|
|
118
|
+
table_name = table["table_name"]
|
|
119
|
+
|
|
120
|
+
# Get table info using PRAGMA
|
|
121
|
+
pragma_query = f"PRAGMA table_info({table_name})"
|
|
122
|
+
table_columns = await self._execute_query(connection, pragma_query)
|
|
123
|
+
|
|
124
|
+
for col in table_columns:
|
|
125
|
+
columns.append(
|
|
126
|
+
{
|
|
127
|
+
"table_schema": "main",
|
|
128
|
+
"table_name": table_name,
|
|
129
|
+
"column_name": col["name"],
|
|
130
|
+
"data_type": col["type"],
|
|
131
|
+
"is_nullable": "YES" if not col["notnull"] else "NO",
|
|
132
|
+
"column_default": col["dflt_value"],
|
|
133
|
+
"character_maximum_length": None,
|
|
134
|
+
"numeric_precision": None,
|
|
135
|
+
"numeric_scale": None,
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
return columns
|
|
140
|
+
|
|
141
|
+
async def get_foreign_keys_info(self, connection, tables: list) -> list:
|
|
142
|
+
"""Get foreign keys information for SQLite."""
|
|
143
|
+
if not tables:
|
|
144
|
+
return []
|
|
145
|
+
|
|
146
|
+
foreign_keys = []
|
|
147
|
+
for table in tables:
|
|
148
|
+
table_name = table["table_name"]
|
|
149
|
+
|
|
150
|
+
# Get foreign key info using PRAGMA
|
|
151
|
+
pragma_query = f"PRAGMA foreign_key_list({table_name})"
|
|
152
|
+
table_fks = await self._execute_query(connection, pragma_query)
|
|
153
|
+
|
|
154
|
+
for fk in table_fks:
|
|
155
|
+
foreign_keys.append(
|
|
156
|
+
{
|
|
157
|
+
"table_schema": "main",
|
|
158
|
+
"table_name": table_name,
|
|
159
|
+
"column_name": fk["from"],
|
|
160
|
+
"foreign_table_schema": "main",
|
|
161
|
+
"foreign_table_name": fk["table"],
|
|
162
|
+
"foreign_column_name": fk["to"],
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
return foreign_keys
|
|
167
|
+
|
|
168
|
+
async def get_primary_keys_info(self, connection, tables: list) -> list:
|
|
169
|
+
"""Get primary keys information for SQLite."""
|
|
170
|
+
if not tables:
|
|
171
|
+
return []
|
|
172
|
+
|
|
173
|
+
primary_keys = []
|
|
174
|
+
for table in tables:
|
|
175
|
+
table_name = table["table_name"]
|
|
176
|
+
|
|
177
|
+
# Get table info using PRAGMA to find primary keys
|
|
178
|
+
pragma_query = f"PRAGMA table_info({table_name})"
|
|
179
|
+
table_columns = await self._execute_query(connection, pragma_query)
|
|
180
|
+
|
|
181
|
+
for col in table_columns:
|
|
182
|
+
if col["pk"]: # Primary key indicator
|
|
183
|
+
primary_keys.append(
|
|
184
|
+
{
|
|
185
|
+
"table_schema": "main",
|
|
186
|
+
"table_name": table_name,
|
|
187
|
+
"column_name": col["name"],
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
return primary_keys
|
|
192
|
+
|
|
193
|
+
async def get_indexes_info(self, connection, tables: list) -> list:
|
|
194
|
+
"""Get indexes information for SQLite."""
|
|
195
|
+
if not tables:
|
|
196
|
+
return []
|
|
197
|
+
|
|
198
|
+
indexes = []
|
|
199
|
+
for table in tables:
|
|
200
|
+
table_name = table["table_name"]
|
|
201
|
+
|
|
202
|
+
# Get index list using PRAGMA
|
|
203
|
+
pragma_query = f"PRAGMA index_list({table_name})"
|
|
204
|
+
table_indexes = await self._execute_query(connection, pragma_query)
|
|
205
|
+
|
|
206
|
+
for idx in table_indexes:
|
|
207
|
+
idx_name = idx["name"]
|
|
208
|
+
unique = bool(idx["unique"])
|
|
209
|
+
|
|
210
|
+
# Skip auto-generated primary key indexes
|
|
211
|
+
if idx_name.startswith("sqlite_autoindex_"):
|
|
212
|
+
continue
|
|
213
|
+
|
|
214
|
+
# Get index columns using PRAGMA
|
|
215
|
+
pragma_info_query = f"PRAGMA index_info({idx_name})"
|
|
216
|
+
idx_cols = await self._execute_query(connection, pragma_info_query)
|
|
217
|
+
columns = [
|
|
218
|
+
c["name"] for c in sorted(idx_cols, key=lambda r: r["seqno"])
|
|
219
|
+
]
|
|
220
|
+
|
|
221
|
+
indexes.append(
|
|
222
|
+
{
|
|
223
|
+
"table_schema": "main",
|
|
224
|
+
"table_name": table_name,
|
|
225
|
+
"index_name": idx_name,
|
|
226
|
+
"is_unique": unique,
|
|
227
|
+
"index_type": None, # SQLite only has B-tree currently
|
|
228
|
+
"column_names": columns,
|
|
229
|
+
}
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
return indexes
|
|
233
|
+
|
|
234
|
+
async def list_tables_info(self, connection) -> list[dict[str, Any]]:
|
|
235
|
+
"""Get list of tables with basic information for SQLite."""
|
|
236
|
+
# Get table names without row counts for better performance
|
|
237
|
+
tables_query = """
|
|
238
|
+
SELECT
|
|
239
|
+
'main' as table_schema,
|
|
240
|
+
name as table_name,
|
|
241
|
+
type as table_type
|
|
242
|
+
FROM sqlite_master
|
|
243
|
+
WHERE type IN ('table', 'view')
|
|
244
|
+
AND name NOT LIKE 'sqlite_%'
|
|
245
|
+
ORDER BY name;
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
tables = await self._execute_query(connection, tables_query)
|
|
249
|
+
|
|
250
|
+
# Convert to expected format
|
|
251
|
+
return [
|
|
252
|
+
{
|
|
253
|
+
"table_schema": table["table_schema"],
|
|
254
|
+
"table_name": table["table_name"],
|
|
255
|
+
"table_type": table["table_type"],
|
|
256
|
+
}
|
|
257
|
+
for table in tables
|
|
258
|
+
]
|
sqlsaber/mcp/mcp.py
CHANGED
|
@@ -6,7 +6,7 @@ from fastmcp import FastMCP
|
|
|
6
6
|
|
|
7
7
|
from sqlsaber.agents.mcp import MCPSQLAgent
|
|
8
8
|
from sqlsaber.config.database import DatabaseConfigManager
|
|
9
|
-
from sqlsaber.database
|
|
9
|
+
from sqlsaber.database import DatabaseConnection
|
|
10
10
|
from sqlsaber.tools import SQLTool, tool_registry
|
|
11
11
|
from sqlsaber.tools.instructions import InstructionBuilder
|
|
12
12
|
|
sqlsaber/tools/sql_tools.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlsaber
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.26.0
|
|
4
4
|
Summary: SQLsaber - Open-source agentic SQL assistant
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -53,14 +53,15 @@ Ask your questions in natural language and `sqlsaber` will gather the right cont
|
|
|
53
53
|
|
|
54
54
|
## Features
|
|
55
55
|
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
56
|
+
- Automatic database schema introspection
|
|
57
|
+
- Safe query execution (read-only by default)
|
|
58
|
+
- Memory management
|
|
59
|
+
- Interactive REPL mode
|
|
60
|
+
- Conversation threads (store, display, and resume conversations)
|
|
61
|
+
- Support for PostgreSQL, MySQL, SQLite, DuckDB, and CSVs
|
|
62
|
+
- MCP (Model Context Protocol) server support
|
|
63
|
+
- Extended thinking mode for select models (Anthropic, OpenAI, Google, Groq)
|
|
64
|
+
- Beautiful formatted output
|
|
64
65
|
|
|
65
66
|
## Installation
|
|
66
67
|
|
|
@@ -122,6 +123,39 @@ saber memory list
|
|
|
122
123
|
|
|
123
124
|
> You can also add memories in an interactive query session by starting with the `#` sign
|
|
124
125
|
|
|
126
|
+
### Extended Thinking Mode
|
|
127
|
+
|
|
128
|
+
For complex queries that require deeper reasoning, `sqlsaber` supports extended thinking mode. When enabled, you will see the model's reasoning process as it generates SQL queries and arrives at conclusions.
|
|
129
|
+
|
|
130
|
+
**Enable/disable via CLI flags:**
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# Enable thinking for a single query
|
|
134
|
+
saber --thinking "analyze sales trends across regions"
|
|
135
|
+
|
|
136
|
+
# Disable thinking for a single query
|
|
137
|
+
saber --no-thinking "show me all users"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Toggle in interactive mode:**
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# In interactive mode, use slash commands
|
|
144
|
+
/thinking on # Enable thinking
|
|
145
|
+
/thinking off # Disable thinking
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Configure default setting:**
|
|
149
|
+
|
|
150
|
+
Thinking is disabled by default. To change the default, edit your config file at `~/.config/sqlsaber/model_config.json`:
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"model": "anthropic:claude-sonnet-4-20250514",
|
|
155
|
+
"thinking_enabled": true
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
125
159
|
## Usage
|
|
126
160
|
|
|
127
161
|
### Interactive Mode
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
sqlsaber/__init__.py,sha256=HjS8ULtP4MGpnTL7njVY45NKV9Fi4e_yeYuY-hyXWQc,73
|
|
2
|
+
sqlsaber/__main__.py,sha256=RIHxWeWh2QvLfah-2OkhI5IJxojWfy4fXpMnVEJYvxw,78
|
|
3
|
+
sqlsaber/agents/__init__.py,sha256=qYI6rLY4q5AbF47vXH5RVoM08-yQjymBSaePh4lFIW4,116
|
|
4
|
+
sqlsaber/agents/base.py,sha256=40-MKEoz5rGrqVIylV1U2DaAUSPFcC75ohRin4E3-kk,2668
|
|
5
|
+
sqlsaber/agents/mcp.py,sha256=Pn8tdDRUEVLYQyEi5nHRp9MKNePwHVVoeNI-uqWcr0Y,757
|
|
6
|
+
sqlsaber/agents/pydantic_ai_agent.py,sha256=wBxKz0pjOkL-HI-TXV6B67bczZNgu7k26Rr3w5usR3o,10064
|
|
7
|
+
sqlsaber/cli/__init__.py,sha256=qVSLVJLLJYzoC6aj6y9MFrzZvAwc4_OgxU9DlkQnZ4M,86
|
|
8
|
+
sqlsaber/cli/auth.py,sha256=jTsRgbmlGPlASSuIKmdjjwfqtKvjfKd_cTYxX0-QqaQ,7400
|
|
9
|
+
sqlsaber/cli/commands.py,sha256=n25CErTLgLeRSkoJI0Ickwtns5EH6O7RLVPgPs6UBxA,7986
|
|
10
|
+
sqlsaber/cli/completers.py,sha256=g-hLDq5fiBx7gg8Bte1Lq8GU-ZxCYVs4dcPsmHPIcK4,6574
|
|
11
|
+
sqlsaber/cli/database.py,sha256=qil7nZGWKm3tULL0cUsAQ_KvhU1oikK0XVh9MibrvP0,13413
|
|
12
|
+
sqlsaber/cli/display.py,sha256=32QaNS0RDgRz93AVy6nPo9blahvMPEoVMFC5spzh0-Y,17041
|
|
13
|
+
sqlsaber/cli/interactive.py,sha256=jGbWNNcEgZuQRZamc5tX5eIf1Rv1T6Sj5NI_WvonTrA,13624
|
|
14
|
+
sqlsaber/cli/memory.py,sha256=OufHFJFwV0_GGn7LvKRTJikkWhV1IwNIUDOxFPHXOaQ,7794
|
|
15
|
+
sqlsaber/cli/models.py,sha256=ZewtwGQwhd9b-yxBAPKePolvI1qQG-EkmeWAGMqtWNQ,8986
|
|
16
|
+
sqlsaber/cli/streaming.py,sha256=YViLCxUv-7WN5TCphLYtAR02HXvuHYuPttGGDZKDUKU,6921
|
|
17
|
+
sqlsaber/cli/threads.py,sha256=zVlbOuD3GjjEVNebXwANKeKt4I_Lunf6itiBUL0TaKA,12877
|
|
18
|
+
sqlsaber/config/__init__.py,sha256=olwC45k8Nc61yK0WmPUk7XHdbsZH9HuUAbwnmKe3IgA,100
|
|
19
|
+
sqlsaber/config/api_keys.py,sha256=RqWQCko1tY7sES7YOlexgBH5Hd5ne_kGXHdBDNqcV2U,3649
|
|
20
|
+
sqlsaber/config/auth.py,sha256=b5qB2h1doXyO9Bn8z0CcL8LAR2jF431gGXBGKLgTmtQ,2756
|
|
21
|
+
sqlsaber/config/database.py,sha256=Yec6_0wdzq-ADblMNnbgvouYCimYOY_DWHT9oweaISc,11449
|
|
22
|
+
sqlsaber/config/oauth_flow.py,sha256=A3bSXaBLzuAfXV2ZPA94m9NV33c2MyL6M4ii9oEkswQ,10291
|
|
23
|
+
sqlsaber/config/oauth_tokens.py,sha256=C9z35hyx-PvSAYdC1LNf3rg9_wsEIY56hkEczelbad0,6015
|
|
24
|
+
sqlsaber/config/providers.py,sha256=JFjeJv1K5Q93zWSlWq3hAvgch1TlgoF0qFa0KJROkKY,2957
|
|
25
|
+
sqlsaber/config/settings.py,sha256=iB4CnGQ4hw8gxkaa9CVLB_JEy6Y9h9FQTAams5OCVyI,6421
|
|
26
|
+
sqlsaber/database/__init__.py,sha256=Gi9N_NOkD459WRWXDg3hSuGoBs3xWbMDRBvsTVmnGAg,2025
|
|
27
|
+
sqlsaber/database/base.py,sha256=yxYcfeNhRPbO5jFRVZH7eRUGj_up-y3p1ZX_obZXi0w,3552
|
|
28
|
+
sqlsaber/database/csv.py,sha256=45eH9mAkBtwSu1Rc_vvG1Z40L4xvfHWSb8OMG15TbCA,4340
|
|
29
|
+
sqlsaber/database/duckdb.py,sha256=v6gFUhih5NMbHHpUv7By2nXyl9aqdPtLt0zhqS4-OKE,11120
|
|
30
|
+
sqlsaber/database/mysql.py,sha256=5qd9gnSCP3umtBJcQDTzzJfMzwqYCJhWlbOeJZ9_-6c,14349
|
|
31
|
+
sqlsaber/database/postgresql.py,sha256=R8I3Y-w0P9qPe47-lmae0X17syIwI8saxEG3etx6Rqc,13097
|
|
32
|
+
sqlsaber/database/resolver.py,sha256=wSCcn__aCqwIfpt_LCjtW2Zgb8RpG5PlmwwZHli1q_U,3628
|
|
33
|
+
sqlsaber/database/schema.py,sha256=68PrNcA-5eR9PZB3i-TUQw5_E7QatwiDU2wv9GgXgM4,6928
|
|
34
|
+
sqlsaber/database/sqlite.py,sha256=zdNj5i4mLJK21sWgftAHDHVihRUWevn__tVF9_nnLfQ,9297
|
|
35
|
+
sqlsaber/mcp/__init__.py,sha256=COdWq7wauPBp5Ew8tfZItFzbcLDSEkHBJSMhxzy8C9c,112
|
|
36
|
+
sqlsaber/mcp/mcp.py,sha256=tpNPHpkaCre1Xjp7c4DHXbTKeuYpDQ8qhmJZvAyr7Vk,3939
|
|
37
|
+
sqlsaber/memory/__init__.py,sha256=GiWkU6f6YYVV0EvvXDmFWe_CxarmDCql05t70MkTEWs,63
|
|
38
|
+
sqlsaber/memory/manager.py,sha256=p3fybMVfH-E4ApT1ZRZUnQIWSk9dkfUPCyfkmA0HALs,2739
|
|
39
|
+
sqlsaber/memory/storage.py,sha256=ne8szLlGj5NELheqLnI7zu21V8YS4rtpYGGC7tOmi-s,5745
|
|
40
|
+
sqlsaber/threads/__init__.py,sha256=Hh3dIG1tuC8fXprREUpslCIgPYz8_6o7aRLx4yNeO48,139
|
|
41
|
+
sqlsaber/threads/storage.py,sha256=rsUdxT4CR52D7xtGir9UlsFnBMk11jZeflzDrk2q4ME,11183
|
|
42
|
+
sqlsaber/tools/__init__.py,sha256=x3YdmX_7P0Qq_HtZHAgfIVKTLxYqKk6oc4tGsujQWsc,586
|
|
43
|
+
sqlsaber/tools/base.py,sha256=mHhvAj27BHmckyvuDLCPlAQdzABJyYxd9SJnaYAwwuA,1777
|
|
44
|
+
sqlsaber/tools/enums.py,sha256=CH32mL-0k9ZA18911xLpNtsgpV6tB85TktMj6uqGz54,411
|
|
45
|
+
sqlsaber/tools/instructions.py,sha256=X-x8maVkkyi16b6Tl0hcAFgjiYceZaSwyWTfmrvx8U8,9024
|
|
46
|
+
sqlsaber/tools/registry.py,sha256=HWOQMsNIdL4XZS6TeNUyrL-5KoSDH6PHsWd3X66o-18,3211
|
|
47
|
+
sqlsaber/tools/sql_tools.py,sha256=2xLD_pkd0t8wKndQAKIr4c9UpWzVWeHbAFpkwo5j4kY,9954
|
|
48
|
+
sqlsaber-0.26.0.dist-info/METADATA,sha256=o4vaJVAG_1U5Tybcx2MY3lX0FvYjBEdKxFDWhFC9xYs,7138
|
|
49
|
+
sqlsaber-0.26.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
50
|
+
sqlsaber-0.26.0.dist-info/entry_points.txt,sha256=qEbOB7OffXPFgyJc7qEIJlMEX5RN9xdzLmWZa91zCQQ,162
|
|
51
|
+
sqlsaber-0.26.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
52
|
+
sqlsaber-0.26.0.dist-info/RECORD,,
|