dbagent-cli 0.5.2__tar.gz → 0.6.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.
Files changed (75) hide show
  1. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/PKG-INFO +1 -1
  2. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/__init__.py +1 -1
  3. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/cli.py +102 -38
  4. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/llm/gemini_provider.py +4 -5
  5. dbagent_cli-0.6.1/dbagent/ui/chat_screen.py +211 -0
  6. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent_cli.egg-info/PKG-INFO +1 -1
  7. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent_cli.egg-info/SOURCES.txt +1 -0
  8. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/pyproject.toml +1 -1
  9. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/README.md +0 -0
  10. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/agent/confidence.py +0 -0
  11. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/agent/conversation.py +0 -0
  12. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/agent/doctor.py +0 -0
  13. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/agent/error_classifier.py +0 -0
  14. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/agent/generator.py +0 -0
  15. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/agent/pipeline.py +0 -0
  16. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/agent/recommender.py +0 -0
  17. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/agent/validator.py +0 -0
  18. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/config.py +0 -0
  19. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/connectors/base.py +0 -0
  20. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/connectors/factory.py +0 -0
  21. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/connectors/mongo.py +0 -0
  22. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/connectors/relational.py +0 -0
  23. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/execution/__init__.py +0 -0
  24. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/execution/cache.py +0 -0
  25. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/execution/changeset.py +0 -0
  26. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/execution/cost.py +0 -0
  27. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/execution/recovery.py +0 -0
  28. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/execution/sandbox.py +0 -0
  29. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/execution/streaming.py +0 -0
  30. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/llm/auto_setup.py +0 -0
  31. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/llm/base.py +0 -0
  32. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/llm/factory.py +0 -0
  33. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/llm/groq_provider.py +0 -0
  34. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/llm/mock_provider.py +0 -0
  35. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/llm/ollama_provider.py +0 -0
  36. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/llm/openrouter_provider.py +0 -0
  37. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/schema/cache.py +0 -0
  38. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/schema/formatter.py +0 -0
  39. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/schema/graph.py +0 -0
  40. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/schema/models.py +0 -0
  41. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/schema/retriever.py +0 -0
  42. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/schema/selector.py +0 -0
  43. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/security/__init__.py +0 -0
  44. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/security/audit.py +0 -0
  45. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/security/environment.py +0 -0
  46. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/security/masking.py +0 -0
  47. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/security/modes.py +0 -0
  48. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/security/rbac.py +0 -0
  49. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/semantic/__init__.py +0 -0
  50. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/semantic/dictionary.py +0 -0
  51. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/semantic/templates.py +0 -0
  52. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/sql/__init__.py +0 -0
  53. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/sql/optimizer.py +0 -0
  54. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/sql/validator.py +0 -0
  55. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/ui/console.py +0 -0
  56. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/ui/grid_window.py +0 -0
  57. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/ui/viewer.py +0 -0
  58. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent/ui/visualizer.py +0 -0
  59. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent_cli.egg-info/dependency_links.txt +0 -0
  60. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent_cli.egg-info/entry_points.txt +0 -0
  61. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent_cli.egg-info/requires.txt +0 -0
  62. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/dbagent_cli.egg-info/top_level.txt +0 -0
  63. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/setup.cfg +0 -0
  64. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/tests/test_cache.py +0 -0
  65. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/tests/test_cli.py +0 -0
  66. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/tests/test_connectors.py +0 -0
  67. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/tests/test_doctor_and_viz.py +0 -0
  68. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/tests/test_generator.py +0 -0
  69. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/tests/test_graph.py +0 -0
  70. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/tests/test_recovery.py +0 -0
  71. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/tests/test_safety.py +0 -0
  72. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/tests/test_sandbox.py +0 -0
  73. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/tests/test_schema.py +0 -0
  74. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/tests/test_security.py +0 -0
  75. {dbagent_cli-0.5.2 → dbagent_cli-0.6.1}/tests/test_semantic.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dbagent-cli
