sqlsaber 0.5.0__py3-none-any.whl → 0.6.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.
- sqlsaber/agents/anthropic.py +55 -17
- sqlsaber/agents/base.py +13 -3
- sqlsaber/cli/display.py +11 -3
- sqlsaber/cli/interactive.py +80 -7
- sqlsaber/cli/streaming.py +21 -2
- {sqlsaber-0.5.0.dist-info → sqlsaber-0.6.0.dist-info}/METADATA +1 -1
- {sqlsaber-0.5.0.dist-info → sqlsaber-0.6.0.dist-info}/RECORD +10 -10
- {sqlsaber-0.5.0.dist-info → sqlsaber-0.6.0.dist-info}/WHEEL +0 -0
- {sqlsaber-0.5.0.dist-info → sqlsaber-0.6.0.dist-info}/entry_points.txt +0 -0
- {sqlsaber-0.5.0.dist-info → sqlsaber-0.6.0.dist-info}/licenses/LICENSE +0 -0
sqlsaber/agents/anthropic.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Anthropic-specific SQL agent implementation."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import json
|
|
4
|
-
from typing import Any, AsyncIterator, Dict, List
|
|
5
|
+
from typing import Any, AsyncIterator, Dict, List
|
|
5
6
|
|
|
6
7
|
from anthropic import AsyncAnthropic
|
|
7
8
|
|
|
@@ -21,7 +22,7 @@ class AnthropicSQLAgent(BaseSQLAgent):
|
|
|
21
22
|
"""SQL Agent using Anthropic SDK directly."""
|
|
22
23
|
|
|
23
24
|
def __init__(
|
|
24
|
-
self, db_connection: BaseDatabaseConnection, database_name:
|
|
25
|
+
self, db_connection: BaseDatabaseConnection, database_name: str | None = None
|
|
25
26
|
):
|
|
26
27
|
super().__init__(db_connection)
|
|
27
28
|
|
|
@@ -164,7 +165,7 @@ Guidelines:
|
|
|
164
165
|
|
|
165
166
|
return base_prompt
|
|
166
167
|
|
|
167
|
-
def add_memory(self, content: str) ->
|
|
168
|
+
def add_memory(self, content: str) -> str | None:
|
|
168
169
|
"""Add a memory for the current database."""
|
|
169
170
|
if not self.database_name:
|
|
170
171
|
return None
|
|
@@ -174,7 +175,7 @@ Guidelines:
|
|
|
174
175
|
self.system_prompt = self._build_system_prompt()
|
|
175
176
|
return memory.id
|
|
176
177
|
|
|
177
|
-
async def execute_sql(self, query: str, limit:
|
|
178
|
+
async def execute_sql(self, query: str, limit: int | None = None) -> str:
|
|
178
179
|
"""Execute a SQL query against the database with streaming support."""
|
|
179
180
|
# Call parent implementation for core functionality
|
|
180
181
|
result = await super().execute_sql(query, limit)
|
|
@@ -203,10 +204,18 @@ Guidelines:
|
|
|
203
204
|
return await super().process_tool_call(tool_name, tool_input)
|
|
204
205
|
|
|
205
206
|
async def _process_stream_events(
|
|
206
|
-
self,
|
|
207
|
+
self,
|
|
208
|
+
stream,
|
|
209
|
+
content_blocks: List[Dict],
|
|
210
|
+
tool_use_blocks: List[Dict],
|
|
211
|
+
cancellation_token: asyncio.Event | None = None,
|
|
207
212
|
) -> AsyncIterator[StreamEvent]:
|
|
208
213
|
"""Process stream events and yield appropriate StreamEvents."""
|
|
209
214
|
async for event in stream:
|
|
215
|
+
# Only check cancellation if token is provided
|
|
216
|
+
if cancellation_token is not None and cancellation_token.is_set():
|
|
217
|
+
return
|
|
218
|
+
|
|
210
219
|
if event.type == "content_block_start":
|
|
211
220
|
if hasattr(event.content_block, "type"):
|
|
212
221
|
if event.content_block.type == "tool_use":
|
|
@@ -253,11 +262,17 @@ Guidelines:
|
|
|
253
262
|
return "stop"
|
|
254
263
|
|
|
255
264
|
async def _process_tool_results(
|
|
256
|
-
self,
|
|
265
|
+
self,
|
|
266
|
+
response: StreamingResponse,
|
|
267
|
+
cancellation_token: asyncio.Event | None = None,
|
|
257
268
|
) -> AsyncIterator[StreamEvent]:
|
|
258
269
|
"""Process tool results and yield appropriate events."""
|
|
259
270
|
tool_results = []
|
|
260
271
|
for block in response.content:
|
|
272
|
+
# Only check cancellation if token is provided
|
|
273
|
+
if cancellation_token is not None and cancellation_token.is_set():
|
|
274
|
+
return
|
|
275
|
+
|
|
261
276
|
if block.get("type") == "tool_use":
|
|
262
277
|
yield StreamEvent(
|
|
263
278
|
"tool_use",
|
|
@@ -304,7 +319,10 @@ Guidelines:
|
|
|
304
319
|
yield StreamEvent("tool_result_data", tool_results)
|
|
305
320
|
|
|
306
321
|
async def query_stream(
|
|
307
|
-
self,
|
|
322
|
+
self,
|
|
323
|
+
user_query: str,
|
|
324
|
+
use_history: bool = True,
|
|
325
|
+
cancellation_token: asyncio.Event | None = None,
|
|
308
326
|
) -> AsyncIterator[StreamEvent]:
|
|
309
327
|
"""Process a user query and stream responses."""
|
|
310
328
|
# Initialize for tracking state
|
|
@@ -322,7 +340,11 @@ Guidelines:
|
|
|
322
340
|
try:
|
|
323
341
|
# Create initial stream and get response
|
|
324
342
|
response = None
|
|
325
|
-
async for event in self._create_and_process_stream(
|
|
343
|
+
async for event in self._create_and_process_stream(
|
|
344
|
+
messages, cancellation_token
|
|
345
|
+
):
|
|
346
|
+
if cancellation_token is not None and cancellation_token.is_set():
|
|
347
|
+
return
|
|
326
348
|
if event.type == "response_ready":
|
|
327
349
|
response = event.data
|
|
328
350
|
else:
|
|
@@ -332,14 +354,21 @@ Guidelines:
|
|
|
332
354
|
|
|
333
355
|
# Process tool calls if needed
|
|
334
356
|
while response is not None and response.stop_reason == "tool_use":
|
|
357
|
+
# Check for cancellation at the start of tool cycle
|
|
358
|
+
if cancellation_token is not None and cancellation_token.is_set():
|
|
359
|
+
return
|
|
360
|
+
|
|
335
361
|
# Add assistant's response to conversation
|
|
336
362
|
collected_content.append(
|
|
337
363
|
{"role": "assistant", "content": response.content}
|
|
338
364
|
)
|
|
339
365
|
|
|
340
|
-
# Process tool results
|
|
366
|
+
# Process tool results - DO NOT check cancellation during tool execution
|
|
367
|
+
# as this would break the tool_use -> tool_result API contract
|
|
341
368
|
tool_results = []
|
|
342
|
-
async for event in self._process_tool_results(
|
|
369
|
+
async for event in self._process_tool_results(
|
|
370
|
+
response, None
|
|
371
|
+
): # Pass None to disable cancellation checks
|
|
343
372
|
if event.type == "tool_result_data":
|
|
344
373
|
tool_results = event.data
|
|
345
374
|
else:
|
|
@@ -347,6 +376,12 @@ Guidelines:
|
|
|
347
376
|
|
|
348
377
|
# Continue conversation with tool results
|
|
349
378
|
collected_content.append({"role": "user", "content": tool_results})
|
|
379
|
+
if use_history:
|
|
380
|
+
self.conversation_history.extend(collected_content)
|
|
381
|
+
|
|
382
|
+
# Check for cancellation AFTER tool results are complete
|
|
383
|
+
if cancellation_token is not None and cancellation_token.is_set():
|
|
384
|
+
return
|
|
350
385
|
|
|
351
386
|
# Signal that we're processing the tool results
|
|
352
387
|
yield StreamEvent("processing", "Analyzing results...")
|
|
@@ -354,8 +389,10 @@ Guidelines:
|
|
|
354
389
|
# Get next response
|
|
355
390
|
response = None
|
|
356
391
|
async for event in self._create_and_process_stream(
|
|
357
|
-
messages + collected_content
|
|
392
|
+
messages + collected_content, cancellation_token
|
|
358
393
|
):
|
|
394
|
+
if cancellation_token is not None and cancellation_token.is_set():
|
|
395
|
+
return
|
|
359
396
|
if event.type == "response_ready":
|
|
360
397
|
response = event.data
|
|
361
398
|
else:
|
|
@@ -363,21 +400,19 @@ Guidelines:
|
|
|
363
400
|
|
|
364
401
|
# Update conversation history if using history
|
|
365
402
|
if use_history:
|
|
366
|
-
self.conversation_history.append(
|
|
367
|
-
{"role": "user", "content": user_query}
|
|
368
|
-
)
|
|
369
|
-
self.conversation_history.extend(collected_content)
|
|
370
403
|
# Add final assistant response
|
|
371
404
|
if response is not None:
|
|
372
405
|
self.conversation_history.append(
|
|
373
406
|
{"role": "assistant", "content": response.content}
|
|
374
407
|
)
|
|
375
408
|
|
|
409
|
+
except asyncio.CancelledError:
|
|
410
|
+
return
|
|
376
411
|
except Exception as e:
|
|
377
412
|
yield StreamEvent("error", str(e))
|
|
378
413
|
|
|
379
414
|
async def _create_and_process_stream(
|
|
380
|
-
self, messages: List[Dict]
|
|
415
|
+
self, messages: List[Dict], cancellation_token: asyncio.Event | None = None
|
|
381
416
|
) -> AsyncIterator[StreamEvent]:
|
|
382
417
|
"""Create a stream and yield events while building response."""
|
|
383
418
|
stream = await self.client.messages.create(
|
|
@@ -393,8 +428,11 @@ Guidelines:
|
|
|
393
428
|
tool_use_blocks = []
|
|
394
429
|
|
|
395
430
|
async for event in self._process_stream_events(
|
|
396
|
-
stream, content_blocks, tool_use_blocks
|
|
431
|
+
stream, content_blocks, tool_use_blocks, cancellation_token
|
|
397
432
|
):
|
|
433
|
+
# Only check cancellation if token is provided
|
|
434
|
+
if cancellation_token is not None and cancellation_token.is_set():
|
|
435
|
+
return
|
|
398
436
|
yield event
|
|
399
437
|
|
|
400
438
|
# Finalize tool blocks and create response
|
sqlsaber/agents/base.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Abstract base class for SQL agents."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import json
|
|
4
5
|
from abc import ABC, abstractmethod
|
|
5
6
|
from typing import Any, AsyncIterator, Dict, List, Optional
|
|
@@ -27,9 +28,18 @@ class BaseSQLAgent(ABC):
|
|
|
27
28
|
|
|
28
29
|
@abstractmethod
|
|
29
30
|
async def query_stream(
|
|
30
|
-
self,
|
|
31
|
+
self,
|
|
32
|
+
user_query: str,
|
|
33
|
+
use_history: bool = True,
|
|
34
|
+
cancellation_token: asyncio.Event | None = None,
|
|
31
35
|
) -> AsyncIterator[StreamEvent]:
|
|
32
|
-
"""Process a user query and stream responses.
|
|
36
|
+
"""Process a user query and stream responses.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
user_query: The user's query to process
|
|
40
|
+
use_history: Whether to include conversation history
|
|
41
|
+
cancellation_token: Optional event to signal cancellation
|
|
42
|
+
"""
|
|
33
43
|
pass
|
|
34
44
|
|
|
35
45
|
def clear_history(self):
|
|
@@ -86,7 +96,7 @@ class BaseSQLAgent(ABC):
|
|
|
86
96
|
except Exception as e:
|
|
87
97
|
return json.dumps({"error": f"Error listing tables: {str(e)}"})
|
|
88
98
|
|
|
89
|
-
async def execute_sql(self, query: str, limit: Optional[int] =
|
|
99
|
+
async def execute_sql(self, query: str, limit: Optional[int] = None) -> str:
|
|
90
100
|
"""Execute a SQL query against the database."""
|
|
91
101
|
try:
|
|
92
102
|
# Security check - only allow SELECT queries unless write is enabled
|
sqlsaber/cli/display.py
CHANGED
|
@@ -62,12 +62,20 @@ class DisplayManager:
|
|
|
62
62
|
)
|
|
63
63
|
|
|
64
64
|
# Create table with columns from first result
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
all_columns = list(results[0].keys())
|
|
66
|
+
display_columns = all_columns[:15] # Limit to first 15 columns
|
|
67
|
+
|
|
68
|
+
# Show warning if columns were truncated
|
|
69
|
+
if len(all_columns) > 15:
|
|
70
|
+
self.console.print(
|
|
71
|
+
f"[yellow]Note: Showing first 15 of {len(all_columns)} columns[/yellow]"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
table = self._create_table(display_columns)
|
|
67
75
|
|
|
68
76
|
# Add rows (show first 20 rows)
|
|
69
77
|
for row in results[:20]:
|
|
70
|
-
table.add_row(*[str(row[key]) for key in
|
|
78
|
+
table.add_row(*[str(row[key]) for key in display_columns])
|
|
71
79
|
|
|
72
80
|
self.console.print(table)
|
|
73
81
|
|
sqlsaber/cli/interactive.py
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"""Interactive mode handling for the CLI."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
3
6
|
import questionary
|
|
7
|
+
from prompt_toolkit.completion import Completer, Completion
|
|
4
8
|
from rich.console import Console
|
|
5
9
|
from rich.panel import Panel
|
|
6
10
|
|
|
@@ -9,6 +13,34 @@ from sqlsaber.cli.display import DisplayManager
|
|
|
9
13
|
from sqlsaber.cli.streaming import StreamingQueryHandler
|
|
10
14
|
|
|
11
15
|
|
|
16
|
+
class SlashCommandCompleter(Completer):
|
|
17
|
+
"""Custom completer for slash commands."""
|
|
18
|
+
|
|
19
|
+
def get_completions(self, document, complete_event):
|
|
20
|
+
"""Get completions for slash commands."""
|
|
21
|
+
# Only provide completions if the line starts with "/"
|
|
22
|
+
text = document.text
|
|
23
|
+
if text.startswith("/"):
|
|
24
|
+
# Get the partial command after the slash
|
|
25
|
+
partial_cmd = text[1:]
|
|
26
|
+
|
|
27
|
+
# Define available commands with descriptions
|
|
28
|
+
commands = [
|
|
29
|
+
("clear", "Clear conversation history"),
|
|
30
|
+
("exit", "Exit the interactive session"),
|
|
31
|
+
("quit", "Exit the interactive session"),
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
# Yield completions that match the partial command
|
|
35
|
+
for cmd, description in commands:
|
|
36
|
+
if cmd.startswith(partial_cmd):
|
|
37
|
+
yield Completion(
|
|
38
|
+
cmd,
|
|
39
|
+
start_position=-len(partial_cmd),
|
|
40
|
+
display_meta=description,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
12
44
|
class InteractiveSession:
|
|
13
45
|
"""Manages interactive CLI sessions."""
|
|
14
46
|
|
|
@@ -17,6 +49,8 @@ class InteractiveSession:
|
|
|
17
49
|
self.agent = agent
|
|
18
50
|
self.display = DisplayManager(console)
|
|
19
51
|
self.streaming_handler = StreamingQueryHandler(console)
|
|
52
|
+
self.current_task: Optional[asyncio.Task] = None
|
|
53
|
+
self.cancellation_token: Optional[asyncio.Event] = None
|
|
20
54
|
|
|
21
55
|
def show_welcome_message(self):
|
|
22
56
|
"""Display welcome message for interactive mode."""
|
|
@@ -28,7 +62,7 @@ class InteractiveSession:
|
|
|
28
62
|
Panel.fit(
|
|
29
63
|
"[bold green]SQLSaber - Use the agent Luke![/bold green]\n\n"
|
|
30
64
|
"[bold]Your agentic SQL assistant.[/bold]\n\n\n"
|
|
31
|
-
"[dim]Use 'clear' to reset conversation, 'exit' or 'quit' to leave.[/dim]\n\n"
|
|
65
|
+
"[dim]Use '/clear' to reset conversation, '/exit' or '/quit' to leave.[/dim]\n\n"
|
|
32
66
|
"[dim]Start a message with '#' to add something to agent's memory for this database.[/dim]",
|
|
33
67
|
border_style="green",
|
|
34
68
|
)
|
|
@@ -38,8 +72,31 @@ class InteractiveSession:
|
|
|
38
72
|
)
|
|
39
73
|
self.console.print(
|
|
40
74
|
"[dim]Press Esc-Enter or Meta-Enter to submit your query.[/dim]\n"
|
|
75
|
+
"[dim]Press Ctrl+C during query execution to interrupt and return to prompt.[/dim]\n"
|
|
41
76
|
)
|
|
42
77
|
|
|
78
|
+
async def _execute_query_with_cancellation(self, user_query: str):
|
|
79
|
+
"""Execute a query with cancellation support."""
|
|
80
|
+
# Create cancellation token
|
|
81
|
+
self.cancellation_token = asyncio.Event()
|
|
82
|
+
|
|
83
|
+
# Create the query task
|
|
84
|
+
query_task = asyncio.create_task(
|
|
85
|
+
self.streaming_handler.execute_streaming_query(
|
|
86
|
+
user_query, self.agent, self.cancellation_token
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
self.current_task = query_task
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
# Simply await the query task
|
|
93
|
+
# Ctrl+C will be handled by the KeyboardInterrupt exception in run()
|
|
94
|
+
await query_task
|
|
95
|
+
|
|
96
|
+
finally:
|
|
97
|
+
self.current_task = None
|
|
98
|
+
self.cancellation_token = None
|
|
99
|
+
|
|
43
100
|
async def run(self):
|
|
44
101
|
"""Run the interactive session loop."""
|
|
45
102
|
self.show_welcome_message()
|
|
@@ -51,12 +108,16 @@ class InteractiveSession:
|
|
|
51
108
|
qmark="",
|
|
52
109
|
multiline=True,
|
|
53
110
|
instruction="",
|
|
111
|
+
completer=SlashCommandCompleter(),
|
|
54
112
|
).ask_async()
|
|
55
113
|
|
|
56
|
-
if user_query
|
|
114
|
+
if not user_query:
|
|
115
|
+
continue
|
|
116
|
+
|
|
117
|
+
if user_query in ["/exit", "/quit"]:
|
|
57
118
|
break
|
|
58
119
|
|
|
59
|
-
if user_query
|
|
120
|
+
if user_query == "/clear":
|
|
60
121
|
self.agent.clear_history()
|
|
61
122
|
self.console.print("[green]Conversation history cleared.[/green]\n")
|
|
62
123
|
continue
|
|
@@ -85,12 +146,24 @@ class InteractiveSession:
|
|
|
85
146
|
)
|
|
86
147
|
continue
|
|
87
148
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
)
|
|
149
|
+
# Execute query with cancellation support
|
|
150
|
+
await self._execute_query_with_cancellation(user_query)
|
|
91
151
|
self.display.show_newline() # Empty line for readability
|
|
92
152
|
|
|
93
153
|
except KeyboardInterrupt:
|
|
94
|
-
|
|
154
|
+
# Handle Ctrl+C - cancel current task if running
|
|
155
|
+
if self.current_task and not self.current_task.done():
|
|
156
|
+
if self.cancellation_token is not None:
|
|
157
|
+
self.cancellation_token.set()
|
|
158
|
+
self.current_task.cancel()
|
|
159
|
+
try:
|
|
160
|
+
await self.current_task
|
|
161
|
+
except asyncio.CancelledError:
|
|
162
|
+
pass
|
|
163
|
+
self.console.print("\n[yellow]Query interrupted[/yellow]")
|
|
164
|
+
else:
|
|
165
|
+
self.console.print(
|
|
166
|
+
"\n[yellow]Use '/exit' or '/quit' to leave.[/yellow]"
|
|
167
|
+
)
|
|
95
168
|
except Exception as e:
|
|
96
169
|
self.console.print(f"[bold red]Error:[/bold red] {str(e)}")
|
sqlsaber/cli/streaming.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Streaming query handling for the CLI."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
3
5
|
from rich.console import Console
|
|
4
6
|
|
|
5
7
|
from sqlsaber.agents.base import BaseSQLAgent
|
|
@@ -13,7 +15,12 @@ class StreamingQueryHandler:
|
|
|
13
15
|
self.console = console
|
|
14
16
|
self.display = DisplayManager(console)
|
|
15
17
|
|
|
16
|
-
async def execute_streaming_query(
|
|
18
|
+
async def execute_streaming_query(
|
|
19
|
+
self,
|
|
20
|
+
user_query: str,
|
|
21
|
+
agent: BaseSQLAgent,
|
|
22
|
+
cancellation_token: asyncio.Event | None = None,
|
|
23
|
+
):
|
|
17
24
|
"""Execute a query with streaming display."""
|
|
18
25
|
|
|
19
26
|
has_content = False
|
|
@@ -24,7 +31,12 @@ class StreamingQueryHandler:
|
|
|
24
31
|
status.start()
|
|
25
32
|
|
|
26
33
|
try:
|
|
27
|
-
async for event in agent.query_stream(
|
|
34
|
+
async for event in agent.query_stream(
|
|
35
|
+
user_query, cancellation_token=cancellation_token
|
|
36
|
+
):
|
|
37
|
+
if cancellation_token is not None and cancellation_token.is_set():
|
|
38
|
+
break
|
|
39
|
+
|
|
28
40
|
if event.type == "tool_use":
|
|
29
41
|
# Stop any ongoing status, but don't mark has_content yet
|
|
30
42
|
self._stop_status(status)
|
|
@@ -83,6 +95,13 @@ class StreamingQueryHandler:
|
|
|
83
95
|
has_content = True
|
|
84
96
|
self.display.show_error(event.data)
|
|
85
97
|
|
|
98
|
+
except asyncio.CancelledError:
|
|
99
|
+
# Handle cancellation gracefully
|
|
100
|
+
self._stop_status(status)
|
|
101
|
+
if explanation_started:
|
|
102
|
+
self.display.show_newline()
|
|
103
|
+
self.console.print("[yellow]Query interrupted[/yellow]")
|
|
104
|
+
return
|
|
86
105
|
finally:
|
|
87
106
|
# Make sure status is stopped
|
|
88
107
|
self._stop_status(status)
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
sqlsaber/__init__.py,sha256=QCFi8xTVMohelfi7zOV1-6oLCcGoiXoOcKQY-HNBCk8,66
|
|
2
2
|
sqlsaber/__main__.py,sha256=RIHxWeWh2QvLfah-2OkhI5IJxojWfy4fXpMnVEJYvxw,78
|
|
3
3
|
sqlsaber/agents/__init__.py,sha256=LWeSeEUE4BhkyAYFF3TE-fx8TtLud3oyEtyB8ojFJgo,167
|
|
4
|
-
sqlsaber/agents/anthropic.py,sha256=
|
|
5
|
-
sqlsaber/agents/base.py,sha256=
|
|
4
|
+
sqlsaber/agents/anthropic.py,sha256=FLVET2HvFmsEuFln9Hu4SaBs-Tnk-GestOgnDnUp3ps,17885
|
|
5
|
+
sqlsaber/agents/base.py,sha256=DAnezHl5RLYoef8XQ-n3KA9PowdrMbQrkjdGKPPnFsI,10570
|
|
6
6
|
sqlsaber/agents/mcp.py,sha256=FKtXgDrPZ2-xqUYCw2baI5JzrWekXaC5fjkYW1_Mg50,827
|
|
7
7
|
sqlsaber/agents/streaming.py,sha256=_EO390-FHUrL1fRCNfibtE9QuJz3LGQygbwG3CB2ViY,533
|
|
8
8
|
sqlsaber/cli/__init__.py,sha256=qVSLVJLLJYzoC6aj6y9MFrzZvAwc4_OgxU9DlkQnZ4M,86
|
|
9
9
|
sqlsaber/cli/commands.py,sha256=Dw24W0jij-8t1lpk99C4PBTgzFSag6vU-FZcjAYGG54,5074
|
|
10
10
|
sqlsaber/cli/database.py,sha256=DUfyvNBDp47oFM_VAC_hXHQy_qyE7JbXtowflJpwwH8,12643
|
|
11
|
-
sqlsaber/cli/display.py,sha256=
|
|
12
|
-
sqlsaber/cli/interactive.py,sha256=
|
|
11
|
+
sqlsaber/cli/display.py,sha256=lZW7BI2LusU5lJhov7u9kKWwfsqcXGSfjrw-kNO3FQA,9200
|
|
12
|
+
sqlsaber/cli/interactive.py,sha256=RNEWyCM1gLLUsXaXaCFzXu0PFDVuAK8NBOOYYFTHTUE,6716
|
|
13
13
|
sqlsaber/cli/memory.py,sha256=LW4ZF2V6Gw6hviUFGZ4ym9ostFCwucgBTIMZ3EANO-I,7671
|
|
14
14
|
sqlsaber/cli/models.py,sha256=3IcXeeU15IQvemSv-V-RQzVytJ3wuQ4YmWk89nTDcSE,7813
|
|
15
|
-
sqlsaber/cli/streaming.py,sha256=
|
|
15
|
+
sqlsaber/cli/streaming.py,sha256=2vLCYqqziQTO52erfgvnEk_hM3BoDM1TMBAXgT7KKfo,4548
|
|
16
16
|
sqlsaber/config/__init__.py,sha256=olwC45k8Nc61yK0WmPUk7XHdbsZH9HuUAbwnmKe3IgA,100
|
|
17
17
|
sqlsaber/config/api_keys.py,sha256=kLdoExF_My9ojmdhO5Ca7-ZeowsO0v1GVa_QT5jjUPo,3658
|
|
18
18
|
sqlsaber/config/database.py,sha256=vKFOxPjVakjQhj1uoLcfzhS9ZFr6Z2F5b4MmYALQZoA,11421
|
|
@@ -28,8 +28,8 @@ sqlsaber/memory/storage.py,sha256=DvZBsSPaAfk_DqrNEn86uMD-TQsWUI6rQLfNw6PSCB8,57
|
|
|
28
28
|
sqlsaber/models/__init__.py,sha256=RJ7p3WtuSwwpFQ1Iw4_DHV2zzCtHqIzsjJzxv8kUjUE,287
|
|
29
29
|
sqlsaber/models/events.py,sha256=q2FackB60J9-7vegYIjzElLwKebIh7nxnV5AFoZc67c,752
|
|
30
30
|
sqlsaber/models/types.py,sha256=3U_30n91EB3IglBTHipwiW4MqmmaA2qfshfraMZyPps,896
|
|
31
|
-
sqlsaber-0.
|
|
32
|
-
sqlsaber-0.
|
|
33
|
-
sqlsaber-0.
|
|
34
|
-
sqlsaber-0.
|
|
35
|
-
sqlsaber-0.
|
|
31
|
+
sqlsaber-0.6.0.dist-info/METADATA,sha256=Mvou6xXxA8T2Cfwq4y1bd0DYW46zBs4Qw7oXWNfihfE,5969
|
|
32
|
+
sqlsaber-0.6.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
33
|
+
sqlsaber-0.6.0.dist-info/entry_points.txt,sha256=jmFo96Ylm0zIKXJBwhv_P5wQ7SXP9qdaBcnTp8iCEe8,195
|
|
34
|
+
sqlsaber-0.6.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
35
|
+
sqlsaber-0.6.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|