sqlsaber 0.30.1__py3-none-any.whl → 0.31.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.

@@ -1,16 +1,16 @@
1
1
  """OAuth token management for SQLSaber."""
2
2
 
3
3
  import json
4
- import logging
5
4
  from datetime import datetime, timedelta, timezone
6
5
  from typing import Any
7
6
 
8
7
  import keyring
9
8
 
9
+ from sqlsaber.config.logging import get_logger
10
10
  from sqlsaber.theme.manager import create_console
11
11
 
12
12
  console = create_console()
13
- logger = logging.getLogger(__name__)
13
+ logger = get_logger(__name__)
14
14
 
15
15
 
16
16
  class OAuthToken:
@@ -99,6 +99,7 @@ class OAuthTokenManager:
99
99
  f"OAuth token for {provider} has expired and needs refresh",
100
100
  style="muted",
101
101
  )
102
+ logger.info("oauth.token.expired", provider=provider)
102
103
  return token # Return anyway for refresh attempt
103
104
 
104
105
  if token.expires_soon():
@@ -106,11 +107,14 @@ class OAuthTokenManager:
106
107
  f"OAuth token for {provider} expires soon, consider refreshing",
107
108
  style="muted",
108
109
  )
110
+ logger.info("oauth.token.expires_soon", provider=provider)
109
111
 
110
112
  return token
111
113
 
112
114
  except Exception as e:
113
- logger.warning(f"Failed to retrieve OAuth token for {provider}: {e}")
115
+ logger.warning(
116
+ "oauth.token.retrieve_failed", provider=provider, error=str(e)
117
+ )
114
118
  return None
115
119
 
116
120
  def store_oauth_token(self, provider: str, token: OAuthToken) -> bool:
@@ -121,9 +125,10 @@ class OAuthTokenManager:
121
125
  token_data = json.dumps(token.to_dict())
122
126
  keyring.set_password(service_name, provider, token_data)
123
127
  console.print(f"OAuth token for {provider} stored securely", style="green")
128
+ logger.info("oauth.token.stored", provider=provider)
124
129
  return True
125
130
  except Exception as e:
126
- logger.error(f"Failed to store OAuth token for {provider}: {e}")
131
+ logger.error("oauth.token.store_failed", provider=provider, error=str(e))
127
132
  console.print(
128
133
  f"Warning: Could not store OAuth token in keyring: {e}",
129
134
  style="warning",
@@ -149,7 +154,10 @@ class OAuthTokenManager:
149
154
  token_type=existing_token.token_type,
150
155
  )
151
156
 
152
- return self.store_oauth_token(provider, updated_token)
157
+ success = self.store_oauth_token(provider, updated_token)
158
+ if success:
159
+ logger.info("oauth.token.updated", provider=provider)
160
+ return success
153
161
 
154
162
  def remove_oauth_token(self, provider: str) -> bool:
155
163
  """Remove OAuth token from storage."""
@@ -158,9 +166,10 @@ class OAuthTokenManager:
158
166
  try:
159
167
  keyring.delete_password(service_name, provider)
160
168
  console.print(f"OAuth token for {provider} removed", style="green")
169
+ logger.info("oauth.token.removed", provider=provider)
161
170
  return True
162
171
  except Exception as e:
163
- logger.error(f"Failed to remove OAuth token for {provider}: {e}")
172
+ logger.error("oauth.token.remove_failed", provider=provider, error=str(e))
164
173
  console.print(
165
174
  f"Warning: Could not remove OAuth token: {e}", style="warning"
166
175
  )
@@ -9,7 +9,6 @@ recommended approach of serializing ModelMessage[] with ModelMessagesTypeAdapter
9
9
  """
10
10
 
11
11
  import asyncio
12
- import logging
13
12
  import time
14
13
  import uuid
15
14
  from pathlib import Path
@@ -19,7 +18,9 @@ import aiosqlite
19
18
  import platformdirs
20
19
  from pydantic_ai.messages import ModelMessage, ModelMessagesTypeAdapter
21
20
 
22
- logger = logging.getLogger(__name__)
21
+ from sqlsaber.config.logging import get_logger
22
+
23
+ logger = get_logger(__name__)
23
24
 
24
25
 
25
26
  SCHEMA_SQL = """
