dbagent-cli 0.6.1__tar.gz → 0.7.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.
Files changed (80) hide show
  1. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/PKG-INFO +1 -1
  2. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/__init__.py +1 -1
  3. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/cli.py +124 -45
  4. dbagent_cli-0.7.0/dbagent/llm/anthropic_provider.py +126 -0
  5. dbagent_cli-0.7.0/dbagent/llm/deepseek_provider.py +128 -0
  6. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/llm/factory.py +47 -10
  7. dbagent_cli-0.7.0/dbagent/llm/mistral_provider.py +129 -0
  8. dbagent_cli-0.7.0/dbagent/llm/openai_provider.py +131 -0
  9. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent_cli.egg-info/PKG-INFO +1 -1
  10. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent_cli.egg-info/SOURCES.txt +5 -0
  11. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/pyproject.toml +1 -1
  12. dbagent_cli-0.7.0/tests/test_providers.py +69 -0
  13. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/README.md +0 -0
  14. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/agent/confidence.py +0 -0
  15. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/agent/conversation.py +0 -0
  16. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/agent/doctor.py +0 -0
  17. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/agent/error_classifier.py +0 -0
  18. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/agent/generator.py +0 -0
  19. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/agent/pipeline.py +0 -0
  20. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/agent/recommender.py +0 -0
  21. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/agent/validator.py +0 -0
  22. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/config.py +0 -0
  23. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/connectors/base.py +0 -0
  24. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/connectors/factory.py +0 -0
  25. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/connectors/mongo.py +0 -0
  26. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/connectors/relational.py +0 -0
  27. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/execution/__init__.py +0 -0
  28. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/execution/cache.py +0 -0
  29. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/execution/changeset.py +0 -0
  30. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/execution/cost.py +0 -0
  31. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/execution/recovery.py +0 -0
  32. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/execution/sandbox.py +0 -0
  33. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/execution/streaming.py +0 -0
  34. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/llm/auto_setup.py +0 -0
  35. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/llm/base.py +0 -0
  36. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/llm/gemini_provider.py +0 -0
  37. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/llm/groq_provider.py +0 -0
  38. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/llm/mock_provider.py +0 -0
  39. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/llm/ollama_provider.py +0 -0
  40. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/llm/openrouter_provider.py +0 -0
  41. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/schema/cache.py +0 -0
  42. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/schema/formatter.py +0 -0
  43. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/schema/graph.py +0 -0
  44. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/schema/models.py +0 -0
  45. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/schema/retriever.py +0 -0
  46. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/schema/selector.py +0 -0
  47. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/security/__init__.py +0 -0
  48. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/security/audit.py +0 -0
  49. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/security/environment.py +0 -0
  50. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/security/masking.py +0 -0
  51. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/security/modes.py +0 -0
  52. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/security/rbac.py +0 -0
  53. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/semantic/__init__.py +0 -0
  54. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/semantic/dictionary.py +0 -0
  55. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/semantic/templates.py +0 -0
  56. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/sql/__init__.py +0 -0
  57. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/sql/optimizer.py +0 -0
  58. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/sql/validator.py +0 -0
  59. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/ui/chat_screen.py +0 -0
  60. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/ui/console.py +0 -0
  61. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/ui/grid_window.py +0 -0
  62. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/ui/viewer.py +0 -0
  63. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent/ui/visualizer.py +0 -0
  64. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent_cli.egg-info/dependency_links.txt +0 -0
  65. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent_cli.egg-info/entry_points.txt +0 -0
  66. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent_cli.egg-info/requires.txt +0 -0
  67. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/dbagent_cli.egg-info/top_level.txt +0 -0
  68. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/setup.cfg +0 -0
  69. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/tests/test_cache.py +0 -0
  70. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/tests/test_cli.py +0 -0
  71. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/tests/test_connectors.py +0 -0
  72. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/tests/test_doctor_and_viz.py +0 -0
  73. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/tests/test_generator.py +0 -0
  74. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/tests/test_graph.py +0 -0
  75. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/tests/test_recovery.py +0 -0
  76. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/tests/test_safety.py +0 -0
  77. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/tests/test_sandbox.py +0 -0
  78. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/tests/test_schema.py +0 -0
  79. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/tests/test_security.py +0 -0
  80. {dbagent_cli-0.6.1 → dbagent_cli-0.7.0}/tests/test_semantic.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dbagent-cli
3
- Version: 0.6.1
3
+ Version: 0.7.0
4
4
  Summary: Enterprise AI Database Copilot, Schema Graph Introspector & Autonomous Recovery Agent (CLI)
5
5
  Author: Santhosh Gupta
6
6
  Requires-Python: >=3.9
@@ -2,5 +2,5 @@
2
2
  DB-Agent: Universal Database Introspection and Script Generation AI Agent (CLI).
