sqlsaber 0.32.1__py3-none-any.whl → 0.34.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.

@@ -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
- db = self.db_type
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
- # Check if auth is already configured
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("API Key", value=AuthMethod.API_KEY),
122
- Choice("Claude Pro/Max (OAuth)", value=AuthMethod.CLAUDE_PRO),
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
- else:
140
- console.print("[error]✗ Anthropic OAuth setup failed.[/error]")
141
- return False, None
188
+
189
+ console.print("[error]✗ Anthropic OAuth setup failed.[/error]")
190
+ return False, None
142
191
 
143
192
  # API key flow
144
- env_var = api_key_manager.get_env_var_name(provider)
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]")
sqlsaber/cli/auth.py CHANGED
@@ -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("auth.reset.api_key_remove_failed", provider=provider, error=str(e))
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
sqlsaber/config/auth.py CHANGED
@@ -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
@@ -193,4 +193,3 @@ __all__ = [
193
193
  "default_log_dir",
194
194
  "default_log_file",
195
195
  ]
196
-
@@ -135,6 +135,35 @@ class PostgreSQLConnection(BaseDatabaseConnection):
135
135
  class PostgreSQLSchemaIntrospector(BaseSchemaIntrospector):
136
136
  """PostgreSQL-specific schema introspection."""
137
137
 
138
+ def _get_excluded_schemas(self) -> list[str]:
139
+ """Return schemas to exclude during introspection.
140
+
141
+ Defaults include PostgreSQL system schemas and TimescaleDB internal
142
+ partitions schema. Additional schemas can be excluded by setting the
143
+ environment variable `SQLSABER_PG_EXCLUDE_SCHEMAS` to a comma-separated
144
+ list of schema names.
145
+ """
146
+ import os
147
+
148
+ # Base exclusions: system schemas and TimescaleDB internal partitions
149
+ excluded = [
150
+ "pg_catalog",
151
+ "information_schema",
152
+ "_timescaledb_internal",
153
+ "_timescaledb_cache",
154
+ "_timescaledb_config",
155
+ "_timescaledb_catalog",
156
+ ]
157
+
158
+ extra = os.getenv("SQLSABER_PG_EXCLUDE_SCHEMAS", "")
159
+ if extra:
160
+ for item in extra.split(","):
161
+ name = item.strip()
162
+ if name and name not in excluded:
163
+ excluded.append(name)
164
+
165
+ return excluded
166
+
138
167
  def _build_table_filter_clause(self, tables: list) -> tuple[str, list]:
139
168
  """Build VALUES clause with bind parameters for table filtering.
140
169
 
@@ -160,23 +189,35 @@ class PostgreSQLSchemaIntrospector(BaseSchemaIntrospector):
160
189
  """Get tables information for PostgreSQL."""
161
190
  pool = await connection.get_pool()
162
191
  async with pool.acquire() as conn:
163
- # Build WHERE clause for filtering
164
- where_conditions = [
165
- "table_schema NOT IN ('pg_catalog', 'information_schema')"
166
- ]
167
- params = []
192
+ # Build WHERE clause for filtering with bind params
193
+ where_conditions: list[str] = []
194
+ params: list[Any] = []
195
+
196
+ excluded = self._get_excluded_schemas()
197
+ if excluded:
198
+ placeholders = ", ".join(f"${i + 1}" for i in range(len(excluded)))
199
+ where_conditions.append(f"table_schema NOT IN ({placeholders})")
200
+ params.extend(excluded)
201
+ else:
202
+ # Fallback safety
203
+ where_conditions.append(
204
+ "table_schema NOT IN ('pg_catalog', 'information_schema')"
205
+ )
168
206
 
169
207
  if table_pattern:
170
208
  # Support patterns like 'schema.table' or just 'table'
171
209
  if "." in table_pattern:
172
210
  schema_pattern, table_name_pattern = table_pattern.split(".", 1)
211
+ s_idx = len(params) + 1
212
+ t_idx = len(params) + 2
173
213
  where_conditions.append(
174
- "(table_schema LIKE $1 AND table_name LIKE $2)"
214
+ f"(table_schema LIKE ${s_idx} AND table_name LIKE ${t_idx})"
175
215
  )
176
216
  params.extend([schema_pattern, table_name_pattern])
177
217
  else:
218
+ p_idx = len(params) + 1
178
219
  where_conditions.append(
179
- "(table_name LIKE $1 OR table_schema || '.' || table_name LIKE $1)"
220
+ f"(table_name LIKE ${p_idx} OR table_schema || '.' || table_name LIKE ${p_idx})"
180
221
  )
181
222
  params.append(table_pattern)
182
223
 
@@ -310,17 +351,28 @@ class PostgreSQLSchemaIntrospector(BaseSchemaIntrospector):
310
351
  """Get list of tables with basic information for PostgreSQL."""
311
352
  pool = await connection.get_pool()
312
353
  async with pool.acquire() as conn:
313
- # Get table names and basic info without row counts for better performance
314
- tables_query = """
354
+ # Exclude system schemas (and TimescaleDB internals) for performance
355
+ excluded = self._get_excluded_schemas()
356
+ params: list[Any] = []
357
+ if excluded:
358
+ placeholders = ", ".join(f"${i + 1}" for i in range(len(excluded)))
359
+ where_clause = f"table_schema NOT IN ({placeholders})"
360
+ params.extend(excluded)
361
+ else:
362
+ where_clause = (
363
+ "table_schema NOT IN ('pg_catalog', 'information_schema')"
364
+ )
365
+
366
+ tables_query = f"""
315
367
  SELECT
316
368
  table_schema,
317
369
  table_name,
318
370
  table_type
319
371
  FROM information_schema.tables
320
- WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
372
+ WHERE {where_clause}
321
373
  ORDER BY table_schema, table_name;
322
374
  """
