Basdat/internal/controllers/ai_controller.go
2025-12-20 00:01:08 +07:00

249 lines
6.6 KiB
Go

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",
})
}