@@ -79,9 +80,9 @@ class ThreadStorage:
79
80
  await db.executescript(SCHEMA_SQL)
80
81
  await db.commit()
81
82
  self._initialized = True
82
- logger.debug("Initialized threads database at %s", self.db_path)
83
+ logger.info("threads.db.init", path=str(self.db_path))
83
84
  except Exception as e: # pragma: no cover - best-effort persistence
84
- logger.warning("Failed to initialize threads DB: %s", e)
85
+ logger.warning("threads.db.init_failed", error=str(e))
85
86
 
86
87
  async def save_snapshot(
87
88
  self,
@@ -116,10 +117,10 @@ class ThreadStorage:
116
117
  ),
117
118
  )
118
119
  await db.commit()
119
- logger.debug("Created thread %s", thread_id)
120
+ logger.info("threads.create", thread_id=thread_id)
120
121
  return thread_id
121
122
  except Exception as e: # pragma: no cover
122
- logger.warning("Failed to create thread: %s", e)
123
+ logger.warning("threads.create_failed", error=str(e))
123
124
  return thread_id
124
125
  else:
125
126
  try:
@@ -140,10 +141,12 @@ class ThreadStorage:
140
141
  ),
141
142
  )
142
143
  await db.commit()
143
- logger.debug("Updated thread %s snapshot", thread_id)
144
+ logger.info("threads.update_snapshot", thread_id=thread_id)
144
145
  return thread_id
145
146
  except Exception as e: # pragma: no cover
146
- logger.warning("Failed to update thread %s: %s", thread_id, e)
147
+ logger.warning(
148
+ "threads.update_snapshot_failed", thread_id=thread_id, error=str(e)
149
+ )
147
150
  return thread_id
148
151
 
149
152
  async def save_metadata(
@@ -167,9 +170,12 @@ class ThreadStorage:
167
170
  (title, model_name, thread_id),
168
171
  )
169
172
  await db.commit()
173
+ logger.info("threads.update_metadata", thread_id=thread_id)
170
174
  return True
171
175
  except Exception as e: # pragma: no cover
172
- logger.warning("Failed to update metadata for thread %s: %s", thread_id, e)
176
+ logger.warning(
177
+ "threads.update_metadata_failed", thread_id=thread_id, error=str(e)
178
+ )
173
179
  return False
174
180
 
175
181
  async def end_thread(self, thread_id: str) -> bool:
@@ -181,9 +187,10 @@ class ThreadStorage:
181
187
  (time.time(), time.time(), thread_id),
182
188
  )
183
189
  await db.commit()
190
+ logger.info("threads.end", thread_id=thread_id)
184
191
  return True
185
192
  except Exception as e: # pragma: no cover
186
- logger.warning("Failed to end thread %s: %s", thread_id, e)
193
+ logger.warning("threads.end_failed", thread_id=thread_id, error=str(e))
187
194
  return False
188
195
 
189
196
  async def get_thread(self, thread_id: str) -> Thread | None:
@@ -211,7 +218,7 @@ class ThreadStorage:
211
218
  model_name=row[6],
212
219
  )
213
220
  except Exception as e: # pragma: no cover
214
- logger.warning("Failed to get thread %s: %s", thread_id, e)
221
+ logger.warning("threads.get_failed", thread_id=thread_id, error=str(e))
215
222
  return None
216
223
 
217
224
  async def get_thread_messages(self, thread_id: str) -> list[ModelMessage]:
@@ -229,7 +236,9 @@ class ThreadStorage:
229
236
  messages_blob: bytes = row[0]
230
237
  return ModelMessagesTypeAdapter.validate_json(messages_blob)
231
238
  except Exception as e: # pragma: no cover
