dbagent-cli 0.3.8__tar.gz → 0.4.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.
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/PKG-INFO +2 -2
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/__init__.py +1 -1
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/cli.py +17 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/llm/gemini_provider.py +14 -12
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/ui/console.py +1 -1
- dbagent_cli-0.4.0/dbagent/ui/grid_window.py +245 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent_cli.egg-info/PKG-INFO +2 -2
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent_cli.egg-info/SOURCES.txt +1 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/pyproject.toml +2 -2
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/README.md +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/agent/confidence.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/agent/conversation.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/agent/doctor.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/agent/error_classifier.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/agent/generator.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/agent/pipeline.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/agent/recommender.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/agent/validator.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/config.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/connectors/base.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/connectors/factory.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/connectors/mongo.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/connectors/relational.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/execution/__init__.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/execution/cache.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/execution/changeset.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/execution/cost.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/execution/recovery.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/execution/sandbox.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/execution/streaming.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/llm/auto_setup.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/llm/base.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/llm/factory.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/llm/groq_provider.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/llm/mock_provider.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/llm/ollama_provider.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/llm/openrouter_provider.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/schema/cache.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/schema/formatter.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/schema/graph.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/schema/models.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/schema/retriever.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/schema/selector.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/security/__init__.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/security/audit.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/security/environment.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/security/masking.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/security/modes.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/security/rbac.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/semantic/__init__.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/semantic/dictionary.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/semantic/templates.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/sql/__init__.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/sql/optimizer.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/sql/validator.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/ui/viewer.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent/ui/visualizer.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent_cli.egg-info/dependency_links.txt +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent_cli.egg-info/entry_points.txt +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent_cli.egg-info/requires.txt +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/dbagent_cli.egg-info/top_level.txt +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/setup.cfg +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/tests/test_cache.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/tests/test_cli.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/tests/test_connectors.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/tests/test_doctor_and_viz.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/tests/test_generator.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/tests/test_graph.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/tests/test_recovery.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/tests/test_safety.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/tests/test_sandbox.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/tests/test_schema.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/tests/test_security.py +0 -0
- {dbagent_cli-0.3.8 → dbagent_cli-0.4.0}/tests/test_semantic.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dbagent-cli
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Enterprise AI Database Copilot with Interactive Data Grid Window & High-Performance LLM (CLI)
|
|
5
5
|
Author: Santhosh Gupta
|
|
6
6
|
Requires-Python: >=3.9
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
@@ -60,6 +60,7 @@ from dbagent.ui.console import (
|
|
|
60
60
|
open_interactive_scroller,
|
|
61
61
|
)
|
|
62
62
|
from dbagent.ui.viewer import SchemaViewer
|
|
63
|
+
from dbagent.ui.grid_window import open_desktop_grid_window, open_browser_grid_window
|
|
63
64
|
|
|
64
65
|
app = typer.Typer(
|
|
65
66
|
name="db-agent",
|
|
@@ -636,6 +637,22 @@ def chat_command(
|
|
|
636
637
|
print_results_table(last_result.columns, last_result.rows, title="All Columns (Wide View)", show_all=True)
|
|
637
638
|
continue
|
|
638
639
|
|
|
640
|
+
if cmd_name in [":window", ":gui", ":grid", ":w"]:
|
|
641
|
+
if not last_result or not last_result.rows:
|
|
642
|
+
print_warning("No previous query results to display.")
|
|
643
|
+
continue
|
|
644
|
+
print_info("Opening desktop interactive window with horizontal & vertical scrollbars...")
|
|
645
|
+
open_desktop_grid_window(last_result.columns, last_result.rows, title=f"DB-Agent Data Grid")
|
|
646
|
+
continue
|
|
647
|
+
|
|
648
|
+
if cmd_name in [":web", ":browser", ":html"]:
|
|
649
|
+
if not last_result or not last_result.rows:
|
|
650
|
+
print_warning("No previous query results to display.")
|
|
651
|
+
continue
|
|
652
|
+
print_info("Opening browser interactive data table...")
|
|
653
|
+
open_browser_grid_window(last_result.columns, last_result.rows, title=f"DB-Agent Data View")
|
|
654
|
+
continue
|
|
655
|
+
|
|
639
656
|
if cmd_name in [":scroll", ":scroller", ":pager"]:
|
|
640
657
|
if not last_result or not last_result.rows:
|
|
641
658
|
print_warning("No previous query results to scroll.")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Google Gemini Free Tier LLM Provider.
|
|
3
|
-
Uses
|
|
3
|
+
Uses gemini-3.6-flash (current primary), gemini-3.7-flash, gemini-3.5-flash, gemini-flash-latest.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import json
|
|
@@ -14,13 +14,15 @@ class GeminiProvider(BaseLLMProvider):
|
|
|
14
14
|
"""Google Gemini LLM provider (Free Tier available)."""
|
|
15
15
|
|
|
16
16
|
FALLBACK_MODELS = [
|
|
17
|
+
"gemini-3.6-flash",
|
|
18
|
+
"gemini-3.7-flash",
|
|
19
|
+
"gemini-3.5-flash",
|
|
20
|
+
"gemini-flash-latest",
|
|
21
|
+
"gemini-pro-latest",
|
|
17
22
|
"gemini-2.5-flash",
|
|
18
|
-
"gemini-2.0-flash",
|
|
19
|
-
"gemini-1.5-flash",
|
|
20
|
-
"gemini-1.5-pro",
|
|
21
23
|
]
|
|
22
24
|
|
|
23
|
-
def __init__(self, api_key: Optional[str] = None, default_model: str = "gemini-
|
|
25
|
+
def __init__(self, api_key: Optional[str] = None, default_model: str = "gemini-3.6-flash"):
|
|
24
26
|
self.api_key = api_key or os.getenv("GEMINI_API_KEY")
|
|
25
27
|
self.default_model = default_model
|
|
26
28
|
|
|
@@ -82,7 +84,7 @@ class GeminiProvider(BaseLLMProvider):
|
|
|
82
84
|
|
|
83
85
|
try:
|
|
84
86
|
if stream_callback:
|
|
85
|
-
response = requests.post(endpoint, json=payload, stream=True, timeout=
|
|
87
|
+
response = requests.post(endpoint, json=payload, stream=True, timeout=10)
|
|
86
88
|
if response.status_code == 200:
|
|
87
89
|
full_text = []
|
|
88
90
|
for line in response.iter_lines():
|
|
@@ -102,13 +104,13 @@ class GeminiProvider(BaseLLMProvider):
|
|
|
102
104
|
except Exception:
|
|
103
105
|
pass
|
|
104
106
|
return "".join(full_text)
|
|
105
|
-
elif response.status_code in (404, 400):
|
|
106
|
-
last_error = f"Model {target_model}
|
|
107
|
+
elif response.status_code in (404, 400, 503, 429):
|
|
108
|
+
last_error = f"Model {target_model} status {response.status_code}"
|
|
107
109
|
continue
|
|
108
110
|
else:
|
|
109
111
|
raise RuntimeError(f"Gemini API error ({response.status_code}): {response.text}")
|
|
110
112
|
else:
|
|
111
|
-
response = requests.post(endpoint, json=payload, timeout=
|
|
113
|
+
response = requests.post(endpoint, json=payload, timeout=10)
|
|
112
114
|
if response.status_code == 200:
|
|
113
115
|
data = response.json()
|
|
114
116
|
candidates = data.get("candidates", [])
|
|
@@ -116,13 +118,13 @@ class GeminiProvider(BaseLLMProvider):
|
|
|
116
118
|
parts = candidates[0].get("content", {}).get("parts", [])
|
|
117
119
|
return "".join(p.get("text", "") for p in parts)
|
|
118
120
|
return ""
|
|
119
|
-
elif response.status_code in (404, 400):
|
|
120
|
-
last_error = f"Model {target_model}
|
|
121
|
+
elif response.status_code in (404, 400, 503, 429):
|
|
122
|
+
last_error = f"Model {target_model} status {response.status_code}"
|
|
121
123
|
continue
|
|
122
124
|
else:
|
|
123
125
|
raise RuntimeError(f"Gemini API error ({response.status_code}): {response.text}")
|
|
124
126
|
except requests.exceptions.Timeout:
|
|
125
|
-
last_error = f"Model {target_model}
|
|
127
|
+
last_error = f"Model {target_model} timed out"
|
|
126
128
|
continue
|
|
127
129
|
except Exception as e:
|
|
128
130
|
last_error = str(e)
|
|
@@ -113,7 +113,7 @@ def print_results_table(
|
|
|
113
113
|
|
|
114
114
|
if total_pages > 1 and not show_all:
|
|
115
115
|
console.print(
|
|
116
|
-
f"[dim]⚡ [bold]:
|
|
116
|
+
f"[dim]⚡ [bold]:window[/bold] (GUI scroll window) | [bold]:page <1-{total_pages}>[/bold] | [bold]:card[/bold] (vertical) | [bold]:wide[/bold] (zoom-out)[/dim]"
|
|
117
117
|
)
|
|
118
118
|
console.print(f"[bold green]Total rows:[/bold green] {len(rows)}\n")
|
|
119
119
|
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Interactive Desktop and Browser Data Grid Viewers with Horizontal & Vertical Scrollbars.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import tempfile
|
|
7
|
+
import webbrowser
|
|
8
|
+
import threading
|
|
9
|
+
from typing import List, Dict, Any, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def open_desktop_grid_window(
|
|
13
|
+
columns: List[str],
|
|
14
|
+
rows: List[Dict[str, Any]],
|
|
15
|
+
title: str = "DB-Agent Interactive Data Viewer",
|
|
16
|
+
) -> None:
|
|
17
|
+
"""
|
|
18
|
+
Opens a native desktop GUI window with smooth horizontal and vertical scrollbars,
|
|
19
|
+
search/filter bar, and resizable columns. Runs in a separate thread.
|
|
20
|
+
"""
|
|
21
|
+
if not columns or not rows:
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
def _launch_gui():
|
|
25
|
+
try:
|
|
26
|
+
import tkinter as tk
|
|
27
|
+
from tkinter import ttk, messagebox, filedialog
|
|
28
|
+
import csv
|
|
29
|
+
|
|
30
|
+
root = tk.Tk()
|
|
31
|
+
root.title(f"{title} - ({len(rows)} rows, {len(columns)} columns)")
|
|
32
|
+
root.geometry("1100x650")
|
|
33
|
+
root.minsize(700, 400)
|
|
34
|
+
|
|
35
|
+
# Modern Styling
|
|
36
|
+
style = ttk.Style()
|
|
37
|
+
try:
|
|
38
|
+
style.theme_use("clam")
|
|
39
|
+
except Exception:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
style.configure("Treeview.Heading", font=("Segoe UI", 10, "bold"), background="#2c3e50", foreground="white")
|
|
43
|
+
style.configure("Treeview", font=("Segoe UI", 9), rowheight=24)
|
|
44
|
+
style.map("Treeview", background=[("selected", "#3498db")])
|
|
45
|
+
|
|
46
|
+
# Top toolbar (Search & Export)
|
|
47
|
+
top_frame = ttk.Frame(root, padding=8)
|
|
48
|
+
top_frame.pack(fill=tk.X, side=tk.TOP)
|
|
49
|
+
|
|
50
|
+
ttk.Label(top_frame, text="🔍 Search / Filter:", font=("Segoe UI", 9, "bold")).pack(side=tk.LEFT, padx=(0, 5))
|
|
51
|
+
search_var = tk.StringVar()
|
|
52
|
+
search_entry = ttk.Entry(top_frame, textvariable=search_var, width=30)
|
|
53
|
+
search_entry.pack(side=tk.LEFT, padx=5)
|
|
54
|
+
|
|
55
|
+
status_lbl = ttk.Label(top_frame, text=f"Total: {len(rows)} records, {len(columns)} columns", font=("Segoe UI", 9))
|
|
56
|
+
status_lbl.pack(side=tk.LEFT, padx=15)
|
|
57
|
+
|
|
58
|
+
def _export_csv():
|
|
59
|
+
fpath = filedialog.asksaveasfilename(
|
|
60
|
+
defaultextension=".csv",
|
|
61
|
+
filetypes=[("CSV Files", "*.csv"), ("All Files", "*.*")],
|
|
62
|
+
initialfile="query_results.csv",
|
|
63
|
+
)
|
|
64
|
+
if fpath:
|
|
65
|
+
try:
|
|
66
|
+
with open(fpath, "w", newline="", encoding="utf-8") as f:
|
|
67
|
+
writer = csv.writer(f)
|
|
68
|
+
writer.writerow(columns)
|
|
69
|
+
for r in rows:
|
|
70
|
+
writer.writerow([r.get(c, "") for c in columns])
|
|
71
|
+
messagebox.showinfo("Export Success", f"Exported {len(rows)} rows to:\n{fpath}")
|
|
72
|
+
except Exception as e:
|
|
73
|
+
messagebox.showerror("Export Failed", str(e))
|
|
74
|
+
|
|
75
|
+
export_btn = ttk.Button(top_frame, text="💾 Export to CSV", command=_export_csv)
|
|
76
|
+
export_btn.pack(side=tk.RIGHT, padx=5)
|
|
77
|
+
|
|
78
|
+
# Center Data Grid Frame with Both Scrollbars
|
|
79
|
+
grid_frame = ttk.Frame(root)
|
|
80
|
+
grid_frame.pack(fill=tk.BOTH, expand=True, padx=8, pady=(0, 8))
|
|
81
|
+
|
|
82
|
+
# Vertical Scrollbar
|
|
83
|
+
v_scroll = ttk.Scrollbar(grid_frame, orient=tk.VERTICAL)
|
|
84
|
+
v_scroll.pack(side=tk.RIGHT, fill=tk.Y)
|
|
85
|
+
|
|
86
|
+
# Horizontal Scrollbar
|
|
87
|
+
h_scroll = ttk.Scrollbar(grid_frame, orient=tk.HORIZONTAL)
|
|
88
|
+
h_scroll.pack(side=tk.BOTTOM, fill=tk.X)
|
|
89
|
+
|
|
90
|
+
# Treeview Table
|
|
91
|
+
tree = ttk.Treeview(
|
|
92
|
+
grid_frame,
|
|
93
|
+
columns=columns,
|
|
94
|
+
show="headings",
|
|
95
|
+
yscrollcommand=v_scroll.set,
|
|
96
|
+
xscrollcommand=h_scroll.set,
|
|
97
|
+
)
|
|
98
|
+
v_scroll.config(command=tree.yview)
|
|
99
|
+
h_scroll.config(command=tree.xview)
|
|
100
|
+
tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
|
101
|
+
|
|
102
|
+
# Configure Columns
|
|
103
|
+
for col in columns:
|
|
104
|
+
tree.heading(col, text=col)
|
|
105
|
+
# Dynamic column width based on column name length
|
|
106
|
+
col_w = max(120, min(280, len(str(col)) * 12))
|
|
107
|
+
tree.column(col, width=col_w, minwidth=80, stretch=False)
|
|
108
|
+
|
|
109
|
+
def populate_table(filter_text=""):
|
|
110
|
+
tree.delete(*tree.get_children())
|
|
111
|
+
f_lower = filter_text.strip().lower()
|
|
112
|
+
count = 0
|
|
113
|
+
for r in rows:
|
|
114
|
+
row_vals = [str(r.get(c, "")) if r.get(c) is not None else "NULL" for c in columns]
|
|
115
|
+
if f_lower:
|
|
116
|
+
if not any(f_lower in v.lower() for v in row_vals):
|
|
117
|
+
continue
|
|
118
|
+
tree.insert("", tk.END, values=row_vals)
|
|
119
|
+
count += 1
|
|
120
|
+
status_lbl.config(text=f"Showing: {count} of {len(rows)} rows ({len(columns)} columns)")
|
|
121
|
+
|
|
122
|
+
populate_table()
|
|
123
|
+
|
|
124
|
+
def on_search_change(*args):
|
|
125
|
+
populate_table(search_var.get())
|
|
126
|
+
|
|
127
|
+
search_var.trace_add("write", on_search_change)
|
|
128
|
+
|
|
129
|
+
root.mainloop()
|
|
130
|
+
except Exception as e:
|
|
131
|
+
print(f"[DB-Agent GUI Error] Could not launch desktop window: {e}")
|
|
132
|
+
|
|
133
|
+
# Launch GUI in background thread so CLI prompt stays responsive
|
|
134
|
+
thread = threading.Thread(target=_launch_gui, daemon=True)
|
|
135
|
+
thread.start()
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def open_browser_grid_window(
|
|
139
|
+
columns: List[str],
|
|
140
|
+
rows: List[Dict[str, Any]],
|
|
141
|
+
title: str = "DB-Agent Interactive Data Grid",
|
|
142
|
+
) -> None:
|
|
143
|
+
"""
|
|
144
|
+
Generates a standalone, ultra-fast HTML data grid with frozen headers,
|
|
145
|
+
instant horizontal & vertical scrolling, search, and CSV export, then opens it in browser.
|
|
146
|
+
"""
|
|
147
|
+
if not columns or not rows:
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
import json
|
|
151
|
+
|
|
152
|
+
cols_json = json.dumps([{"title": c, "field": c, "headerFilter": "input", "resizable": True} for c in columns])
|
|
153
|
+
data_json = json.dumps(rows, default=str)
|
|
154
|
+
|
|
155
|
+
html_content = f"""<!DOCTYPE html>
|
|
156
|
+
<html lang="en">
|
|
157
|
+
<head>
|
|
158
|
+
<meta charset="UTF-8">
|
|
159
|
+
<title>{title}</title>
|
|
160
|
+
<link href="https://unpkg.com/tabulator-tables@5.5.0/dist/css/tabulator_midnight.min.css" rel="stylesheet">
|
|
161
|
+
<script type="text/javascript" src="https://unpkg.com/tabulator-tables@5.5.0/dist/js/tabulator.min.js"></script>
|
|
162
|
+
<style>
|
|
163
|
+
body {{
|
|
164
|
+
margin: 0;
|
|
165
|
+
padding: 16px;
|
|
166
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
167
|
+
background: #0f172a;
|
|
168
|
+
color: #f8fafc;
|
|
169
|
+
}}
|
|
170
|
+
.header {{
|
|
171
|
+
display: flex;
|
|
172
|
+
justify-content: space-between;
|
|
173
|
+
align-items: center;
|
|
174
|
+
margin-bottom: 12px;
|
|
175
|
+
}}
|
|
176
|
+
h1 {{
|
|
177
|
+
margin: 0;
|
|
178
|
+
font-size: 1.3rem;
|
|
179
|
+
color: #38bdf8;
|
|
180
|
+
}}
|
|
181
|
+
.badge {{
|
|
182
|
+
background: #1e293b;
|
|
183
|
+
padding: 4px 10px;
|
|
184
|
+
border-radius: 6px;
|
|
185
|
+
font-size: 0.85rem;
|
|
186
|
+
color: #94a3b8;
|
|
187
|
+
border: 1px solid #334155;
|
|
188
|
+
}}
|
|
189
|
+
.btn {{
|
|
190
|
+
background: #0284c7;
|
|
191
|
+
color: white;
|
|
192
|
+
border: none;
|
|
193
|
+
padding: 6px 14px;
|
|
194
|
+
border-radius: 6px;
|
|
195
|
+
font-weight: 600;
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
}}
|
|
198
|
+
.btn:hover {{
|
|
199
|
+
background: #0369a1;
|
|
200
|
+
}}
|
|
201
|
+
#data-table {{
|
|
202
|
+
height: calc(100vh - 85px);
|
|
203
|
+
background: #1e293b;
|
|
204
|
+
border-radius: 8px;
|
|
205
|
+
border: 1px solid #334155;
|
|
206
|
+
}}
|
|
207
|
+
</style>
|
|
208
|
+
</head>
|
|
209
|
+
<body>
|
|
210
|
+
<div class="header">
|
|
211
|
+
<div>
|
|
212
|
+
<h1>📊 {title}</h1>
|
|
213
|
+
<span class="badge">{len(rows)} Records • {len(columns)} Columns</span>
|
|
214
|
+
</div>
|
|
215
|
+
<div>
|
|
216
|
+
<button class="btn" onclick="table.download('csv', 'data_export.csv')">💾 Download CSV</button>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
<div id="data-table"></div>
|
|
220
|
+
|
|
221
|
+
<script>
|
|
222
|
+
const tableData = {data_json};
|
|
223
|
+
const tableCols = {cols_json};
|
|
224
|
+
|
|
225
|
+
const table = new Tabulator("#data-table", {{
|
|
226
|
+
data: tableData,
|
|
227
|
+
columns: tableCols,
|
|
228
|
+
layout: "fitData",
|
|
229
|
+
pagination: false,
|
|
230
|
+
renderVertical: "virtual",
|
|
231
|
+
renderHorizontal: "virtual",
|
|
232
|
+
movableColumns: true,
|
|
233
|
+
responsiveLayout: false,
|
|
234
|
+
}});
|
|
235
|
+
</script>
|
|
236
|
+
</body>
|
|
237
|
+
</html>
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
temp_dir = tempfile.gettempdir()
|
|
241
|
+
temp_path = os.path.join(temp_dir, "dbagent_data_grid.html")
|
|
242
|
+
with open(temp_path, "w", encoding="utf-8") as f:
|
|
243
|
+
f.write(html_content)
|
|
244
|
+
|
|
245
|
+
webbrowser.open(f"file://{temp_path}")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dbagent-cli
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Enterprise AI Database Copilot with Interactive Data Grid Window & High-Performance LLM (CLI)
|
|
5
5
|
Author: Santhosh Gupta
|
|
6
6
|
Requires-Python: >=3.9
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dbagent-cli"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "
|
|
7
|
+
version = "0.4.0"
|
|
8
|
+
description = "Enterprise AI Database Copilot with Interactive Data Grid Window & High-Performance LLM (CLI)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
11
11
|
authors = [
|
|
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
|