3
- Version: 0.5.2
3
+ Version: 0.6.1
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.5.2"
5
+ __version__ = "0.6.1"
6
6
  __author__ = "Santhosh Gupta"
@@ -14,9 +14,10 @@ import typer
14
14
  from rich.prompt import Prompt, Confirm
15
15
  from rich.table import Table
16
16
  from rich.markdown import Markdown
17
- from rich.panel import Panel
18
- from prompt_toolkit import PromptSession
17
+ from prompt_toolkit import PromptSession, HTML
19
18
  from prompt_toolkit.history import FileHistory
19
+ from prompt_toolkit.completion import WordCompleter
20
+ from prompt_toolkit.styles import Style
20
21
 
21
22
  from dbagent import __version__, __author__
22
23
  from dbagent.config import ConfigManager
@@ -63,6 +64,13 @@ from dbagent.ui.console import (
63
64
  )
64
65
  from dbagent.ui.viewer import SchemaViewer
65
66
  from dbagent.ui.grid_window import open_desktop_grid_window, open_browser_grid_window
67
+ from dbagent.ui.chat_screen import (
68
+ clear_screen,
69
+ render_chat_header,
70
+ render_user_turn,
71
+ render_assistant_turn,
72
+ render_session_history,
73
+ )
66
74
 
