package controllers import ( "bytes" "encoding/json" "fmt" "io" "lost-and-found/internal/utils" "net/http" "os" "github.com/gin-gonic/gin" "gorm.io/gorm" ) type AIMessage struct { Role string `json:"role"` Content string `json:"content"` } type FrontendMessage struct { Text string `json:"text"` Sender string `json:"sender"` } type AIController struct { DB *gorm.DB } func NewAIController(db *gorm.DB) *AIController { return &AIController{DB: db} } // Groq API structures type GroqMessage struct { Role string `json:"role"` Content string `json:"content"` } type GroqRequest struct { Model string `json:"model"` Messages []GroqMessage `json:"messages"` Temperature float64 `json:"temperature,omitempty"` MaxTokens int `json:"max_tokens,omitempty"` TopP float64 `json:"top_p,omitempty"` Stream bool `json:"stream"` } type GroqResponse struct { ID string `json:"id"` Choices []struct { Index int `json:"index"` Message struct { Role string `json:"role"` Content string `json:"content"` } `json:"message"` FinishReason string `json:"finish_reason"` } `json:"choices"` Usage struct { PromptTokens int `json:"prompt_tokens"` CompletionTokens int `json:"completion_tokens"` TotalTokens int `json:"total_tokens"` } `json:"usage"` } func (c *AIController) Chat(ctx *gin.Context) { var request struct { Message string `json:"message" binding:"required"` History []FrontendMessage `json:"history"` } if err := ctx.ShouldBindJSON(&request); err != nil { utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid request", err.Error()) return } // Get Groq API key from environment apiKey := os.Getenv("GROQ_API_KEY") if apiKey == "" { utils.ErrorResponse(ctx, http.StatusInternalServerError, "Groq API key not configured", "") return } // Get model from environment or use default model := os.Getenv("GROQ_MODEL") if model == "" { model = "llama-3.3-70b-versatile" // Default to best model } // System prompt for Lost & Found context systemPrompt := `Kamu adalah AI Assistant untuk sistem Lost & Found (Barang Hilang & Temuan) bernama "FindItBot". Konteks Sistem: - Sistem ini membantu mahasiswa dan staff melaporkan barang hilang dan menemukan barang - User bisa melaporkan barang hilang mereka - User bisa melaporkan barang yang mereka temukan - User bisa klaim barang yang hilang - Manager dan Admin memverifikasi klaim Tugasmu: 1. 🔍 Jawab pertanyaan tentang cara menggunakan sistem 2. 📝 Bantu user memahami proses pelaporan dan klaim 3. ✅ Berikan informasi yang jelas dan membantu 4. 💬 Gunakan bahasa Indonesia yang ramah dan profesional 5. 🎯 Fokus pada solusi praktis Panduan Respons: - Gunakan emoji yang relevan - Jawab dengan singkat tapi lengkap - Jika user mencari barang, tanyakan detail spesifik - Jika user ingin lapor kehilangan, tanyakan: nama barang, kategori, lokasi, tanggal hilang - Jika user ingin klaim, jelaskan proses verifikasi Jawab dengan helpful dan supportive!` // Build messages array for Groq groqMessages := []GroqMessage{ { Role: "system", Content: systemPrompt, }, } // Add conversation history for _, msg := range request.History { role := "user" if msg.Sender == "ai" { role = "assistant" } groqMessages = append(groqMessages, GroqMessage{ Role: role, Content: msg.Text, }) } // Add current user message groqMessages = append(groqMessages, GroqMessage{ Role: "user", Content: request.Message, }) // Prepare Groq request groqReq := GroqRequest{ Model: model, Messages: groqMessages, Temperature: 0.7, MaxTokens: 1024, TopP: 0.95, Stream: false, } // Make API call to Groq groqURL := "https://api.groq.com/openai/v1/chat/completions" jsonData, err := json.Marshal(groqReq) if err != nil { utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to prepare request", err.Error()) return } req, err := http.NewRequest("POST", groqURL, bytes.NewBuffer(jsonData)) if err != nil { utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to create request", err.Error()) return } req.Header.Set("Authorization", "Bearer "+apiKey) req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil { utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to call Groq API", err.Error()) return } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to read response", err.Error()) return } if resp.StatusCode != http.StatusOK { utils.ErrorResponse(ctx, http.StatusInternalServerError, fmt.Sprintf("Groq API error (status %d)", resp.StatusCode), string(body)) return } var groqResp GroqResponse if err := json.Unmarshal(body, &groqResp); err != nil { utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to parse response", err.Error()) return } // Extract AI response aiResponse := "Maaf, tidak dapat memproses permintaan Anda." if len(groqResp.Choices) > 0 { aiResponse = groqResp.Choices[0].Message.Content } response := AIMessage{ Role: "assistant", Content: aiResponse, } utils.SuccessResponse(ctx, http.StatusOK, "AI response generated", gin.H{ "message": response, "response": aiResponse, "model": model, "usage": gin.H{ "prompt_tokens": groqResp.Usage.PromptTokens, "completion_tokens": groqResp.Usage.CompletionTokens, "total_tokens": groqResp.Usage.TotalTokens, }, }) } func (c *AIController) GetHistory(ctx *gin.Context) { userID, exists := ctx.Get("user_id") if !exists { utils.ErrorResponse(ctx, http.StatusUnauthorized, "User not authenticated", "") return } // TODO: Implement actual history retrieval from database history := []AIMessage{} utils.SuccessResponse(ctx, http.StatusOK, "Chat history retrieved", gin.H{ "user_id": userID, "history": history, "count": len(history), }) } func (c *AIController) ClearHistory(ctx *gin.Context) { userID, exists := ctx.Get("user_id") if !exists { utils.ErrorResponse(ctx, http.StatusUnauthorized, "User not authenticated", "") return } // TODO: Implement actual history clearing from database utils.SuccessResponse(ctx, http.StatusOK, "Chat history cleared", gin.H{ "user_id": userID, "message": "History successfully deleted", }) }