dbagent-cli 0.9.1__tar.gz → 0.9.3__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.9.1 → dbagent_cli-0.9.3}/PKG-INFO +1 -1
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/__init__.py +1 -1
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/agent/conversation.py +5 -1
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/agent/generator.py +45 -25
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/agent/pipeline.py +40 -5
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/connectors/base.py +32 -4
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent_cli.egg-info/PKG-INFO +1 -1
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/pyproject.toml +1 -1
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/README.md +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/agent/confidence.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/agent/doctor.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/agent/error_classifier.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/agent/recommender.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/agent/validator.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/cli.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/config.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/connectors/factory.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/connectors/mongo.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/connectors/relational.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/execution/__init__.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/execution/cache.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/execution/changeset.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/execution/cost.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/execution/recovery.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/execution/sandbox.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/execution/streaming.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/llm/anthropic_provider.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/llm/auto_setup.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/llm/base.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/llm/deepseek_provider.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/llm/factory.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/llm/gemini_provider.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/llm/groq_provider.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/llm/mistral_provider.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/llm/mock_provider.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/llm/ollama_provider.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/llm/openai_provider.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/llm/openrouter_provider.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/schema/cache.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/schema/formatter.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/schema/graph.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/schema/ground_truth.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/schema/models.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/schema/retriever.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/schema/selector.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/security/__init__.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/security/audit.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/security/environment.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/security/masking.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/security/modes.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/security/rbac.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/semantic/__init__.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/semantic/dictionary.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/semantic/knowledge.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/semantic/templates.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/sql/__init__.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/sql/optimizer.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/sql/validator.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/ui/chat_screen.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/ui/console.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/ui/grid_window.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/ui/viewer.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent/ui/visualizer.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent_cli.egg-info/SOURCES.txt +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent_cli.egg-info/dependency_links.txt +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent_cli.egg-info/entry_points.txt +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent_cli.egg-info/requires.txt +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/dbagent_cli.egg-info/top_level.txt +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/setup.cfg +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/tests/test_cache.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/tests/test_cli.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/tests/test_connectors.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/tests/test_direct_sql.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/tests/test_doctor_and_viz.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/tests/test_generator.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/tests/test_graph.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/tests/test_knowledge.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/tests/test_providers.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/tests/test_recovery.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/tests/test_safety.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/tests/test_sandbox.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/tests/test_schema.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/tests/test_security.py +0 -0
- {dbagent_cli-0.9.1 → dbagent_cli-0.9.3}/tests/test_semantic.py +0 -0
|
@@ -54,9 +54,13 @@ class ConversationContext:
|
|
|
54
54
|
if len(self.turns) > self.max_history:
|
|
55
55
|
self.turns.pop(0)
|
|
56
56
|
|
|
57
|
-
if tables
|
|
57
|
+
if tables:
|
|
58
58
|
self.active_tables.update(t.lower() for t in tables)
|
|
59
59
|
|
|
60
|
+
def get_active_tables(self) -> List[str]:
|
|
61
|
+
"""Returns currently active database table names discussed in this session."""
|
|
62
|
+
return list(self.active_tables)
|
|
63
|
+
|
|
60
64
|
def get_last_turn(self) -> Optional[ConversationTurn]:
|
|
61
65
|
if self.turns:
|
|
62
66
|
return self.turns[-1]
|
|
@@ -106,6 +106,11 @@ Provide ready-to-run, dialect-accurate SQL JOIN queries answering the user's spe
|
|
|
106
106
|
|
|
107
107
|
### 4. 💡 Architectural Insights & Best Practices
|
|
108
108
|
Mention indexing recommendations, cascade rules, or performance considerations for these join paths.
|
|
109
|
+
|
|
110
|
+
### 🛑 Critical Operational Directives:
|
|
111
|
+
1. NEVER ask passive questions like "Would you like me to inspect tables?" or "Should I run a query?". Always proactively inspect and deliver the full architectural blueprint immediately.
|
|
112
|
+
2. NEVER give generic textbook definitions. Always ground every explanation in the real database schema provided above.
|
|
113
|
+
3. Always include ALL 4 sections (Diagram, Theoretical Breakdown, SQL Queries, and Insights).
|
|
109
114
|
"""
|
|
110
115
|
|
|
111
116
|
|
|
@@ -142,37 +147,56 @@ class ScriptGenerator:
|
|
|
142
147
|
prompt_lower = user_prompt.lower().strip()
|
|
143
148
|
clean_prompt = prompt_lower.rstrip(".!?,:;~ ")
|
|
144
149
|
|
|
145
|
-
#
|
|
146
|
-
chat_triggers = [
|
|
147
|
-
"hello", "hi", "hey", "howdy", "greetings", "good morning", "good evening", "good afternoon",
|
|
148
|
-
"who are you", "what can you do", "what are you", "help me", "how to use",
|
|
149
|
-
"what is this", "tell me about this database", "how does this work",
|
|
150
|
-
"can you help", "thanks", "thank you", "thx", "bye", "goodbye", "see ya",
|
|
151
|
-
"good", "great", "nice", "cool", "awesome", "perfect", "ok", "okay",
|
|
152
|
-
"yes", "no", "sure", "got it", "understood", "nice work", "well done",
|
|
153
|
-
"sounds good", "looks good", "alright", "super", "neat", "excellent",
|
|
154
|
-
"amazing", "wonderful", "cool thanks", "great thanks", "thanks a lot",
|
|
155
|
-
]
|
|
156
|
-
for trig in chat_triggers:
|
|
157
|
-
if clean_prompt == trig or prompt_lower.startswith(f"{trig} ") or prompt_lower.startswith(f"{trig},") or prompt_lower.startswith(f"{trig}!"):
|
|
158
|
-
return "chat"
|
|
159
|
-
|
|
160
|
-
# Architecture / Relationship / Linkage / Analysis indicators
|
|
150
|
+
# 1. Architecture / Relationship / Linkage / Analysis indicators (Highest Priority)
|
|
161
151
|
analysis_keywords = [
|
|
162
152
|
"linkage architecture", "linkage", "architecture", "relationship between",
|
|
163
153
|
"how are", "how is", "how were", "how they were related", "how were they related",
|
|
164
|
-
"how they were mapped", "how to map", "mapped
|
|
154
|
+
"how they were mapped", "how to map", "where they mapped", "where is it mapped",
|
|
155
|
+
"how they were assigned", "how are they assigned", "how will be linked", "how is it linked",
|
|
156
|
+
"mapped to", "mapped with", "er diagram", "diagram of",
|
|
165
157
|
"data model", "explain relationship", "explain schema", "schema analysis",
|
|
166
158
|
"analyze the database", "analyze database", "table relationship",
|
|
167
159
|
"foreign key relationship", "how are tables related", "which sites in",
|
|
168
160
|
"how to know that which", "how to know which", "mapping architecture",
|
|
169
161
|
"entity relationship", "theoritical information", "theoretical information",
|
|
162
|
+
"check with my previous context", "check with previous context", "previous context",
|
|
163
|
+
"complete architecture", "scope architecture", "tenant level to user level",
|
|
164
|
+
"tenant level", "user level",
|
|
165
|
+
"tables that were linked", "tables linked with", "linked with", "links of",
|
|
166
|
+
"related tables", "related tebales", "table mappings", "related tables mappings",
|
|
167
|
+
"related tebales mappings", "not correct", "not right", "check all the tables",
|
|
168
|
+
"mappings also", "find all mappings", "check links", "check mappings",
|
|
170
169
|
]
|
|
171
170
|
for kw in analysis_keywords:
|
|
172
171
|
if kw in prompt_lower:
|
|
173
172
|
return "analysis"
|
|
174
173
|
|
|
175
|
-
#
|
|
174
|
+
# Contextual clarification for analysis (e.g. "i mean ivis_scope", "i mean table_x")
|
|
175
|
+
if re.search(r'\bi mean\b', prompt_lower) and not re.search(r'\b(insert|update|delete|drop)\b', prompt_lower):
|
|
176
|
+
return "analysis"
|
|
177
|
+
|
|
178
|
+
# 2. Pure Greeting / Feedback / Help triggers (Only when not asking a technical question)
|
|
179
|
+
pure_chat_greetings = ["hello", "hi", "hey", "howdy", "greetings", "good morning", "good evening", "good afternoon"]
|
|
180
|
+
pure_chat_feedback = [
|
|
181
|
+
"thanks", "thank you", "thx", "bye", "goodbye", "see ya",
|
|
182
|
+
"good", "great", "nice", "cool", "awesome", "perfect", "ok", "okay",
|
|
183
|
+
"yes", "no", "sure", "got it", "understood", "nice work", "well done",
|
|
184
|
+
"sounds good", "looks good", "alright", "super", "neat", "excellent",
|
|
185
|
+
"amazing", "wonderful", "cool thanks", "great thanks", "thanks a lot",
|
|
186
|
+
]
|
|
187
|
+
pure_chat_help = [
|
|
188
|
+
"who are you", "what can you do", "what are you", "help me", "how to use",
|
|
189
|
+
"what is this", "tell me about this database", "how does this work", "can you help",
|
|
190
|
+
]
|
|
191
|
+
|
|
192
|
+
if clean_prompt in pure_chat_greetings or clean_prompt in pure_chat_feedback or clean_prompt in pure_chat_help:
|
|
193
|
+
return "chat"
|
|
194
|
+
|
|
195
|
+
for trig in pure_chat_greetings + pure_chat_help:
|
|
196
|
+
if prompt_lower.startswith(f"{trig} ") or prompt_lower.startswith(f"{trig},") or prompt_lower.startswith(f"{trig}!"):
|
|
197
|
+
return "chat"
|
|
198
|
+
|
|
199
|
+
# 3. DDL indicators
|
|
176
200
|
ddl_keywords = [
|
|
177
201
|
"create table", "alter table", "drop table", "add column",
|
|
178
202
|
"remove column", "rename column", "rename table", "modify column",
|
|
@@ -182,7 +206,7 @@ class ScriptGenerator:
|
|
|
182
206
|
if kw in prompt_lower:
|
|
183
207
|
return "ddl"
|
|
184
208
|
|
|
185
|
-
# Write indicators (word-boundary matching to avoid false positives like "show dataset")
|
|
209
|
+
# 4. Write indicators (word-boundary matching to avoid false positives like "show dataset")
|
|
186
210
|
write_phrases = [
|
|
187
211
|
"insert", "add record", "add row", "add new",
|
|
188
212
|
"update", "delete", "drop record",
|
|
@@ -191,14 +215,10 @@ class ScriptGenerator:
|
|
|
191
215
|
for kw in write_phrases:
|
|
192
216
|
if kw in prompt_lower:
|
|
193
217
|
return "write"
|
|
194
|
-
# Single-word write verbs need word boundary to avoid false positives
|
|
195
|
-
# e.g. "set" should not match "dataset", "change" should not match "show changes"
|
|
196
|
-
import re as _re
|
|
197
218
|
write_verbs = ["set", "change", "modify", "remove"]
|
|
198
219
|
for verb in write_verbs:
|
|
199
|
-
if
|
|
200
|
-
|
|
201
|
-
read_context = _re.search(r'\b(?:show|list|display|view|get|count)\b.*\b' + verb, prompt_lower)
|
|
220
|
+
if re.search(r'\b' + verb + r'\b', prompt_lower):
|
|
221
|
+
read_context = re.search(r'\b(?:show|list|display|view|get|count)\b.*\b' + verb, prompt_lower)
|
|
202
222
|
if not read_context:
|
|
203
223
|
return "write"
|
|
204
224
|
|
|
@@ -157,17 +157,40 @@ class QueryPipeline:
|
|
|
157
157
|
max_tables=8,
|
|
158
158
|
)
|
|
159
159
|
|
|
160
|
-
# 2. Targeted on-demand table introspection
|
|
160
|
+
# 2. Targeted on-demand table introspection with automatic junction expansion
|
|
161
161
|
target_names = list(set(exact + fuzzy))
|
|
162
162
|
if target_names:
|
|
163
|
+
try:
|
|
164
|
+
all_table_names = self.connector.get_table_names()
|
|
165
|
+
stems = set()
|
|
166
|
+
for t in target_names:
|
|
167
|
+
unprefixed = re.sub(r'^(?:ivis_|tbl_|table_|app_)', '', t.lower())
|
|
168
|
+
unprefixed = re.sub(r'(?:s|es)$', '', unprefixed)
|
|
169
|
+
if len(unprefixed) >= 3:
|
|
170
|
+
stems.add(unprefixed)
|
|
171
|
+
|
|
172
|
+
for t in all_table_names:
|
|
173
|
+
t_lower = t.lower()
|
|
174
|
+
if t in target_names:
|
|
175
|
+
continue
|
|
176
|
+
is_map = any(m in t_lower for m in ["_map", "mapping", "_rel", "_junction", "_link", "_access", "_config"])
|
|
177
|
+
matches_stem = any(s in t_lower for s in stems)
|
|
178
|
+
if (is_map and matches_stem) or sum(1 for s in stems if s in t_lower) >= 2:
|
|
179
|
+
if t not in target_names:
|
|
180
|
+
target_names.append(t)
|
|
181
|
+
if len(target_names) >= 12:
|
|
182
|
+
break
|
|
183
|
+
except Exception:
|
|
184
|
+
pass
|
|
185
|
+
|
|
163
186
|
targeted = self.connector.inspect_schema(table_names=target_names, include_samples=True, max_samples=2)
|
|
164
187
|
retriever = ContextRetriever(targeted)
|
|
165
|
-
return retriever.retrieve_context(user_prompt, exact_tables=exact, fuzzy_tables=fuzzy, max_tables=
|
|
188
|
+
return retriever.retrieve_context(user_prompt, exact_tables=exact, fuzzy_tables=fuzzy, max_tables=12)
|
|
166
189
|
|
|
167
190
|
# 3. Fallback: inspect targeted
|
|
168
|
-
targeted = self.connector.inspect_targeted(user_prompt, max_tables=
|
|
191
|
+
targeted = self.connector.inspect_targeted(user_prompt, max_tables=12, include_samples=True)
|
|
169
192
|
retriever = ContextRetriever(targeted)
|
|
170
|
-
return retriever.retrieve_context(user_prompt, exact_tables=exact, fuzzy_tables=fuzzy, max_tables=
|
|
193
|
+
return retriever.retrieve_context(user_prompt, exact_tables=exact, fuzzy_tables=fuzzy, max_tables=12)
|
|
171
194
|
|
|
172
195
|
def run(self, user_prompt: str, force: bool = False, bypass_cache: bool = False) -> PipelineResult:
|
|
173
196
|
"""
|
|
@@ -214,6 +237,12 @@ class QueryPipeline:
|
|
|
214
237
|
except Exception:
|
|
215
238
|
exact, fuzzy = [], []
|
|
216
239
|
|
|
240
|
+
# Inherit active tables from conversation context if follow-up prompt doesn't name tables explicitly
|
|
241
|
+
if not exact and not fuzzy:
|
|
242
|
+
inherited = self.conversation.get_active_tables()
|
|
243
|
+
if inherited:
|
|
244
|
+
exact = inherited[:4]
|
|
245
|
+
|
|
217
246
|
sub_schema, join_steps = self._get_fast_schema_context(user_prompt, exact, fuzzy)
|
|
218
247
|
result.schema = sub_schema
|
|
219
248
|
result.join_steps = join_steps
|
|
@@ -244,7 +273,13 @@ class QueryPipeline:
|
|
|
244
273
|
result.was_executed = True
|
|
245
274
|
result.query_type = "analysis"
|
|
246
275
|
result.execution_time_ms = (time.time() - t_start) * 1000
|
|
247
|
-
self.conversation.add_turn(
|
|
276
|
+
self.conversation.add_turn(
|
|
277
|
+
user_prompt=user_prompt,
|
|
278
|
+
sql="",
|
|
279
|
+
assistant_response=result.message,
|
|
280
|
+
tables=exact + fuzzy,
|
|
281
|
+
is_chat=True,
|
|
282
|
+
)
|
|
248
283
|
return result
|
|
249
284
|
except Exception as e:
|
|
250
285
|
result.error = f"Architecture analysis failed: {str(e)}"
|
|
@@ -44,14 +44,42 @@ class BaseConnector(ABC):
|
|
|
44
44
|
|
|
45
45
|
def resolve_tables(self, user_prompt: str) -> Tuple[List[str], List[str]]:
|
|
46
46
|
"""
|
|
47
|
-
Resolve which tables the user is referring to.
|
|
47
|
+
Resolve which tables the user is referring to with prefix stripping, pluralization handling, and fuzzy matching.
|
|
48
48
|
Returns (exact_matches, fuzzy_matches).
|
|
49
|
-
Default implementation uses get_table_names with simple substring matching.
|
|
50
49
|
"""
|
|
50
|
+
import re
|
|
51
51
|
all_tables = self.get_table_names()
|
|
52
52
|
prompt_lower = user_prompt.lower()
|
|
53
|
-
exact = [
|
|
54
|
-
|
|
53
|
+
exact = []
|
|
54
|
+
fuzzy = []
|
|
55
|
+
|
|
56
|
+
tokens = set(re.findall(r'\b[a-zA-Z0-9_]+\b', prompt_lower))
|
|
57
|
+
|
|
58
|
+
for t in all_tables:
|
|
59
|
+
t_lower = t.lower()
|
|
60
|
+
# 1. Exact full table name in prompt
|
|
61
|
+
if re.search(r'\b' + re.escape(t_lower) + r'\b', prompt_lower):
|
|
62
|
+
exact.append(t)
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
# 2. Match without common prefixes (e.g. "ivis_scope" -> "scope", "scopes")
|
|
66
|
+
unprefixed = re.sub(r'^(?:ivis_|tbl_|table_|app_)', '', t_lower)
|
|
67
|
+
if unprefixed and len(unprefixed) >= 3:
|
|
68
|
+
if (unprefixed in tokens or
|
|
69
|
+
f"{unprefixed}s" in tokens or
|
|
70
|
+
f"{unprefixed}es" in tokens or
|
|
71
|
+
re.search(r'\b' + re.escape(unprefixed) + r'(?:s|es)?\b', prompt_lower)):
|
|
72
|
+
if t not in exact:
|
|
73
|
+
exact.append(t)
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
# 3. Fuzzy partial match
|
|
77
|
+
for tok in tokens:
|
|
78
|
+
if len(tok) >= 4 and (tok in t_lower or t_lower in tok):
|
|
79
|
+
if t not in exact and t not in fuzzy:
|
|
80
|
+
fuzzy.append(t)
|
|
81
|
+
|
|
82
|
+
return exact, fuzzy
|
|
55
83
|
|
|
56
84
|
def refresh_cache(self) -> None:
|
|
57
85
|
"""Invalidate any cached metadata. Override in subclasses."""
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dbagent-cli"
|
|
7
|
-
version = "0.9.
|
|
7
|
+
version = "0.9.3"
|
|
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
|
|
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
|
|
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
|