fix: ia controller soporta OpenAI-compatible + native Ollama; TelegramChatID en Users
This commit is contained in:
+7
-7
@@ -30,6 +30,7 @@ type Users struct {
|
||||
NumeroMedidor string `json:"numero_medidor" gorm:"numero_medidor"`
|
||||
TelefonoFijo string `json:"telefono_fijo" gorm:"telefono_fijo"`
|
||||
Celular string `json:"celular" gorm:"celular"`
|
||||
TelegramChatID string `json:"telegram_chat_id" gorm:"column:telegram_chat_id"`
|
||||
AceptoInfo bool `json:"acepto_info" gorm:"acepto_info"`
|
||||
}
|
||||
|
||||
@@ -103,16 +104,15 @@ func CreateUser(User Users) error {
|
||||
}
|
||||
|
||||
// UpdateUser updates an existing user
|
||||
func UpdateUser(userID uint, Name string, NombreUsuario string, Email string, RoleID int) error {
|
||||
// Crea un mapa con los campos a actualizar
|
||||
func UpdateUser(userID uint, Name string, NombreUsuario string, Email string, RoleID int, TelegramChatID string) error {
|
||||
updates := map[string]interface{}{
|
||||
"name": Name,
|
||||
"nombre_usuario": NombreUsuario,
|
||||
"email": Email,
|
||||
"role_id": RoleID,
|
||||
"name": Name,
|
||||
"nombre_usuario": NombreUsuario,
|
||||
"email": Email,
|
||||
"role_id": RoleID,
|
||||
"telegram_chat_id": TelegramChatID,
|
||||
}
|
||||
|
||||
// Realiza la actualización de los campos en la base de datos
|
||||
if err := app.Http.Database.DB.Model(&Users{}).Where("id = ?", userID).Updates(updates).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -27,6 +27,28 @@ type respuestaOllama struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type openaiChatMsg struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type openaiChatReq struct {
|
||||
Model string `json:"model"`
|
||||
Messages []openaiChatMsg `json:"messages"`
|
||||
Stream bool `json:"stream"`
|
||||
}
|
||||
|
||||
type openaiChatChoice struct {
|
||||
Delta struct {
|
||||
Content string `json:"content"`
|
||||
} `json:"delta"`
|
||||
FinishReason *string `json:"finish_reason"`
|
||||
}
|
||||
|
||||
type openaiChatResp struct {
|
||||
Choices []openaiChatChoice `json:"choices"`
|
||||
}
|
||||
|
||||
func GeneraTextoStream(c *fiber.Ctx) error {
|
||||
var data DataIa
|
||||
if err := c.BodyParser(&data); err != nil {
|
||||
@@ -34,16 +56,13 @@ func GeneraTextoStream(c *fiber.Ctx) error {
|
||||
}
|
||||
data.Stream = true
|
||||
|
||||
hasDBConfig := true
|
||||
config, err := models.GetAiConfigForService("ia")
|
||||
if err != nil {
|
||||
hasDBConfig = false
|
||||
config = &models.AiConfig{
|
||||
BaseURL: "http://72.60.24.97:8080/ollama",
|
||||
ApiKey: "",
|
||||
ModelName: "gemma3:1b",
|
||||
}
|
||||
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{
|
||||
"error": "No hay configuración de IA activa. Configura una en /app/ai-config",
|
||||
})
|
||||
}
|
||||
|
||||
if data.Model == "" {
|
||||
data.Model = config.ModelName
|
||||
}
|
||||
@@ -51,37 +70,64 @@ func GeneraTextoStream(c *fiber.Ctx) error {
|
||||
data.Model = "gemma3:1b"
|
||||
}
|
||||
|
||||
baseURL := strings.TrimSuffix(strings.TrimRight(config.BaseURL, "/"), "/v1")
|
||||
endpoint := baseURL + "/api/generate"
|
||||
provider := strings.ToLower(config.Provider)
|
||||
baseURL := strings.TrimRight(config.BaseURL, "/")
|
||||
hasV1 := strings.Contains(baseURL, "/v1")
|
||||
|
||||
if !hasDBConfig {
|
||||
log.Printf("[IA] Usando config default: endpoint=%s model=%s", endpoint, data.Model)
|
||||
var endpoint string
|
||||
var bodyReader io.Reader
|
||||
useOpenAI := provider != "ollama" || hasV1
|
||||
|
||||
// OpenAI-compatible (openai, anthropic, qwen, o llameo con /v1)
|
||||
if !useOpenAI {
|
||||
// Native Ollama API
|
||||
endpoint = baseURL + "/api/generate"
|
||||
jsonData, _ := json.Marshal(data)
|
||||
bodyReader = bytes.NewBuffer(jsonData)
|
||||
} else {
|
||||
log.Printf("[IA] Usando config BD: endpoint=%s model=%s", endpoint, data.Model)
|
||||
// OpenAI-compatible (/v1/chat/completions)
|
||||
baseURL = strings.TrimSuffix(baseURL, "/v1")
|
||||
endpoint = baseURL + "/v1/chat/completions"
|
||||
chatReq := openaiChatReq{
|
||||
Model: data.Model,
|
||||
Messages: []openaiChatMsg{
|
||||
{Role: "user", Content: data.Prompt},
|
||||
},
|
||||
Stream: true,
|
||||
}
|
||||
jsonData, _ := json.Marshal(chatReq)
|
||||
bodyReader = bytes.NewBuffer(jsonData)
|
||||
}
|
||||
|
||||
jsonData, _ := json.Marshal(data)
|
||||
req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(jsonData))
|
||||
log.Printf("[IA] endpoint=%s model=%s provider=%s", endpoint, data.Model, provider)
|
||||
|
||||
req, err := http.NewRequest("POST", endpoint, bodyReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
if config.ApiKey != "" && config.ApiKey != "ollama" {
|
||||
|
||||
// Auth
|
||||
if provider == "ollama" && config.ApiKey != "" && config.ApiKey != "ollama" {
|
||||
req.SetBasicAuth("ollama", config.ApiKey)
|
||||
} else if config.ApiKey != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+config.ApiKey)
|
||||
}
|
||||
|
||||
client := &http.Client{Timeout: 120 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Printf("[IA] Error conectando a Ollama: %v", err)
|
||||
return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{"error": err.Error()})
|
||||
log.Printf("[IA] Error conectando: %v", err)
|
||||
return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{
|
||||
"error": fmt.Sprintf("Error conectando a %s: %v", provider, err),
|
||||
})
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
log.Printf("[IA] Ollama status %d: %s", resp.StatusCode, string(bodyBytes))
|
||||
log.Printf("[IA] %s status %d: %s", provider, resp.StatusCode, string(bodyBytes))
|
||||
return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{
|
||||
"error": fmt.Sprintf("Ollama respondió con status %d", resp.StatusCode),
|
||||
"error": fmt.Sprintf("%s respondió con status %d", provider, resp.StatusCode),
|
||||
"details": string(bodyBytes),
|
||||
})
|
||||
}
|
||||
@@ -94,25 +140,46 @@ func GeneraTextoStream(c *fiber.Ctx) error {
|
||||
defer resp.Body.Close()
|
||||
dec := json.NewDecoder(resp.Body)
|
||||
for {
|
||||
var msg respuestaOllama
|
||||
if err := dec.Decode(&msg); err != nil {
|
||||
if err != io.EOF {
|
||||
log.Printf("[IA] Error decodificando: %v", err)
|
||||
if useOpenAI {
|
||||
var msg openaiChatResp
|
||||
if err := dec.Decode(&msg); err != nil {
|
||||
if err != io.EOF {
|
||||
log.Printf("[IA] Error decodificando respuesta OpenAI: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
if len(msg.Choices) == 0 {
|
||||
continue
|
||||
}
|
||||
content := msg.Choices[0].Delta.Content
|
||||
if content != "" {
|
||||
fmt.Fprint(w, content)
|
||||
w.Flush()
|
||||
}
|
||||
if msg.Choices[0].FinishReason != nil {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
var msg respuestaOllama
|
||||
if err := dec.Decode(&msg); err != nil {
|
||||
if err != io.EOF {
|
||||
log.Printf("[IA] Error decodificando respuesta Ollama: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
if msg.Error != "" {
|
||||
log.Printf("[IA] Ollama error: %s", msg.Error)
|
||||
fmt.Fprintf(w, "[Error: %s]", msg.Error)
|
||||
w.Flush()
|
||||
break
|
||||
}
|
||||
if msg.Response != "" {
|
||||
fmt.Fprint(w, msg.Response)
|
||||
w.Flush()
|
||||
}
|
||||
if msg.Done {
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
if msg.Error != "" {
|
||||
log.Printf("[IA] Ollama error: %s", msg.Error)
|
||||
fmt.Fprintf(w, "[Error: %s]", msg.Error)
|
||||
w.Flush()
|
||||
break
|
||||
}
|
||||
if msg.Response != "" {
|
||||
fmt.Fprint(w, msg.Response)
|
||||
w.Flush()
|
||||
}
|
||||
if msg.Done {
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -240,8 +240,10 @@ func UpdateUser(c *fiber.Ctx) error {
|
||||
updates["role_id"] = m.RoleID
|
||||
}
|
||||
|
||||
updates["telegram_chat_id"] = m.TelegramChatID
|
||||
|
||||
// Realiza la actualización de los campos seleccionados
|
||||
if err := models.UpdateUser(m.ID, m.Name, m.NombreUsuario, m.Email, int(m.RoleID)); err != nil {
|
||||
if err := models.UpdateUser(m.ID, m.Name, m.NombreUsuario, m.Email, int(m.RoleID), m.TelegramChatID); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"message": err.Error(),
|
||||
"error": true,
|
||||
|
||||
Reference in New Issue
Block a user