67
75
  app = typer.Typer(
68
76
  name="db-agent",
@@ -447,27 +455,38 @@ def chat_command(
447
455
  dialect = connector.engine.dialect.name
448
456
  current_mode = OperationMode(mode.upper()) if mode.upper() in OperationMode.__members__ else OperationMode.CONFIRM
449
457
 
450
- console.print(f"[bold green]Connected:[/bold green] {db_name} ({dialect}) - {len(table_names)} tables")
451
- console.print(f"[bold cyan]AI:[/bold cyan] {llm.name} | [bold yellow]Mode:[/bold yellow] {current_mode.value}")
452
- console.print("[dim]Type your question in plain English or use colon commands (e.g. :help, :tables, :doctor).[/dim]")
453
-
454
458
  # Display AI recommendations on connect (instant <0.1ms with zero network overhead)
459
+ recs = []
455
460
  try:
456
461
  cached_schema = cache_mgr.load_cached_schema(db_url)
457
462
  if cached_schema:
458
463
  recs = QueryRecommender.generate_recommendations(cached_schema, max_suggestions=3)
459
464
  else:
460
465
  recs = QueryRecommender.generate_from_table_names(table_names, max_suggestions=3)
461
- if recs:
462
- console.print("\n[bold cyan]💡 Try asking:[/bold cyan]")
463
- for r in recs:
464
- console.print(f" → [italic]{r}[/italic]")
465
466
  except Exception:
466
467
  pass
467
- console.print("")
468
+
469
+ # Clear screen and render Claude-style clean header card
470
+ clear_screen()
471
+ render_chat_header(
472
+ db_name=db_name,
473
+ dialect=dialect,
474
+ table_count=len(table_names),
475
+ ai_provider=llm.name,
476
+ ai_model=model or getattr(llm, "default_model", None),
477
+ mode=current_mode.value,
478
+ recommendations=recs,
479
+ )
468
480
 
469
481
  history_file = config_mgr.config_dir / "chat_history.txt"
470
- session = PromptSession(history=FileHistory(str(history_file)))
482
+ chat_completer = WordCompleter([
483
+ ":help", ":tables", ":schema", ":dt", ":table", ":fk", ":path", ":join",
484
+ ":doctor", ":mode", ":changes", ":provider", ":model", ":models",
485
+ ":page", ":card", ":wide", ":scroll", ":window", ":grid", ":web",
486
+ ":explain", ":history", ":auto", ":noauto", ":run", ":clear", ":export",
487
+ ":exit", ":quit", ":q",
488
+ ], ignore_case=True)
489
+ session = PromptSession(history=FileHistory(str(history_file)), completer=chat_completer)
471
490
 
472
491
  auto_execute = True
473
492
  conversation_ctx = ConversationContext()
@@ -488,7 +507,7 @@ def chat_command(
488
507
 
489
508
  while True:
490
509
  try:
491
- user_input = session.prompt("db-agent> ").strip()
510
+ user_input = session.prompt(HTML("<ansicyan><b>db-agent</b></ansicyan> <ansiblue><b>❯</b></ansiblue> ")).strip()
492
511
  if not user_input:
493
512
  continue
494
513
 
@@ -512,9 +531,9 @@ def chat_command(
512
531
  console.print(" [bold]:doctor[/bold] -> Run database health diagnostics")
513
532
  console.print(" [bold]:mode <name>[/bold] -> Switch mode (SAFE, READ_ONLY, CONFIRM, SANDBOX, ADMIN)")
514
533
  console.print(" [bold]:changes[/bold] -> List recent write ChangeSets")
515
- console.print(" [bold]:provider <name>[/bold] -> Switch AI provider (ollama, gemini, groq, openrouter)")
516
- console.print(" [bold]:model <name>[/bold] -> Switch active model name (e.g. gemini-3.6-flash)")
517
- console.print(" [bold]:models[/bold] -> Show active AI provider and model status")
534
+ console.print(" [bold]:provider <name>[/bold] -> Switch AI provider (gemini, ollama, groq, openrouter)")
535
+ console.print(" [bold]:models[/bold] -> View list of available AI models & speed benchmarks")
536
+ console.print(" [bold]:model <num|name>[/bold] -> Switch active model (:model 1, :model 3.7, :model 3.6)")
518
537
  console.print(" [bold]:page <n>[/bold] -> View column page <n> for wide multi-column tables")
519
538
  console.print(" [bold]:card[/bold] -> View last result in expanded vertical card format (all columns)")
520
539
  console.print(" [bold]:wide[/bold] -> Force-show all columns in terminal (zoom out)")
@@ -680,21 +699,53 @@ def chat_command(
680
699
  print_error(f"Unknown provider '{cmd_arg}'. Supported: ollama, gemini, groq, openrouter")
681
700
  continue
682
701
 
683
- if cmd_name == ":model":
684
- if not cmd_arg:
685
- curr_model = model or getattr(llm, "default_model", "default")
686
- console.print(f"Active Model: [bold green]{curr_model}[/bold green] (Provider: {llm.name})")
687
- console.print("[dim]Usage: [bold]:model <model_name>[/bold][/dim]")
702
+ if cmd_name in [":model", ":models", ":providers", ":ai"]:
703
+ avail_models = llm.list_models() if hasattr(llm, "list_models") else []
704
+ curr_model = model or getattr(llm, "default_model", "default")
705
+
706
+ # If no arg passed or :models/:providers called, show rich model selection menu
707
+ if not cmd_arg or cmd_name in [":models", ":providers", ":ai"]:
708
+ console.print(f"\n[bold cyan]Active AI Provider:[/bold cyan] [bold white]{llm.name}[/bold white]")
709
+ console.print(f"[bold cyan]Active Model:[/bold cyan] [bold green]{curr_model}[/bold green]\n")
710
+ if avail_models:
711
+ console.print(f"[bold]Available Models for {llm.name}:[/bold]")
712
+ model_descriptions = {
713
+ "gemini-flash-lite-latest": "⚡ Ultra-Fast (~0.8s) • Recommended for instant chat & queries",
714
+ "gemini-3.6-flash": "⚡ High Quality (~3.5s) • Strong SQL reasoning & accuracy",
715
+ "gemini-3.7-flash": "🧠 Deep Reasoning (~13s) • Latest flagship model",
716
+ "gemini-3.1-flash-lite": "⚡ Fast Lite (~4.0s) • Low latency",
717
+ "gemini-flash-latest": "⚡ Standard Flash (~7.0s)",
718
+ }
719
+ for idx, m_name in enumerate(avail_models, 1):
720
+ is_active = " [bold green]✓ Active[/bold green]" if m_name == curr_model else ""
721
+ desc = model_descriptions.get(m_name, "")
722
+ desc_str = f" - [dim]{desc}[/dim]" if desc else ""
723
+ 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")
688
725
  continue
689
- model = cmd_arg
690
- print_success(f"Switched active model to [bold cyan]{model}[/bold cyan] (Provider: {llm.name})")
691
- continue
692
726
 
693
- if cmd_name in [":models", ":providers", ":ai"]:
694
- curr_model = model or getattr(llm, "default_model", "default")
695
- console.print(f"\n[bold cyan]Active AI Provider:[/bold cyan] [bold white]{llm.name}[/bold white]")
696
- console.print(f"[bold cyan]Active Model:[/bold cyan] [bold green]{curr_model}[/bold green]")
697
- console.print("[dim]Switch anytime: [bold]:provider <ollama|gemini|groq|openrouter>[/bold] or [bold]:model <name>[/bold][/dim]\n")
727
+ # Handle numeric shortcut or name shortcut
728
+ target_model = cmd_arg.strip()
729
+ if target_model.isdigit() and avail_models:
730
+ choice_idx = int(target_model) - 1
731
+ if 0 <= choice_idx < len(avail_models):
732
+ target_model = avail_models[choice_idx]
733
+ elif target_model in ["3.7", "37"]:
734
+ target_model = "gemini-3.7-flash"
735
+ elif target_model in ["3.6", "36"]:
736
+ 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"
741
+ elif avail_models:
742
+ matches = [m for m in avail_models if target_model.lower() in m.lower()]
743
+ if matches:
744
+ target_model = matches[0]
745
+
746
+ model = target_model
747
+ pipeline.model = model
748
+ print_success(f"Switched active model to [bold cyan]{model}[/bold cyan] (Provider: {llm.name})")
698
749
  continue
699
750
 
700
751
  if cmd_name == ":explain":
@@ -710,14 +761,20 @@ def chat_command(
710
761
  continue
711
762
 
712
763
  if cmd_name == ":history":
713
- if not query_history:
714
- print_warning("No query history in this session.")
715
- continue
716
- for i, r in enumerate(query_history[-10:], 1):
717
- status = "[green]OK[/green]" if r.success else "[red]FAIL[/red]"
718
- sql_str = r.sql or ""
719
- sql_preview = (sql_str[:80] + "...") if len(sql_str) > 80 else sql_str
720
- console.print(f" {i}. {status} [{r.query_type}] {sql_preview}")
764
+ render_session_history(query_history)
765
+ continue
766
+
767
+ if cmd_name in [":clear", ":cls"]:
768
+ clear_screen()
769
+ render_chat_header(
770
+ db_name=db_name,
771
+ dialect=dialect,
772
+ table_count=len(table_names),
773
+ ai_provider=llm.name,
774
+ ai_model=model or getattr(llm, "default_model", None),
775
+ mode=current_mode.value,
776
+ recommendations=recs,
777
+ )
721
778
  continue
722
779
 
723
780
  if cmd_name in [":page", ":cols", ":p"]:
@@ -817,10 +874,17 @@ def chat_command(
817
874
  pipeline.mode = current_mode
818
875
  pipeline.auto_execute = auto_execute
819
876
 
877
+ render_user_turn(user_input)
878
+
820
879
  with DynamicSpinner():
821
880
  result = pipeline.run(user_prompt=user_input)
822
881
 
823
- _display_pipeline_result(result, show_sql=True)
882
+ render_assistant_turn(
883
+ result,
884
+ show_sql=True,
885
+ ai_provider=llm.name,
886
+ ai_model=model or getattr(llm, "default_model", None),
887
+ )
824
888
 
825
889
  last_result = result
826
890
  query_history.append(result)
@@ -14,19 +14,18 @@ class GeminiProvider(BaseLLMProvider):
14
14
  """Google Gemini LLM provider (Free Tier available)."""
15
15
 
16
16
  FALLBACK_MODELS = [
17
+ "gemini-flash-lite-latest",
17
18
  "gemini-3.6-flash",
18
19
  "gemini-3.7-flash",
19
- "gemini-3.5-flash",
20
+ "gemini-3.1-flash-lite",
20
21
  "gemini-flash-latest",
21
- "gemini-pro-latest",
22
- "gemini-2.5-flash",
23
22
  ]
24
23
 
25
24
  def __init__(self, api_key: Optional[str] = None, default_model: str = "gemini-3.6-flash"):
26
25
  self.api_key = api_key or os.getenv("GEMINI_API_KEY")
27
26
  self.default_model = default_model
28
27
  self._session = requests.Session()
29
- self._session.headers.update({"User-Agent": "DB-Agent/0.5.0"})
28
+ self._session.headers.update({"User-Agent": "DB-Agent/0.6.0"})
30
29
 
31
30
  @property
32
31
  def name(self) -> str:
@@ -117,7 +116,7 @@ class GeminiProvider(BaseLLMProvider):
117
116
  else:
118
117
  raise RuntimeError(f"Gemini API error ({response.status_code}): {response.text}")
119
118
  else:
120
- response = self._session.post(endpoint, json=payload, timeout=30)
119
+ response = self._session.post(endpoint, json=payload, timeout=15)
121
120
  if response.status_code == 200:
122
121
  data = response.json()
123
122
  candidates = data.get("candidates", [])
@@ -0,0 +1,211 @@
1
+ """
2
+ Claude-Style Clean Chat Screen UI Component for DB-Agent.
3
+ Renders clean status headers, message turn cards, visual history tables, and styled prompt layouts.
4
+ """
5
+
6
+ import os
7
+ from typing import List, Optional, Dict, Any
8
+ from rich.console import Console
9
+ from rich.panel import Panel
10
+ from rich.table import Table
11
+ from rich.text import Text
12
+ from rich.markdown import Markdown
13
+ from rich.syntax import Syntax
14
+ from rich.rule import Rule
15
+
16
+ from dbagent import __version__
17
+ from dbagent.agent.pipeline import PipelineResult
18
+ from dbagent.ui.console import print_results_table, print_error, print_warning, print_info
19
+ from dbagent.ui.visualizer import ResultVisualizer
20
+
21
+ console = Console()
22
+
23
+
24
+ def clear_screen() -> None:
25
+ """Clear terminal screen cleanly across Windows, macOS, and Linux."""
26
+ os.system("cls" if os.name == "nt" else "clear")
27
+
28
+
29
+ def render_chat_header(
30
+ db_name: str,
31
+ dialect: str,
32
+ table_count: int,
33
+ ai_provider: str,
34
+ ai_model: Optional[str] = None,
35
+ mode: str = "CONFIRM",
36
+ recommendations: Optional[List[str]] = None,
37
+ ) -> None:
38
+ """Render the top Claude-style status header card."""
39
+ model_str = f" ({ai_model})" if ai_model else ""
40
+
41
+ header_content = Text()
42
+ header_content.append("⚡ DB-AGENT ", style="bold cyan")
43
+ header_content.append(f"v{__version__}\n", style="bold green")
44
+
45
+ header_content.append("🟢 Connected: ", style="bold green")
46
+ header_content.append(f"{db_name} ", style="bold white")
47
+ header_content.append(f"({dialect}) • {table_count} tables\n", style="dim")
48
+
49
+ header_content.append("🤖 AI: ", style="bold cyan")
50
+ header_content.append(f"{ai_provider}{model_str}", style="bold white")
51
+ header_content.append(" | ", style="dim")
52
+ header_content.append("🛡️ Mode: ", style="bold yellow")
53
+ header_content.append(f"{mode}", style="bold yellow")
54
+ header_content.append(" | ", style="dim")
55
+ header_content.append("⌨️ Type ", style="dim")
56
+ header_content.append(":help", style="bold cyan")
57
+ header_content.append(" for commands", style="dim")
58
+
59
+ panel = Panel(
60
+ header_content,
61
+ border_style="cyan",
62
+ padding=(0, 1),
63
+ )
64
+ console.print(panel)
65
+
66
+ if recommendations:
67
+ console.print("[dim italic]💡 Try asking:[/dim italic]")
68
+ for rec in recommendations[:3]:
69
+ console.print(f" [dim]→[/dim] [cyan]{rec}[/cyan]")
70
+ console.print("")
71
+
72
+
73
+ def render_user_turn(user_prompt: str) -> None:
74
+ """Render a user turn message bubble/card."""
75
+ user_text = Text()
76
+ user_text.append("💬 You: ", style="bold blue")
77
+ user_text.append(user_prompt, style="bold white")
78
+ console.print("")
79
+ console.print(Rule(style="dim blue"))
80
+ console.print(user_text)
81
+
82
+
83
+ def render_assistant_turn(
84
+ result: PipelineResult,
85
+ show_sql: bool = True,
86
+ ai_provider: str = "gemini",
87
+ ai_model: Optional[str] = None,
88
+ ) -> None:
89
+ """Render the assistant response card with SQL, data grid, and execution timing."""
90
+ model_display = f" ({ai_model})" if ai_model else ""
91
+ timing_str = f"⚡ {result.execution_time_ms / 1000:.1f}s" if result.execution_time_ms > 0 else ""
92
+
93
+ # Conversational Chat Response
94
+ if result.is_chat and result.message:
95
+ console.print(f"\n[bold cyan]🤖 DB-Agent{model_display}[/bold cyan] [dim]{timing_str}[/dim]")
96
+ console.print(Markdown(result.message))
97
+ console.print("")
98
+ return
99
+
100
+ # Error Response
101
+ if result.error:
102
+ console.print(f"\n[bold red]🤖 DB-Agent (Error)[/bold red] [dim]{timing_str}[/dim]")
103
+ if result.friendly_error:
104
+ print_error(result.friendly_error)
105
+ else:
106
+ print_error(f"Error: {result.error}")
107
+
108
+ if result.error_suggestions:
109
+ console.print(f"[dim]Did you mean: {', '.join(result.error_suggestions)}[/dim]")
110
+
111
+ if result.sql:
112
+ console.print("[dim]Generated SQL:[/dim]")
113
+ syntax = Syntax(result.sql.strip(), "sql", theme="monokai", line_numbers=False)
114
+ console.print(Panel(syntax, border_style="red", padding=(0, 1)))
115
+ if result.retries > 0:
116
+ console.print(f"[dim](Retried {result.retries} time(s))[/dim]")
117
+ console.print("")
118
+ return
119
+
120
+ # Executed Result Response
121
+ if result.was_executed:
122
+ console.print(f"\n[bold cyan]🤖 DB-Agent{model_display}[/bold cyan] [dim]{timing_str}[/dim]")
123
+
124
+ if show_sql and result.sql:
125
+ sql_clean = result.sql.strip()
126
+ syntax = Syntax(sql_clean, "sql", theme="monokai", line_numbers=False)
127
+ console.print(Panel(syntax, title="[dim]SQL Statement[/dim]", border_style="dim", padding=(0, 1)))
128
+ if result.is_cached:
129
+ console.print(f"[dim italic]⚡ Cached result ({round(result.cache_age_seconds, 1)}s old)[/dim italic]")
130
+
131
+ if result.has_results:
132
+ print_results_table(result.columns, result.rows, title="Results")
133
+
134
+ # ASCII visualization if detected
135
+ if result.visualization and result.visualization.chart_type == "BAR":
136
+ console.print("")
137
+ bar_chart = ResultVisualizer.render_ascii_bar_chart(
138
+ result.visualization.x_column,
139
+ result.visualization.y_column,
140
+ result.rows,
141
+ )
142
+ console.print(bar_chart)
143
+ console.print("")
144
+ else:
145
+ print_info("Query executed successfully. No rows returned.")
146
+
147
+ if result.confidence:
148
+ console.print(result.confidence.format_summary())
149
+
150
+ if result.retries > 0:
151
+ console.print(f"[dim](Auto-corrected after {result.retries} retry(ies))[/dim]")
152
+
153
+ # Recovery details for writes
154
+ if result.change_id:
155
+ console.print(f"\n[bold green]✓ Change committed successfully[/bold green]")
156
+ console.print(f" * Change ID: [bold white]{result.change_id}[/bold white]")
157
+ if result.recovery_status == "AVAILABLE":
158
+ console.print(f" * Recovery: [bold cyan]{result.recovery_message}[/bold cyan]")
159
+ console.print(f" * Rollback command: [bold]db-agent rollback {result.change_id}[/bold]\n")
160
+ else:
161
+ console.print(f" * Recovery: [yellow]{result.recovery_message or result.recovery_status}[/yellow]\n")
162
+ console.print("")
163
+ elif result.needs_confirmation and not result.was_executed:
164
+ if result.sql:
165
+ syntax = Syntax(result.sql.strip(), "sql", theme="monokai", line_numbers=False)
166
+ console.print(Panel(syntax, title=f"Generated {result.query_type.upper()} Query", border_style="yellow"))
167
+ console.print("[dim]This query was not executed (requires confirmation).[/dim]\n")
168
+ elif result.sql:
169
+ syntax = Syntax(result.sql.strip(), "sql", theme="monokai", line_numbers=False)
170
+ console.print(Panel(syntax, title="Generated SQL", border_style="dim"))
171
+ console.print("")
172
+
173
+
174
+ def render_session_history(query_history: List[PipelineResult]) -> None:
175
+ """Render a clean visual session history table."""
176
+ if not query_history:
177
+ console.print("[dim]No query history in this session yet.[/dim]\n")
178
+ return
179
+
180
+ table = Table(title="📜 Session Query History", border_style="cyan", show_lines=True)
181
+ table.add_column("#", style="dim", width=4, justify="right")
182
+ table.add_column("Status", width=8, justify="center")
183
+ table.add_column("Type", width=8, justify="center")
184
+ table.add_column("Query / Message Preview", style="white", min_width=30)
185
+ table.add_column("Rows", style="cyan", width=6, justify="right")
186
+ table.add_column("Time", style="green", width=8, justify="right")
187
+
188
+ for i, r in enumerate(query_history, 1):
189
+ if r.is_chat:
190
+ status = "[bold blue]CHAT[/bold blue]"
191
+ q_type = "chat"
192
+ preview = (r.message or "")[:60] + "..." if len(r.message or "") > 60 else (r.message or "")
193
+ rows = "-"
194
+ elif r.success:
195
+ status = "[bold green]OK[/bold green]"
196
+ q_type = r.query_type
197
+ sql_str = r.sql or ""
198
+ preview = (sql_str[:60] + "...") if len(sql_str) > 60 else sql_str
199
+ rows = str(len(r.rows)) if r.has_results else "0"
200
+ else:
201
+ status = "[bold red]FAIL[/bold red]"
202
+ q_type = r.query_type or "error"
203
+ preview = (r.error or r.sql or "")[:60]
204
+ rows = "0"
205
+
206
+ time_str = f"{r.execution_time_ms / 1000:.1f}s" if r.execution_time_ms > 0 else "-"
207
+ table.add_row(str(i), status, q_type, preview, rows, time_str)
208
+
209
+ console.print("")
210
+ console.print(table)
211
+ console.print("")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dbagent-cli
3
- Version: 0.5.2
3
+ Version: 0.6.1
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
@@ -48,6 +48,7 @@ dbagent/semantic/templates.py
48
48
  dbagent/sql/__init__.py
49
49
  dbagent/sql/optimizer.py
50
50
  dbagent/sql/validator.py
51
+ dbagent/ui/chat_screen.py
51
52
  dbagent/ui/console.py
52
53
  dbagent/ui/grid_window.py
53
54
  dbagent/ui/viewer.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dbagent-cli"
7
- version = "0.5.2"
7
+ version = "0.6.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