sqlsaber 0.22.0__tar.gz → 0.23.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.

Files changed (105) hide show
  1. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/PKG-INFO +1 -1
  2. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/src/content/docs/changelog.md +7 -0
  3. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/pyproject.toml +1 -1
  4. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/cli/display.py +17 -3
  5. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/cli/streaming.py +8 -2
  6. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/cli/threads.py +4 -0
  7. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/uv.lock +1 -1
  8. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/.github/workflows/claude-code-review.yml +0 -0
  9. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/.github/workflows/claude.yml +0 -0
  10. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/.github/workflows/deploy-docs.yml +0 -0
  11. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/.github/workflows/publish.yml +0 -0
  12. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/.github/workflows/test.yml +0 -0
  13. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/.gitignore +0 -0
  14. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/.python-version +0 -0
  15. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/AGENT.md +0 -0
  16. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/CLAUDE.md +0 -0
  17. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/LICENSE +0 -0
  18. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/README.md +0 -0
  19. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/.gitignore +0 -0
  20. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/.vscode/extensions.json +0 -0
  21. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/.vscode/launch.json +0 -0
  22. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/CLAUDE.md +0 -0
  23. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/astro.config.mjs +0 -0
  24. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/package-lock.json +0 -0
  25. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/package.json +0 -0
  26. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/public/CNAME +0 -0
  27. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/public/favicon.svg +0 -0
  28. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/src/assets/sqlsaber-hero.svg +0 -0
  29. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/src/content/docs/guides/authentication.mdx +0 -0
  30. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/src/content/docs/guides/database-setup.mdx +0 -0
  31. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/src/content/docs/guides/getting-started.mdx +0 -0
  32. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/src/content/docs/guides/memory.mdx +0 -0
  33. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/src/content/docs/guides/models.mdx +0 -0
  34. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/src/content/docs/guides/queries.mdx +0 -0
  35. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/src/content/docs/guides/threads.md +0 -0
  36. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/src/content/docs/index.mdx +0 -0
  37. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/src/content/docs/installation.mdx +0 -0
  38. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/src/content/docs/reference/commands.md +0 -0
  39. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/src/content.config.ts +0 -0
  40. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/src/styles/global.css +0 -0
  41. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/docs/tsconfig.json +0 -0
  42. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/legislators.db +0 -0
  43. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/pytest.ini +0 -0
  44. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/sqlsaber.gif +0 -0
  45. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/sqlsaber.svg +0 -0
  46. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/__init__.py +0 -0
  47. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/__main__.py +0 -0
  48. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/agents/__init__.py +0 -0
  49. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/agents/base.py +0 -0
  50. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/agents/mcp.py +0 -0
  51. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/agents/pydantic_ai_agent.py +0 -0
  52. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/cli/__init__.py +0 -0
  53. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/cli/auth.py +0 -0
  54. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/cli/commands.py +0 -0
  55. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/cli/completers.py +0 -0
  56. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/cli/database.py +0 -0
  57. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/cli/interactive.py +0 -0
  58. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/cli/memory.py +0 -0
  59. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/cli/models.py +0 -0
  60. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/config/__init__.py +0 -0
  61. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/config/api_keys.py +0 -0
  62. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/config/auth.py +0 -0
  63. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/config/database.py +0 -0
  64. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/config/oauth_flow.py +0 -0
  65. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/config/oauth_tokens.py +0 -0
  66. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/config/providers.py +0 -0
  67. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/config/settings.py +0 -0
  68. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/database/__init__.py +0 -0
  69. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/database/connection.py +0 -0
  70. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/database/resolver.py +0 -0
  71. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/database/schema.py +0 -0
  72. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/mcp/__init__.py +0 -0
  73. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/mcp/mcp.py +0 -0
  74. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/memory/__init__.py +0 -0
  75. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/memory/manager.py +0 -0
  76. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/memory/storage.py +0 -0
  77. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/threads/__init__.py +0 -0
  78. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/threads/storage.py +0 -0
  79. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/tools/__init__.py +0 -0
  80. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/tools/base.py +0 -0
  81. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/tools/enums.py +0 -0
  82. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/tools/instructions.py +0 -0
  83. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/tools/registry.py +0 -0
  84. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/src/sqlsaber/tools/sql_tools.py +0 -0
  85. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/__init__.py +0 -0
  86. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/conftest.py +0 -0
  87. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_cli/__init__.py +0 -0
  88. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_cli/test_auth_reset.py +0 -0
  89. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_cli/test_commands.py +0 -0
  90. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_cli/test_threads.py +0 -0
  91. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_config/__init__.py +0 -0
  92. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_config/test_database.py +0 -0
  93. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_config/test_oauth.py +0 -0
  94. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_config/test_providers.py +0 -0
  95. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_config/test_settings.py +0 -0
  96. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_database/__init__.py +0 -0
  97. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_database/test_connection.py +0 -0
  98. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_database/test_timeout.py +0 -0
  99. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_database_resolver.py +0 -0
  100. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_threads_storage.py +0 -0
  101. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_tools/__init__.py +0 -0
  102. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_tools/test_base.py +0 -0
  103. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_tools/test_instructions.py +0 -0
  104. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_tools/test_registry.py +0 -0
  105. {sqlsaber-0.22.0 → sqlsaber-0.23.0}/tests/test_tools/test_sql_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlsaber
