This commit is contained in:
Lizandro Guarnizo
2026-06-02 13:13:29 -05:00
parent ba76754331
commit 28d0eae715
7 changed files with 434 additions and 77 deletions
+73 -2
View File
@@ -320,6 +320,77 @@ func quoteIdent(name, driver string) string {
return "`" + strings.ReplaceAll(name, "`", "``") + "`"
}
// NL2SQLHandler convierte lenguaje natural a SQL usando IA.
// POST /app/query-runner/nl2sql
// Body: { conx_db_id, database, text, schema }
// schema es un array de { table, columns: [{name, type}] } para dar contexto a la IA.
func NL2SQLHandler(c *fiber.Ctx) error {
var body struct {
ConxDbID uint `json:"conx_db_id"`
Database string `json:"database"`
Text string `json:"text"`
Schema []struct {
Table string `json:"table"`
Columns []struct {
Name string `json:"name"`
Type string `json:"type"`
} `json:"columns"`
} `json:"schema"`
}
if err := c.BodyParser(&body); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Cuerpo inválido"})
}
if strings.TrimSpace(body.Text) == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "El texto está vacío"})
}
// Construir representación del esquema para el prompt
var schemaLines []string
for _, t := range body.Schema {
var cols []string
for _, col := range t.Columns {
cols = append(cols, col.Name+" ("+col.Type+")")
}
if len(cols) > 0 {
schemaLines = append(schemaLines, fmt.Sprintf(" %s: %s", t.Table, strings.Join(cols, ", ")))
} else {
schemaLines = append(schemaLines, " "+t.Table)
}
}
schemaStr := ""
if len(schemaLines) > 0 {
schemaStr = "Esquema disponible:\n" + strings.Join(schemaLines, "\n")
}
aiConfig, err := models.GetAiConfigForService("query_runner")
if err != nil {
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{
"error": "No hay configuración de IA activa. Configura una en /app/ai-config",
})
}
prompt := fmt.Sprintf(`Eres un experto en SQL. Convierte la siguiente descripción en lenguaje natural a una consulta SQL válida.
%s
Descripción: %s
Reglas:
- Devuelve SOLO el SQL, sin explicaciones ni bloques de código markdown.
- Usa los nombres de tablas y columnas exactamente como aparecen en el esquema.
- Si la base de datos es PostgreSQL, usa comillas dobles para identificadores.
- Termina siempre con punto y coma.
- Si no hay suficiente información para generar el SQL, devuelve el SQL más razonable posible.
SQL:`, schemaStr, body.Text)
result := callAISQLSuggestion(aiConfig, prompt, "nl2sql", body.ConxDbID, body.Database)
if result == nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "La IA no pudo generar el SQL"})
}
return c.JSON(result)
}
// CorrectQueryHandler usa IA o heurística para corregir/sugerir queries.
// POST /app/query-runner/suggest
// Body: { conx_db_id, database, sql, action: "correct"|"complete"|"optimize" }
@@ -340,8 +411,8 @@ func CorrectQueryHandler(c *fiber.Ctx) error {
body.Action = "correct"
}
// 1. Si hay una configuración de IA activa, usarla
aiConfig, aiErr := models.GetActiveAiConfig("")
// 1. Si hay una configuración de IA activa para este servicio, usarla
aiConfig, aiErr := models.GetAiConfigForService("query_runner")
if aiErr == nil && aiConfig != nil {
aiResult := callAISQLSuggestion(aiConfig, body.SQL, body.Action, body.ConxDbID, body.Database)
if aiResult != nil {