dbagent-cli 0.1.3__tar.gz → 0.1.5__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.1.3 → dbagent_cli-0.1.5}/PKG-INFO +1 -1
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/__init__.py +1 -1
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/agent/generator.py +9 -3
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/cli.py +132 -26
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent_cli.egg-info/PKG-INFO +1 -1
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/pyproject.toml +1 -1
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/README.md +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/agent/pipeline.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/agent/validator.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/config.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/connectors/base.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/connectors/factory.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/connectors/mongo.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/connectors/relational.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/llm/auto_setup.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/llm/base.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/llm/factory.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/llm/gemini_provider.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/llm/groq_provider.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/llm/mock_provider.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/llm/ollama_provider.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/llm/openrouter_provider.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/schema/formatter.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/schema/models.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/schema/selector.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/ui/console.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent/ui/viewer.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent_cli.egg-info/SOURCES.txt +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent_cli.egg-info/dependency_links.txt +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent_cli.egg-info/entry_points.txt +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent_cli.egg-info/requires.txt +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/dbagent_cli.egg-info/top_level.txt +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/setup.cfg +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/tests/test_cli.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/tests/test_connectors.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/tests/test_generator.py +0 -0
- {dbagent_cli-0.1.3 → dbagent_cli-0.1.5}/tests/test_schema.py +0 -0
|
@@ -51,9 +51,15 @@ Generate ONLY the raw executable SQL query for the given database. No markdown f
|
|
|
51
51
|
2. Use the exact table and column names from the schema above.
|
|
52
52
|
3. Use dialect-specific syntax for `{dialect_name}`.
|
|
53
53
|
4. Write clean, optimized SQL with uppercase keywords.
|
|
54
|
-
5. If the user asks for recent/time-based data, use
|
|
55
|
-
6.
|
|
56
|
-
|
|
54
|
+
5. If the user asks for recent/time-based data, use native datetime functions (e.g. for PostgreSQL: `NOW() - INTERVAL '1 hour'`, for SQLite: `datetime('now', '-1 hour')`, for MySQL: `NOW() - INTERVAL 1 HOUR`).
|
|
55
|
+
6. For queries inspecting PostgreSQL foreign key mappings or table relationships via system catalog, use standard joins:
|
|
56
|
+
SELECT tc.table_name, kcu.column_name, ccu.table_name AS foreign_table_name, ccu.column_name AS foreign_column_name
|
|
57
|
+
FROM information_schema.table_constraints tc
|
|
58
|
+
JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name
|
|
59
|
+
JOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_name
|
|
60
|
+
WHERE tc.constraint_type = 'FOREIGN KEY';
|
|
61
|
+
7. Always include appropriate WHERE clauses, JOINs, and ORDER BY as needed.
|
|
62
|
+
8. Default to LIMIT 50 unless the user specifies a count.
|
|
57
63
|
"""
|
|
58
64
|
|
|
59
65
|
|
|
@@ -509,7 +509,7 @@ def chat_command(
|
|
|
509
509
|
console.print(f"[bold green]Connected:[/bold green] {db_name} ({dialect}) - {len(table_names)} tables")
|
|
510
510
|
console.print(f"[bold cyan]AI:[/bold cyan] {llm.name}")
|
|
511
511
|
console.print("[dim]Type your question in plain English. Read queries auto-execute. Write queries ask for confirmation.[/dim]")
|
|
512
|
-
console.print("[dim]Commands: [bold]:tables[/bold] | [bold]:table <name>[/bold] | [bold]:explain[/bold] | [bold]:history[/bold] | [bold]:auto[/bold]/[bold]:noauto[/bold] | [bold]:run[/bold] | [bold]:export <file>[/bold] | [bold]:exit[/bold][/dim]\n")
|
|
512
|
+
console.print("[dim]Commands: [bold]:tables[/bold] | [bold]:table <name>[/bold] | [bold]:fk <name>[/bold] | [bold]:explain[/bold] | [bold]:history[/bold] | [bold]:auto[/bold]/[bold]:noauto[/bold] | [bold]:run[/bold] | [bold]:export <file>[/bold] | [bold]:exit[/bold][/dim]\n")
|
|
513
513
|
|
|
514
514
|
history_file = config_mgr.config_dir / "chat_history.txt"
|
|
515
515
|
session = PromptSession(history=FileHistory(str(history_file)))
|
|
@@ -537,15 +537,47 @@ def chat_command(
|
|
|
537
537
|
|
|
538
538
|
if user_input.startswith(":table "):
|
|
539
539
|
tname = user_input.split(" ", 1)[1].strip()
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
540
|
+
exact, fuzzy = connector.resolve_tables(tname)
|
|
541
|
+
target = exact[0] if exact else (fuzzy[0] if fuzzy else tname)
|
|
542
|
+
with console.status(f"[bold cyan]Inspecting {target}...[/bold cyan]"):
|
|
543
|
+
on_demand_schema = connector.inspect_schema(table_names=[target], include_samples=True)
|
|
544
|
+
tmodel = on_demand_schema.get_table(target)
|
|
543
545
|
if tmodel:
|
|
544
546
|
SchemaViewer.display_table_detail(tmodel)
|
|
545
547
|
else:
|
|
546
548
|
print_error(f"Table '{tname}' not found.")
|
|
547
549
|
continue
|
|
548
550
|
|
|
551
|
+
if user_input.startswith(":relations ") or user_input.startswith(":fk ") or user_input.startswith(":mapped "):
|
|
552
|
+
tname = user_input.split(" ", 1)[1].strip()
|
|
553
|
+
exact, fuzzy = connector.resolve_tables(tname)
|
|
554
|
+
target = exact[0] if exact else (fuzzy[0] if fuzzy else tname)
|
|
555
|
+
with console.status(f"[bold cyan]Finding relationships for {target}...[/bold cyan]"):
|
|
556
|
+
full_schema = connector.inspect_schema(include_samples=False, max_samples=0)
|
|
557
|
+
|
|
558
|
+
# Find FKs where target references others, and others reference target
|
|
559
|
+
tmodel = full_schema.get_table(target)
|
|
560
|
+
outgoing = tmodel.foreign_keys if tmodel else []
|
|
561
|
+
incoming = []
|
|
562
|
+
for other in full_schema.tables:
|
|
563
|
+
for fk in other.foreign_keys:
|
|
564
|
+
if fk.referred_table.lower() == target.lower():
|
|
565
|
+
incoming.append((other.name, fk))
|
|
566
|
+
|
|
567
|
+
console.print(f"\n[bold cyan]Relationships mapped to table: [bold white]{target}[/bold white][/bold cyan]\n")
|
|
568
|
+
if outgoing:
|
|
569
|
+
console.print("[bold green]Outgoing (This table references):[/bold green]")
|
|
570
|
+
for fk in outgoing:
|
|
571
|
+
console.print(f" * ({', '.join(fk.constrained_columns)}) -> [bold]{fk.referred_table}[/bold]({', '.join(fk.referred_columns)})")
|
|
572
|
+
if incoming:
|
|
573
|
+
console.print("\n[bold yellow]Incoming (Other tables reference this table):[/bold yellow]")
|
|
574
|
+
for src_table, fk in incoming:
|
|
575
|
+
console.print(f" * [bold]{src_table}[/bold]({', '.join(fk.constrained_columns)}) -> ({', '.join(fk.referred_columns)})")
|
|
576
|
+
if not outgoing and not incoming:
|
|
577
|
+
console.print(f"[dim]No foreign key constraints found directly mapped to '{target}'.[/dim]")
|
|
578
|
+
console.print("")
|
|
579
|
+
continue
|
|
580
|
+
|
|
549
581
|
if user_input.lower() == ":explain":
|
|
550
582
|
if last_result and last_result.sql:
|
|
551
583
|
print_code(last_result.sql, "sql", "Last Executed SQL")
|
|
@@ -767,26 +799,33 @@ def models_command():
|
|
|
767
799
|
|
|
768
800
|
@app.command(name="setup")
|
|
769
801
|
def setup_command(
|
|
770
|
-
model: str = typer.Option("qwen2.5-coder:1.5b", "--model", "-m", help="Ollama model to install"),
|
|
771
|
-
yes: bool = typer.Option(False, "--yes", "-y", help="Skip
|
|
802
|
+
model: str = typer.Option("qwen2.5-coder:1.5b", "--model", "-m", help="Ollama model to install for local AI"),
|
|
803
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip prompts and install local Ollama automatically"),
|
|
772
804
|
):
|
|
773
|
-
"""
|
|
805
|
+
"""Interactive setup wizard: Configure local offline AI, cloud API keys, or standard DB tools."""
|
|
774
806
|
print_banner()
|
|
775
|
-
console.print("[bold cyan]DB-Agent
|
|
807
|
+
console.print("[bold cyan]DB-Agent Setup & Configuration Wizard[/bold cyan]\n")
|
|
776
808
|
|
|
777
|
-
|
|
778
|
-
if not yes:
|
|
779
|
-
console.print("[bold white]DB-Agent supports two ways to run AI:[/bold white]")
|
|
780
|
-
console.print(" 1. [bold green]Local Offline AI (Ollama)[/bold green]: 100% private, zero-cost, runs on your machine without API keys.")
|
|
781
|
-
console.print(" 2. [bold cyan]Free Cloud AI (Gemini / Groq)[/bold cyan]: Lightweight, runs in the cloud via free API keys.\n")
|
|
809
|
+
chosen_provider = "ollama"
|
|
782
810
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
811
|
+
if yes:
|
|
812
|
+
choice = "1"
|
|
813
|
+
else:
|
|
814
|
+
console.print("[bold white]Choose how you would like to setup DB-Agent:[/bold white]")
|
|
815
|
+
console.print(" [bold green]1. Local Offline AI[/bold green] (Ollama + Coding Model) - [italic]100% Free, Private, Zero API Keys[/italic]")
|
|
816
|
+
console.print(" [bold cyan]2. Free Cloud AI[/bold cyan] (Google Gemini / Groq / OpenRouter) - [italic]Fast, requires free API key[/italic]")
|
|
817
|
+
console.print(" [bold yellow]3. Normal Database CLI[/bold yellow] - [italic]No AI, use standard connect, scan & SQL tools only[/italic]\n")
|
|
818
|
+
|
|
819
|
+
choice = Prompt.ask(
|
|
820
|
+
"Select setup mode",
|
|
821
|
+
choices=["1", "2", "3"],
|
|
822
|
+
default="1",
|
|
786
823
|
)
|
|
787
824
|
|
|
788
|
-
|
|
789
|
-
|
|
825
|
+
# --- Mode 1: Local Offline AI ---
|
|
826
|
+
if choice == "1":
|
|
827
|
+
console.print("\n[bold cyan]Setting up Local Offline AI (Ollama)...[/bold cyan]")
|
|
828
|
+
console.print("[dim]Downloading and configuring local AI engine (one-time setup)...[/dim]\n")
|
|
790
829
|
from dbagent.llm.auto_setup import ensure_ollama_ready
|
|
791
830
|
|
|
792
831
|
def _log(msg, level="info"):
|
|
@@ -804,16 +843,83 @@ def setup_command(
|
|
|
804
843
|
success, message = ensure_ollama_ready(model=model, print_fn=_log)
|
|
805
844
|
if success:
|
|
806
845
|
config_mgr.set_setting("default_provider", "ollama")
|
|
807
|
-
|
|
808
|
-
|
|
846
|
+
chosen_provider = "ollama"
|
|
847
|
+
print_success("Local AI engine configured and ready!")
|
|
809
848
|
else:
|
|
810
849
|
print_error(message)
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
console.print("
|
|
816
|
-
|
|
850
|
+
console.print("[yellow]You can still use DB-Agent with cloud AI or standard SQL tools.[/yellow]\n")
|
|
851
|
+
|
|
852
|
+
# --- Mode 2: Cloud AI with API Key ---
|
|
853
|
+
elif choice == "2":
|
|
854
|
+
console.print("\n[bold cyan]Configure Free Cloud AI Provider[/bold cyan]\n")
|
|
855
|
+
prov = Prompt.ask(
|
|
856
|
+
"Select Cloud AI Provider",
|
|
857
|
+
choices=["gemini", "groq", "openrouter"],
|
|
858
|
+
default="gemini",
|
|
859
|
+
)
|
|
860
|
+
if prov == "gemini":
|
|
861
|
+
console.print("[dim]Get a free Gemini API key at: [bold]https://aistudio.google.com/apikey[/bold][/dim]")
|
|
862
|
+
key = Prompt.ask("Enter GEMINI_API_KEY").strip()
|
|
863
|
+
if key:
|
|
864
|
+
config_mgr.set_setting("gemini_api_key", key)
|
|
865
|
+
config_mgr.set_setting("default_provider", "gemini")
|
|
866
|
+
chosen_provider = "gemini"
|
|
867
|
+
print_success("Google Gemini Free Tier configured successfully!")
|
|
868
|
+
elif prov == "groq":
|
|
869
|
+
console.print("[dim]Get a free Groq API key at: [bold]https://console.groq.com[/bold][/dim]")
|
|
870
|
+
key = Prompt.ask("Enter GROQ_API_KEY").strip()
|
|
871
|
+
if key:
|
|
872
|
+
config_mgr.set_setting("groq_api_key", key)
|
|
873
|
+
config_mgr.set_setting("default_provider", "groq")
|
|
874
|
+
chosen_provider = "groq"
|
|
875
|
+
print_success("Groq Ultra-Fast Free Tier configured successfully!")
|
|
876
|
+
elif prov == "openrouter":
|
|
877
|
+
console.print("[dim]Get an OpenRouter API key at: [bold]https://openrouter.ai[/bold][/dim]")
|
|
878
|
+
key = Prompt.ask("Enter OPENROUTER_API_KEY").strip()
|
|
879
|
+
if key:
|
|
880
|
+
config_mgr.set_setting("openrouter_api_key", key)
|
|
881
|
+
config_mgr.set_setting("default_provider", "openrouter")
|
|
882
|
+
chosen_provider = "openrouter"
|
|
883
|
+
print_success("OpenRouter configured successfully!")
|
|
884
|
+
|
|
885
|
+
# --- Mode 3: Normal Database CLI ---
|
|
886
|
+
elif choice == "3":
|
|
887
|
+
chosen_provider = "normal (no AI)"
|
|
888
|
+
print_info("Configured in Normal mode. You can use connect, scan, and run commands.")
|
|
889
|
+
console.print("[dim]To add AI later, simply run [bold]db-agent setup[/bold] or [bold]db-agent config[/bold].[/dim]\n")
|
|
890
|
+
|
|
891
|
+
# --- Optional Database Connection Setup ---
|
|
892
|
+
console.print("")
|
|
893
|
+
if not yes and Confirm.ask("Would you like to connect and save a database profile now?", default=True):
|
|
894
|
+
db_url = Prompt.ask("Enter Database URL (e.g. postgresql://user:pass@host:5432/dbname or sqlite:///mydb.db)").strip()
|
|
895
|
+
if db_url:
|
|
896
|
+
alias = Prompt.ask("Enter alias name (e.g. devdb, prod, myapp)", default="default_db").strip()
|
|
897
|
+
with console.status("[bold cyan]Testing connection credentials...[/bold cyan]"):
|
|
898
|
+
try:
|
|
899
|
+
connector = create_connector(db_url)
|
|
900
|
+
success, msg = connector.test_connection()
|
|
901
|
+
if success:
|
|
902
|
+
config_mgr.save_profile(name=alias, connection_url=db_url, description=f"Profile for {alias}")
|
|
903
|
+
print_success(f"Successfully connected and saved profile as alias '[bold cyan]{alias}[/bold cyan]'!")
|
|
904
|
+
else:
|
|
905
|
+
print_warning(f"Connection test failed: {msg}")
|
|
906
|
+
if Confirm.ask("Save profile anyway?", default=True):
|
|
907
|
+
config_mgr.save_profile(name=alias, connection_url=db_url, description=f"Profile for {alias}")
|
|
908
|
+
print_success(f"Saved profile as alias '[bold cyan]{alias}[/bold cyan]'.")
|
|
909
|
+
connector.close()
|
|
910
|
+
except Exception as e:
|
|
911
|
+
print_error(f"Error testing connection: {str(e)}")
|
|
912
|
+
|
|
913
|
+
# --- Final Summary ---
|
|
914
|
+
console.print("\n" + "=" * 70)
|
|
915
|
+
console.print("[bold green][OK] DB-Agent Setup Complete![/bold green]")
|
|
916
|
+
console.print(f"AI Mode: [bold cyan]{chosen_provider}[/bold cyan]")
|
|
917
|
+
console.print("=" * 70)
|
|
918
|
+
console.print("\n[bold white]Quick Commands:[/bold white]")
|
|
919
|
+
console.print(" * [bold]db-agent ask \"your question\" --db <alias>[/bold] -> Ask questions & get live results")
|
|
920
|
+
console.print(" * [bold]db-agent chat --db <alias>[/bold] -> Interactive CLI shell")
|
|
921
|
+
console.print(" * [bold]db-agent scan --db <alias>[/bold] -> Introspect database schema")
|
|
922
|
+
console.print(" * [bold]db-agent guide[/bold] -> View full command reference\n")
|
|
817
923
|
|
|
818
924
|
|
|
819
925
|
if __name__ == "__main__":
|
|
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
|