feat(ai-config): agregar botón Probar para verificar conectividad del provider
- Endpoint GET /ai-config/:id/test que llama /api/tags (Ollama) o /v1/models (OpenAI-compat/Anthropic) - Botón "Probar" en cada fila de la tabla con modal de resultado JSON Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
6062f31c70
commit
569d8b0f28
@@ -1,9 +1,12 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
@@ -164,6 +167,81 @@ func GetAiConfigSelect(c *fiber.Ctx) error {
|
||||
return c.JSON(fiber.Map{"registros": items})
|
||||
}
|
||||
|
||||
// TestAiConfigHandler verifica conectividad con el provider de la config.
|
||||
func TestAiConfigHandler(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
|
||||
}
|
||||
var item models.AiConfig
|
||||
if err := models.GetAiConfigByID(uint(id), &item); err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "config no encontrada"})
|
||||
}
|
||||
|
||||
client := &http.Client{Timeout: 10 * time.Second}
|
||||
|
||||
var testURL string
|
||||
var req *http.Request
|
||||
|
||||
switch item.Provider {
|
||||
case "ollama":
|
||||
base := strings.TrimSuffix(strings.TrimRight(item.BaseURL, "/"), "/v1")
|
||||
testURL = base + "/api/tags"
|
||||
req, _ = http.NewRequest("GET", testURL, nil)
|
||||
if item.ApiKey != "" && item.ApiKey != "ollama" {
|
||||
req.SetBasicAuth("ollama", item.ApiKey)
|
||||
}
|
||||
case "anthropic":
|
||||
// Anthropic no tiene /models; usamos /v1/models igual (devuelve 200 con lista)
|
||||
base := item.BaseURL
|
||||
if base == "" {
|
||||
base = "https://api.anthropic.com/v1"
|
||||
}
|
||||
testURL = base + "/models"
|
||||
req, _ = http.NewRequest("GET", testURL, nil)
|
||||
req.Header.Set("x-api-key", item.ApiKey)
|
||||
req.Header.Set("anthropic-version", "2023-06-01")
|
||||
default:
|
||||
base := item.BaseURL
|
||||
if base == "" {
|
||||
switch item.Provider {
|
||||
case "openai":
|
||||
base = "https://api.openai.com/v1"
|
||||
case "qwen":
|
||||
base = "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||
case "groq":
|
||||
base = "https://api.groq.com/openai/v1"
|
||||
default:
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "BaseURL requerida para este provider"})
|
||||
}
|
||||
}
|
||||
testURL = base + "/models"
|
||||
req, _ = http.NewRequest("GET", testURL, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+item.ApiKey)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return c.JSON(fiber.Map{"ok": false, "error": err.Error()})
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
var body map[string]interface{}
|
||||
json.NewDecoder(resp.Body).Decode(&body)
|
||||
return c.JSON(fiber.Map{"ok": false, "status": resp.StatusCode, "error": body})
|
||||
}
|
||||
|
||||
// Para Ollama devolvemos la lista de modelos disponibles
|
||||
if item.Provider == "ollama" {
|
||||
var body map[string]interface{}
|
||||
json.NewDecoder(resp.Body).Decode(&body)
|
||||
return c.JSON(fiber.Map{"ok": true, "status": resp.StatusCode, "data": body})
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{"ok": true, "status": resp.StatusCode})
|
||||
}
|
||||
|
||||
// DeleteAiConfigHandler elimina una configuración de IA.
|
||||
func DeleteAiConfigHandler(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
||||
|
||||
Reference in New Issue
Block a user