chore(issues): mueve 0078/0079/0080 a completed/
3 issues cerradas movidas al directorio completed/ por convencion: - 0078 tables playground joins MBQL (fase 9) - 0079 tables playground drill-through extendido (fase 10) - 0080 tables playground LLM Ask AI + TQL->SQL emit (fase 11) 0081 (promote a registry, fase 12) permanece en dev/issues/ — status partial, 0081-A done, 0081-B..L pending. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
---
|
||||
id: 0078
|
||||
title: tables playground — joins MBQL-style (fase 9)
|
||||
status: done
|
||||
priority: medium
|
||||
created: 2026-05-12
|
||||
closed: 2026-05-12
|
||||
related_components: [cpp/apps/primitives_gallery/playground/tables, lua_engine, tql]
|
||||
---
|
||||
|
||||
## Contexto
|
||||
|
||||
Fase 9 del roadmap del tables playground. Hoy `render()` acepta un solo input
|
||||
table. MBQL permite `:joins` para combinar varias tablas en una sola query.
|
||||
Queremos lo mismo en TQL.
|
||||
|
||||
Roadmap restante tras esta fase:
|
||||
- 10: drill-through extendido
|
||||
- 11: LLM API ("Ask AI")
|
||||
- 12: promote a registry + migrar apps C++
|
||||
|
||||
## Diseño (referencia MBQL)
|
||||
|
||||
MBQL `:joins`:
|
||||
|
||||
```clojure
|
||||
:joins [{:source-table 2
|
||||
:alias "orders"
|
||||
:condition [:= [:field "user_id"] [:field "user_id" {:join-alias "orders"}]]
|
||||
:strategy :left-join
|
||||
:fields :all}]
|
||||
```
|
||||
|
||||
Adaptacion a nuestro modelo:
|
||||
- Sin DB ids — usamos nombres de inputs pasados en runtime.
|
||||
- Soportar multi-key composite via vector de pares.
|
||||
- Strategies: `left` | `inner` | `right` | `full`.
|
||||
- Fields: `all` | `none` | lista.
|
||||
|
||||
## Cambios
|
||||
|
||||
### Tipos / API
|
||||
|
||||
- `struct TableInput { string name; vector<string> headers; vector<ColumnType> types; vector<const char*> cells; int rows; int cols; }`
|
||||
- `struct Join { string alias; string source; vector<pair<string,string>> on; JoinStrategy strategy; vector<string> fields; }` en `data_table_logic.h`.
|
||||
- `State.joins: vector<Join>` (antes de stages[0]).
|
||||
- `render()` signature extendida:
|
||||
```cpp
|
||||
void render(const char* id,
|
||||
const char* const* headers, int col_count,
|
||||
const char* const* cells, int row_count,
|
||||
State& st,
|
||||
const ColumnType* declared_types,
|
||||
const std::vector<TableInput>* joinables = nullptr);
|
||||
```
|
||||
|
||||
### Logica pura
|
||||
|
||||
- `join_tables_cpp_core(left, right, alias, on, strategy, fields) -> StageOutput`.
|
||||
- Tests: left/inner/right/full join + multi-key + NULL handling (left propaga `""`).
|
||||
- Pre-pipeline: si `state.joins` no vacio, materializar tabla joined (recorriendo joinables[]) ANTES de aplicar stage 0.
|
||||
- Headers post-join prefijados: `alias.col` (preserva originales del main).
|
||||
|
||||
### UI
|
||||
|
||||
- **Chip row "Joins:"** debajo de "Filters:". + button abre popup con:
|
||||
- Combo alias (auto-sugerido) + combo source (nombre de input)
|
||||
- Strategy combo
|
||||
- on[] list: par left-col / right-col con + para añadir mas pares (multi-key)
|
||||
- Fields radio: all / none / pick
|
||||
- Chip muestra `alias <- source on left=right (left-join)`. Right-click edit, X borrar.
|
||||
- **CONDICIONAL**: la fila de joins solo se renderiza si `joinables != nullptr && !joinables->empty()`. Sin tablas extra → no UI de joins, no se pierde espacio en apps que solo pasan una tabla.
|
||||
|
||||
### TQL
|
||||
|
||||
Nuevo bloque root-level:
|
||||
|
||||
```lua
|
||||
return {
|
||||
version = 1,
|
||||
display = "table",
|
||||
joins = {
|
||||
{alias = "orders", source = "orders_tbl",
|
||||
on = { {"user_id", "user_id"} },
|
||||
strategy = "left", fields = "all"},
|
||||
},
|
||||
stages = { ... },
|
||||
columns = { ... },
|
||||
views = { ... },
|
||||
}
|
||||
```
|
||||
|
||||
emit/apply round-trip + tests.
|
||||
|
||||
### Lua engine
|
||||
|
||||
YA aplicado preempt (2026-05-12): `ident_cont` acepta `.` para parsear
|
||||
`[alias.col]` post-join sin colision.
|
||||
|
||||
### compute_stage / find_orig_col
|
||||
|
||||
No requieren cambios — operan sobre strings de col names. Aceptan `alias.col` directamente.
|
||||
|
||||
### extra_panels
|
||||
|
||||
No requieren cambios — ven el StageOutput post-join automaticamente.
|
||||
|
||||
## Pasos de implementacion
|
||||
|
||||
1. `join_tables_cpp_core` (pure) + tests unit.
|
||||
2. `TableInput` struct + `Join` struct + `State.joins`.
|
||||
3. `render()` signature extendida (default `nullptr`, back-compat).
|
||||
4. Pre-pipeline materialize join cuando `state.joins` no vacio.
|
||||
5. UI chip row (condicional a joinables disponibles).
|
||||
6. TQL emit/apply joins + tests round-trip.
|
||||
7. Lua `[alias.col]` resolver test (la sintaxis ya parsea).
|
||||
8. Actualizar tests phase9: 4 join types + multi-key + NULL + TQL round-trip.
|
||||
|
||||
## No-objetivos (esta fase)
|
||||
|
||||
- Subqueries / source-query MBQL — no aplicable, las inputs ya estan materializadas.
|
||||
- Joins recursivos — flat list, sin chains internos.
|
||||
- Outer joins con conditions no-igualdad (`>`, `<`, range) — solo `=` por ahora.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- Performance con tablas grandes — hash join sobre key cols. Para v1, nested loop
|
||||
con hash map sobre right table es suficiente.
|
||||
- Memoria — joined table puede ser N*M en worst case. Documentar.
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
id: 0079
|
||||
title: tables playground — drill-through extendido (fase 10)
|
||||
status: done
|
||||
priority: medium
|
||||
created: 2026-05-12
|
||||
closed: 2026-05-12
|
||||
related_components: [cpp/apps/primitives_gallery/playground/tables]
|
||||
---
|
||||
|
||||
## Contexto
|
||||
|
||||
Fase 10 del roadmap del tables playground. Drill-down basico ya implementado
|
||||
(`make_drill_filter` + chip de filtro en stage previo). Falta granularidad de
|
||||
zoom, presets rapidos, click sobre elementos del chart, historial de drill y
|
||||
row inspector.
|
||||
|
||||
## Cambios
|
||||
|
||||
### 1. Zoom granularity sobre cols Date
|
||||
|
||||
- Detectar col tipo `Date` en breakout.
|
||||
- Combo en chip de breakout: "year / month / week / day / hour".
|
||||
- Pure fn `truncate_date_cpp_core(date_str, granularity) -> string`.
|
||||
- Stage 1 con breakout `<col>:month` agrega valores formateados al granular.
|
||||
- TQL: `breakout = {"col:month"}` (sufijo despues de `:`).
|
||||
- Auto-detect granularity inicial: si rango > 2 anios -> year; > 60 dias -> month; > 14 dias -> week; resto -> day.
|
||||
|
||||
### 2. Quick filter presets
|
||||
|
||||
- Boton "Presets" en chip row de filtros.
|
||||
- Menu con:
|
||||
- "Top 10 by <col>" (auto-sugiere col numerica)
|
||||
- "Last 7 / 30 / 90 dias" (si hay col Date)
|
||||
- "Exclude nulls in <col>"
|
||||
- "Non-zero only"
|
||||
- Aplica como filtros al stage activo.
|
||||
|
||||
### 3. Click-to-drill sobre chart elements
|
||||
|
||||
- Bar/Column/Pie/Funnel: click en elemento -> `make_drill_filter(col_idx, value)` -> push filter en stage previo, `active_stage--`.
|
||||
- Scatter/Bubble: click en punto -> filter por X y Y rangos cercanos (snap to nearest data point) o muestra row inspector.
|
||||
- Heatmap: click en celda -> filtro por par (row, col).
|
||||
- ImPlot api: `ImPlot::IsPlotHovered() + GetPlotMousePos()`. Hit-test propio para barras.
|
||||
|
||||
### 4. Drill history (back/forward)
|
||||
|
||||
- Pila de `DrillStep { stage_idx, filter_added }` en `UiState.drill_history`.
|
||||
- Botones `<` `>` en breadcrumb para back/forward.
|
||||
- TQL preserva el stage agrupado pero no la history (es UI state efimero).
|
||||
|
||||
### 5. Row inspector
|
||||
|
||||
- Click derecho sobre row de tabla o punto de chart -> popup modal con todas las cols + valores de la fila.
|
||||
- Incluye cols ocultas.
|
||||
- Solo lectura. Boton "Copy as TSV" + "Filter by this row".
|
||||
|
||||
### 6. Drill-up: vuelve un stage atras sin perder filtros nuevos
|
||||
|
||||
- Boton `<` en breadcrumb del stage previo: pushdown del filter actual al stage anterior + active--.
|
||||
- Inverso de drill-down. Usuario navega la jerarquia sin perder camino.
|
||||
|
||||
## Tests
|
||||
|
||||
- `truncate_date_cpp_core` para granularities (round-trip por fecha conocida).
|
||||
- Pure fn de quick filter presets (build `vector<Filter>` desde preset id).
|
||||
- Round-trip TQL con breakout sufijo `:month`.
|
||||
|
||||
## No-objetivos
|
||||
|
||||
- Map drill (lat/lng -> region) — fuera de scope.
|
||||
- Cross-filter entre paneles (click en panel A filtra panel B) — fase futura.
|
||||
@@ -0,0 +1,242 @@
|
||||
---
|
||||
id: 0080
|
||||
title: tables playground — LLM "Ask AI" + TQL/SQL emit (fase 11)
|
||||
status: done
|
||||
priority: medium
|
||||
created: 2026-05-12
|
||||
updated: 2026-05-13
|
||||
closed: 2026-05-13
|
||||
notes: |
|
||||
pure layer + LLM client + Ask AI modal + DuckDB adapter (FN_TQL_DUCKDB=ON opt-in).
|
||||
618 tests pass con DuckDB (round-trip TQL emit -> execute -> match). 603 sin.
|
||||
e2e linux+windows OK ambos modos.
|
||||
related_components: [cpp/apps/primitives_gallery/playground/tables, lua_engine, tql, duckdb]
|
||||
---
|
||||
|
||||
## Contexto
|
||||
|
||||
Fase 11 del roadmap del tables playground. Dos capacidades que se construyen juntas porque comparten infra (prompt schema, runtime adapter, tests round-trip):
|
||||
|
||||
1. **LLM "Ask AI"** — usuario o agente pregunta en lenguaje natural, modelo devuelve un nuevo TQL (o SQL DuckDB si esta linkado).
|
||||
2. **TQL → SQL (DuckDB) emitter** — permite a agentes escribir SQL contra el mismo modelo de datos. Ejecutable si la app linkó DuckDB; si no, solo emite el string.
|
||||
|
||||
Diseño one-way: **TQL → SQL si**, **SQL → TQL no**. Razon documentada en investigacion Metabase MBQL ↔ SQL: la traduccion inversa es lossy (CTEs, window fns, set ops, lateral, correlated subqueries no caben en MBQL/TQL). Patron canonico Malloy/Cube/LookML/Metabase = compile-down one-way.
|
||||
|
||||
## Cambios
|
||||
|
||||
### 1. UI "Ask AI"
|
||||
|
||||
- Boton "Ask AI" en toolbar (al lado de "+ Viz").
|
||||
- Modal:
|
||||
- InputText multiline para la pregunta.
|
||||
- Toggle output mode: `TQL` (default) | `SQL (DuckDB)` (visible solo si app fue compilada con `FN_TQL_DUCKDB=1`).
|
||||
- Boton "Send" + spinner.
|
||||
- Diff side-by-side: actual vs propuesto (texto highlight).
|
||||
- Botones "Apply" / "Reject" / "Edit before apply".
|
||||
|
||||
### 2. Backend LLM
|
||||
|
||||
- Provider: Anthropic Claude. API key via `pass anthropic/api-key`.
|
||||
- Endpoint: `https://api.anthropic.com/v1/messages`. Model: `claude-sonnet-4-6`. Override env `FN_LLM_MODEL`.
|
||||
- Cliente HTTP: cURL via popen (sin deps nuevas).
|
||||
- Prompt template incluye:
|
||||
- Esquema TQL (de `docs/TQL.md`).
|
||||
- **Si SQL mode**: dialecto DuckDB + funciones DuckDB relevantes (date_trunc, regexp_replace, etc.).
|
||||
- Cols disponibles del stage 0 (name, type) + cols joinables.
|
||||
- **Grammar Lua subset** (ver §4) cuando aplique.
|
||||
- Funciones Lua disponibles (de `lua_engine`).
|
||||
- TQL actual.
|
||||
- Pregunta del user.
|
||||
- Response: extraer ```lua``` (TQL) o ```sql``` block del markdown, strip prose.
|
||||
|
||||
### 3. TQL → SQL DuckDB emitter
|
||||
|
||||
Nuevo modulo `tql_to_sql.{h,cpp}` (pure). Funciones:
|
||||
|
||||
```cpp
|
||||
struct SqlEmit {
|
||||
std::string sql; // SELECT ... statement
|
||||
std::vector<std::string> params; // bound values (?-placeholders)
|
||||
std::vector<std::string> warnings;
|
||||
std::string error; // si emit fallo (subset out of bounds)
|
||||
};
|
||||
|
||||
// Pure: emite SQL DuckDB equivalente a la pipeline State (stages 0..active).
|
||||
// `tables` provee el schema de cada TableInput (no los cells — el caller
|
||||
// decide como hidratar las tablas en DuckDB).
|
||||
SqlEmit emit_sql(const State& state, const std::vector<TableInput>& tables,
|
||||
int up_to_stage = -1 /* default = active_stage */);
|
||||
```
|
||||
|
||||
Mapeo MBQL-style:
|
||||
- Stage 0 = CTE base `t0` con `SELECT cols + derived FROM main_t [LEFT/INNER/RIGHT/FULL JOIN joinables ON ...]`.
|
||||
- Stage N = CTE `tN` con `SELECT breakouts, aggregations FROM tN-1 [WHERE filters] [GROUP BY breakouts] [ORDER BY sorts]`.
|
||||
- Final query `SELECT * FROM t<active>`.
|
||||
|
||||
Stage emit detalle:
|
||||
- `filter Op::Eq col = "v"` → `WHERE col = ?` con `params.push_back(v)` (DuckDB acepta `$1`/`?`).
|
||||
- `breakout "ts:month"` → `date_trunc('month', ts) AS "ts:month"`. Granularity sufijo → DuckDB `date_trunc`.
|
||||
- `aggregation count` → `COUNT(*) AS count`.
|
||||
- `aggregation p95(col)` → `quantile_cont(col, 0.95) AS p95_col`.
|
||||
- `aggregation distinct col` → `COUNT(DISTINCT col) AS distinct_col`.
|
||||
- `sort {desc, col}` → `ORDER BY col DESC`.
|
||||
- Joins: 4 strategies mapean directo a `LEFT/INNER/RIGHT/FULL JOIN ... ON l.k = r.k`.
|
||||
- Derived cols: transpiladas via Lua subset (§4). Si formula fuera de subset → `SqlEmit.error = "lua formula 'X' out of subset: <razon>"`.
|
||||
|
||||
Salida es **string SQL valido DuckDB**. No ejecuta — eso es responsabilidad del adapter opcional (§5).
|
||||
|
||||
### 4. Lua subset transpilable a SQL — GRAMATICA
|
||||
|
||||
Documentar en `docs/TQL.md` seccion nueva "SQL transpile subset".
|
||||
|
||||
**Reglas duras: Lua sigue siendo potente y sin limites en runtime general.** El subset solo aplica si el caller pide `tql_to_sql::emit_sql()`. Fuera del subset → error claro en tiempo de emit, NO en tiempo de eval. El playground sigue ejecutando Lua arbitrario sin restriccion.
|
||||
|
||||
**Subset permitido (transpila a SQL):**
|
||||
|
||||
| Lua | SQL DuckDB |
|
||||
|---|---|
|
||||
| Literales: numero, string `"x"`, bool `true/false`, `nil` | `1.5`, `'x'`, `TRUE/FALSE`, `NULL` |
|
||||
| Col ref: `[colname]` | `colname` (identifier quoted si necesario) |
|
||||
| Aritmetica: `+ - * / % - (unary)` | mismas |
|
||||
| Comparacion: `== ~= < <= > >=` | `= <> < <= > >=` |
|
||||
| Logica: `and or not` | `AND OR NOT` |
|
||||
| String concat: `..` | `\|\|` |
|
||||
| Ternary: `if A then B else C end` | `CASE WHEN A THEN B ELSE C END` |
|
||||
| Ternary inline: `(A and B) or C` (pattern comun Lua) | `CASE WHEN A THEN B ELSE C END` |
|
||||
| `math.floor/ceil/abs/round/sqrt/sin/cos/log` | `floor/ceiling/abs/round/sqrt/sin/cos/ln` |
|
||||
| `math.min(a,b)/max(a,b)` | `least(a,b)/greatest(a,b)` |
|
||||
| `string.upper/lower/len(s)` | `upper(s)/lower(s)/length(s)` |
|
||||
| `string.sub(s, i, j)` | `substring(s, i, j-i+1)` |
|
||||
| `tostring(x)/tonumber(x)` | `CAST(x AS VARCHAR)/CAST(x AS DOUBLE)` |
|
||||
| Paréntesis y precedencia | mismas |
|
||||
|
||||
**Fuera de subset (error compile-time):**
|
||||
|
||||
- Closures: `function() ... end`
|
||||
- Loops: `for/while/repeat`
|
||||
- Locals: `local x = ...`
|
||||
- Tables: `{...}`, `t[k]`, `t.field`, `table.*`
|
||||
- Multi-return / vararg
|
||||
- `string.gsub/find/match/format` (mapeo manual posible v2)
|
||||
- IO: `io.*`, `os.*`, `print`
|
||||
- Coroutines, metatables, debug
|
||||
- Recursion, multi-statement bodies
|
||||
|
||||
**Error message ejemplo:**
|
||||
|
||||
```
|
||||
SQL transpile error en derived col 'fullname':
|
||||
formula = "[first] .. ' ' .. table.concat(parts, ',')"
|
||||
causa: 'table.concat' no esta en SQL transpile subset
|
||||
ver docs/TQL.md#sql-transpile-subset
|
||||
workaround: usar TQL puro (sin SQL emit) o reescribir formula con `..`
|
||||
```
|
||||
|
||||
**Helper:** `tql_to_sql::is_transpilable(formula, error_out)` pure fn que valida una formula sin emitir.
|
||||
|
||||
### 5. DuckDB adapter (opcional)
|
||||
|
||||
Build flag `FN_TQL_DUCKDB=1` en `cpp/CMakeLists.txt` opta-in. Vendor DuckDB header-only o lib (depende de tamaño). Default OFF — playground sigue compilando sin DuckDB.
|
||||
|
||||
API adapter:
|
||||
|
||||
```cpp
|
||||
namespace tql_duckdb {
|
||||
struct Result {
|
||||
StageOutput out; // materializado como TableInput compatible
|
||||
std::string error;
|
||||
double duration_ms = 0;
|
||||
};
|
||||
// Hidrata `tables` como views temp + ejecuta sql + materializa resultado.
|
||||
Result execute(const std::string& sql,
|
||||
const std::vector<std::string>& params,
|
||||
const std::vector<TableInput>& tables);
|
||||
}
|
||||
```
|
||||
|
||||
Apps que lo usen (registry_dashboard, sqlite_api): linkean DuckDB + invocan adapter cuando user/agent pide SQL output. Playground por defecto NO linka — `Ask AI` solo ofrece SQL mode si `#ifdef FN_TQL_DUCKDB`.
|
||||
|
||||
### 6. Validacion + safety
|
||||
|
||||
- Antes de aplicar TQL del LLM: `tql::apply` dry-run. Si fail, mostrar error + "Ask AI again with this error".
|
||||
- Antes de ejecutar SQL del LLM: parsing DuckDB en sandbox read-only (DuckDB connection sin `INSERT/UPDATE/DELETE/DROP`, attach read-only).
|
||||
- Lua sandbox ya cubre side effects en formulas TQL.
|
||||
|
||||
### 7. Streaming
|
||||
|
||||
- Stream tokens via SSE (`stream=true` Anthropic).
|
||||
- Texto en vivo en modal.
|
||||
- Cuando termina, parse lua/sql block final.
|
||||
|
||||
### 8. Persistencia conversacion
|
||||
|
||||
- UiState guarda lista de turns (pregunta + output propuesto + apply result + engine usado TQL/SQL).
|
||||
- Siguiente "Ask AI" turn incluye history previa.
|
||||
- Boton "Reset chat".
|
||||
- NO persistido en TQL (UI state efimero).
|
||||
|
||||
### 9. Coste / rate limit
|
||||
|
||||
- Mostrar tokens estimados antes de enviar (rough char count / 4).
|
||||
- Cap input a 8000 tokens.
|
||||
- Error handling: 429 / 5xx → mensaje + reintentar.
|
||||
|
||||
## Tests
|
||||
|
||||
### Pure (sin red, sin DuckDB linkado)
|
||||
|
||||
- **Lua subset validator:** `is_transpilable` true para casos subset, false con error claro para fuera de subset (closures, loops, table.*, string.gsub, etc.).
|
||||
- **TQL → SQL emit golden tests** (~20 casos):
|
||||
- stage 0 simple filter + sort → `SELECT ... WHERE ... ORDER BY ...`
|
||||
- stage 1 group + count → CTE chain con GROUP BY
|
||||
- granularity sufijo `:month` → `date_trunc('month', ts)`
|
||||
- join 4 strategies con multi-key
|
||||
- derived cols subset → CASE/expressions
|
||||
- derived cols fuera subset → `SqlEmit.error` no vacio + warning
|
||||
- aggregation p25/p50/p75/p99 → `quantile_cont(col, p)`
|
||||
- empty pipeline → `SELECT * FROM t0`
|
||||
- **TQL parseo:** prompt build incluye schema + TQL + pregunta en formato esperado (mockear HTTP).
|
||||
- **Response parse:** extrae lua/sql block correctamente.
|
||||
|
||||
### Round-trip (requiere DuckDB linkado)
|
||||
|
||||
Solo corren si `FN_TQL_DUCKDB=1`:
|
||||
- TQL → emit SQL → ejecutar DuckDB → resultado coincide bit-a-bit con `compute_stage` pure sobre los mismos cells.
|
||||
- Casos: filter, group+agg, join inner, multi-stage chain, breakout granularity month/week, derived col `[a] + [b] * 2`.
|
||||
|
||||
### LLM (red real, opt-in)
|
||||
|
||||
- Test integration con `ANTHROPIC_API_KEY` real (`make test-llm`): pregunta simple → recibe TQL valido → apply OK.
|
||||
- Mock test (CI): cURL stub responde con JSON predefinido → parser extrae bloque OK.
|
||||
|
||||
## No-objetivos
|
||||
|
||||
- **SQL → TQL**: no se implementa. Documentado en doc + en mensajes de error del Ask AI ("no soportamos SQL como input, use TQL").
|
||||
- **Multi-provider** (OpenAI, local): fase futura. Anthropic hardcoded v1.
|
||||
- **Generacion de viz desde LLM** mas alla de `display` token: la viz la elige TQL existente.
|
||||
- **Lua subset extension** (string.gsub, regex, table.*): postpone v2 si demanda real.
|
||||
- **DuckDB write ops**: solo SELECT/CTE. Apps que quieran INSERT/UPDATE lo hacen fuera del playground.
|
||||
|
||||
## Flujo agente (resumen)
|
||||
|
||||
```
|
||||
Agente -> "muestrame top 10 langs por total size"
|
||||
LLM (TQL default) -> emite TQL { stages = {...} }
|
||||
tql::apply -> State + dry-run OK
|
||||
User clickea Apply -> compute_stage en memoria
|
||||
|
||||
Agente -> "lo mismo pero como SQL"
|
||||
[Si FN_TQL_DUCKDB=1 y app linkó adapter]
|
||||
LLM (SQL mode toggled) -> emite SELECT ... DuckDB
|
||||
duckdb::execute(sql, params, tables) -> resultado materializado
|
||||
[Si NO linkado] -> error "SQL mode requiere DuckDB. Compila con FN_TQL_DUCKDB=1"
|
||||
```
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Subset Lua restrictivo en SQL emit**: usuarios usan Lua arbitrario en playground → al pedir SQL falla. Mitigacion: error message claro + sugerencia workaround.
|
||||
- **DuckDB tamaño**: lib ~10MB. Solo se paga si app opta-in con build flag.
|
||||
- **Dialect drift DuckDB**: funciones SQL pueden cambiar entre versiones. Pinear DuckDB version en CMake.
|
||||
- **LLM hallucinations**: TQL invalido → dry-run rechaza con error. Loop "Ask AI again with this error" recupera.
|
||||
- **API key leak**: `pass` integration mantiene fuera del repo. Build flag NUNCA imprime key.
|
||||
- **Coste tokens**: prompt grande (schema + grammar + TQL). Cap input + warning visual.
|
||||
Reference in New Issue
Block a user