232
- logger.warning("Failed to load thread %s messages: %s", thread_id, e)
239
+ logger.warning(
240
+ "threads.get_messages_failed", thread_id=thread_id, error=str(e)
241
+ )
233
242
  return []
234
243
 
235
244
  async def list_threads(
@@ -265,7 +274,7 @@ class ThreadStorage:
265
274
  )
266
275
  return threads
267
276
  except Exception as e: # pragma: no cover
268
- logger.warning("Failed to list threads: %s", e)
277
+ logger.warning("threads.list_failed", error=str(e))
269
278
  return []
270
279
 
271
280
  async def delete_thread(self, thread_id: str) -> bool:
@@ -274,9 +283,12 @@ class ThreadStorage:
274
283
  async with self._lock, aiosqlite.connect(self.db_path) as db:
275
284
  cur = await db.execute("DELETE FROM threads WHERE id = ?", (thread_id,))
276
285
  await db.commit()
277
- return cur.rowcount > 0
286
+ deleted = cur.rowcount > 0
287
+ if deleted:
288
+ logger.info("threads.delete", thread_id=thread_id)
289
+ return deleted
278
290
  except Exception as e: # pragma: no cover
279
- logger.warning("Failed to delete thread %s: %s", thread_id, e)
291
+ logger.warning("threads.delete_failed", thread_id=thread_id, error=str(e))
280
292
  return False
281
293
 
282
294
  async def prune_threads(self, older_than_days: int = 30) -> int:
@@ -297,7 +309,9 @@ class ThreadStorage:
297
309
  (cutoff,),
298
310
  )
299
311
  await db.commit()
300
- return cur.rowcount or 0
312
+ deleted = cur.rowcount or 0
313
+ logger.info("threads.prune", days=older_than_days, deleted=deleted)
314
+ return deleted
301
315
  except Exception as e: # pragma: no cover
302
- logger.warning("Failed to prune threads: %s", e)
316
+ logger.warning("threads.prune_failed", days=older_than_days, error=str(e))
303
317
  return 0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlsaber
3
- Version: 0.30.1
3
+ Version: 0.31.0
4
4
  Summary: SQLsaber - Open-source agentic SQL assistant
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -11,12 +11,14 @@ Requires-Dist: cyclopts>=3.22.1
11
11
  Requires-Dist: duckdb>=0.9.2
12
12
  Requires-Dist: httpx>=0.28.1
13
13
  Requires-Dist: keyring>=25.6.0
14
+ Requires-Dist: keyrings-cryptfile; sys_platform == 'linux'
14
15
  Requires-Dist: platformdirs>=4.0.0
15
16
  Requires-Dist: prompt-toolkit>3.0.51
16
17
  Requires-Dist: pydantic-ai
17
18
  Requires-Dist: questionary>=2.1.0
18
19
  Requires-Dist: rich>=13.7.0
19
20
  Requires-Dist: sqlglot[rs]>=27.20.0
21
+ Requires-Dist: structlog>=25.4.0
20
22
  Description-Content-Type: text/markdown
21
23
 
22
24
  # SQLsaber
@@ -9,24 +9,25 @@ sqlsaber/application/db_setup.py,sha256=ZSgR9rJJVHttIjsbYQS9GEIyzkM09k5RLrVGdegr
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=elUpw8gypHGlxbHx4a4_z4wFznx2vr6V1h8lqpeC6OQ,6121
13
- sqlsaber/cli/commands.py,sha256=Pii_SlVKpNEtt57_QPQzwC1u-x6tA8kuG8yd43undWE,8628
12
+ sqlsaber/cli/auth.py,sha256=8BsAkOYarLSpGipYi2CCUqyAv7EbOCqVXKLmdrX_g2c,6966
13
+ sqlsaber/cli/commands.py,sha256=eH6jC2coaH6QhJAlOySBxEHfETWW283fBr6BQG60Bl8,9732
14
14
  sqlsaber/cli/completers.py,sha256=g-hLDq5fiBx7gg8Bte1Lq8GU-ZxCYVs4dcPsmHPIcK4,6574
