mirror of
https://github.com/ForFarmTeam/ForFarm.git
synced 2025-12-19 05:54:08 +01:00
143 lines
4.7 KiB
Go
143 lines
4.7 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
|
|
"github.com/danielgtaylor/huma/v2"
|
|
"github.com/forfarm/backend/internal/services"
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/google/generative-ai-go/genai"
|
|
)
|
|
|
|
func (a *api) registerChatRoutes(_ chi.Router, apiInstance huma.API) {
|
|
tags := []string{"chat"}
|
|
|
|
huma.Register(apiInstance, huma.Operation{
|
|
OperationID: "chatWithAssistantContextual",
|
|
Method: http.MethodPost,
|
|
Path: "/chat/specific",
|
|
Tags: tags,
|
|
Summary: "Send a message to the assistant with farm/crop context",
|
|
Description: "Allows users to interact with the AI chatbot, providing farm/crop context.",
|
|
}, a.chatHandler)
|
|
|
|
huma.Register(apiInstance, huma.Operation{
|
|
OperationID: "chatWithAssistantGeneral",
|
|
Method: http.MethodPost,
|
|
Path: "/chat",
|
|
Tags: tags,
|
|
Summary: "Send a message to the general farming assistant",
|
|
Description: "Allows users to interact with the AI chatbot without specific farm/crop context.",
|
|
}, a.generalChatHandler)
|
|
}
|
|
|
|
type HistoryItem struct {
|
|
Role string `json:"role" example:"user" enum:"user,model" doc:"Role of the sender (user or model)"`
|
|
Text string `json:"text" doc:"The content of the message"`
|
|
}
|
|
|
|
type ChatInput struct {
|
|
Header string `header:"Authorization" required:"true" example:"Bearer token"`
|
|
Body struct {
|
|
Message string `json:"message" required:"true" doc:"The user's message to the assistant"`
|
|
FarmID string `json:"farmId,omitempty" doc:"Optional UUID of the farm context"`
|
|
CropID string `json:"cropId,omitempty" doc:"Optional UUID of the crop context"`
|
|
History []HistoryItem `json:"history,omitempty" doc:"Previous turns in the conversation"`
|
|
}
|
|
}
|
|
|
|
type ChatOutput struct {
|
|
Body struct {
|
|
Response string `json:"response" doc:"The assistant's response message"`
|
|
}
|
|
}
|
|
|
|
type GeneralChatInput struct {
|
|
Header string `header:"Authorization" required:"true" example:"Bearer token"`
|
|
Body struct {
|
|
Message string `json:"message" required:"true" doc:"The user's message to the assistant"`
|
|
History []HistoryItem `json:"history,omitempty" doc:"Previous turns in the conversation"`
|
|
}
|
|
}
|
|
|
|
type GeneralChatOutput = ChatOutput
|
|
|
|
// convertInputHistory converts the API HistoryItem slice to genai.Content slice.
|
|
func convertInputHistory(apiHistory []HistoryItem) []*genai.Content {
|
|
if apiHistory == nil {
|
|
return nil
|
|
}
|
|
genaiHistory := make([]*genai.Content, 0, len(apiHistory))
|
|
for _, item := range apiHistory {
|
|
if (item.Role == "user" || item.Role == "model") && item.Text != "" {
|
|
content := &genai.Content{
|
|
Role: item.Role,
|
|
Parts: []genai.Part{genai.Text(item.Text)},
|
|
}
|
|
genaiHistory = append(genaiHistory, content)
|
|
}
|
|
}
|
|
return genaiHistory
|
|
}
|
|
|
|
func (a *api) chatHandler(ctx context.Context, input *ChatInput) (*ChatOutput, error) {
|
|
userID, err := a.getUserIDFromHeader(input.Header)
|
|
if err != nil {
|
|
return nil, huma.Error401Unauthorized("Authentication failed", err)
|
|
}
|
|
if a.chatService == nil {
|
|
a.logger.Error("Chat service is not initialized")
|
|
return nil, huma.Error500InternalServerError("Chat service is not available")
|
|
}
|
|
|
|
genaiHistory := convertInputHistory(input.Body.History)
|
|
|
|
serviceInput := services.GenerateResponseInput{
|
|
UserID: userID,
|
|
Message: input.Body.Message,
|
|
FarmID: input.Body.FarmID,
|
|
CropID: input.Body.CropID,
|
|
History: genaiHistory,
|
|
}
|
|
|
|
botResponse, err := a.chatService.GenerateResponse(ctx, serviceInput)
|
|
if err != nil {
|
|
a.logger.Error("Failed to get response from chat service (contextual)", "userId", userID, "farmId", input.Body.FarmID, "cropId", input.Body.CropID, "error", err)
|
|
return nil, huma.Error500InternalServerError("Failed to get response from assistant")
|
|
}
|
|
|
|
resp := &ChatOutput{}
|
|
resp.Body.Response = botResponse
|
|
return resp, nil
|
|
}
|
|
|
|
func (a *api) generalChatHandler(ctx context.Context, input *GeneralChatInput) (*GeneralChatOutput, error) {
|
|
userID, err := a.getUserIDFromHeader(input.Header)
|
|
if err != nil {
|
|
return nil, huma.Error401Unauthorized("Authentication failed", err)
|
|
}
|
|
if a.chatService == nil {
|
|
a.logger.Error("Chat service is not initialized")
|
|
return nil, huma.Error500InternalServerError("Chat service is not available")
|
|
}
|
|
|
|
genaiHistory := convertInputHistory(input.Body.History)
|
|
|
|
serviceInput := services.GenerateResponseInput{
|
|
UserID: userID,
|
|
Message: input.Body.Message,
|
|
History: genaiHistory,
|
|
}
|
|
|
|
botResponse, err := a.chatService.GenerateResponse(ctx, serviceInput)
|
|
if err != nil {
|
|
a.logger.Error("Failed to get response from chat service (general)", "userId", userID, "error", err)
|
|
return nil, huma.Error500InternalServerError("Failed to get response from assistant")
|
|
}
|
|
|
|
resp := &GeneralChatOutput{}
|
|
resp.Body.Response = botResponse
|
|
return resp, nil
|
|
}
|