3
3
  """
4
4
 
5
- __version__ = "0.6.1"
5
+ __version__ = "0.7.0"
6
6
  __author__ = "Santhosh Gupta"
@@ -687,16 +687,18 @@ def chat_command(
687
687
  if not cmd_arg:
688
688
  curr_model = model or getattr(llm, "default_model", "default")
689
689
  console.print(f"\n[bold cyan]Active AI Provider:[/bold cyan] [bold white]{llm.name}[/bold white] | Model: [bold green]{curr_model}[/bold green]")
690
- console.print("[dim]Usage: [bold]:provider <ollama|gemini|groq|openrouter>[/bold][/dim]\n")
690
+ console.print("[dim]Supported: [bold]claude, openai, deepseek, mistral, gemini, groq, openrouter, ollama[/bold][/dim]\n")
691
691
  continue
692
- prov_target = cmd_arg.lower()
693
- if prov_target in ["ollama", "gemini", "groq", "openrouter", "mock"]:
692
+ prov_target = cmd_arg.lower().strip()
693
+ if prov_target in ["claude", "anthropic", "openai", "chatgpt", "gpt", "deepseek", "mistral", "gemini", "groq", "openrouter", "ollama", "local", "mock"]:
694
694
  llm = get_llm_provider(provider_name=prov_target, config_mgr=config_mgr)
695
695
  model = None
696
696
  pipeline.generator.llm = llm # Update reused pipeline
697
697
  print_success(f"Switched AI provider to [bold cyan]{llm.name}[/bold cyan]")
698
+ if not llm.is_available() and llm.name not in ["ollama", "mock"]:
699
+ print_warning(f"Note: API key for {llm.name} is not set. Run `db-agent config` or set {llm.name.upper()}_API_KEY.")
698
700
  else:
699
- print_error(f"Unknown provider '{cmd_arg}'. Supported: ollama, gemini, groq, openrouter")
701
+ print_error(f"Unknown provider '{cmd_arg}'. Supported: claude, openai, deepseek, mistral, gemini, groq, openrouter, ollama")
700
702
  continue
701
703
 
702
704
  if cmd_name in [":model", ":models", ":providers", ":ai"]:
@@ -710,18 +712,41 @@ def chat_command(
710
712
  if avail_models:
711
713
  console.print(f"[bold]Available Models for {llm.name}:[/bold]")
712
714
  model_descriptions = {
713
- "gemini-flash-lite-latest": "⚡ Ultra-Fast (~0.8s) • Recommended for instant chat & queries",
715
+ # Gemini
716
+ "gemini-flash-lite-latest": "⚡ Ultra-Fast (~0.8s) • Instant chat & queries",
714
717
  "gemini-3.6-flash": "⚡ High Quality (~3.5s) • Strong SQL reasoning & accuracy",
715
718
  "gemini-3.7-flash": "🧠 Deep Reasoning (~13s) • Latest flagship model",
716
719
  "gemini-3.1-flash-lite": "⚡ Fast Lite (~4.0s) • Low latency",
717
720
  "gemini-flash-latest": "⚡ Standard Flash (~7.0s)",
721
+ # Claude
722
+ "claude-3-7-sonnet-20250219": "🧠 Hybrid Reasoning Flagship • Deepest architecture understanding",
723
+ "claude-3-5-sonnet-20241022": "⚡ High Intelligence • Expert SQL engineer & schema specialist",
724
+ "claude-3-5-haiku-20241022": "⚡ Ultra-Fast (~1s) • High speed and cost efficient",
725
+ "claude-3-opus-20240229": "Deep complex schema reasoning",
726
+ # OpenAI
727
+ "gpt-4o": "⚡ Omni Flagship • High speed multimodal & complex SQL reasoning",
728
+ "gpt-4o-mini": "⚡ Ultra-Fast (~1s) • High speed and cost efficient",
729
+ "gpt-4.5-preview": "🧠 Next-Generation Reasoning model",
730
+ "o3-mini": "🧠 Deep STEM & Advanced Coding Reasoning",
731
+ "o1": "🧠 Maximum Reasoning & Math Architecture",
732
+ # DeepSeek
733
+ "deepseek-chat": "⚡ DeepSeek-V3 Ultra-Fast • Strong code & SQL",
734
+ "deepseek-reasoner": "🧠 DeepSeek-R1 Full Chain-of-Thought Reasoning",
735
+ # Mistral
736
+ "codestral-latest": "⚡ Specialized Code & SQL Architect (22B)",
737
+ "mistral-large-latest": "🧠 Flagship Reasoning & Multi-language",
738
+ "mistral-small-latest": "⚡ Fast & lightweight",
739
+ # Groq
740
+ "llama-3.3-70b-versatile": "⚡ Llama 3.3 70B (500+ tokens/sec on Groq LPUs)",
741
+ "llama-3.1-8b-instant": "⚡ Ultra-Fast Llama 3.1 8B (800+ tokens/sec)",
742
+ "qwen-2.5-coder-32b": "⚡ Qwen 2.5 Coder 32B specialized SQL engineer",
718
743
  }
719
744
  for idx, m_name in enumerate(avail_models, 1):
720
745
  is_active = " [bold green]✓ Active[/bold green]" if m_name == curr_model else ""
721
746
  desc = model_descriptions.get(m_name, "")
722
747
  desc_str = f" - [dim]{desc}[/dim]" if desc else ""
723
748
  console.print(f" [bold cyan]{idx}[/bold cyan]. [bold white]{m_name}[/bold white]{desc_str}{is_active}")
724
- console.print("\n[dim]Quick Switch: [bold]:model 1[/bold] (ultra-fast) | [bold]:model 3.7[/bold] | [bold]:model 3.6[/bold] | [bold]:model <name>[/bold][/dim]\n")
749
+ console.print("\n[dim]Quick Switch: [bold]:model <number|name>[/bold] (e.g. [bold]:model 1[/bold] or [bold]:model 3.7[/bold] or [bold]:model 4o[/bold] or [bold]:model r1[/bold])[/dim]\n")
725
750
  continue
726
751
 
727
752
  # Handle numeric shortcut or name shortcut
@@ -731,13 +756,23 @@ def chat_command(
731
756
  if 0 <= choice_idx < len(avail_models):
732
757
  target_model = avail_models[choice_idx]
733
758
  elif target_model in ["3.7", "37"]:
734
- target_model = "gemini-3.7-flash"
759
+ target_model = "claude-3-7-sonnet-20250219" if llm.name == "claude" else "gemini-3.7-flash"
760
+ elif target_model in ["3.5", "35"]:
761
+ target_model = "claude-3-5-sonnet-20241022" if llm.name == "claude" else "gemini-3.5-flash"
735
762
  elif target_model in ["3.6", "36"]:
736
763
  target_model = "gemini-3.6-flash"
737
- elif target_model in ["lite", "fast", "flash-lite", "1"]:
738
- target_model = "gemini-flash-lite-latest"
739
- elif target_model in ["3.1"]:
740
- target_model = "gemini-3.1-flash-lite"
764
+ elif target_model in ["4o", "gpt4o"]:
765
+ target_model = "gpt-4o"
766
+ elif target_model in ["mini", "4o-mini"]:
767
+ target_model = "gpt-4o-mini"
768
+ elif target_model in ["r1", "reasoner"]:
769
+ target_model = "deepseek-reasoner"
770
+ elif target_model in ["v3", "chat"]:
771
+ target_model = "deepseek-chat"
772
+ elif target_model in ["codestral"]:
773
+ target_model = "codestral-latest"
774
+ elif target_model in ["lite", "fast", "flash-lite"]:
775
+ target_model = "gemini-flash-lite-latest" if llm.name == "gemini" else "claude-3-5-haiku-20241022"
741
776
  elif avail_models:
742
777
  matches = [m for m in avail_models if target_model.lower() in m.lower()]
743
778
  if matches:
@@ -1586,7 +1621,7 @@ def run_command(
1586
1621
 
1587
1622
  @app.command(name="config")
1588
1623
  def config_command():
1589
- """Interactive wizard to configure AI providers and default settings."""
1624
+ """Interactive wizard to configure AI providers and credentials."""
1590
1625
  print_banner()
1591
1626
  console.print("[bold cyan]DB-Agent Configuration Wizard[/bold cyan]\n")
1592
1627
 
@@ -1594,14 +1629,38 @@ def config_command():
1594
1629
  console.print(f"Current Default AI Provider: [bold green]{curr_provider}[/bold green]")
1595
1630
  provider_choice = Prompt.ask(
1596
1631
  "Select Default AI Provider",
1597
- choices=["ollama", "gemini", "groq", "openrouter", "keep"],
1632
+ choices=["gemini", "claude", "openai", "deepseek", "mistral", "groq", "openrouter", "ollama", "keep"],
1598
1633
  default="keep",
1599
1634
  )
1600
1635
  if provider_choice != "keep":
1601
1636
  config_mgr.set_setting("default_provider", provider_choice)
1602
1637
  print_success(f"Default provider updated to [bold]{provider_choice}[/bold]")
1603
1638
 
1604
- if provider_choice == "gemini":
1639
+ if provider_choice in ["claude", "anthropic"]:
1640
+ console.print("[dim]Get your Anthropic API key at: [bold]https://console.anthropic.com[/bold][/dim]")
1641
+ key = Prompt.ask("Enter ANTHROPIC_API_KEY", default=config_mgr.get_setting("anthropic_api_key", "")).strip()
1642
+ if key:
1643
+ config_mgr.set_setting("anthropic_api_key", key)
1644
+ print_success("Anthropic Claude API key saved.")
1645
+ elif provider_choice in ["openai", "chatgpt"]:
1646
+ console.print("[dim]Get your OpenAI API key at: [bold]https://platform.openai.com/api-keys[/bold][/dim]")
1647
+ key = Prompt.ask("Enter OPENAI_API_KEY", default=config_mgr.get_setting("openai_api_key", "")).strip()
1648
+ if key:
1649
+ config_mgr.set_setting("openai_api_key", key)
1650
+ print_success("OpenAI API key saved.")
1651
+ elif provider_choice == "deepseek":
1652
+ console.print("[dim]Get your DeepSeek API key at: [bold]https://platform.deepseek.com[/bold][/dim]")
1653
+ key = Prompt.ask("Enter DEEPSEEK_API_KEY", default=config_mgr.get_setting("deepseek_api_key", "")).strip()
1654
+ if key:
1655
+ config_mgr.set_setting("deepseek_api_key", key)
1656
+ print_success("DeepSeek API key saved.")
1657
+ elif provider_choice == "mistral":
1658
+ console.print("[dim]Get your Mistral API key at: [bold]https://console.mistral.ai[/bold][/dim]")
1659
+ key = Prompt.ask("Enter MISTRAL_API_KEY", default=config_mgr.get_setting("mistral_api_key", "")).strip()
1660
+ if key:
1661
+ config_mgr.set_setting("mistral_api_key", key)
1662
+ print_success("Mistral API key saved.")
1663
+ elif provider_choice == "gemini":
1605
1664
  console.print("[dim]Get your free Gemini API key at: [bold]https://aistudio.google.com/apikey[/bold][/dim]")
1606
1665
  key = Prompt.ask("Enter GEMINI_API_KEY", default=config_mgr.get_setting("gemini_api_key", "")).strip()
1607
1666
  if key:
@@ -1652,33 +1711,29 @@ def profiles_command():
1652
1711
 
1653
1712
  @app.command(name="models")
1654
1713
  def models_command():
1655
- """List available models across local Ollama and free cloud providers."""
1714
+ """List available models across local Ollama and all major cloud providers."""
1656
1715
  print_banner()
1657
- console.print("[bold cyan]Supported Free & Standalone LLMs:[/bold cyan]\n")
1658
-
1659
- ollama = get_llm_provider("ollama")
1660
- ollama_status = "[green]ONLINE (Ready)[/green]" if ollama.is_available() else "[red]OFFLINE (Run 'ollama serve')[/red]"
1661
- console.print(f"1. [bold white]Ollama (100% Local, Offline & Zero-Cost)[/bold white] - Status: {ollama_status}")
1662
- if ollama.is_available():
1663
- models = ollama.list_models()
1664
- console.print(f" Installed Local Models: {', '.join(models) if models else 'None'}")
1665
- else:
1666
- console.print(" Recommended models: `ollama run qwen2.5-coder` or `ollama run llama3.2`")
1667
-
1668
- gemini = get_llm_provider("gemini")
1669
- gemini_status = "[green]Configured[/green]" if gemini.is_available() else "[yellow]Not Configured (Free Key: aistudio.google.com)[/yellow]"
1670
- console.print(f"\n2. [bold white]Google Gemini (Free Tier)[/bold white] - Status: {gemini_status}")
1671
- console.print(" Available Models: `gemini-3.6-flash`, `gemini-2.5-flash`, `gemini-2.5-pro`")
1672
-
1673
- groq = get_llm_provider("groq")
1674
- groq_status = "[green]Configured[/green]" if groq.is_available() else "[yellow]Not Configured (Free Key: console.groq.com)[/yellow]"
1675
- console.print(f"\n3. [bold white]Groq (Free Ultra-Fast Tier)[/bold white] - Status: {groq_status}")
1676
- console.print(" Available Models: `llama-3.3-70b-versatile`, `qwen-2.5-32b`")
1716
+ console.print("[bold cyan]Supported AI Providers & Available Models:[/bold cyan]\n")
1717
+
1718
+ providers_to_show = [
1719
+ ("gemini", "Google Gemini", "https://aistudio.google.com/apikey"),
1720
+ ("claude", "Anthropic Claude", "https://console.anthropic.com"),
1721
+ ("openai", "OpenAI / ChatGPT", "https://platform.openai.com"),
1722
+ ("deepseek", "DeepSeek AI", "https://platform.deepseek.com"),
1723
+ ("mistral", "Mistral AI", "https://console.mistral.ai"),
1724
+ ("groq", "Groq (Ultra-Fast LPUs)", "https://console.groq.com"),
1725
+ ("openrouter", "OpenRouter (100+ Models)", "https://openrouter.ai"),
1726
+ ("ollama", "Ollama (100% Offline / Local)", "http://localhost:11434"),
1727
+ ]
1677
1728
 
1678
- openrouter = get_llm_provider("openrouter")
1679
- openrouter_status = "[green]Configured[/green]" if openrouter.is_available() else "[yellow]Not Configured (openrouter.ai)[/yellow]"
1680
- console.print(f"\n4. [bold white]OpenRouter (Free Community Models)[/bold white] - Status: {openrouter_status}")
1681
- console.print(" Available Models: `meta-llama/llama-3.3-70b-instruct:free`, `qwen/qwen-2.5-coder-32b-instruct:free`\n")
1729
+ for idx, (p_slug, p_label, p_url) in enumerate(providers_to_show, 1):
1730
+ prov = get_llm_provider(p_slug, config_mgr=config_mgr)
1731
+ status = "[bold green] Configured & Ready[/bold green]" if prov.is_available() else f"[yellow]Not Configured ({p_url})[/yellow]"
1732
+ console.print(f"{idx}. [bold white]{p_label}[/bold white] - Status: {status}")
1733
+ models = prov.list_models() if hasattr(prov, "list_models") else []
1734
+ if models:
1735
+ console.print(f" Models: [cyan]{', '.join(models[:5])}[/cyan]")
1736
+ console.print("")
1682
1737
 
1683
1738
 
1684
1739
  # ---- setup ----
@@ -1699,7 +1754,7 @@ def setup_command(
1699
1754
  else:
1700
1755
  console.print("[bold white]Choose how you would like to setup DB-Agent:[/bold white]")
1701
1756
  console.print(" [bold green]1. Local Offline AI[/bold green] (Ollama + Coding Model) - [italic]100% Free, Private, Zero API Keys[/italic]")
1702
- console.print(" [bold cyan]2. Free Cloud AI[/bold cyan] (Google Gemini / Groq / OpenRouter) - [italic]Fast, requires free API key[/italic]")
1757
+ console.print(" [bold cyan]2. Cloud AI[/bold cyan] (Claude / OpenAI / DeepSeek / Mistral / Gemini / Groq) - [italic]High speed & accuracy[/italic]")
1703
1758
  console.print(" [bold yellow]3. Normal Database CLI[/bold yellow] - [italic]No AI, use standard connect, scan & SQL tools only[/italic]\n")
1704
1759
 
1705
1760
  choice = Prompt.ask(
@@ -1720,17 +1775,41 @@ def setup_command(
1720
1775
  print_warning("Local setup incomplete. You can still use cloud AI or standard DB tools.")
1721
1776
  chosen_provider = "cloud"
1722
1777
  elif choice == "2":
1723
- console.print("\n[bold cyan]Setting up Free Cloud AI Provider...[/bold cyan]")
1724
- prov = Prompt.ask("Choose provider", choices=["gemini", "groq", "openrouter"], default="gemini")
1778
+ console.print("\n[bold cyan]Setting up Cloud AI Provider...[/bold cyan]")
1779
+ prov = Prompt.ask("Choose provider", choices=["gemini", "claude", "openai", "deepseek", "mistral", "groq", "openrouter"], default="gemini")
1725
1780
  config_mgr.set_setting("default_provider", prov)
1726
- if prov == "gemini":
1727
- console.print("[dim]Get free key at: [bold]https://aistudio.google.com/apikey[/bold][/dim]")
1781
+ if prov in ["claude", "anthropic"]:
1782
+ console.print("[dim]Get Anthropic key at: [bold]https://console.anthropic.com[/bold][/dim]")
1783
+ k = Prompt.ask("Enter ANTHROPIC_API_KEY", default="")
1784
+ if k:
1785
+ config_mgr.set_setting("anthropic_api_key", k)
1786
+ print_success("Anthropic Claude API key saved.")
1787
+ elif prov in ["openai", "chatgpt"]:
1788
+ console.print("[dim]Get OpenAI key at: [bold]https://platform.openai.com/api-keys[/bold][/dim]")
1789
+ k = Prompt.ask("Enter OPENAI_API_KEY", default="")
1790
+ if k:
1791
+ config_mgr.set_setting("openai_api_key", k)
1792
+ print_success("OpenAI API key saved.")
1793
+ elif prov == "deepseek":
1794
+ console.print("[dim]Get DeepSeek key at: [bold]https://platform.deepseek.com[/bold][/dim]")
1795
+ k = Prompt.ask("Enter DEEPSEEK_API_KEY", default="")
1796
+ if k:
1797
+ config_mgr.set_setting("deepseek_api_key", k)
1798
+ print_success("DeepSeek API key saved.")
1799
+ elif prov == "mistral":
1800
+ console.print("[dim]Get Mistral key at: [bold]https://console.mistral.ai[/bold][/dim]")
1801
+ k = Prompt.ask("Enter MISTRAL_API_KEY", default="")
1802
+ if k:
1803
+ config_mgr.set_setting("mistral_api_key", k)
1804
+ print_success("Mistral API key saved.")
1805
+ elif prov == "gemini":
1806
+ console.print("[dim]Get free Gemini key at: [bold]https://aistudio.google.com/apikey[/bold][/dim]")
1728
1807
  k = Prompt.ask("Enter GEMINI_API_KEY", default="")
1729
1808
  if k:
1730
1809
  config_mgr.set_setting("gemini_api_key", k)
1731
1810
  print_success("Gemini API key saved.")
1732
1811
  elif prov == "groq":
1733
- console.print("[dim]Get free key at: [bold]https://console.groq.com[/bold][/dim]")
1812
+ console.print("[dim]Get free Groq key at: [bold]https://console.groq.com[/bold][/dim]")
1734
1813
  k = Prompt.ask("Enter GROQ_API_KEY", default="")
1735
1814
  if k:
1736
1815
  config_mgr.set_setting("groq_api_key", k)
@@ -0,0 +1,126 @@
1
+ """
2
+ Anthropic Claude LLM Provider.
3
+ Supports claude-3-7-sonnet-20250219, claude-3-5-sonnet-20241022, claude-3-5-haiku-20241022, claude-3-opus-20240229.
4
+ """
5
+
6
+ import json
7
+ import os
8
+ import requests
9
+ from typing import Optional, Callable, List
10
+ from dbagent.llm.base import BaseLLMProvider
11
+
12
+
13
+ class ClaudeProvider(BaseLLMProvider):
14
+ """Anthropic Claude provider using direct HTTP REST API."""
15
+
16
+ FALLBACK_MODELS = [
17
+ "claude-3-7-sonnet-20250219",
18
+ "claude-3-5-sonnet-20241022",
19
+ "claude-3-5-haiku-20241022",
20
+ "claude-3-opus-20240229",
21
+ ]
22
+
23
+ def __init__(self, api_key: Optional[str] = None, default_model: str = "claude-3-7-sonnet-20250219"):
24
+ if api_key is not None:
25
+ self.api_key = api_key
26
+ else:
27
+ self.api_key = os.getenv("ANTHROPIC_API_KEY") or os.getenv("CLAUDE_API_KEY")
28
+ self.default_model = default_model
29
+ self._session = requests.Session()
30
+ self._session.headers.update({
31
+ "User-Agent": "DB-Agent/0.7.0",
32
+ "anthropic-version": "2023-06-01",
33
+ "content-type": "application/json",
34
+ })
35
+ if self.api_key:
36
+ self._session.headers.update({"x-api-key": self.api_key})
37
+
38
+ @property
39
+ def name(self) -> str:
40
+ return "claude"
41
+
42
+ def is_available(self) -> bool:
43
+ return bool(self.api_key)
44
+
45
+ def list_models(self) -> List[str]:
46
+ return self.FALLBACK_MODELS
47
+
48
+ def generate(
49
+ self,
50
+ prompt: str,
51
+ system_prompt: Optional[str] = None,
52
+ model: Optional[str] = None,
53
+ stream_callback: Optional[Callable[[str], None]] = None,
54
+ ) -> str:
55
+ if not self.api_key:
56
+ raise ValueError(
57
+ "Anthropic API key is not configured. Set ANTHROPIC_API_KEY environment variable or run `db-agent config`."
58
+ )
59
+
60
+ requested_model = model or self.default_model
61
+ models_to_try = [requested_model]
62
+ for fb in self.FALLBACK_MODELS:
63
+ if fb != requested_model and fb not in models_to_try:
64
+ models_to_try.append(fb)
65
+
66
+ payload = {
67
+ "model": requested_model,
68
+ "max_tokens": 4096,
69
+ "temperature": 0.1,
70
+ "messages": [{"role": "user", "content": prompt}],
71
+ }
72
+ if system_prompt:
73
+ payload["system"] = system_prompt
74
+
75
+ endpoint = "https://api.anthropic.com/v1/messages"
76
+ last_error = None
77
+
78
+ for target_model in models_to_try:
79
+ payload["model"] = target_model
80
+ try:
81
+ if stream_callback:
82
+ payload["stream"] = True
83
+ response = self._session.post(endpoint, json=payload, stream=True, timeout=15)
84
+ if response.status_code == 200:
85
+ full_text = []
86
+ for line in response.iter_lines():
87
+ if line:
88
+ decoded = line.decode("utf-8")
89
+ if decoded.startswith("data: "):
90
+ raw_json = decoded[6:]
91
+ try:
92
+ chunk = json.loads(raw_json)
93
+ delta = chunk.get("delta", {})
94
+ text_piece = delta.get("text", "")
95
+ if text_piece:
96
+ full_text.append(text_piece)
97
+ stream_callback(text_piece)
98
+ except Exception:
99
+ pass
100
+ return "".join(full_text)
101
+ elif response.status_code in (404, 400, 503, 429):
102
+ last_error = f"Model {target_model} status {response.status_code}"
103
+ continue
104
+ else:
105
+ raise RuntimeError(f"Anthropic API error ({response.status_code}): {response.text}")
106
+ else:
107
+ payload["stream"] = False
108
+ response = self._session.post(endpoint, json=payload, timeout=20)
109
+ if response.status_code == 200:
110
+ data = response.json()
111
+ content_blocks = data.get("content", [])
112
+ text_blocks = [b.get("text", "") for b in content_blocks if b.get("type") == "text"]
113
+ return "".join(text_blocks)
114
+ elif response.status_code in (404, 400, 503, 429):
115
+ last_error = f"Model {target_model} status {response.status_code}"
116
+ continue
117
+ else:
118
+ raise RuntimeError(f"Anthropic API error ({response.status_code}): {response.text}")
119
+ except requests.exceptions.Timeout:
120
+ last_error = f"Model {target_model} timed out"
121
+ continue
122
+ except Exception as e:
123
+ last_error = str(e)
124
+ continue
125
+
126
+ raise RuntimeError(f"Anthropic generation error: {last_error or 'All model attempts failed'}")
@@ -0,0 +1,128 @@
1
+ """
2
+ DeepSeek AI LLM Provider.
3
+ Supports deepseek-chat (V3) and deepseek-reasoner (R1).
4
+ """
5
+
6
+ import json
7
+ import os
8
+ import requests
9
+ from typing import Optional, Callable, List
10
+ from dbagent.llm.base import BaseLLMProvider
11
+
12
+
13
+ class DeepSeekProvider(BaseLLMProvider):
14
+ """DeepSeek AI provider using direct HTTP REST API."""
15
+
16
+ FALLBACK_MODELS = [
17
+ "deepseek-chat",
18
+ "deepseek-reasoner",
19
+ ]
20
+
21
+ def __init__(self, api_key: Optional[str] = None, default_model: str = "deepseek-chat"):
22
+ if api_key is not None:
23
+ self.api_key = api_key
24
+ else:
25
+ self.api_key = os.getenv("DEEPSEEK_API_KEY")
26
+ self.default_model = default_model
27
+ self._session = requests.Session()
28
+ self._session.headers.update({
29
+ "User-Agent": "DB-Agent/0.7.0",
30
+ "content-type": "application/json",
31
+ })
32
+ if self.api_key:
33
+ self._session.headers.update({"Authorization": f"Bearer {self.api_key}"})
34
+
35
+ @property
36
+ def name(self) -> str:
37
+ return "deepseek"
38
+
39
+ def is_available(self) -> bool:
40
+ return bool(self.api_key)
41
+
42
+ def list_models(self) -> List[str]:
43
+ return self.FALLBACK_MODELS
44
+
45
+ def generate(
46
+ self,
47
+ prompt: str,
48
+ system_prompt: Optional[str] = None,
49
+ model: Optional[str] = None,
50
+ stream_callback: Optional[Callable[[str], None]] = None,
51
+ ) -> str:
52
+ if not self.api_key:
53
+ raise ValueError(
54
+ "DeepSeek API key is not configured. Set DEEPSEEK_API_KEY environment variable or run `db-agent config`."
55
+ )
56
+
57
+ requested_model = model or self.default_model
58
+ models_to_try = [requested_model]
59
+ for fb in self.FALLBACK_MODELS:
60
+ if fb != requested_model and fb not in models_to_try:
61
+ models_to_try.append(fb)
62
+
63
+ messages = []
64
+ if system_prompt:
65
+ messages.append({"role": "system", "content": system_prompt})
66
+ messages.append({"role": "user", "content": prompt})
67
+
68
+ payload = {
69
+ "model": requested_model,
70
+ "messages": messages,
71
+ "temperature": 0.1,
72
+ }
73
+
74
+ endpoint = "https://api.deepseek.com/v1/chat/completions"
75
+ last_error = None
76
+
77
+ for target_model in models_to_try:
78
+ payload["model"] = target_model
79
+ try:
80
+ if stream_callback:
81
+ payload["stream"] = True
82
+ response = self._session.post(endpoint, json=payload, stream=True, timeout=15)
83
+ if response.status_code == 200:
84
+ full_text = []
85
+ for line in response.iter_lines():
86
+ if line:
87
+ decoded = line.decode("utf-8")
88
+ if decoded.startswith("data: ") and not decoded.startswith("data: [DONE]"):
89
+ raw_json = decoded[6:]
90
+ try:
91
+ chunk = json.loads(raw_json)
92
+ choices = chunk.get("choices", [])
93
+ if choices:
94
+ delta = choices[0].get("delta", {})
95
+ t = delta.get("content", "")
96
+ if t:
97
+ full_text.append(t)
98
+ stream_callback(t)
99
+ except Exception:
100
+ pass
101
+ return "".join(full_text)
102
+ elif response.status_code in (404, 400, 503, 429):
103
+ last_error = f"Model {target_model} status {response.status_code}"
104
+ continue
105
+ else:
106
+ raise RuntimeError(f"DeepSeek API error ({response.status_code}): {response.text}")
107
+ else:
108
+ payload["stream"] = False
109
+ response = self._session.post(endpoint, json=payload, timeout=20)
110
+ if response.status_code == 200:
111
+ data = response.json()
112
+ choices = data.get("choices", [])
113
+ if choices:
114
+ return choices[0].get("message", {}).get("content", "")
115
+ return ""
116
+ elif response.status_code in (404, 400, 503, 429):
117
+ last_error = f"Model {target_model} status {response.status_code}"
118
+ continue
119
+ else:
120
+ raise RuntimeError(f"DeepSeek API error ({response.status_code}): {response.text}")
121
+ except requests.exceptions.Timeout:
122
+ last_error = f"Model {target_model} timed out"
123
+ continue
124
+ except Exception as e:
125
+ last_error = str(e)
126
+ continue
127
+
128
+ raise RuntimeError(f"DeepSeek generation error: {last_error or 'All model attempts failed'}")
@@ -1,6 +1,7 @@
1
1
  """
2
2
  LLM Provider Factory.
3
- Auto-detects available providers: Ollama (local) -> Gemini Free -> Groq Free -> OpenRouter Free -> Offline Fallback.
3
+ Auto-detects and instantiates providers:
4
+ Ollama (local) -> Claude (Anthropic) -> OpenAI (ChatGPT) -> DeepSeek -> Mistral -> Gemini -> Groq -> OpenRouter -> Mock.
4
5
  """
5
6
 
6
7
  from typing import Optional
@@ -8,6 +9,10 @@ from dbagent.config import ConfigManager
8
9
  from dbagent.llm.base import BaseLLMProvider
9
10
  from dbagent.llm.ollama_provider import OllamaProvider
10
11
  from dbagent.llm.gemini_provider import GeminiProvider
12
+ from dbagent.llm.anthropic_provider import ClaudeProvider
13
+ from dbagent.llm.openai_provider import OpenAIProvider
14
+ from dbagent.llm.deepseek_provider import DeepSeekProvider
15
+ from dbagent.llm.mistral_provider import MistralProvider
11
16
  from dbagent.llm.groq_provider import GroqProvider
12
17
  from dbagent.llm.openrouter_provider import OpenRouterProvider
13
18
  from dbagent.llm.mock_provider import MockProvider
@@ -23,17 +28,29 @@ def get_llm_provider(
23
28
 
24
29
  # 1. Explicit provider requested
25
30
  if provider_name:
26
- p = provider_name.lower()
27
- if p == "ollama":
31
+ p = provider_name.lower().strip()
32
+ if p in ["ollama", "local"]:
28
33
  base_url = cfg.get_setting("ollama_base_url", "http://localhost:11434")
29
34
  return OllamaProvider(base_url=base_url, default_model=model or "qwen2.5-coder")
30
- elif p == "gemini":
35
+ elif p in ["claude", "anthropic"]:
36
+ key = cfg.get_setting("anthropic_api_key") or cfg.get_setting("claude_api_key")
37
+ return ClaudeProvider(api_key=key, default_model=model or "claude-3-7-sonnet-20250219")
38
+ elif p in ["openai", "chatgpt", "gpt"]:
39
+ key = cfg.get_setting("openai_api_key")
40
+ return OpenAIProvider(api_key=key, default_model=model or "gpt-4o")
41
+ elif p in ["deepseek", "r1", "v3"]:
42
+ key = cfg.get_setting("deepseek_api_key")
43
+ return DeepSeekProvider(api_key=key, default_model=model or "deepseek-chat")
44
+ elif p in ["mistral", "codestral"]:
45
+ key = cfg.get_setting("mistral_api_key")
46
+ return MistralProvider(api_key=key, default_model=model or "codestral-latest")
47
+ elif p in ["gemini", "google"]:
31
48
  key = cfg.get_setting("gemini_api_key")
32
49
  return GeminiProvider(api_key=key, default_model=model or "gemini-3.6-flash")
33
- elif p == "groq":
50
+ elif p in ["groq"]:
34
51
  key = cfg.get_setting("groq_api_key")
35
52
  return GroqProvider(api_key=key, default_model=model or "llama-3.3-70b-versatile")
36
- elif p == "openrouter":
53
+ elif p in ["openrouter", "router"]:
37
54
  key = cfg.get_setting("openrouter_api_key")
38
55
  return OpenRouterProvider(api_key=key, default_model=model or "meta-llama/llama-3.3-70b-instruct:free")
39
56
  elif p in ["mock", "offline"]:
@@ -54,20 +71,40 @@ def get_llm_provider(
54
71
  if ollama.is_available():
55
72
  return ollama
56
73
 
57
- # B. Gemini if API key is present
74
+ # B. Claude / Anthropic if API key is present
75
+ claude_key = cfg.get_setting("anthropic_api_key") or cfg.get_setting("claude_api_key")
76
+ if claude_key:
77
+ return ClaudeProvider(api_key=claude_key, default_model=model or "claude-3-7-sonnet-20250219")
78
+
79
+ # C. OpenAI ChatGPT if API key is present
80
+ openai_key = cfg.get_setting("openai_api_key")
81
+ if openai_key:
82
+ return OpenAIProvider(api_key=openai_key, default_model=model or "gpt-4o")
83
+
84
+ # D. DeepSeek if API key is present
85
+ deepseek_key = cfg.get_setting("deepseek_api_key")
86
+ if deepseek_key:
87
+ return DeepSeekProvider(api_key=deepseek_key, default_model=model or "deepseek-chat")
88
+
89
+ # E. Mistral if API key is present
90
+ mistral_key = cfg.get_setting("mistral_api_key")
91
+ if mistral_key:
92
+ return MistralProvider(api_key=mistral_key, default_model=model or "codestral-latest")
93
+
94
+ # F. Gemini if API key is present
58
95
  gemini_key = cfg.get_setting("gemini_api_key")
59
96
  if gemini_key:
60
97
  return GeminiProvider(api_key=gemini_key)
61
98
 
62
- # C. Groq if API key is present
99
+ # G. Groq if API key is present
63
100
  groq_key = cfg.get_setting("groq_api_key")
64
101
  if groq_key:
65
102
  return GroqProvider(api_key=groq_key)
66
103
 
67
- # D. OpenRouter if API key is present
104
+ # H. OpenRouter if API key is present
68
105
  openrouter_key = cfg.get_setting("openrouter_api_key")
69
106
  if openrouter_key:
70
107
  return OpenRouterProvider(api_key=openrouter_key)
71
108
 
72
- # E. Fallback to Ollama or Mock
109
+ # Fallback to Ollama or Mock
73
110
  return ollama