15
- sqlsaber/cli/database.py,sha256=Tqy8H5MnjsrmOSPcbA5Qy-u-IOYJCIXRJVhk0veLNDk,10726
15
+ sqlsaber/cli/database.py,sha256=BBGj0eyduh5DDXNLZLDtWfY9kWpeT_ZX0J9R9INZyyU,12421
16
16
  sqlsaber/cli/display.py,sha256=WB5JCumhXadziDEX1EZHG3vN1Chol5FNAaTXHieqFK0,17892
17
- sqlsaber/cli/interactive.py,sha256=PcY6mszImo_3PsqjjWmx_cOfj44OmKvD9ENOvGA-wjU,13715
18
- sqlsaber/cli/memory.py,sha256=IKq09DUbqpvvtATsyDlpm7rDlGqWEhdUX9wgnR-oiq4,7850
19
- sqlsaber/cli/models.py,sha256=nbn75gCnkRciGt4Q47yxa8wImiZcCkDdQZNVeehDim8,8530
17
+ sqlsaber/cli/interactive.py,sha256=XSl_W1NOozFfxf3On5r6w53qMNULyttHnH4vR4iQGus,14140
18
+ sqlsaber/cli/memory.py,sha256=kAY5LLFueIF30gJ8ibfrFw42rOyy5wajeJGS4h5XQw4,9475
19
+ sqlsaber/cli/models.py,sha256=aVHazP_fiT-Mj9AtCdjliDtq3E3fJrhgP4oF5p4CuwI,9593
20
20
  sqlsaber/cli/onboarding.py,sha256=iBGT-W-OJFRvQoEpuHYyO1c9Mym5c97eIefRvxGHtTg,11265
21
- sqlsaber/cli/streaming.py,sha256=eggj25ZlA-xKrAF726S29vfS2MHTFC5wTmgXLbS-RvM,6515
22
- sqlsaber/cli/theme.py,sha256=hP0kmsMLCtqaT7b5wB1dk1hW1hV94oP4BHdz8S6887A,4243
23
- sqlsaber/cli/threads.py,sha256=o9q9Hst1Wt7cxSyrpAtwG6pkUct6csgiAmN_0P_WO3k,13637
21
+ sqlsaber/cli/streaming.py,sha256=jicSDLWQ3efitpdc2y4QsasHcEW8ogZ4lHcWmftq9Ao,6763
22
+ sqlsaber/cli/theme.py,sha256=D6HIt7rmF00B5ZOCV5lXKzPICE4uppHdraOdVs7k5Nw,4672
23
+ sqlsaber/cli/threads.py,sha256=zYvs1epmRRuQxOofF85eXk1_YHS6co7oq_F33DdNdf0,14643
24
24
  sqlsaber/config/__init__.py,sha256=olwC45k8Nc61yK0WmPUk7XHdbsZH9HuUAbwnmKe3IgA,100
25
25
  sqlsaber/config/api_keys.py,sha256=dJ7cCSFOM6CRLHxEVgKJXGIOd_wQkRuQO4W88-8ng_w,3672
26
26
  sqlsaber/config/auth.py,sha256=b5qB2h1doXyO9Bn8z0CcL8LAR2jF431gGXBGKLgTmtQ,2756
27
27
  sqlsaber/config/database.py,sha256=Yec6_0wdzq-ADblMNnbgvouYCimYOY_DWHT9oweaISc,11449
