LIANJie-Jason Claude Opus 4.6 commited on
Commit
a8fd301
·
1 Parent(s): d6a01c7

fix: harden route parsing, column dedup, and column name access

Browse files

- Normalize QU route output (strip/lowercase) before validation
- Fix column name deduplication to handle suffix collisions
(e.g., 'Score_A', 'Score_A', 'Score_A_2' no longer produces duplicates)
- Use defensive .get() for column name access in SQL result formatting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Files changed (3) hide show
  1. src/query_engine.py +2 -0
  2. src/sql_ingest.py +11 -8
  3. src/sql_retriever.py +1 -1
src/query_engine.py CHANGED
@@ -162,6 +162,8 @@ def _parse_qu_result(raw: str, original_query: str) -> dict:
162
  search_query = parsed.get("search_query", original_query)
163
  display_query = parsed.get("display_query", original_query)
164
  route = parsed.get("route", "vector")
 
 
165
  if route not in ("sql", "vector", "both"):
166
  route = "vector"
167
  sql_query = parsed.get("sql_query") if route in ("sql", "both") else None
 
162
  search_query = parsed.get("search_query", original_query)
163
  display_query = parsed.get("display_query", original_query)
164
  route = parsed.get("route", "vector")
165
+ if isinstance(route, str):
166
+ route = route.strip().lower()
167
  if route not in ("sql", "vector", "both"):
168
  route = "vector"
169
  sql_query = parsed.get("sql_query") if route in ("sql", "both") else None
src/sql_ingest.py CHANGED
@@ -499,15 +499,18 @@ def _ingest_tables(
499
 
500
  # Infer column types, collect samples and stats
501
  safe_headers = [_sanitize_column_name(h) for h in headers]
502
- # Deduplicate: append _2, _3, etc. for collisions
503
- seen: dict[str, int] = {}
 
 
 
504
  for i, name in enumerate(safe_headers):
505
- lower = name.lower()
506
- if lower in seen:
507
- seen[lower] += 1
508
- safe_headers[i] = f"{name}_{seen[lower]}"
509
- else:
510
- seen[lower] = 1
511
  col_types = []
512
  col_samples = []
513
  col_stats = []
 
499
 
500
  # Infer column types, collect samples and stats
501
  safe_headers = [_sanitize_column_name(h) for h in headers]
502
+ # Deduplicate: ensure all column names are unique after sanitization.
503
+ # Checks every name against all previously assigned names,
504
+ # handling both same-name collisions and suffix collisions
505
+ # (e.g., 'Score_A', 'Score_A', 'Score_A_2' won't produce duplicates).
506
+ used: set[str] = set()
507
  for i, name in enumerate(safe_headers):
508
+ if name.lower() in used:
509
+ suffix = 2
510
+ while f"{name}_{suffix}".lower() in used:
511
+ suffix += 1
512
+ safe_headers[i] = f"{name}_{suffix}"
513
+ used.add(safe_headers[i].lower())
514
  col_types = []
515
  col_samples = []
516
  col_stats = []
src/sql_retriever.py CHANGED
@@ -93,7 +93,7 @@ def format_sql_results_as_context(
93
  if table_desc:
94
  parts.append(f"Dataset description: {table_desc}\n")
95
  col_descs = {
96
- c["name"]: c.get("description", "")
97
  for c in table_info.get("columns", [])
98
  }
99
  desc_lines = [f" {name}: {desc}" for name, desc in col_descs.items() if desc]
 
93
  if table_desc:
94
  parts.append(f"Dataset description: {table_desc}\n")
95
  col_descs = {
96
+ c.get("name", "unknown"): c.get("description", "")
97
  for c in table_info.get("columns", [])
98
  }
99
  desc_lines = [f" {name}: {desc}" for name, desc in col_descs.items() if desc]