323
- tables = await conn.fetch(tables_query)
375
+ tables = await conn.fetch(tables_query, *params)
324
376
 
325
377
  # Convert to expected format
326
378
  return [
File without changes
@@ -0,0 +1,52 @@
1
+ SONNET_4_5 = """You are a helpful SQL assistant designed to help users query their {db} database using natural language requests.
2
+
3
+ ## Your Core Responsibilities
4
+
5
+ 1. **Understand requests**: Convert natural language queries into appropriate SQL
6
+ 2. **Explore schema**: Use provided tools to discover and understand database structure
7
+ 3. **Generate queries**: Create safe, efficient SQL queries with proper syntax
8
+ 4. **Execute safely**: Run only SELECT queries (no data modification allowed)
9
+ 5. **Explain results**: Format outputs clearly and explain what the queries accomplish
10
+
11
+ ## Tool Usage Strategy
12
+
13
+ You must follow this systematic approach:
14
+
15
+ 1. **Always start with `list_tables`** to discover available tables
16
+ 2. **Use `introspect_schema` strategically** with table patterns (like 'sample%' or '%experiment%') to get details only for relevant tables
17
+ 3. **Execute SQL queries safely** - all SELECT statements automatically include LIMIT clauses for safety
18
+
19
+ ## Important Guidelines
20
+
21
+ - Write proper JOIN syntax and avoid cartesian products
22
+ - Include appropriate WHERE clauses to filter results effectively
23
+ - Convert timestamp columns to text format in your queries
24
+ - Use parameterized queries when needed for security
25
+ - Handle errors gracefully and suggest fixes when issues arise
26
+ - Explain each query's purpose in simple non-technical terms
27
+
28
+ ## Response Format
29
+
30
+ For each user request, structure your response as follows:
31
+
32
+ Before proceeding with database exploration, work through the problem systematically in <analysis> tags inside your thinking block:
33
+ - Parse the user's natural language request and identify the core question being asked
34
+ - Extract key terms, entities, and concepts that might correspond to database tables or columns
35
+ - Consider what types of data relationships might be involved (e.g., one-to-many, many-to-many)
36
+ - Plan your database exploration approach step by step
37
+ - Design your overall SQL strategy, including potential JOINs, filters, and aggregations
38
+ - Anticipate potential challenges or edge cases specific to this database type
39
+ - Verify your approach makes logical sense for the business question
40
+
41
+ It's OK for this analysis section to be quite long if the request is complex.
42
+
43
+ Then, execute the planned database exploration and queries, providing clear explanations of results.
44
+
45
+ ## Example Response Structure
46
+
47
+ Working through this systematically in my analysis, then exploring tables and executing queries...
48
+
49
+ Now I need to address your specific request. Before proceeding with database exploration, let me analyze what you're asking for:
50
+
51
+ Your final response should focus on the database exploration, query execution, and results explanation, without duplicating or rehashing the analytical work done in the thinking block.
52
+ """
@@ -0,0 +1,4 @@
1
+ MEMORY_ADDITION = """# Contextual Memory Integration
2
+ Below is any relevant context, prior memories, or database-specific quirks provided from the user.
3
+ Prioritize only information that directly influences query generation or interpretation to optimize clarity and focus.
4
+ """
@@ -0,0 +1,40 @@
1
+ GPT_5 = """# Role and Objective
2
+ A helpful SQL assistant that assists users in querying their {db} database.
3
+
4
+ # Instructions
5
+ - Understand user requests in natural language and convert them into SQL queries.
6
+ - Efficiently utilize provided tools to investigate the database schema.
7
+ - Generate and execute only safe and appropriate SQL queries (only SELECT statements are allowed — no modifications to the database).
8
+ - Clearly format and explain results to the user.
9
+ - Set reasoning_effort = medium due to moderate task complexity; keep tool call outputs concise, provide fuller explanations in the final output.
10
+
11
+ ## Workflow Checklist
12
+ Begin by thinking about what you will do; keep items conceptual, not implementation-level, before substantive work on user queries.
13
+
14
+ - Analyze the user request and determine intent.
15
+ - List available tables and their row counts using the appropriate tool.
16
+ - Identify relevant tables via schema introspection with filtered patterns.
17
+ - Formulate a safe and appropriate SELECT query with LIMIT clause.
18
+ - Execute the query and validate the result for correctness and safety.
19
+ - Clearly explain the result or guide the user if adjustments are needed.
20
+
21
+
22
+ ## Tool Usage Strategy
23
+ 1. **Start with `list_tables`**: Always begin by listing available tables and row counts to discover what's present in the database. Before any significant tool call, state the purpose and minimal inputs.
24
+ 2. **Use `introspect_schema` with a table pattern**: Retrieve schema details only for tables relevant to the user's request, employing patterns like `sample%` or `%experiment%` to filter.
25
+ 3. **Execute SQL queries safely**: All SELECT statements must include LIMIT clauses for safety. Only SELECT queries are permitted for security purposes.
26
+
27
+ ## Tool-Specific Guidelines
28
+ - **introspect_schema**: Limit introspection to relevant tables using appropriate patterns (e.g., `sample%`, `%experiment%`).
29
+ - **execute_sql**: Only run SELECT queries, ensuring they have LIMIT clauses. Do not permit any queries that modify the database.
30
+
31
+ # Guidelines
32
+ - Apply proper JOIN syntax to avoid cartesian products.
33
+ - Use appropriate WHERE clauses to filter results.
34
+ - Explain each query in simple terms for user understanding.
35
+ - Handle errors gracefully and suggest corrections to users.
36
+ - Be security conscious — use parameterized queries when necessary.
37
+ - Convert timestamp columns to text within the SQL queries you generate.
38
+ - Use table patterns (like `sample%` or `%experiment%`) to narrow down contextually relevant tables.
39
+ - After each tool call or code edit, validate result in 1-2 lines and proceed or self-correct if validation fails.
40
+ """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlsaber
3
- Version: 0.32.1
3
+ Version: 0.34.0
4
4
  Summary: SQLsaber - Open-source agentic SQL assistant
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -2,14 +2,14 @@ sqlsaber/__init__.py,sha256=HjS8ULtP4MGpnTL7njVY45NKV9Fi4e_yeYuY-hyXWQc,73
2
2
  sqlsaber/__main__.py,sha256=RIHxWeWh2QvLfah-2OkhI5IJxojWfy4fXpMnVEJYvxw,78
3
3
  sqlsaber/agents/__init__.py,sha256=qYI6rLY4q5AbF47vXH5RVoM08-yQjymBSaePh4lFIW4,116
4
4
  sqlsaber/agents/base.py,sha256=T05UsMZPwAlMhsJFpuuVI1RNDhdiwiEsgCWr9MbPoAU,2654
5
- sqlsaber/agents/pydantic_ai_agent.py,sha256=cHHwQHJf9TqrBhItWJgnScL31lvyvKLcCBTSjRSwWug,12002
5
+ sqlsaber/agents/pydantic_ai_agent.py,sha256=6_KppII8YcMw74KOGsYI5Dt6AP8WSduK3yAXCawVex4,10643
6
6
  sqlsaber/application/__init__.py,sha256=KY_-d5nEdQyAwNOsK5r-f7Tb69c63XbuEkHPeLpJal8,84
7
- sqlsaber/application/auth_setup.py,sha256=D94dyU9bOVfnNHLnnFJb5PaeWsKPTL21CiS_DLcY93A,5114
7
+ sqlsaber/application/auth_setup.py,sha256=wbi9MaYl6q27LjcSBZmqFC12JtE5hrUHEX1NmD-7UVc,7778
8
8
  sqlsaber/application/db_setup.py,sha256=ZSgR9rJJVHttIjsbYQS9GEIyzkM09k5RLrVGdegrfYc,6859
9
9
  sqlsaber/application/model_selection.py,sha256=fSC06MZNKinHDR-csMFVYYJFyK8MydKf6pStof74Jp0,3191
10
10
  sqlsaber/application/prompts.py,sha256=4rMGcWpYJbNWPMzqVWseUMx0nwvXOkWS6GaTAJ5mhfc,3473
11
11
  sqlsaber/cli/__init__.py,sha256=qVSLVJLLJYzoC6aj6y9MFrzZvAwc4_OgxU9DlkQnZ4M,86
12
- sqlsaber/cli/auth.py,sha256=eCHxNO2IzgW7Og5970Yc9NZO5PPq1b2WpqVqEXDa66w,7021
12
+ sqlsaber/cli/auth.py,sha256=TmAD4BJr2gIjPeW2LA1QkKK5gUZ2OOS6vOmBvB6PC0M,7051
13
13
  sqlsaber/cli/commands.py,sha256=WocWlLrxA5kM8URfvIvWFtc0ocfgKWAwoYTxVNZhmM4,10962
14
14
  sqlsaber/cli/completers.py,sha256=g-hLDq5fiBx7gg8Bte1Lq8GU-ZxCYVs4dcPsmHPIcK4,6574
15
15
  sqlsaber/cli/database.py,sha256=BBGj0eyduh5DDXNLZLDtWfY9kWpeT_ZX0J9R9INZyyU,12421
@@ -22,10 +22,10 @@ sqlsaber/cli/streaming.py,sha256=jicSDLWQ3efitpdc2y4QsasHcEW8ogZ4lHcWmftq9Ao,676
22
22
  sqlsaber/cli/theme.py,sha256=D6HIt7rmF00B5ZOCV5lXKzPICE4uppHdraOdVs7k5Nw,4672
23
23
  sqlsaber/cli/threads.py,sha256=zYvs1epmRRuQxOofF85eXk1_YHS6co7oq_F33DdNdf0,14643
24
24
  sqlsaber/config/__init__.py,sha256=olwC45k8Nc61yK0WmPUk7XHdbsZH9HuUAbwnmKe3IgA,100
25
- sqlsaber/config/api_keys.py,sha256=dJ7cCSFOM6CRLHxEVgKJXGIOd_wQkRuQO4W88-8ng_w,3672
26
- sqlsaber/config/auth.py,sha256=b5qB2h1doXyO9Bn8z0CcL8LAR2jF431gGXBGKLgTmtQ,2756
25
+ sqlsaber/config/api_keys.py,sha256=H7xBU1mflngXIlnaDjbTvuNqZsW_92AIve31eDLij34,4513
26
+ sqlsaber/config/auth.py,sha256=G1uulySUclWSId8EIt1hPNmsUNhbfRzfp8VVQftYyG8,2964
27
27
  sqlsaber/config/database.py,sha256=Yec6_0wdzq-ADblMNnbgvouYCimYOY_DWHT9oweaISc,11449
28
- sqlsaber/config/logging.py,sha256=V-DGhKm7XYPtcA6bXL-aQQyTTlyHi2WHTqTTzu2xipw,6174
28
+ sqlsaber/config/logging.py,sha256=vv4oCuQePeYQ7bMs0OLKj8ZSiNcFWbHmWtdC0lTsUyc,6173
29
29
  sqlsaber/config/oauth_flow.py,sha256=cDfaJjqr4spNuzxbAlzuJfk6SEe1ojSRAkoOWlvQYy0,11037
30
30
  sqlsaber/config/oauth_tokens.py,sha256=KCC2u3lOjdh0M-rd0K1rW0PWk58w7mqpodAhlPVp9NE,6424
31
31
  sqlsaber/config/providers.py,sha256=JFjeJv1K5Q93zWSlWq3hAvgch1TlgoF0qFa0KJROkKY,2957
@@ -35,13 +35,17 @@ sqlsaber/database/base.py,sha256=oaipLxlvoylX6oJCITPAWWqRqv09hRELqqEBufsmFic,370
35
35
  sqlsaber/database/csv.py,sha256=41wuP40FaGPfj28HMiD0I69uG0JbUxArpoTLC3MG2uc,4464
36
36
  sqlsaber/database/duckdb.py,sha256=8HNKdx208aFK_YtwGjLz6LTne0xEmNevD-f9dRWlrFg,11244
37
37
  sqlsaber/database/mysql.py,sha256=wMzDQqq4GFbfEdqXtv_sCb4Qbr9GSWqYAvOLeo5UryY,14472
38
- sqlsaber/database/postgresql.py,sha256=fuf2Wl29NKXvD3mqsR08PDleNQ1PG-fNvWSxT6HDh2M,13223
38
+ sqlsaber/database/postgresql.py,sha256=jNmjDY8fODfjRo0gHC1KU-NNP3K5ZQ9RVq69QwRP4Ws,15214
39
39
  sqlsaber/database/resolver.py,sha256=wSCcn__aCqwIfpt_LCjtW2Zgb8RpG5PlmwwZHli1q_U,3628
40
40
  sqlsaber/database/schema.py,sha256=CuV0ewoVaERe1gj_fJFJFWAP8aEPgepmn6X6B7bgkfQ,6962
41
41
  sqlsaber/database/sqlite.py,sha256=iReEIiSpkhhS1VzITd79ZWqSL3fHMyfe3DRCDpM0DvE,9421
42
42
  sqlsaber/memory/__init__.py,sha256=GiWkU6f6YYVV0EvvXDmFWe_CxarmDCql05t70MkTEWs,63
43
43
  sqlsaber/memory/manager.py,sha256=p3fybMVfH-E4ApT1ZRZUnQIWSk9dkfUPCyfkmA0HALs,2739
44
44
  sqlsaber/memory/storage.py,sha256=ne8szLlGj5NELheqLnI7zu21V8YS4rtpYGGC7tOmi-s,5745
45
+ sqlsaber/prompts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
+ sqlsaber/prompts/claude.py,sha256=wvitHetuUi9s_Bfy8desgBUigEbI-URqcIS447WtV2k,2757
47
+ sqlsaber/prompts/memory.py,sha256=UciA3JPdNp6Iup20Vy-Nv7PGU7tL2yOVNYkE61r9AL4,275
48
+ sqlsaber/prompts/openai.py,sha256=gPBvJR7WgmWELPZbP4kC53Wm6jDu1zsCmHKmyc5U1uY,2640
45
49
  sqlsaber/theme/__init__.py,sha256=qCICX1Cg4B6yCbZ1UrerxglWxcqldRFVSRrSs73na_8,188
46
50
  sqlsaber/theme/manager.py,sha256=TPourIKGU-UzHtImgexgtazpuDaFhqUYtVauMblgGAQ,6480
47
51
  sqlsaber/threads/__init__.py,sha256=Hh3dIG1tuC8fXprREUpslCIgPYz8_6o7aRLx4yNeO48,139
@@ -51,8 +55,8 @@ sqlsaber/tools/base.py,sha256=NKEEooliPKTJj_Pomwte_wW0Xd9Z5kXNfVdCRfTppuw,883
51
55
  sqlsaber/tools/registry.py,sha256=XmBzERq0LJXtg3BZ-r8cEyt8J54NUekgUlTJ_EdSYMk,2204
52
56
  sqlsaber/tools/sql_guard.py,sha256=dTDwcZP-N4xPGzcr7MQtKUxKrlDzlc1irr9aH5a4wvk,6182
53
57
  sqlsaber/tools/sql_tools.py,sha256=eo-NTxiXGHMopAjujvDDjmv9hf5bQNbiy3nTpxoJ_E8,7369
54
- sqlsaber-0.32.1.dist-info/METADATA,sha256=ykPTVg8m9G3OgtJ8kHGSADMwNFAdVq7Abni5_74UYA8,5915
55
- sqlsaber-0.32.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
56
- sqlsaber-0.32.1.dist-info/entry_points.txt,sha256=tw1mB0fjlkXQiOsC0434X6nE-o1cFCuQwt2ZYHv_WAE,91
57
- sqlsaber-0.32.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
58
- sqlsaber-0.32.1.dist-info/RECORD,,
58
+ sqlsaber-0.34.0.dist-info/METADATA,sha256=A5Xu_z5Q2dw-qJSHZX6MR8uiM1mIf941pHbXnl7BPCg,5915
59
+ sqlsaber-0.34.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
60
+ sqlsaber-0.34.0.dist-info/entry_points.txt,sha256=tw1mB0fjlkXQiOsC0434X6nE-o1cFCuQwt2ZYHv_WAE,91
61
+ sqlsaber-0.34.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
62
+ sqlsaber-0.34.0.dist-info/RECORD,,