dbagent-cli 0.7.3__tar.gz → 0.8.1__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.
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/PKG-INFO +1 -1
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/__init__.py +1 -1
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/cli.py +168 -70
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/ui/chat_screen.py +56 -32
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent_cli.egg-info/PKG-INFO +1 -1
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/pyproject.toml +1 -1
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/README.md +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/agent/confidence.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/agent/conversation.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/agent/doctor.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/agent/error_classifier.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/agent/generator.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/agent/pipeline.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/agent/recommender.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/agent/validator.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/config.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/connectors/base.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/connectors/factory.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/connectors/mongo.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/connectors/relational.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/execution/__init__.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/execution/cache.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/execution/changeset.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/execution/cost.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/execution/recovery.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/execution/sandbox.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/execution/streaming.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/llm/anthropic_provider.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/llm/auto_setup.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/llm/base.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/llm/deepseek_provider.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/llm/factory.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/llm/gemini_provider.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/llm/groq_provider.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/llm/mistral_provider.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/llm/mock_provider.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/llm/ollama_provider.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/llm/openai_provider.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/llm/openrouter_provider.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/schema/cache.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/schema/formatter.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/schema/graph.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/schema/models.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/schema/retriever.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/schema/selector.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/security/__init__.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/security/audit.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/security/environment.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/security/masking.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/security/modes.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/security/rbac.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/semantic/__init__.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/semantic/dictionary.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/semantic/templates.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/sql/__init__.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/sql/optimizer.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/sql/validator.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/ui/console.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/ui/grid_window.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/ui/viewer.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent/ui/visualizer.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent_cli.egg-info/SOURCES.txt +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent_cli.egg-info/dependency_links.txt +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent_cli.egg-info/entry_points.txt +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent_cli.egg-info/requires.txt +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/dbagent_cli.egg-info/top_level.txt +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/setup.cfg +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/tests/test_cache.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/tests/test_cli.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/tests/test_connectors.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/tests/test_direct_sql.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/tests/test_doctor_and_viz.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/tests/test_generator.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/tests/test_graph.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/tests/test_providers.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/tests/test_recovery.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/tests/test_safety.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/tests/test_sandbox.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/tests/test_schema.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/tests/test_security.py +0 -0
- {dbagent_cli-0.7.3 → dbagent_cli-0.8.1}/tests/test_semantic.py +0 -0
|
@@ -9,6 +9,13 @@ import json
|
|
|
9
9
|
import time
|
|
10
10
|
import threading
|
|
11
11
|
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
if sys.platform == "win32":
|
|
14
|
+
try:
|
|
15
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
16
|
+
sys.stderr.reconfigure(encoding="utf-8")
|
|
17
|
+
except Exception:
|
|
18
|
+
pass
|
|
12
19
|
from typing import Optional, List, Dict, Any
|
|
13
20
|
import typer
|
|
14
21
|
from rich.prompt import Prompt, Confirm
|
|
@@ -432,7 +439,6 @@ def chat_command(
|
|
|
432
439
|
mode: str = typer.Option("CONFIRM", "--mode", help="Operation mode (SAFE, READ_ONLY, CONFIRM, SANDBOX, ADMIN)"),
|
|
433
440
|
):
|
|
434
441
|
"""Interactive multi-turn AI database copilot session."""
|
|
435
|
-
print_banner()
|
|
436
442
|
db_url = resolve_db_url(db)
|
|
437
443
|
|
|
438
444
|
with DynamicSpinner(DynamicSpinner.CONNECT_MESSAGES):
|
|
@@ -508,17 +514,18 @@ def chat_command(
|
|
|
508
514
|
def _get_bottom_toolbar():
|
|
509
515
|
curr_model = model or getattr(llm, "default_model", "default")
|
|
510
516
|
return HTML(
|
|
511
|
-
f" <b><ansigreen
|
|
512
|
-
f"<b><ansicyan>🤖
|
|
517
|
+
f" <b><ansigreen>● Connected:</ansigreen></b> <ansibrightwhite>{db_name}</ansibrightwhite> <ansidarkgray>({dialect} • {len(table_names)} tables)</ansidarkgray> │ "
|
|
518
|
+
f"<b><ansicyan>🤖 {curr_model}</ansicyan></b> <ansidarkgray>({llm.name})</ansidarkgray> │ "
|
|
513
519
|
f"<b><ansiyellow>🛡️ {current_mode.value}</ansiyellow></b> │ "
|
|
514
|
-
f"<ansidarkgray
|
|
520
|
+
f"<ansidarkgray><b>:help</b> for commands · <b>:models</b> to switch</ansidarkgray> "
|
|
515
521
|
)
|
|
516
522
|
|
|
517
523
|
while True:
|
|
518
524
|
try:
|
|
519
525
|
user_input = session.prompt(
|
|
520
|
-
HTML("<
|
|
526
|
+
HTML("<ansired><b>❯</b></ansired> "),
|
|
521
527
|
bottom_toolbar=_get_bottom_toolbar,
|
|
528
|
+
placeholder=HTML("<ansidarkgray><i>Ask in plain English or paste SQL (e.g. \"list active users\")...</i></ansidarkgray>"),
|
|
522
529
|
).strip()
|
|
523
530
|
if not user_input:
|
|
524
531
|
continue
|
|
@@ -534,31 +541,30 @@ def chat_command(
|
|
|
534
541
|
break
|
|
535
542
|
|
|
536
543
|
if cmd_name in [":help", ":h", ":?"]:
|
|
537
|
-
console.print("\n[bold cyan]
|
|
538
|
-
console.print("
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
console.print(
|
|
559
|
-
console.print("
|
|
560
|
-
console.print("
|
|
561
|
-
console.print(" [bold]:exit[/bold] -> Exit chat session\n")
|
|
544
|
+
console.print("\n[bold cyan]──────────────────────────────────────────────────────────────────────────────[/bold cyan]")
|
|
545
|
+
console.print("[bold cyan]⌨️ DB-Agent Interactive Commands:[/bold cyan]\n")
|
|
546
|
+
cmd_table = Table(box=None, padding=(0, 2), show_header=False)
|
|
547
|
+
cmd_table.add_column("Command", style="bold cyan", width=22)
|
|
548
|
+
cmd_table.add_column("Description", style="dim")
|
|
549
|
+
cmd_table.add_row(":models, :model", "Switch active AI model (Claude 3.7, GPT-4o, DeepSeek, Gemini...)")
|
|
550
|
+
cmd_table.add_row(":provider <name>", "Switch AI provider (claude, openai, deepseek, mistral, gemini)")
|
|
551
|
+
cmd_table.add_row(":tables, :dt", "List all tables & schemas in this database")
|
|
552
|
+
cmd_table.add_row(":table <name>", "Inspect columns, primary keys, data types & sample data")
|
|
553
|
+
cmd_table.add_row(":fk <name>", "View foreign key relationships & dependency graph")
|
|
554
|
+
cmd_table.add_row(":path <t1> <t2>", "Discover shortest SQL join path between two tables")
|
|
555
|
+
cmd_table.add_row(":doctor", "Run database health, index, and query performance diagnostics")
|
|
556
|
+
cmd_table.add_row(":mode <name>", "Switch safety mode (SAFE, CONFIRM, ADMIN, READ_ONLY)")
|
|
557
|
+
cmd_table.add_row(":history", "View session query history, execution timing & row counts")
|
|
558
|
+
cmd_table.add_row(":page <n>, :card", "Inspect wide multi-column query results")
|
|
559
|
+
cmd_table.add_row(":window, :web", "Open interactive desktop or browser data grid")
|
|
560
|
+
cmd_table.add_row(":explain", "Explain last generated SQL query with execution plan")
|
|
561
|
+
cmd_table.add_row(":run", "Re-execute the last generated SQL query")
|
|
562
|
+
cmd_table.add_row(":clear", "Clear conversation memory & redraw screen")
|
|
563
|
+
cmd_table.add_row(":export <file>", "Save the last generated SQL to a file")
|
|
564
|
+
cmd_table.add_row(":exit, :q", "Exit DB-Agent session")
|
|
565
|
+
console.print(cmd_table)
|
|
566
|
+
console.print("[dim]──────────────────────────────────────────────────────────────────────────────[/dim]")
|
|
567
|
+
console.print("[dim]Enter to continue · Type any natural language question or direct SQL[/dim]\n")
|
|
562
568
|
continue
|
|
563
569
|
|
|
564
570
|
if cmd_name in [":tables", ":schema", ":dt"]:
|
|
@@ -719,46 +725,46 @@ def chat_command(
|
|
|
719
725
|
|
|
720
726
|
# If no arg passed or :models/:providers called, show rich model selection menu
|
|
721
727
|
if not cmd_arg or cmd_name in [":models", ":providers", ":ai"]:
|
|
722
|
-
console.print(f"\n[bold cyan]
|
|
723
|
-
console.print(f"[bold cyan]
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
728
|
+
console.print(f"\n[bold cyan]──────────────────────────────────────────────────────────────────────────────[/bold cyan]")
|
|
729
|
+
console.print(f"[bold cyan]Available Models for {llm.name}:[/bold cyan]\n")
|
|
730
|
+
model_descriptions = {
|
|
731
|
+
# Gemini
|
|
732
|
+
"gemini-flash-lite-latest": "⚡ Ultra-Fast (~0.8s) · Instant chat & queries",
|
|
733
|
+
"gemini-3.6-flash": "⚡ High Quality (~3.5s) · Strong SQL reasoning & accuracy",
|
|
734
|
+
"gemini-3.7-flash": "🧠 Deep Reasoning (~13s) · Latest flagship model",
|
|
735
|
+
"gemini-3.1-flash-lite": "⚡ Fast Lite (~4.0s) · Low latency",
|
|
736
|
+
"gemini-flash-latest": "⚡ Standard Flash (~7.0s)",
|
|
737
|
+
# Claude
|
|
738
|
+
"claude-3-7-sonnet-20250219": "🧠 Hybrid Reasoning Flagship · Deepest architecture understanding",
|
|
739
|
+
"claude-3-5-sonnet-20241022": "⚡ High Intelligence · Expert SQL engineer & schema specialist",
|
|
740
|
+
"claude-3-5-haiku-20241022": "⚡ Ultra-Fast (~1s) · High speed and cost efficient",
|
|
741
|
+
"claude-3-opus-20240229": "Deep complex schema reasoning",
|
|
742
|
+
# OpenAI
|
|
743
|
+
"gpt-4o": "⚡ Omni Flagship · High speed multimodal & complex SQL reasoning",
|
|
744
|
+
"gpt-4o-mini": "⚡ Ultra-Fast (~1s) · High speed and cost efficient",
|
|
745
|
+
"gpt-4.5-preview": "🧠 Next-Generation Reasoning model",
|
|
746
|
+
"o3-mini": "🧠 Deep STEM & Advanced Coding Reasoning",
|
|
747
|
+
"o1": "🧠 Maximum Reasoning & Math Architecture",
|
|
748
|
+
# DeepSeek
|
|
749
|
+
"deepseek-chat": "⚡ DeepSeek-V3 Ultra-Fast · Strong code & SQL",
|
|
750
|
+
"deepseek-reasoner": "🧠 DeepSeek-R1 Full Chain-of-Thought Reasoning",
|
|
751
|
+
# Mistral
|
|
752
|
+
"codestral-latest": "⚡ Specialized Code & SQL Architect (22B)",
|
|
753
|
+
"mistral-large-latest": "🧠 Flagship Reasoning & Multi-language",
|
|
754
|
+
"mistral-small-latest": "⚡ Fast & lightweight",
|
|
755
|
+
# Groq
|
|
756
|
+
"llama-3.3-70b-versatile": "⚡ Llama 3.3 70B (500+ tokens/sec on Groq LPUs)",
|
|
757
|
+
"llama-3.1-8b-instant": "⚡ Ultra-Fast Llama 3.1 8B (800+ tokens/sec)",
|
|
758
|
+
"qwen-2.5-coder-32b": "⚡ Qwen 2.5 Coder 32B specialized SQL engineer",
|
|
759
|
+
}
|
|
760
|
+
for idx, m_name in enumerate(avail_models, 1):
|
|
761
|
+
marker = "[bold cyan]❯[/bold cyan]" if m_name == curr_model else " "
|
|
762
|
+
active_str = " [bold green]✓ Active[/bold green]" if m_name == curr_model else ""
|
|
763
|
+
desc = model_descriptions.get(m_name, "")
|
|
764
|
+
desc_str = f" · [dim]{desc}[/dim]" if desc else ""
|
|
765
|
+
console.print(f" {marker} [bold cyan]{idx}.[/bold cyan] [bold white]{m_name}[/bold white]{active_str}{desc_str}")
|
|
766
|
+
console.print("\n[dim]──────────────────────────────────────────────────────────────────────────────[/dim]")
|
|
767
|
+
console.print("[dim]Quick Switch: [bold]:model <1-5|name>[/bold] (e.g. [bold]:model 1[/bold] or [bold]:model 3.7[/bold] or [bold]:model 4o[/bold])[/dim]\n")
|
|
762
768
|
continue
|
|
763
769
|
|
|
764
770
|
# Handle numeric shortcut or name shortcut
|
|
@@ -1859,6 +1865,98 @@ def use_command(
|
|
|
1859
1865
|
print_success(f"Default AI provider switched to [bold cyan]{p}[/bold cyan]!")
|
|
1860
1866
|
|
|
1861
1867
|
|
|
1868
|
+
def interactive_entrypoint(
|
|
1869
|
+
db: Optional[str] = None,
|
|
1870
|
+
provider: Optional[str] = None,
|
|
1871
|
+
model: Optional[str] = None,
|
|
1872
|
+
mode: Optional[str] = None,
|
|
1873
|
+
):
|
|
1874
|
+
"""
|
|
1875
|
+
Direct root interactive launcher for DB-Agent.
|
|
1876
|
+
Prompts user to select or configure a database connection and transitions into chat.
|
|
1877
|
+
"""
|
|
1878
|
+
if db:
|
|
1879
|
+
chat_command(db=db, alias=None, provider=provider, model=model, mode=mode or "CONFIRM")
|
|
1880
|
+
return
|
|
1881
|
+
|
|
1882
|
+
saved_profiles = config_mgr.list_profiles()
|
|
1883
|
+
|
|
1884
|
+
console.print("")
|
|
1885
|
+
console.print("[bold cyan]──────────────────────────────────────────────────────────────────────────────[/bold cyan]")
|
|
1886
|
+
console.print("[bold cyan]⚡ DB-AGENT — Universal AI Database Copilot[/bold cyan]\n")
|
|
1887
|
+
console.print(f"[dim white]Working Directory: {os.getcwd()}[/dim white]\n")
|
|
1888
|
+
console.print("[bold white]Select a database connection to launch:[/bold white]\n")
|
|
1889
|
+
|
|
1890
|
+
options = []
|
|
1891
|
+
# If saved connections exist
|
|
1892
|
+
if saved_profiles:
|
|
1893
|
+
for p_name, prof in saved_profiles.items():
|
|
1894
|
+
p_url = prof.get("url", "")
|
|
1895
|
+
dialect = p_url.split("://")[0] if "://" in p_url else "database"
|
|
1896
|
+
options.append((p_name, f"[bold white]{p_name}[/bold white] [dim]({dialect})[/dim]"))
|
|
1897
|
+
|
|
1898
|
+
# Also add quick options
|
|
1899
|
+
options.append(("__new__", "[bold cyan]+ Add a new database connection URL[/bold cyan]"))
|
|
1900
|
+
options.append(("__sqlite__", "[bold white]Connect to local SQLite (:memory:)[/bold white]"))
|
|
1901
|
+
options.append(("__exit__", "[dim]Exit[/dim]"))
|
|
1902
|
+
|
|
1903
|
+
for idx, (opt_id, opt_label) in enumerate(options, 1):
|
|
1904
|
+
marker = "[bold cyan]❯[/bold cyan]" if idx == 1 else " "
|
|
1905
|
+
console.print(f" {marker} [bold cyan]{idx}.[/bold cyan] {opt_label}")
|
|
1906
|
+
|
|
1907
|
+
console.print(f"\n[dim]Enter to confirm · Type number [1-{len(options)}] to select · Ctrl+C to exit[/dim]\n")
|
|
1908
|
+
|
|
1909
|
+
try:
|
|
1910
|
+
raw_choice = Prompt.ask("[bold cyan]Select database[/bold cyan]", default="1")
|
|
1911
|
+
raw_choice = raw_choice.strip()
|
|
1912
|
+
if not raw_choice.isdigit() or int(raw_choice) < 1 or int(raw_choice) > len(options):
|
|
1913
|
+
print_error("Invalid selection.")
|
|
1914
|
+
return
|
|
1915
|
+
|
|
1916
|
+
selected_idx = int(raw_choice) - 1
|
|
1917
|
+
sel_id, _ = options[selected_idx]
|
|
1918
|
+
|
|
1919
|
+
if sel_id == "__exit__":
|
|
1920
|
+
return
|
|
1921
|
+
elif sel_id == "__sqlite__":
|
|
1922
|
+
chat_command(db="sqlite:///:memory:", alias=None, provider=provider, model=model, mode=mode or "CONFIRM")
|
|
1923
|
+
elif sel_id == "__new__":
|
|
1924
|
+
new_url = Prompt.ask("Enter database connection URL (e.g. postgresql://user:pass@host:5432/db)")
|
|
1925
|
+
if not new_url.strip():
|
|
1926
|
+
return
|
|
1927
|
+
new_name = Prompt.ask("Save profile name as (leave empty for temporary)", default="")
|
|
1928
|
+
if new_name.strip():
|
|
1929
|
+
config_mgr.save_profile(name=new_name.strip(), connection_url=new_url.strip(), description=f"Connection {new_name}")
|
|
1930
|
+
print_success(f"Connection profile '[bold cyan]{new_name}[/bold cyan]' saved!")
|
|
1931
|
+
chat_command(db=new_name.strip(), alias=None, provider=provider, model=model, mode=mode or "CONFIRM")
|
|
1932
|
+
else:
|
|
1933
|
+
chat_command(db=new_url.strip(), alias=None, provider=provider, model=model, mode=mode or "CONFIRM")
|
|
1934
|
+
else:
|
|
1935
|
+
# Saved connection name
|
|
1936
|
+
chat_command(db=sel_id, alias=None, provider=provider, model=model, mode=mode or "CONFIRM")
|
|
1937
|
+
except (KeyboardInterrupt, EOFError):
|
|
1938
|
+
console.print("\n[dim]Operation cancelled.[/dim]")
|
|
1939
|
+
return
|
|
1940
|
+
|
|
1941
|
+
|
|
1942
|
+
@app.callback(invoke_without_command=True)
|
|
1943
|
+
def main_callback(
|
|
1944
|
+
ctx: typer.Context,
|
|
1945
|
+
db: Optional[str] = typer.Option(None, "--db", "-d", help="Database connection profile or URL"),
|
|
1946
|
+
provider: Optional[str] = typer.Option(None, "--provider", "-p", help="AI provider name"),
|
|
1947
|
+
model: Optional[str] = typer.Option(None, "--model", "-m", help="AI model name"),
|
|
1948
|
+
mode: Optional[str] = typer.Option("CONFIRM", "--mode", help="Operation mode (SAFE, CONFIRM, ADMIN)"),
|
|
1949
|
+
version: Optional[bool] = typer.Option(None, "--version", "-v", help="Show DB-Agent version"),
|
|
1950
|
+
):
|
|
1951
|
+
"""DB-Agent: Enterprise AI Database Copilot CLI."""
|
|
1952
|
+
if version:
|
|
1953
|
+
console.print(f"DB-Agent version [bold green]{__version__}[/bold green] by {__author__}")
|
|
1954
|
+
raise typer.Exit(0)
|
|
1955
|
+
|
|
1956
|
+
if ctx.invoked_subcommand is None:
|
|
1957
|
+
interactive_entrypoint(db=db, provider=provider, model=model, mode=mode)
|
|
1958
|
+
|
|
1959
|
+
|
|
1862
1960
|
def main():
|
|
1863
1961
|
"""Top-level entry point with graceful Ctrl+C / interrupt handling."""
|
|
1864
1962
|
try:
|
|
@@ -26,6 +26,15 @@ def clear_screen() -> None:
|
|
|
26
26
|
os.system("cls" if os.name == "nt" else "clear")
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
MASCOT_ART = """
|
|
30
|
+
▄ ▄
|
|
31
|
+
██▀█ █▀██
|
|
32
|
+
█████████
|
|
33
|
+
█ █ █ █ █
|
|
34
|
+
█ █
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
|
|
29
38
|
def render_chat_header(
|
|
30
39
|
db_name: str,
|
|
31
40
|
dialect: str,
|
|
@@ -35,33 +44,44 @@ def render_chat_header(
|
|
|
35
44
|
mode: str = "CONFIRM",
|
|
36
45
|
recommendations: Optional[List[str]] = None,
|
|
37
46
|
) -> None:
|
|
38
|
-
"""Render
|
|
39
|
-
model_str =
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
47
|
+
"""Render DB-Agent's split-card welcome header in signature cyan & green theme."""
|
|
48
|
+
model_str = ai_model or "gemini-3.6-flash"
|
|
49
|
+
|
|
50
|
+
# Left Column: Greeting, Mascot, Connected Model & DB Path
|
|
51
|
+
left_grid = Table.grid(padding=(0, 1))
|
|
52
|
+
left_grid.add_column(justify="center", min_width=28)
|
|
53
|
+
left_grid.add_row("[bold white]Welcome to DB-Agent![/bold white]")
|
|
54
|
+
left_grid.add_row(f"[bold cyan]{MASCOT_ART.strip()}[/bold cyan]")
|
|
55
|
+
left_grid.add_row(f"[bold cyan]{model_str}[/bold cyan] [dim]({ai_provider})[/dim]")
|
|
56
|
+
left_grid.add_row(f"[bold green]🟢 Connected:[/bold green] [bold white]{db_name}[/bold white] [dim]({dialect} • {table_count} tables)[/dim]")
|
|
57
|
+
|
|
58
|
+
# Right Column: Quick Tips + Powerful Features
|
|
59
|
+
right_grid = Table.grid(padding=(0, 1))
|
|
60
|
+
right_grid.add_column()
|
|
61
|
+
right_grid.add_row("[bold cyan]💡 Quick Database Tips[/bold cyan]")
|
|
62
|
+
right_grid.add_row("[dim]Ask in plain English or paste direct SQL for 0ms execution[/dim]")
|
|
63
|
+
right_grid.add_row("[dim]Type :help to inspect tables (:tables), relations (:fk), or diagnostics (:doctor)[/dim]")
|
|
64
|
+
right_grid.add_row("[dim]────────────────────────────────────────────────────────────────────────[/dim]")
|
|
65
|
+
right_grid.add_row(f"[bold green]🚀 Features in v{__version__}[/bold green]")
|
|
66
|
+
right_grid.add_row("[dim]Universal AI: Claude 3.7 Sonnet, OpenAI GPT-4o, DeepSeek R1, Mistral, Gemini[/dim]")
|
|
67
|
+
right_grid.add_row("[dim]Zero-Latency Direct SQL Fast-Path with autonomous AI auto-repair on error[/dim]")
|
|
68
|
+
right_grid.add_row("[dim]Multi-turn conversational session memory graph & persistent connection pooling[/dim]")
|
|
69
|
+
|
|
70
|
+
layout_table = Table.grid(expand=True, padding=(0, 2))
|
|
71
|
+
layout_table.add_column(ratio=2)
|
|
72
|
+
layout_table.add_column(ratio=3)
|
|
73
|
+
layout_table.add_row(left_grid, right_grid)
|
|
74
|
+
|
|
75
|
+
header_panel = Panel(
|
|
76
|
+
layout_table,
|
|
77
|
+
title=f"[bold cyan]⚡ DB-AGENT [/bold cyan][bold green]v{__version__}[/bold green]",
|
|
78
|
+
title_align="left",
|
|
61
79
|
border_style="cyan",
|
|
62
|
-
padding=(
|
|
80
|
+
padding=(1, 1),
|
|
63
81
|
)
|
|
64
|
-
|
|
82
|
+
|
|
83
|
+
console.print(header_panel)
|
|
84
|
+
console.print(f"[bold cyan]│[/bold cyan] [bold cyan]⚡ Multi-AI Engine Live![/bold cyan] [dim]Type [bold white]:models[/bold white] to switch AI models · [bold white]:doctor[/bold white] for diagnostics · Mode: [bold yellow]{mode}[/bold yellow][/dim]\n")
|
|
65
85
|
|
|
66
86
|
if recommendations:
|
|
67
87
|
console.print("[dim italic]💡 Try asking:[/dim italic]")
|
|
@@ -180,16 +200,20 @@ def render_assistant_turn(
|
|
|
180
200
|
|
|
181
201
|
|
|
182
202
|
def render_session_history(query_history: List[PipelineResult]) -> None:
|
|
183
|
-
"""Render a clean visual session history table."""
|
|
203
|
+
"""Render a clean visual session history table in DB-Agent cyan/green style."""
|
|
184
204
|
if not query_history:
|
|
185
205
|
console.print("[dim]No query history in this session yet.[/dim]\n")
|
|
186
206
|
return
|
|
187
207
|
|
|
188
|
-
|
|
208
|
+
console.print("")
|
|
209
|
+
console.print("[bold cyan]──────────────────────────────────────────────────────────────────────────────[/bold cyan]")
|
|
210
|
+
console.print("[bold cyan]📜 Session Query History:[/bold cyan]\n")
|
|
211
|
+
|
|
212
|
+
table = Table(box=None, padding=(0, 2), show_header=True, header_style="bold cyan")
|
|
189
213
|
table.add_column("#", style="dim", width=4, justify="right")
|
|
190
|
-
table.add_column("Status", width=
|
|
214
|
+
table.add_column("Status", width=10, justify="center")
|
|
191
215
|
table.add_column("Type", width=8, justify="center")
|
|
192
|
-
table.add_column("Query / Message Preview", style="white", min_width=
|
|
216
|
+
table.add_column("Query / Message Preview", style="white", min_width=32)
|
|
193
217
|
table.add_column("Rows", style="cyan", width=6, justify="right")
|
|
194
218
|
table.add_column("Time", style="green", width=8, justify="right")
|
|
195
219
|
|
|
@@ -211,9 +235,9 @@ def render_session_history(query_history: List[PipelineResult]) -> None:
|
|
|
211
235
|
preview = (r.error or r.sql or "")[:60]
|
|
212
236
|
rows = "0"
|
|
213
237
|
|
|
214
|
-
time_str = f"{r.execution_time_ms / 1000:.
|
|
238
|
+
time_str = f"{r.execution_time_ms / 1000:.2f}s" if r.execution_time_ms > 0 else "-"
|
|
215
239
|
table.add_row(str(i), status, q_type, preview, rows, time_str)
|
|
216
240
|
|
|
217
|
-
console.print("")
|
|
218
241
|
console.print(table)
|
|
219
|
-
console.print("")
|
|
242
|
+
console.print("[dim]──────────────────────────────────────────────────────────────────────────────[/dim]")
|
|
243
|
+
console.print("[dim]Enter to continue · :run to re-execute last query[/dim]\n")
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dbagent-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8.1"
|
|
8
8
|
description = "Enterprise AI Database Copilot, Schema Graph Introspector & Autonomous Recovery Agent (CLI)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|