sqlsaber 0.32.1__tar.gz → 0.34.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.32.1 → sqlsaber-0.34.0}/PKG-INFO +1 -1
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/src/content/docs/changelog.md +20 -1
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/src/content/docs/guides/database-setup.mdx +29 -4
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/src/content/docs/guides/getting-started.mdx +23 -45
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/src/content/docs/guides/models.mdx +7 -6
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/src/content/docs/index.mdx +0 -6
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/src/content/docs/reference/commands.md +9 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/pyproject.toml +1 -1
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/agents/pydantic_ai_agent.py +15 -26
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/application/auth_setup.py +83 -11
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/cli/auth.py +3 -1
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/config/api_keys.py +23 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/config/auth.py +6 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/config/logging.py +0 -1
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/database/postgresql.py +63 -11
- sqlsaber-0.34.0/src/sqlsaber/prompts/claude.py +52 -0
- sqlsaber-0.34.0/src/sqlsaber/prompts/memory.py +4 -0
- sqlsaber-0.34.0/src/sqlsaber/prompts/openai.py +40 -0
- sqlsaber-0.34.0/tests/test_application/test_auth_setup.py +122 -0
- sqlsaber-0.34.0/tests/test_database/__init__.py +0 -0
- sqlsaber-0.34.0/tests/test_database/test_postgresql_module.py +95 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/uv.lock +1 -1
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/.github/workflows/claude-code-review.yml +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/.github/workflows/claude.yml +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/.github/workflows/deploy-docs.yml +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/.github/workflows/publish.yml +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/.github/workflows/test.yml +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/.gitignore +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/.python-version +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/AGENTS.md +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/CLAUDE.md +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/LICENSE +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/README.md +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/.gitignore +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/.vscode/extensions.json +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/.vscode/launch.json +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/CLAUDE.md +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/astro.config.mjs +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/package-lock.json +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/package.json +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/public/CNAME +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/public/favicon.svg +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/src/assets/sqlsaber.gif +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/src/content/docs/guides/authentication.mdx +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/src/content/docs/guides/memory.mdx +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/src/content/docs/guides/queries.mdx +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/src/content/docs/guides/threads.md +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/src/content/docs/installation.mdx +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/src/content.config.ts +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/src/styles/global.css +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/docs/tsconfig.json +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/legislators.db +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/pytest.ini +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/sqlsaber.gif +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/sqlsaber.svg +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/__init__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/__main__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/agents/__init__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/agents/base.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/application/__init__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/application/db_setup.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/application/model_selection.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/application/prompts.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/cli/__init__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/cli/commands.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/cli/completers.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/cli/database.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/cli/display.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/cli/interactive.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/cli/memory.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/cli/models.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/cli/onboarding.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/cli/streaming.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/cli/theme.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/cli/threads.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/config/__init__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/config/database.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/config/oauth_flow.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/config/oauth_tokens.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/config/providers.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/config/settings.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/database/__init__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/database/base.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/database/csv.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/database/duckdb.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/database/mysql.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/database/resolver.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/database/schema.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/database/sqlite.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/memory/__init__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/memory/manager.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/memory/storage.py +0 -0
- {sqlsaber-0.32.1/tests → sqlsaber-0.34.0/src/sqlsaber/prompts}/__init__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/theme/__init__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/theme/manager.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/threads/__init__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/threads/storage.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/tools/__init__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/tools/base.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/tools/registry.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/tools/sql_guard.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/src/sqlsaber/tools/sql_tools.py +0 -0
- {sqlsaber-0.32.1/tests/test_cli → sqlsaber-0.34.0/tests}/__init__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/conftest.py +0 -0
- {sqlsaber-0.32.1/tests/test_config → sqlsaber-0.34.0/tests/test_cli}/__init__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_cli/test_auth_reset.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_cli/test_commands.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_cli/test_threads.py +0 -0
- {sqlsaber-0.32.1/tests/test_database → sqlsaber-0.34.0/tests/test_config}/__init__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_config/test_database.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_config/test_oauth.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_config/test_providers.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_config/test_settings.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_database/test_connection.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_database/test_csv_connection.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_database/test_csv_module.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_database/test_duckdb_module.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_database/test_schema.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_database/test_schema_display.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_database/test_sqlite_module.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_database/test_timeout.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_database_resolver.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_theme/test_manager.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_threads_storage.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_tools/__init__.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_tools/test_base.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_tools/test_registry.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_tools/test_sql_guard.py +0 -0
- {sqlsaber-0.32.1 → sqlsaber-0.34.0}/tests/test_tools/test_sql_tools.py +0 -0
|
@@ -9,6 +9,24 @@ All notable changes to SQLsaber will be documented here.
|
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
+
### v0.34.0 - 2025-10-17
|
|
13
|
+
|
|
14
|
+
#### Changed
|
|
15
|
+
|
|
16
|
+
- Improved `auth setup` command: Reworked to select the provider first, offer to reset existing credentials per provider, and allow configuring multiple providers in one session without manual cleanup.
|
|
17
|
+
- PostgreSQL schema introspection: Exclude TimescaleDB internal schemas by default and add env-based filtering
|
|
18
|
+
- Default exclusions now include `pg_catalog`, `information_schema`, `_timescaledb_internal`, `_timescaledb_cache`, `_timescaledb_config`, `_timescaledb_catalog`
|
|
19
|
+
- New environment variable `SQLSABER_PG_EXCLUDE_SCHEMAS` allows excluding additional schemas (comma-separated)
|
|
20
|
+
- Applies to both `list_tables` and `introspect_schema` tools
|
|
21
|
+
|
|
22
|
+
### v0.33.0 - 2025-10-16
|
|
23
|
+
|
|
24
|
+
#### Changed
|
|
25
|
+
|
|
26
|
+
- Improved system prompt for GPT-5 using OpenAI's prompt optimizer tool
|
|
27
|
+
- Improved system prompt for Sonnet 4.5 and others using Anthropic's prompt improvement tool
|
|
28
|
+
- Dedicated prompt for GPT-5
|
|
29
|
+
|
|
12
30
|
### v0.32.1 - 2025-10-15
|
|
13
31
|
|
|
14
32
|
#### Fixed
|
|
@@ -53,7 +71,7 @@ All notable changes to SQLsaber will be documented here.
|
|
|
53
71
|
|
|
54
72
|
> `sqlsaber` can still be used as a cli in coding agents like Claude Code, Codex, or Amp.
|
|
55
73
|
>
|
|
56
|
-
> Just
|
|
74
|
+
> Just _ask_ the coding agent to invoke `sqlsaber` cli with your question.
|
|
57
75
|
|
|
58
76
|
### v0.29.1 - 2025-10-05
|
|
59
77
|
|
|
@@ -348,6 +366,7 @@ All notable changes to SQLsaber will be documented here.
|
|
|
348
366
|
#### Added
|
|
349
367
|
|
|
350
368
|
- Table name autocomplete with "@" prefix in interactive mode
|
|
369
|
+
|
|
351
370
|
- Type "@" followed by table name to get fuzzy matching completions
|
|
352
371
|
- Supports schema-aware completions (e.g., "@sample" matches "public.sample")
|
|
353
372
|
|
|
@@ -3,7 +3,7 @@ title: Database Setup
|
|
|
3
3
|
description: Configure database connections in SQLsaber
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
import { Aside } from
|
|
6
|
+
import { Aside } from "@astrojs/starlight/components";
|
|
7
7
|
|
|
8
8
|
SQLsaber supports PostgreSQL, MySQL, SQLite, DuckDB, and CSV data sources. This guide covers all the ways to configure database connections.
|
|
9
9
|
|
|
@@ -66,7 +66,8 @@ saber -d "./customers.csv" "How many customers are from each state?"
|
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
<Aside type="note">
|
|
69
|
-
For CSV files, first row should contain column headers. SQLsaber will infer
|
|
69
|
+
For CSV files, first row should contain column headers. SQLsaber will infer
|
|
70
|
+
data types automatically.
|
|
70
71
|
</Aside>
|
|
71
72
|
|
|
72
73
|
### Managing Connections
|
|
@@ -86,7 +87,7 @@ saber db set-default my-database
|
|
|
86
87
|
```
|
|
87
88
|
|
|
88
89
|
<Aside type="note">
|
|
89
|
-
By default, SQLsaber will set the first added connection as default.
|
|
90
|
+
By default, SQLsaber will set the first added connection as default.
|
|
90
91
|
</Aside>
|
|
91
92
|
|
|
92
93
|
#### Test Connections
|
|
@@ -115,10 +116,13 @@ While SQLsaber ensures, via checks before executing queries, that only read quer
|
|
|
115
116
|
If you have a read-replica of your production database, consider using those credentials to setup connection.
|
|
116
117
|
|
|
117
118
|
In addition to the read-only role, this will provide additional layer of security and ensure no data modifying queries are executed.
|
|
119
|
+
|
|
118
120
|
</Aside>
|
|
119
121
|
|
|
120
122
|
<Aside type="note">
|
|
121
|
-
For development setups, this isn't required but is a good practice if you wish
|
|
123
|
+
For development setups, this isn't required but is a good practice if you wish
|
|
124
|
+
to ensure with absolute certainty that no data modifying queries are ever
|
|
125
|
+
executed.
|
|
122
126
|
</Aside>
|
|
123
127
|
|
|
124
128
|
#### Password Storage
|
|
@@ -129,6 +133,27 @@ SQLsaber stores database passwords securely using your operating system's creden
|
|
|
129
133
|
|
|
130
134
|
Store SSL certificates in a secure location and use absolute paths when configuring connections.
|
|
131
135
|
|
|
136
|
+
### Schema Introspection Filters (PostgreSQL)
|
|
137
|
+
|
|
138
|
+
By default, SQLsaber excludes system and TimescaleDB internal schemas during schema discovery to keep results focused and fast.
|
|
139
|
+
|
|
140
|
+
Excluded by default:
|
|
141
|
+
|
|
142
|
+
- `pg_catalog`
|
|
143
|
+
- `information_schema`
|
|
144
|
+
- `_timescaledb_internal`
|
|
145
|
+
- `_timescaledb_cache`
|
|
146
|
+
- `_timescaledb_config`
|
|
147
|
+
- `_timescaledb_catalog`
|
|
148
|
+
|
|
149
|
+
You can exclude additional schemas via environment variable before running `saber`:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
export SQLSABER_PG_EXCLUDE_SCHEMAS="schema1,schema2"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Both the `list_tables` and `introspect_schema` tools respect these filters.
|
|
156
|
+
|
|
132
157
|
### Troubleshooting
|
|
133
158
|
|
|
134
159
|
Always test new connections after adding them.
|
|
@@ -15,72 +15,50 @@ Before you begin, make sure you have:
|
|
|
15
15
|
|
|
16
16
|
### Quick Setup
|
|
17
17
|
|
|
18
|
-
#### 1.
|
|
18
|
+
#### 1. Launch the Onboarding Flow
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
SQLsaber uses Claude Sonnet 4 as the model by default, but supports models from [multiple providers](/guides/models).
|
|
20
|
+
Run SQLsaber with no arguments to start the guided setup:
|
|
23
21
|
|
|
24
22
|
```bash
|
|
25
|
-
saber
|
|
23
|
+
saber
|
|
26
24
|
```
|
|
27
25
|
|
|
28
|
-
|
|
26
|
+
On your first run, SQLsaber walks you through two guided steps:
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
1. **Database connection**:
|
|
29
|
+
Choose the database type and provide connection details.
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
is one of the most cost-effective options.
|
|
36
|
-
</Aside>
|
|
31
|
+
2. **Authentication**:
|
|
32
|
+
Sign in to your preferred AI provider and pick a model. We recommend Sonnet 4.5, 4 or GPT-5.
|
|
37
33
|
|
|
38
|
-
|
|
39
|
-
If you choose a provider other than Anthropic, please select the appropriate
|
|
40
|
-
model for the provider by running `saber models set`.
|
|
41
|
-
</Aside>
|
|
34
|
+
`sqlsaber` will store your API key securely using your OS credential store.
|
|
42
35
|
|
|
43
|
-
|
|
36
|
+
When both steps succeed you'll see a confirmation screen, and SQLsaber immediately opens an interactive session.
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
This interactive command will ask you for:
|
|
38
|
+
<Aside type="tip">
|
|
39
|
+
If you have a Claude Pro or Max subscription, you can use it with SQLsaber for
|
|
40
|
+
a cost-effective setup.
|
|
41
|
+
</Aside>
|
|
52
42
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
- SQLite
|
|
57
|
-
- DuckDB
|
|
58
|
-
- Connection details (host, port, database name, username)
|
|
59
|
-
- SSL configuration (if needed)
|
|
43
|
+
<Aside type="tip">
|
|
44
|
+
If you already have a connection string handy, pass it via `saber -d <connection-string>` to skip the onboarding flow.
|
|
45
|
+
</Aside>
|
|
60
46
|
|
|
61
47
|
<Aside type="note">
|
|
62
|
-
|
|
48
|
+
Need a sample database?
|
|
49
|
+
|
|
50
|
+
Download the `legislators` SQLite file, then choose `sqlite` when the onboarding flow prompts for a database location (or add it with `saber db add`):
|
|
63
51
|
|
|
64
52
|
```bash
|
|
65
53
|
curl -L -o legislators.db https://github.com/SarthakJariwala/sqlsaber/raw/refs/heads/main/legislators.db
|
|
66
|
-
|
|
67
|
-
# Add it to SQLsaber
|
|
68
|
-
saber db add legislators --type sqlite --database ./legislators.db
|
|
69
54
|
```
|
|
70
55
|
|
|
71
56
|
</Aside>
|
|
72
57
|
|
|
73
|
-
####
|
|
74
|
-
|
|
75
|
-
Now you're ready to ask your first question!
|
|
76
|
-
|
|
77
|
-
Start SQLsaber in interactive mode:
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
saber
|
|
81
|
-
```
|
|
58
|
+
#### 2. Ask Your First Question
|
|
82
59
|
|
|
83
|
-
Try asking a
|
|
60
|
+
Once onboarding completes, you're already in interactive mode. Try asking a
|
|
61
|
+
question:
|
|
84
62
|
|
|
85
63
|
```
|
|
86
64
|
> How many VPs have become presidents via election in 20th century?
|
|
@@ -3,7 +3,7 @@ title: Models
|
|
|
3
3
|
description: Configure and select AI models for SQLsaber
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
import { Aside } from
|
|
6
|
+
import { Aside } from "@astrojs/starlight/components";
|
|
7
7
|
|
|
8
8
|
SQLsaber supports many LLM models across different providers.
|
|
9
9
|
|
|
@@ -11,19 +11,19 @@ This guide covers model selection, configuration, and recommendations for the be
|
|
|
11
11
|
|
|
12
12
|
### Default Model
|
|
13
13
|
|
|
14
|
-
SQLsaber uses **Claude Sonnet 4** by default, which provides excellent SQL generation capabilities with good speed and accuracy.
|
|
14
|
+
SQLsaber uses **Claude Sonnet 4.5** by default, which provides excellent SQL generation capabilities with good speed and accuracy.
|
|
15
15
|
|
|
16
16
|
### Recommended Models
|
|
17
17
|
|
|
18
18
|
While SQLsaber supports a lot of models, we recommend the following for best results:
|
|
19
19
|
|
|
20
|
-
- Claude Sonnet 4, 3.7, 3.5
|
|
21
|
-
- Claude Opus 4, 4.1
|
|
22
20
|
- GPT-5
|
|
21
|
+
- Claude Sonnet 4.5, 4.0, 3.7
|
|
22
|
+
- Claude Opus 4, 4.1
|
|
23
23
|
- Gemini 2.5 Pro
|
|
24
24
|
|
|
25
25
|
<Aside type="tip">
|
|
26
|
-
Consider using one of the recommended models for best results.
|
|
26
|
+
Consider using one of the recommended models for best results.
|
|
27
27
|
</Aside>
|
|
28
28
|
|
|
29
29
|
### Model Management
|
|
@@ -43,12 +43,13 @@ saber models set
|
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
This interactive command lets you:
|
|
46
|
+
|
|
46
47
|
1. Choose from configured providers
|
|
47
48
|
2. Select a specific model
|
|
48
49
|
3. Set it as your default
|
|
49
50
|
|
|
50
51
|
<Aside type="tip">
|
|
51
|
-
|
|
52
|
+
This list is long - type to search and filter the list.
|
|
52
53
|
</Aside>
|
|
53
54
|
|
|
54
55
|
#### Reset to Default
|
|
@@ -79,12 +79,6 @@ import {
|
|
|
79
79
|
|
|
80
80
|
</Card>
|
|
81
81
|
|
|
82
|
-
<script
|
|
83
|
-
src="https://asciinema.org/a/YITbKtH4mPn8LLTr6QEUh7e8f.js"
|
|
84
|
-
id="asciicast-YITbKtH4mPn8LLTr6QEUh7e8f"
|
|
85
|
-
async="true"
|
|
86
|
-
></script>
|
|
87
|
-
|
|
88
82
|
{/* Section: Get Started Now */}
|
|
89
83
|
|
|
90
84
|
<section class="mx-auto max-w-screen-lg px-4 sm:px-6 lg:px-8 py-16 text-center">
|
|
@@ -436,3 +436,12 @@ When in interactive mode (`saber` with no arguments), you have access to a few a
|
|
|
436
436
|
|
|
437
437
|
- **Table names** - Type `@table_name[TAB]` for completions
|
|
438
438
|
- **Slash commands** - Type `/[TAB]` for command completions
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
### Environment Variables
|
|
443
|
+
|
|
444
|
+
These environment variables adjust runtime behavior:
|
|
445
|
+
|
|
446
|
+
- `SQLSABER_THEME` — Override the configured theme for the session.
|
|
447
|
+
- `SQLSABER_PG_EXCLUDE_SCHEMAS` — Comma-separated list of PostgreSQL schemas to exclude from schema discovery and introspection. Defaults already exclude `pg_catalog`, `information_schema`, `_timescaledb_internal`, `_timescaledb_cache`, `_timescaledb_config`, `_timescaledb_catalog`.
|
|
@@ -24,6 +24,9 @@ from sqlsaber.database import (
|
|
|
24
24
|
SQLiteConnection,
|
|
25
25
|
)
|
|
26
26
|
from sqlsaber.memory.manager import MemoryManager
|
|
27
|
+
from sqlsaber.prompts.claude import SONNET_4_5
|
|
28
|
+
from sqlsaber.prompts.memory import MEMORY_ADDITION
|
|
29
|
+
from sqlsaber.prompts.openai import GPT_5
|
|
27
30
|
from sqlsaber.tools.registry import tool_registry
|
|
28
31
|
from sqlsaber.tools.sql_tools import SQLTool
|
|
29
32
|
|
|
@@ -168,6 +171,16 @@ class SQLSaberAgent:
|
|
|
168
171
|
|
|
169
172
|
@agent.system_prompt(dynamic=True)
|
|
170
173
|
async def sqlsaber_system_prompt(ctx: RunContext) -> str:
|
|
174
|
+
if "gpt-5" in agent.model.model_name:
|
|
175
|
+
base = GPT_5.format(db=self.db_type)
|
|
176
|
+
|
|
177
|
+
if self.database_name:
|
|
178
|
+
mem = self.memory_manager.format_memories_for_prompt(
|
|
179
|
+
self.database_name
|
|
180
|
+
)
|
|
181
|
+
mem = mem.strip()
|
|
182
|
+
if mem:
|
|
183
|
+
return f"{base}\n\n{MEMORY_ADDITION}\n\n{mem}"
|
|
171
184
|
return self.system_prompt_text(include_memory=True)
|
|
172
185
|
else:
|
|
173
186
|
|
|
@@ -178,37 +191,13 @@ class SQLSaberAgent:
|
|
|
178
191
|
|
|
179
192
|
def system_prompt_text(self, include_memory: bool = True) -> str:
|
|
180
193
|
"""Return the original SQLSaber system prompt as a single string."""
|
|
181
|
-
|
|
182
|
-
base = (
|
|
183
|
-
f"You are a helpful SQL assistant that helps users query their {db} database.\n\n"
|
|
184
|
-
"Your responsibilities:\n"
|
|
185
|
-
"1. Understand user's natural language requests, think and convert them to SQL\n"
|
|
186
|
-
"2. Use the provided tools efficiently to explore database schema\n"
|
|
187
|
-
"3. Generate appropriate SQL queries\n"
|
|
188
|
-
"4. Execute queries safely - queries that modify the database are not allowed\n"
|
|
189
|
-
"5. Format and explain results clearly\n\n"
|
|
190
|
-
"IMPORTANT - Tool Usage Strategy:\n"
|
|
191
|
-
"1. ALWAYS start with 'list_tables' to see available tables and row counts. Use this first to discover available tables.\n"
|
|
192
|
-
"2. Use 'introspect_schema' with a table_pattern to get details ONLY for relevant tables. Use table patterns like 'sample%' or '%experiment%' to filter related tables.\n"
|
|
193
|
-
"3. Execute SQL queries safely with automatic LIMIT clauses for SELECT statements. Only SELECT queries are permitted for security.\n\n"
|
|
194
|
-
"Tool-Specific Guidelines:\n"
|
|
195
|
-
"- introspect_schema: Use 'introspect_schema' with a table_pattern to get details ONLY for relevant tables. Use table patterns like 'sample%' or '%experiment%' to filter related tables.\n"
|
|
196
|
-
"- execute_sql: Execute SQL queries safely with automatic LIMIT clauses for SELECT statements. Only SELECT queries are permitted for security.\n\n"
|
|
197
|
-
"Guidelines:\n"
|
|
198
|
-
"- Use proper JOIN syntax and avoid cartesian products\n"
|
|
199
|
-
"- Include appropriate WHERE clauses to limit results\n"
|
|
200
|
-
"- Explain what the query does in simple terms\n"
|
|
201
|
-
"- Handle errors gracefully and suggest fixes\n"
|
|
202
|
-
"- Be security conscious - use parameterized queries when needed\n"
|
|
203
|
-
"- Timestamp columns must be converted to text when you write queries\n"
|
|
204
|
-
"- Use table patterns like 'sample%' or '%experiment%' to filter related tables"
|
|
205
|
-
)
|
|
194
|
+
base = SONNET_4_5.format(db=self.db_type)
|
|
206
195
|
|
|
207
196
|
if include_memory and self.database_name:
|
|
208
197
|
mem = self.memory_manager.format_memories_for_prompt(self.database_name)
|
|
209
198
|
mem = mem.strip()
|
|
210
199
|
if mem:
|
|
211
|
-
return f"{base}\n\n{mem}"
|
|
200
|
+
return f"{base}\n\n{MEMORY_ADDITION}\n\n{mem}\n\n"
|
|
212
201
|
return base
|
|
213
202
|
|
|
214
203
|
def _register_tools(self, agent: Agent) -> None:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Shared auth setup logic for onboarding and CLI."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import os
|
|
4
5
|
|
|
5
6
|
from questionary import Choice
|
|
6
7
|
|
|
@@ -9,6 +10,7 @@ from sqlsaber.config import providers
|
|
|
9
10
|
from sqlsaber.config.api_keys import APIKeyManager
|
|
10
11
|
from sqlsaber.config.auth import AuthConfigManager, AuthMethod
|
|
11
12
|
from sqlsaber.config.oauth_flow import AnthropicOAuthFlow
|
|
13
|
+
from sqlsaber.config.oauth_tokens import OAuthTokenManager
|
|
12
14
|
from sqlsaber.theme.manager import create_console
|
|
13
15
|
|
|
14
16
|
console = create_console()
|
|
@@ -102,24 +104,49 @@ async def setup_auth(
|
|
|
102
104
|
Returns:
|
|
103
105
|
Tuple of (success: bool, provider: str | None)
|
|
104
106
|
"""
|
|
105
|
-
|
|
106
|
-
if auth_manager.has_auth_configured():
|
|
107
|
-
console.print("[success]✓ Authentication already configured![/success]")
|
|
108
|
-
return True, None
|
|
107
|
+
oauth_manager = OAuthTokenManager()
|
|
109
108
|
|
|
110
|
-
# Select provider
|
|
111
109
|
provider = await select_provider(prompter, default=default_provider)
|
|
112
110
|
|
|
113
111
|
if provider is None:
|
|
114
112
|
return False, None
|
|
115
113
|
|
|
114
|
+
env_var = api_key_manager.get_env_var_name(provider)
|
|
115
|
+
api_key_in_env = bool(os.getenv(env_var))
|
|
116
|
+
api_key_in_keyring = api_key_manager.has_stored_api_key(provider)
|
|
117
|
+
has_oauth = (
|
|
118
|
+
oauth_manager.has_oauth_token("anthropic")
|
|
119
|
+
if provider == "anthropic" and allow_oauth
|
|
120
|
+
else False
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
if api_key_in_env or api_key_in_keyring or has_oauth:
|
|
124
|
+
parts: list[str] = []
|
|
125
|
+
if api_key_in_keyring:
|
|
126
|
+
parts.append("stored API key")
|
|
127
|
+
if api_key_in_env:
|
|
128
|
+
parts.append(f"{env_var} environment variable")
|
|
129
|
+
if has_oauth:
|
|
130
|
+
parts.append("OAuth token")
|
|
131
|
+
summary = ", ".join(parts)
|
|
132
|
+
console.print(
|
|
133
|
+
f"[info]Existing authentication found for {provider}: {summary}[/info]"
|
|
134
|
+
)
|
|
135
|
+
|
|
116
136
|
# For Anthropic, offer OAuth or API key
|
|
117
137
|
if provider == "anthropic" and allow_oauth:
|
|
138
|
+
api_key_label = "API Key"
|
|
139
|
+
if api_key_in_keyring or api_key_in_env:
|
|
140
|
+
api_key_label += " [configured]"
|
|
141
|
+
oauth_label = "Claude Pro/Max (OAuth)"
|
|
142
|
+
if has_oauth:
|
|
143
|
+
oauth_label += " [configured]"
|
|
144
|
+
|
|
118
145
|
method_choice = await prompter.select(
|
|
119
146
|
"Authentication method:",
|
|
120
147
|
choices=[
|
|
121
|
-
Choice(
|
|
122
|
-
Choice(
|
|
148
|
+
Choice(api_key_label, value=AuthMethod.API_KEY),
|
|
149
|
+
Choice(oauth_label, value=AuthMethod.CLAUDE_PRO),
|
|
123
150
|
],
|
|
124
151
|
)
|
|
125
152
|
|
|
@@ -127,6 +154,28 @@ async def setup_auth(
|
|
|
127
154
|
return False, None
|
|
128
155
|
|
|
129
156
|
if method_choice == AuthMethod.CLAUDE_PRO:
|
|
157
|
+
if has_oauth:
|
|
158
|
+
reset = await prompter.confirm(
|
|
159
|
+
"Anthropic OAuth is already configured. Reset before continuing?",
|
|
160
|
+
default=False,
|
|
161
|
+
)
|
|
162
|
+
if not reset:
|
|
163
|
+
console.print(
|
|
164
|
+
"[warning]No changes made to Anthropic OAuth credentials.[/warning]"
|
|
165
|
+
)
|
|
166
|
+
return True, None
|
|
167
|
+
|
|
168
|
+
removal_success = oauth_manager.remove_oauth_token("anthropic")
|
|
169
|
+
if not removal_success:
|
|
170
|
+
console.print(
|
|
171
|
+
"[error]Failed to remove existing Anthropic OAuth credentials.[/error]"
|
|
172
|
+
)
|
|
173
|
+
return False, None
|
|
174
|
+
|
|
175
|
+
current_method = auth_manager.get_auth_method()
|
|
176
|
+
if current_method == AuthMethod.CLAUDE_PRO:
|
|
177
|
+
auth_manager.clear_auth_method()
|
|
178
|
+
|
|
130
179
|
console.print()
|
|
131
180
|
oauth_success = await configure_oauth_anthropic(
|
|
132
181
|
auth_manager, run_in_thread=run_oauth_in_thread
|
|
@@ -136,12 +185,35 @@ async def setup_auth(
|
|
|
136
185
|
"[green]✓ Anthropic OAuth configured successfully![/green]"
|
|
137
186
|
)
|
|
138
187
|
return True, provider
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
188
|
+
|
|
189
|
+
console.print("[error]✗ Anthropic OAuth setup failed.[/error]")
|
|
190
|
+
return False, None
|
|
142
191
|
|
|
143
192
|
# API key flow
|
|
144
|
-
|
|
193
|
+
if api_key_in_keyring:
|
|
194
|
+
reset_api_key = await prompter.confirm(
|
|
195
|
+
f"{provider.title()} API key is stored in your keyring. Reset before continuing?",
|
|
196
|
+
default=False,
|
|
197
|
+
)
|
|
198
|
+
if not reset_api_key:
|
|
199
|
+
console.print(
|
|
200
|
+
"[warning]No changes made to stored API key credentials.[/warning]"
|
|
201
|
+
)
|
|
202
|
+
return True, None
|
|
203
|
+
if not api_key_manager.delete_api_key(provider):
|
|
204
|
+
console.print(
|
|
205
|
+
"[error]Failed to remove existing API key credentials.[/error]"
|
|
206
|
+
)
|
|
207
|
+
return False, None
|
|
208
|
+
console.print(
|
|
209
|
+
f"[muted]{provider.title()} API key removed from keyring.[/muted]"
|
|
210
|
+
)
|
|
211
|
+
api_key_in_keyring = False
|
|
212
|
+
|
|
213
|
+
if api_key_in_env:
|
|
214
|
+
console.print(
|
|
215
|
+
f"[muted]{env_var} is set in your environment. Update it there if you need a new value.[/muted]"
|
|
216
|
+
)
|
|
145
217
|
|
|
146
218
|
console.print()
|
|
147
219
|
console.print(f"[dim]To use {provider.title()}, you need an API key.[/dim]")
|
|
@@ -167,7 +167,9 @@ def reset():
|
|
|
167
167
|
pass
|
|
168
168
|
except Exception as e:
|
|
169
169
|
console.print(f"Warning: Could not remove API key: {e}", style="warning")
|
|
170
|
-
logger.warning(
|
|
170
|
+
logger.warning(
|
|
171
|
+
"auth.reset.api_key_remove_failed", provider=provider, error=str(e)
|
|
172
|
+
)
|
|
171
173
|
|
|
172
174
|
# Optionally clear global auth method if removing Anthropic OAuth configuration
|
|
173
175
|
if provider == "anthropic" and oauth_present:
|
|
@@ -41,6 +41,29 @@ class APIKeyManager:
|
|
|
41
41
|
# 3. Prompt user for API key
|
|
42
42
|
return self._prompt_and_store_key(provider, env_var_name, service_name)
|
|
43
43
|
|
|
44
|
+
def has_stored_api_key(self, provider: str) -> bool:
|
|
45
|
+
"""Check if an API key is stored for the provider."""
|
|
46
|
+
service_name = self._get_service_name(provider)
|
|
47
|
+
try:
|
|
48
|
+
return keyring.get_password(service_name, provider) is not None
|
|
49
|
+
except Exception:
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
def delete_api_key(self, provider: str) -> bool:
|
|
53
|
+
"""Remove stored API key for the provider."""
|
|
54
|
+
service_name = self._get_service_name(provider)
|
|
55
|
+
try:
|
|
56
|
+
keyring.delete_password(service_name, provider)
|
|
57
|
+
return True
|
|
58
|
+
except keyring.errors.PasswordDeleteError:
|
|
59
|
+
return True
|
|
60
|
+
except Exception as e:
|
|
61
|
+
console.print(
|
|
62
|
+
f"Warning: Could not remove API key: {e}",
|
|
63
|
+
style="warning",
|
|
64
|
+
)
|
|
65
|
+
return False
|
|
66
|
+
|
|
44
67
|
def get_env_var_name(self, provider: str) -> str:
|
|
45
68
|
"""Get the expected environment variable name for a provider."""
|
|
46
69
|
# Normalize aliases to canonical provider keys
|
|
@@ -81,6 +81,12 @@ class AuthConfigManager:
|
|
|
81
81
|
config["auth_method"] = auth_method.value
|
|
82
82
|
self._save_config(config)
|
|
83
83
|
|
|
84
|
+
def clear_auth_method(self) -> None:
|
|
85
|
+
"""Clear any configured authentication method."""
|
|
86
|
+
config = self._load_config()
|
|
87
|
+
config["auth_method"] = None
|
|
88
|
+
self._save_config(config)
|
|
89
|
+
|
|
84
90
|
def has_auth_configured(self) -> bool:
|
|
85
91
|
"""Check if authentication method is configured."""
|
|
86
92
|
return self.get_auth_method() is not None
|