249 lines
6.6 KiB
Go
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",
|
|
})
|
|
} |