28
- sqlsaber/config/oauth_flow.py,sha256=P81lHhtICdhiQu8lNwyqn2m45FGEqCEzLgUQTLG5UW0,10343
29
- sqlsaber/config/oauth_tokens.py,sha256=nkCPFV1Yxs6W5wDRze2PAu8g5HqCN3n-Ihvv-6lmXPI,5954
28
+ sqlsaber/config/logging.py,sha256=V-DGhKm7XYPtcA6bXL-aQQyTTlyHi2WHTqTTzu2xipw,6174
29
+ sqlsaber/config/oauth_flow.py,sha256=cDfaJjqr4spNuzxbAlzuJfk6SEe1ojSRAkoOWlvQYy0,11037
30
+ sqlsaber/config/oauth_tokens.py,sha256=KCC2u3lOjdh0M-rd0K1rW0PWk58w7mqpodAhlPVp9NE,6424
30
31
  sqlsaber/config/providers.py,sha256=JFjeJv1K5Q93zWSlWq3hAvgch1TlgoF0qFa0KJROkKY,2957
31
32
  sqlsaber/config/settings.py,sha256=-nIBNt9E0tCRGd14bk4x-bNAwO12sbsjRsN8fFannK4,6449
32
33
  sqlsaber/database/__init__.py,sha256=Gi9N_NOkD459WRWXDg3hSuGoBs3xWbMDRBvsTVmnGAg,2025
@@ -44,14 +45,14 @@ sqlsaber/memory/storage.py,sha256=ne8szLlGj5NELheqLnI7zu21V8YS4rtpYGGC7tOmi-s,57
44
45
  sqlsaber/theme/__init__.py,sha256=qCICX1Cg4B6yCbZ1UrerxglWxcqldRFVSRrSs73na_8,188
45
46
  sqlsaber/theme/manager.py,sha256=TPourIKGU-UzHtImgexgtazpuDaFhqUYtVauMblgGAQ,6480
46
47
  sqlsaber/threads/__init__.py,sha256=Hh3dIG1tuC8fXprREUpslCIgPYz8_6o7aRLx4yNeO48,139
47
- sqlsaber/threads/storage.py,sha256=rsUdxT4CR52D7xtGir9UlsFnBMk11jZeflzDrk2q4ME,11183
48
+ sqlsaber/threads/storage.py,sha256=gs-BMjGbow7KOF3dOfbNS64B8TNfxdbktV9K2PyiszI,11801
48
49
  sqlsaber/tools/__init__.py,sha256=O6eqkMk8mkhYDniQD1eYgAElOjiHz03I2bGARdgkDkk,421
49
50
  sqlsaber/tools/base.py,sha256=NKEEooliPKTJj_Pomwte_wW0Xd9Z5kXNfVdCRfTppuw,883
50
51
  sqlsaber/tools/registry.py,sha256=XmBzERq0LJXtg3BZ-r8cEyt8J54NUekgUlTJ_EdSYMk,2204
51
52
  sqlsaber/tools/sql_guard.py,sha256=dTDwcZP-N4xPGzcr7MQtKUxKrlDzlc1irr9aH5a4wvk,6182
52
53
  sqlsaber/tools/sql_tools.py,sha256=eo-NTxiXGHMopAjujvDDjmv9hf5bQNbiy3nTpxoJ_E8,7369
53
- sqlsaber-0.30.1.dist-info/METADATA,sha256=9Pfi9tzGwiqGMtci8l2DuhQpvTTzqHhIzZyyfljzEuQ,5823
54
- sqlsaber-0.30.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
55
- sqlsaber-0.30.1.dist-info/entry_points.txt,sha256=tw1mB0fjlkXQiOsC0434X6nE-o1cFCuQwt2ZYHv_WAE,91
56
- sqlsaber-0.30.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
57
- sqlsaber-0.30.1.dist-info/RECORD,,
54
+ sqlsaber-0.31.0.dist-info/METADATA,sha256=B5_gtNMDPZ9pEIMaAHvuvx9XvyaTS3ENl3A37d1-A_o,5915
55
+ sqlsaber-0.31.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
56
+ sqlsaber-0.31.0.dist-info/entry_points.txt,sha256=tw1mB0fjlkXQiOsC0434X6nE-o1cFCuQwt2ZYHv_WAE,91
57
+ sqlsaber-0.31.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
58
+ sqlsaber-0.31.0.dist-info/RECORD,,