sqlsaber 0.8.2__tar.gz → 0.9.0__tar.gz
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-0.8.2 → sqlsaber-0.9.0}/.gitignore +2 -0
- sqlsaber-0.9.0/AGENT.md +24 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/CHANGELOG.md +12 -0
- sqlsaber-0.9.0/CLAUDE.md +22 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/PKG-INFO +19 -13
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/README.md +17 -11
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/pyproject.toml +2 -3
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/auth.py +9 -10
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/commands.py +62 -53
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/database.py +88 -62
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/memory.py +83 -73
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/models.py +15 -15
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/uv.lock +58 -3
- sqlsaber-0.8.2/CLAUDE.md +0 -5
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/.github/workflows/publish.yml +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/.python-version +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/LICENSE +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/pytest.ini +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/sqlsaber.svg +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/__init__.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/__main__.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/agents/__init__.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/agents/anthropic.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/agents/base.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/agents/mcp.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/agents/streaming.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/__init__.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/completers.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/display.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/interactive.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/streaming.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/clients/__init__.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/clients/anthropic.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/clients/base.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/clients/exceptions.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/clients/models.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/clients/streaming.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/config/__init__.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/config/api_keys.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/config/auth.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/config/database.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/config/oauth_flow.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/config/oauth_tokens.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/config/settings.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/database/__init__.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/database/connection.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/database/schema.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/mcp/__init__.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/mcp/mcp.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/memory/__init__.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/memory/manager.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/memory/storage.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/models/__init__.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/models/events.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/models/types.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/__init__.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/conftest.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_agents/test_anthropic_oauth.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_cli/__init__.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_cli/test_commands.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_clients/test_anthropic_client.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_clients/test_streaming.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_config/__init__.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_config/test_database.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_config/test_oauth.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_config/test_settings.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_database/__init__.py +0 -0
- {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_database/test_connection.py +0 -0
sqlsaber-0.9.0/AGENT.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# SQLSaber Agent Guide
|
|
2
|
+
|
|
3
|
+
## Commands
|
|
4
|
+
|
|
5
|
+
- **Run Python**: `uv run python`
|
|
6
|
+
- **Run tests**: `uv run python -m pytest`
|
|
7
|
+
- **Run single test**: `uv run python -m pytest tests/test_path/test_file.py::test_function`
|
|
8
|
+
- **Lint**: `uv run ruff check --fix`
|
|
9
|
+
- **Format**: `uv run ruff format`
|
|
10
|
+
|
|
11
|
+
## Architecture
|
|
12
|
+
|
|
13
|
+
- **CLI App**: Agentic SQL assistant for natural language to SQL
|
|
14
|
+
- **Core modules**: `agents/` (AI logic), `cli/` (commands), `clients/` (LLM clients), `database/` (connections), `mcp/` (Model Context Protocol server)
|
|
15
|
+
- **Database support**: PostgreSQL, SQLite, MySQL via asyncpg/aiosqlite/aiomysql
|
|
16
|
+
- **MCP integration**: Exposes tools via `sqlsaber-mcp` for Claude Code and other MCP clients
|
|
17
|
+
|
|
18
|
+
## Code Style
|
|
19
|
+
|
|
20
|
+
- **Imports**: stdlib → 3rd party → local, use relative imports within modules
|
|
21
|
+
- **Naming**: snake_case functions/vars, PascalCase classes, UPPER_SNAKE constants, `_private` methods
|
|
22
|
+
- **Types**: Always use modern type hints (3.12+), async functions for I/O
|
|
23
|
+
- **Errors**: Custom exception hierarchy from `LLMClientError`, use try/finally for cleanup
|
|
24
|
+
- **Docstrings**: Triple-quoted with Args/Returns sections
|
|
@@ -4,6 +4,18 @@ All notable changes to SQLSaber will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.9.0] - 2025-07-08
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Migrated from Typer to Cyclopts for CLI framework
|
|
12
|
+
- Improved command structure and parameter handling
|
|
13
|
+
- Better support for sub-commands and help documentation
|
|
14
|
+
- Made interactive mode more ergonomic
|
|
15
|
+
- `saber` now directly starts interactive mode (previously `saber query`)
|
|
16
|
+
- `saber "question"` executes a single query (previously `saber query "question"`)
|
|
17
|
+
- Removed the `query` subcommand for a cleaner interface
|
|
18
|
+
|
|
7
19
|
## [0.8.2] - 2025-07-08
|
|
8
20
|
|
|
9
21
|
## Changed
|
sqlsaber-0.9.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
## Commands
|
|
2
|
+
|
|
3
|
+
- **Run Python**: `uv run python`
|
|
4
|
+
- **Run tests**: `uv run python -m pytest`
|
|
5
|
+
- **Run single test**: `uv run python -m pytest tests/test_path/test_file.py::test_function`
|
|
6
|
+
- **Lint**: `uv run ruff check --fix`
|
|
7
|
+
- **Format**: `uv run ruff format`
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
- **CLI App**: Agentic SQL assistant for natural language to SQL
|
|
12
|
+
- **Core modules**: `agents/` (AI logic), `cli/` (commands), `clients/` (LLM clients), `database/` (connections), `mcp/` (Model Context Protocol server)
|
|
13
|
+
- **Database support**: PostgreSQL, SQLite, MySQL via asyncpg/aiosqlite/aiomysql
|
|
14
|
+
- **MCP integration**: Exposes tools via `sqlsaber-mcp` for Claude Code and other MCP clients
|
|
15
|
+
|
|
16
|
+
## Code Style
|
|
17
|
+
|
|
18
|
+
- **Imports**: stdlib → 3rd party → local, use relative imports within modules
|
|
19
|
+
- **Naming**: snake_case functions/vars, PascalCase classes, UPPER_SNAKE constants, `_private` methods
|
|
20
|
+
- **Types**: Always use modern type hints (3.12+), async functions for I/O
|
|
21
|
+
- **Errors**: Custom exception hierarchy from `LLMClientError`, use try/finally for cleanup
|
|
22
|
+
- **Docstrings**: Triple-quoted with Args/Returns sections
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlsaber
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: SQLSaber - Agentic SQL assistant like Claude Code
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -8,6 +8,7 @@ Requires-Dist: aiomysql>=0.2.0
|
|
|
8
8
|
Requires-Dist: aiosqlite>=0.21.0
|
|
9
9
|
Requires-Dist: anthropic>=0.54.0
|
|
10
10
|
Requires-Dist: asyncpg>=0.30.0
|
|
11
|
+
Requires-Dist: cyclopts>=3.22.1
|
|
11
12
|
Requires-Dist: fastmcp>=2.9.0
|
|
12
13
|
Requires-Dist: httpx>=0.28.1
|
|
13
14
|
Requires-Dist: keyring>=25.6.0
|
|
@@ -15,7 +16,6 @@ Requires-Dist: pandas>=2.0.0
|
|
|
15
16
|
Requires-Dist: platformdirs>=4.0.0
|
|
16
17
|
Requires-Dist: questionary>=2.1.0
|
|
17
18
|
Requires-Dist: rich>=13.7.0
|
|
18
|
-
Requires-Dist: typer>=0.16.0
|
|
19
19
|
Requires-Dist: uniplot>=0.21.2
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
|
|
@@ -126,7 +126,7 @@ saber memory list
|
|
|
126
126
|
Start an interactive session:
|
|
127
127
|
|
|
128
128
|
```bash
|
|
129
|
-
saber
|
|
129
|
+
saber
|
|
130
130
|
```
|
|
131
131
|
|
|
132
132
|
> You can also add memories in an interactive session by starting your message with the `#` sign
|
|
@@ -136,7 +136,7 @@ saber query
|
|
|
136
136
|
Execute a single natural language query:
|
|
137
137
|
|
|
138
138
|
```bash
|
|
139
|
-
saber
|
|
139
|
+
saber "show me all users created this month"
|
|
140
140
|
```
|
|
141
141
|
|
|
142
142
|
### Database Selection
|
|
@@ -144,33 +144,39 @@ saber query "show me all users created this month"
|
|
|
144
144
|
Use a specific database connection:
|
|
145
145
|
|
|
146
146
|
```bash
|
|
147
|
-
#
|
|
148
|
-
saber
|
|
147
|
+
# Interactive mode with specific database
|
|
148
|
+
saber -d mydb
|
|
149
|
+
|
|
150
|
+
# Single query with specific database
|
|
151
|
+
saber -d mydb "count all orders"
|
|
149
152
|
```
|
|
150
153
|
|
|
151
154
|
## Examples
|
|
152
155
|
|
|
153
156
|
```bash
|
|
154
157
|
# Show database schema
|
|
155
|
-
saber
|
|
158
|
+
saber "what tables are in my database?"
|
|
156
159
|
|
|
157
160
|
# Count records
|
|
158
|
-
saber
|
|
161
|
+
saber "how many active users do we have?"
|
|
159
162
|
|
|
160
163
|
# Complex queries with joins
|
|
161
|
-
saber
|
|
164
|
+
saber "show me orders with customer details for this week"
|
|
162
165
|
|
|
163
166
|
# Aggregations
|
|
164
|
-
saber
|
|
167
|
+
saber "what's the total revenue by product category?"
|
|
165
168
|
|
|
166
169
|
# Date filtering
|
|
167
|
-
saber
|
|
170
|
+
saber "list users who haven't logged in for 30 days"
|
|
168
171
|
|
|
169
172
|
# Data exploration
|
|
170
|
-
saber
|
|
173
|
+
saber "show me the distribution of customer ages"
|
|
171
174
|
|
|
172
175
|
# Business analytics
|
|
173
|
-
saber
|
|
176
|
+
saber "which products had the highest sales growth last quarter?"
|
|
177
|
+
|
|
178
|
+
# Start interactive mode
|
|
179
|
+
saber
|
|
174
180
|
```
|
|
175
181
|
|
|
176
182
|
## MCP Server Integration
|
|
@@ -105,7 +105,7 @@ saber memory list
|
|
|
105
105
|
Start an interactive session:
|
|
106
106
|
|
|
107
107
|
```bash
|
|
108
|
-
saber
|
|
108
|
+
saber
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
> You can also add memories in an interactive session by starting your message with the `#` sign
|
|
@@ -115,7 +115,7 @@ saber query
|
|
|
115
115
|
Execute a single natural language query:
|
|
116
116
|
|
|
117
117
|
```bash
|
|
118
|
-
saber
|
|
118
|
+
saber "show me all users created this month"
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
### Database Selection
|
|
@@ -123,33 +123,39 @@ saber query "show me all users created this month"
|
|
|
123
123
|
Use a specific database connection:
|
|
124
124
|
|
|
125
125
|
```bash
|
|
126
|
-
#
|
|
127
|
-
saber
|
|
126
|
+
# Interactive mode with specific database
|
|
127
|
+
saber -d mydb
|
|
128
|
+
|
|
129
|
+
# Single query with specific database
|
|
130
|
+
saber -d mydb "count all orders"
|
|
128
131
|
```
|
|
129
132
|
|
|
130
133
|
## Examples
|
|
131
134
|
|
|
132
135
|
```bash
|
|
133
136
|
# Show database schema
|
|
134
|
-
saber
|
|
137
|
+
saber "what tables are in my database?"
|
|
135
138
|
|
|
136
139
|
# Count records
|
|
137
|
-
saber
|
|
140
|
+
saber "how many active users do we have?"
|
|
138
141
|
|
|
139
142
|
# Complex queries with joins
|
|
140
|
-
saber
|
|
143
|
+
saber "show me orders with customer details for this week"
|
|
141
144
|
|
|
142
145
|
# Aggregations
|
|
143
|
-
saber
|
|
146
|
+
saber "what's the total revenue by product category?"
|
|
144
147
|
|
|
145
148
|
# Date filtering
|
|
146
|
-
saber
|
|
149
|
+
saber "list users who haven't logged in for 30 days"
|
|
147
150
|
|
|
148
151
|
# Data exploration
|
|
149
|
-
saber
|
|
152
|
+
saber "show me the distribution of customer ages"
|
|
150
153
|
|
|
151
154
|
# Business analytics
|
|
152
|
-
saber
|
|
155
|
+
saber "which products had the highest sales growth last quarter?"
|
|
156
|
+
|
|
157
|
+
# Start interactive mode
|
|
158
|
+
saber
|
|
153
159
|
```
|
|
154
160
|
|
|
155
161
|
## MCP Server Integration
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sqlsaber"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.9.0"
|
|
4
4
|
description = "SQLSaber - Agentic SQL assistant like Claude Code"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
7
7
|
dependencies = [
|
|
8
8
|
"asyncpg>=0.30.0",
|
|
9
|
-
"typer>=0.16.0",
|
|
10
9
|
"rich>=13.7.0",
|
|
11
10
|
"anthropic>=0.54.0",
|
|
12
11
|
"keyring>=25.6.0",
|
|
@@ -18,6 +17,7 @@ dependencies = [
|
|
|
18
17
|
"pandas>=2.0.0",
|
|
19
18
|
"fastmcp>=2.9.0",
|
|
20
19
|
"uniplot>=0.21.2",
|
|
20
|
+
"cyclopts>=3.22.1",
|
|
21
21
|
]
|
|
22
22
|
|
|
23
23
|
[tool.uv]
|
|
@@ -33,6 +33,5 @@ packages = ["src/sqlsaber"]
|
|
|
33
33
|
[project.scripts]
|
|
34
34
|
sqlsaber = "sqlsaber.cli.commands:main"
|
|
35
35
|
saber = "sqlsaber.cli.commands:main"
|
|
36
|
-
sql = "sqlsaber.cli.commands:main"
|
|
37
36
|
sqlsaber-mcp = "sqlsaber.mcp.mcp:main"
|
|
38
37
|
saber-mcp = "sqlsaber.mcp.mcp:main"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Authentication CLI commands."""
|
|
2
2
|
|
|
3
3
|
import questionary
|
|
4
|
-
import
|
|
4
|
+
import cyclopts
|
|
5
5
|
from rich.console import Console
|
|
6
6
|
|
|
7
7
|
from sqlsaber.config.auth import AuthConfigManager, AuthMethod
|
|
@@ -12,15 +12,14 @@ console = Console()
|
|
|
12
12
|
config_manager = AuthConfigManager()
|
|
13
13
|
|
|
14
14
|
# Create the authentication management CLI app
|
|
15
|
-
auth_app =
|
|
15
|
+
auth_app = cyclopts.App(
|
|
16
16
|
name="auth",
|
|
17
17
|
help="Manage authentication configuration",
|
|
18
|
-
add_completion=True,
|
|
19
18
|
)
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
@auth_app.command
|
|
23
|
-
def
|
|
21
|
+
@auth_app.command
|
|
22
|
+
def setup():
|
|
24
23
|
"""Configure authentication method for SQLSaber."""
|
|
25
24
|
console.print("\n[bold]SQLSaber Authentication Setup[/bold]\n")
|
|
26
25
|
|
|
@@ -79,8 +78,8 @@ def setup_auth():
|
|
|
79
78
|
)
|
|
80
79
|
|
|
81
80
|
|
|
82
|
-
@auth_app.command
|
|
83
|
-
def
|
|
81
|
+
@auth_app.command
|
|
82
|
+
def status():
|
|
84
83
|
"""Show current authentication configuration."""
|
|
85
84
|
auth_method = config_manager.get_auth_method()
|
|
86
85
|
|
|
@@ -104,8 +103,8 @@ def show_auth_status():
|
|
|
104
103
|
console.print("[yellow]OAuth token missing or expired[/yellow]")
|
|
105
104
|
|
|
106
105
|
|
|
107
|
-
@auth_app.command
|
|
108
|
-
def
|
|
106
|
+
@auth_app.command
|
|
107
|
+
def reset():
|
|
109
108
|
"""Reset authentication configuration."""
|
|
110
109
|
if not config_manager.has_auth_configured():
|
|
111
110
|
console.print("[yellow]No authentication configuration to reset.[/yellow]")
|
|
@@ -137,6 +136,6 @@ def reset_auth():
|
|
|
137
136
|
console.print("Reset cancelled.")
|
|
138
137
|
|
|
139
138
|
|
|
140
|
-
def create_auth_app() ->
|
|
139
|
+
def create_auth_app() -> cyclopts.App:
|
|
141
140
|
"""Return the authentication management CLI app."""
|
|
142
141
|
return auth_app
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"""CLI command definitions and handlers."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import sys
|
|
4
5
|
from pathlib import Path
|
|
6
|
+
from typing import Annotated
|
|
5
7
|
|
|
6
|
-
import
|
|
8
|
+
import cyclopts
|
|
7
9
|
from rich.console import Console
|
|
8
10
|
|
|
9
11
|
from sqlsaber.agents.anthropic import AnthropicSQLAgent
|
|
@@ -16,10 +18,18 @@ from sqlsaber.cli.streaming import StreamingQueryHandler
|
|
|
16
18
|
from sqlsaber.config.database import DatabaseConfigManager
|
|
17
19
|
from sqlsaber.database.connection import DatabaseConnection
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
|
|
22
|
+
class CLIError(Exception):
|
|
23
|
+
"""Exception raised for CLI errors that should result in exit."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, message: str, exit_code: int = 1):
|
|
26
|
+
super().__init__(message)
|
|
27
|
+
self.exit_code = exit_code
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
app = cyclopts.App(
|
|
20
31
|
name="sqlsaber",
|
|
21
32
|
help="SQLSaber - Use the agent Luke!\n\nSQL assistant for your database",
|
|
22
|
-
add_completion=True,
|
|
23
33
|
)
|
|
24
34
|
|
|
25
35
|
|
|
@@ -27,40 +37,49 @@ console = Console()
|
|
|
27
37
|
config_manager = DatabaseConfigManager()
|
|
28
38
|
|
|
29
39
|
|
|
30
|
-
@app.
|
|
31
|
-
def
|
|
32
|
-
database:
|
|
33
|
-
None,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
@app.meta.default
|
|
41
|
+
def meta_handler(
|
|
42
|
+
database: Annotated[
|
|
43
|
+
str | None,
|
|
44
|
+
cyclopts.Parameter(
|
|
45
|
+
["--database", "-d"],
|
|
46
|
+
help="Database connection name (uses default if not specified)",
|
|
47
|
+
),
|
|
48
|
+
] = None,
|
|
38
49
|
):
|
|
39
50
|
"""
|
|
40
51
|
Query your database using natural language.
|
|
41
52
|
|
|
42
53
|
Examples:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
saber # Start interactive mode
|
|
55
|
+
saber "show me all users" # Run a single query with default database
|
|
56
|
+
saber -d mydb "show me users" # Run a query with specific database
|
|
46
57
|
"""
|
|
47
|
-
|
|
58
|
+
# Store database in app context for commands to access
|
|
59
|
+
app.meta["database"] = database
|
|
48
60
|
|
|
49
61
|
|
|
50
|
-
@app.
|
|
62
|
+
@app.default
|
|
51
63
|
def query(
|
|
52
|
-
query_text:
|
|
53
|
-
None,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
query_text: Annotated[
|
|
65
|
+
str | None,
|
|
66
|
+
cyclopts.Parameter(
|
|
67
|
+
help="SQL query in natural language (if not provided, starts interactive mode)",
|
|
68
|
+
),
|
|
69
|
+
] = None,
|
|
70
|
+
database: Annotated[
|
|
71
|
+
str | None,
|
|
72
|
+
cyclopts.Parameter(
|
|
73
|
+
["--database", "-d"],
|
|
74
|
+
help="Database connection name (uses default if not specified)",
|
|
75
|
+
),
|
|
76
|
+
] = None,
|
|
62
77
|
):
|
|
63
|
-
"""Run a query against the database or start interactive mode.
|
|
78
|
+
"""Run a query against the database or start interactive mode.
|
|
79
|
+
|
|
80
|
+
When called without arguments, starts interactive mode.
|
|
81
|
+
When called with a query string, executes that query and exits.
|
|
82
|
+
"""
|
|
64
83
|
|
|
65
84
|
async def run_session():
|
|
66
85
|
# Get database configuration or handle direct CSV file
|
|
@@ -69,35 +88,24 @@ def query(
|
|
|
69
88
|
if database.endswith(".csv"):
|
|
70
89
|
csv_path = Path(database).expanduser().resolve()
|
|
71
90
|
if not csv_path.exists():
|
|
72
|
-
|
|
73
|
-
f"[bold red]Error:[/bold red] CSV file '{database}' not found."
|
|
74
|
-
)
|
|
75
|
-
raise typer.Exit(1)
|
|
91
|
+
raise CLIError(f"CSV file '{database}' not found.")
|
|
76
92
|
connection_string = f"csv:///{csv_path}"
|
|
77
93
|
db_name = csv_path.stem
|
|
78
94
|
else:
|
|
79
95
|
# Look up configured database connection
|
|
80
96
|
db_config = config_manager.get_database(database)
|
|
81
97
|
if not db_config:
|
|
82
|
-
|
|
83
|
-
f"
|
|
84
|
-
)
|
|
85
|
-
console.print(
|
|
86
|
-
"Use 'sqlsaber db list' to see available connections."
|
|
98
|
+
raise CLIError(
|
|
99
|
+
f"Database connection '{database}' not found. Use 'sqlsaber db list' to see available connections."
|
|
87
100
|
)
|
|
88
|
-
raise typer.Exit(1)
|
|
89
101
|
connection_string = db_config.to_connection_string()
|
|
90
102
|
db_name = db_config.name
|
|
91
103
|
else:
|
|
92
104
|
db_config = config_manager.get_default_database()
|
|
93
105
|
if not db_config:
|
|
94
|
-
|
|
95
|
-
"
|
|
96
|
-
)
|
|
97
|
-
console.print(
|
|
98
|
-
"Use 'sqlsaber db add <name>' to add a database connection."
|
|
106
|
+
raise CLIError(
|
|
107
|
+
"No database connections configured. Use 'sqlsaber db add <name>' to add a database connection."
|
|
99
108
|
)
|
|
100
|
-
raise typer.Exit(1)
|
|
101
109
|
connection_string = db_config.to_connection_string()
|
|
102
110
|
db_name = db_config.name
|
|
103
111
|
|
|
@@ -105,10 +113,7 @@ def query(
|
|
|
105
113
|
try:
|
|
106
114
|
db_conn = DatabaseConnection(connection_string)
|
|
107
115
|
except Exception as e:
|
|
108
|
-
|
|
109
|
-
f"[bold red]Error creating database connection:[/bold red] {e}"
|
|
110
|
-
)
|
|
111
|
-
raise typer.Exit(1)
|
|
116
|
+
raise CLIError(f"Error creating database connection: {e}")
|
|
112
117
|
|
|
113
118
|
# Create agent instance with database name for memory context
|
|
114
119
|
agent = AnthropicSQLAgent(db_conn, db_name)
|
|
@@ -132,25 +137,29 @@ def query(
|
|
|
132
137
|
await db_conn.close()
|
|
133
138
|
console.print("\n[green]Goodbye![/green]")
|
|
134
139
|
|
|
135
|
-
# Run the async function
|
|
136
|
-
|
|
140
|
+
# Run the async function with proper error handling
|
|
141
|
+
try:
|
|
142
|
+
asyncio.run(run_session())
|
|
143
|
+
except CLIError as e:
|
|
144
|
+
console.print(f"[bold red]Error:[/bold red] {e}")
|
|
145
|
+
sys.exit(e.exit_code)
|
|
137
146
|
|
|
138
147
|
|
|
139
148
|
# Add authentication management commands
|
|
140
149
|
auth_app = create_auth_app()
|
|
141
|
-
app.
|
|
150
|
+
app.command(auth_app, name="auth")
|
|
142
151
|
|
|
143
152
|
# Add database management commands after main callback is defined
|
|
144
153
|
db_app = create_db_app()
|
|
145
|
-
app.
|
|
154
|
+
app.command(db_app, name="db")
|
|
146
155
|
|
|
147
156
|
# Add memory management commands
|
|
148
157
|
memory_app = create_memory_app()
|
|
149
|
-
app.
|
|
158
|
+
app.command(memory_app, name="memory")
|
|
150
159
|
|
|
151
160
|
# Add model management commands
|
|
152
161
|
models_app = create_models_app()
|
|
153
|
-
app.
|
|
162
|
+
app.command(models_app, name="models")
|
|
154
163
|
|
|
155
164
|
|
|
156
165
|
def main():
|