dbagent-cli 0.3.2__tar.gz → 0.3.4__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.2 → dbagent_cli-0.3.4}/PKG-INFO +2 -2
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/__init__.py +1 -1
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/agent/recommender.py +19 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/cli.py +2 -4
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/connectors/relational.py +10 -30
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/ui/console.py +25 -10
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent_cli.egg-info/PKG-INFO +2 -2
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/pyproject.toml +2 -2
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/README.md +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/agent/confidence.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/agent/conversation.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/agent/doctor.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/agent/error_classifier.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/agent/generator.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/agent/pipeline.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/agent/validator.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/config.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/connectors/base.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/connectors/factory.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/connectors/mongo.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/execution/__init__.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/execution/cache.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/execution/changeset.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/execution/cost.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/execution/recovery.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/execution/sandbox.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/execution/streaming.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/llm/auto_setup.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/llm/base.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/llm/factory.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/llm/gemini_provider.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/llm/groq_provider.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/llm/mock_provider.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/llm/ollama_provider.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/llm/openrouter_provider.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/schema/cache.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/schema/formatter.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/schema/graph.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/schema/models.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/schema/retriever.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/schema/selector.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/security/__init__.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/security/audit.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/security/environment.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/security/masking.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/security/modes.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/security/rbac.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/semantic/__init__.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/semantic/dictionary.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/semantic/templates.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/sql/__init__.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/sql/optimizer.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/sql/validator.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/ui/viewer.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent/ui/visualizer.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent_cli.egg-info/SOURCES.txt +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent_cli.egg-info/dependency_links.txt +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent_cli.egg-info/entry_points.txt +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent_cli.egg-info/requires.txt +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/dbagent_cli.egg-info/top_level.txt +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/setup.cfg +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/tests/test_cache.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/tests/test_cli.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/tests/test_connectors.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/tests/test_doctor_and_viz.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/tests/test_generator.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/tests/test_graph.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/tests/test_recovery.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/tests/test_safety.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/tests/test_sandbox.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/tests/test_schema.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/tests/test_security.py +0 -0
- {dbagent_cli-0.3.2 → dbagent_cli-0.3.4}/tests/test_semantic.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dbagent-cli
|
|
3
|
-
Version: 0.3.
|
|
4
|
-
Summary: Enterprise AI Database Copilot with
|
|
3
|
+
Version: 0.3.4
|
|
4
|
+
Summary: Enterprise AI Database Copilot with Instant Schema Caching & Clean Wide-Table Display (CLI)
|
|
5
5
|
Author: Santhosh Gupta
|
|
6
6
|
Requires-Python: >=3.9
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
@@ -59,3 +59,22 @@ class QueryRecommender:
|
|
|
59
59
|
suggestions.append(f"Count total rows in `{tables[0].name}`")
|
|
60
60
|
|
|
61
61
|
return suggestions[:max_suggestions]
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def generate_from_table_names(cls, table_names: List[str], max_suggestions: int = 3) -> List[str]:
|
|
65
|
+
"""Instant zero-query recommendation generator from table names (<0.01ms)."""
|
|
66
|
+
suggestions: List[str] = []
|
|
67
|
+
user_tables = [t for t in table_names if any(w in t.lower() for w in ["user", "customer", "account", "member"])]
|
|
68
|
+
if user_tables:
|
|
69
|
+
suggestions.append(f"List top 10 most recent records from `{user_tables[0]}`")
|
|
70
|
+
event_tables = [t for t in table_names if any(w in t.lower() for w in ["event", "log", "audit", "activity", "history", "camera", "device", "order"])]
|
|
71
|
+
if event_tables:
|
|
72
|
+
suggestions.append(f"Count total records in `{event_tables[0]}`")
|
|
73
|
+
for t in table_names:
|
|
74
|
+
if len(suggestions) >= max_suggestions:
|
|
75
|
+
break
|
|
76
|
+
candidate = f"Show recent rows from `{t}`"
|
|
77
|
+
if candidate not in suggestions:
|
|
78
|
+
suggestions.append(candidate)
|
|
79
|
+
return suggestions[:max_suggestions]
|
|
80
|
+
|
|
@@ -357,15 +357,13 @@ def chat_command(
|
|
|
357
357
|
console.print(f"[bold cyan]AI:[/bold cyan] {llm.name} | [bold yellow]Mode:[/bold yellow] {current_mode.value}")
|
|
358
358
|
console.print("[dim]Type your question in plain English or use colon commands (e.g. :help, :tables, :doctor).[/dim]")
|
|
359
359
|
|
|
360
|
-
# Display AI recommendations on connect (instant
|
|
360
|
+
# Display AI recommendations on connect (instant <0.1ms with zero network overhead)
|
|
361
361
|
try:
|
|
362
362
|
cached_schema = cache_mgr.load_cached_schema(db_url)
|
|
363
363
|
if cached_schema:
|
|
364
364
|
recs = QueryRecommender.generate_recommendations(cached_schema, max_suggestions=3)
|
|
365
365
|
else:
|
|
366
|
-
|
|
367
|
-
quick_schema = connector.inspect_schema(table_names=quick_tables, include_samples=False, max_samples=0)
|
|
368
|
-
recs = QueryRecommender.generate_recommendations(quick_schema, max_suggestions=3)
|
|
366
|
+
recs = QueryRecommender.generate_from_table_names(table_names, max_suggestions=3)
|
|
369
367
|
if recs:
|
|
370
368
|
console.print("\n[bold cyan]💡 Try asking:[/bold cyan]")
|
|
371
369
|
for r in recs:
|
|
@@ -249,44 +249,24 @@ class RelationalConnector(BaseConnector):
|
|
|
249
249
|
def inspect_targeted(
|
|
250
250
|
self,
|
|
251
251
|
user_prompt: str,
|
|
252
|
-
max_tables: int =
|
|
253
|
-
include_samples: bool =
|
|
252
|
+
max_tables: int = 4,
|
|
253
|
+
include_samples: bool = False,
|
|
254
254
|
) -> DatabaseSchema:
|
|
255
255
|
"""
|
|
256
|
-
|
|
257
|
-
Returns (schema, exact_matches, fuzzy_matches) for conflict resolution.
|
|
256
|
+
Ultra-fast on-demand targeted inspection with fuzzy matching (<30ms).
|
|
258
257
|
"""
|
|
259
258
|
all_tables = self.get_table_names()
|
|
260
259
|
exact, fuzzy = _fuzzy_match_tables(all_tables, user_prompt)
|
|
261
260
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if f not in matched:
|
|
266
|
-
matched.append(f)
|
|
267
|
-
|
|
268
|
-
if matched:
|
|
269
|
-
# Also pull in FK-related tables for completeness
|
|
270
|
-
fk_related = self._get_fk_related_tables(matched[:max_tables])
|
|
271
|
-
all_to_inspect = matched[:max_tables]
|
|
272
|
-
for r in fk_related:
|
|
273
|
-
if r not in all_to_inspect and len(all_to_inspect) < max_tables:
|
|
274
|
-
all_to_inspect.append(r)
|
|
275
|
-
|
|
276
|
-
return self.inspect_schema(
|
|
277
|
-
table_names=all_to_inspect,
|
|
278
|
-
include_samples=include_samples,
|
|
279
|
-
max_samples=2,
|
|
280
|
-
include_views=False,
|
|
281
|
-
include_row_counts=False,
|
|
282
|
-
)
|
|
261
|
+
matched = exact[:] if exact else fuzzy[:max_tables]
|
|
262
|
+
if not matched:
|
|
263
|
+
matched = all_tables[:max_tables]
|
|
283
264
|
|
|
284
|
-
# Fallback: inspect top max_tables
|
|
285
265
|
return self.inspect_schema(
|
|
286
|
-
table_names=
|
|
287
|
-
include_samples=
|
|
288
|
-
max_samples=
|
|
289
|
-
include_views=
|
|
266
|
+
table_names=matched[:max_tables],
|
|
267
|
+
include_samples=False,
|
|
268
|
+
max_samples=0,
|
|
269
|
+
include_views=False,
|
|
290
270
|
include_row_counts=False,
|
|
291
271
|
)
|
|
292
272
|
|
|
@@ -56,22 +56,37 @@ def print_code(code: str, language: str = "sql", title: Optional[str] = None) ->
|
|
|
56
56
|
console.print(panel)
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
def print_results_table(columns: List[str], rows: List[Dict[str, Any]], title: str = "
|
|
60
|
-
"""Print query results in a clean Rich table."""
|
|
61
|
-
if not columns:
|
|
62
|
-
console.print("[dim]Query returned
|
|
59
|
+
def print_results_table(columns: List[str], rows: List[Dict[str, Any]], title: str = "Results") -> None:
|
|
60
|
+
"""Print query results in a clean, readable Rich table with wide-table protection."""
|
|
61
|
+
if not columns or not rows:
|
|
62
|
+
console.print("[dim]Query returned 0 rows.[/dim]\n")
|
|
63
63
|
return
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
MAX_DISPLAY_COLS = 8
|
|
66
|
+
is_truncated = len(columns) > MAX_DISPLAY_COLS
|
|
67
|
+
display_cols = columns[:MAX_DISPLAY_COLS] if is_truncated else columns
|
|
68
|
+
|
|
69
|
+
table = Table(title=f"[bold cyan]{title}[/bold cyan]", show_header=True, header_style="bold magenta", border_style="dim")
|
|
70
|
+
|
|
71
|
+
for col in display_cols:
|
|
72
|
+
table.add_column(str(col), overflow="ellipsis", max_width=35)
|
|
68
73
|
|
|
69
74
|
for row in rows:
|
|
70
75
|
row_vals = []
|
|
71
|
-
for col in
|
|
76
|
+
for col in display_cols:
|
|
72
77
|
v = row.get(col)
|
|
73
|
-
|
|
78
|
+
val_str = str(v) if v is not None else "[dim]NULL[/dim]"
|
|
79
|
+
# Clean up excessively long raw json/strings
|
|
80
|
+
if len(val_str) > 40:
|
|
81
|
+
val_str = val_str[:37] + "..."
|
|
82
|
+
row_vals.append(val_str)
|
|
74
83
|
table.add_row(*row_vals)
|
|
75
84
|
|
|
85
|
+
console.print("")
|
|
76
86
|
console.print(table)
|
|
77
|
-
|
|
87
|
+
if is_truncated:
|
|
88
|
+
remaining = len(columns) - MAX_DISPLAY_COLS
|
|
89
|
+
console.print(f"[dim](Showing {MAX_DISPLAY_COLS} of {len(columns)} columns. Hidden: {', '.join(columns[MAX_DISPLAY_COLS:MAX_DISPLAY_COLS+5])}{'...' if remaining > 5 else ''})[/dim]")
|
|
90
|
+
console.print(f"[dim]Tip: Ask for specific fields (e.g., 'list id, username, email, status from ivis_user') for targeted view.[/dim]")
|
|
91
|
+
console.print(f"[bold green]Total rows:[/bold green] {len(rows)}\n")
|
|
92
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dbagent-cli
|
|
3
|
-
Version: 0.3.
|
|
4
|
-
Summary: Enterprise AI Database Copilot with
|
|
3
|
+
Version: 0.3.4
|
|
4
|
+
Summary: Enterprise AI Database Copilot with Instant Schema Caching & Clean Wide-Table Display (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.3.
|
|
8
|
-
description = "Enterprise AI Database Copilot with
|
|
7
|
+
version = "0.3.4"
|
|
8
|
+
description = "Enterprise AI Database Copilot with Instant Schema Caching & Clean Wide-Table Display (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
|