3
- Version: 0.22.0
3
+ Version: 0.23.0
4
4
  Summary: SQLsaber - Open-source agentic SQL assistant
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -7,6 +7,13 @@ All notable changes to SQLsaber will be documented here.
7
7
 
8
8
  ### Unreleased
9
9
 
10
+ ### v0.23.0 - 2025-09-16
11
+
12
+ #### Added
13
+
14
+ - Smoother markdown streaming and prevent duplicate messages
15
+ - SQL execution errors now display in interactive sessions instead of being silently dropped
16
+
10
17
  ### v0.22.0 - 2025-09-15
11
18
 
12
19
  #### Added
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sqlsaber"
3
- version = "0.22.0"
3
+ version = "0.23.0"
4
4
  description = "SQLsaber - Open-source agentic SQL assistant"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -93,12 +93,15 @@ class LiveMarkdownRenderer:
93
93
  """Finalize and stop the current Live segment, if any."""
94
94
  if self._live is None:
95
95
  return
96
- if self._buffer:
97
- self._live.update(Markdown(self._buffer))
96
+ # Persist the *final* render exactly once, then shut Live down.
97
+ buf = self._buffer
98
98
  self._live.stop()
99
99
  self._live = None
100
100
  self._buffer = ""
101
101
  self._current_kind = None
102
+ # Print the complete markdown to scroll-back for permanent reference
103
+ if buf:
104
+ self.console.print(Markdown(buf))
102
105
 
103
106
  def end_if_active(self) -> None:
104
107
  self.end()
@@ -154,10 +157,12 @@ class LiveMarkdownRenderer:
154
157
  if self._live is not None:
155
158
  self.end()
156
159
  self._buffer = initial_markdown or ""
160
+ # NOTE: Use transient=True so the live widget disappears on exit,
161
+ # giving a clean transition to the final printed result.
157
162
  live = Live(
158
163
  Markdown(self._buffer),
159
164
  console=self.console,
160
- vertical_overflow="visible",
165
+ transient=True,
161
166
  refresh_per_second=12,
162
167
  )
163
168
  self._live = live
@@ -251,6 +256,15 @@ class DisplayManager:
251
256
  """Display error message."""
252
257
  self.console.print(f"\n[bold red]Error:[/bold red] {error_message}")
253
258
 
259
+ def show_sql_error(self, error_message: str, suggestions: list[str] | None = None):
260
+ """Display SQL-specific error with optional suggestions."""
261
+ self.show_newline()
262
+ self.console.print(f"[bold red]SQL error:[/bold red] {error_message}")
263
+ if suggestions:
264
+ self.console.print("[yellow]Hints:[/yellow]")
265
+ for suggestion in suggestions:
266
+ self.console.print(f" • {suggestion}")
267
+
254
268
  def show_processing(self, message: str):
255
269
  """Display processing message."""
256
270
  self.console.print() # Add newline
@@ -114,8 +114,14 @@ class StreamingQueryHandler:
114
114
  pass
115
115
  elif isinstance(content, dict):
116
116
  data = content
117
- if isinstance(data, dict) and data.get("success") and data.get("results"):
118
- self.display.show_query_results(data["results"]) # type: ignore[arg-type]
117
+
118
+ if isinstance(data, dict):
119
+ if data.get("success") and data.get("results"):
120
+ self.display.show_query_results(data["results"]) # type: ignore[arg-type]
121
+ elif "error" in data:
122
+ self.display.show_sql_error(
123
+ data.get("error"), data.get("suggestions")
124
+ )
119
125
  # Add a blank line after tool output to separate from next segment
120
126
  self.display.show_newline()
121
127
  # Show status while agent sends a follow-up request to the model
@@ -131,6 +131,10 @@ def _render_transcript(
131
131
  and data.get("results")
132
132
  ):
133
133
  dm.show_query_results(data["results"]) # type: ignore[arg-type]
134
+ elif isinstance(data, dict) and "error" in data:
135
+ dm.show_sql_error(
136
+ data.get("error"), data.get("suggestions")
137
+ )
134
138
  else:
135
139
  console.print(
136
140
  Panel.fit(
@@ -1910,7 +1910,7 @@ wheels = [
1910
1910
 
1911
1911
  [[package]]
1912
1912
  name = "sqlsaber"
1913
- version = "0.22.0"
1913
+ version = "0.23.0"
1914
1914
  source = { editable = "." }
1915
1915
  dependencies = [
1916
1916
  { name = "aiomysql" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes