Files
InsightReply/server/internal/handler/ai_handler.go
zs 8cf6cb944b
Some checks failed
Extension Build & Release / build (push) Failing after 1m5s
Backend Deploy (Go + Docker) / deploy (push) Failing after 1m40s
Web Console Deploy (Vue 3 + Vite) / deploy (push) Has been cancelled
feat: 部署初版测试
2026-03-02 21:25:21 +08:00

108 lines
3.1 KiB
Go

package handler
import (
"encoding/json"
"net/http"
"strings"
"github.com/zs/InsightReply/internal/service"
)
type AIHandler struct {
svc *service.AIService
profileSvc *service.ProductProfileService
strategySvc *service.CustomStrategyService
}
func NewAIHandler(svc *service.AIService, profileSvc *service.ProductProfileService, strategySvc *service.CustomStrategyService) *AIHandler {
return &AIHandler{
svc: svc,
profileSvc: profileSvc,
strategySvc: strategySvc,
}
}
func (h *AIHandler) Test(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
msg, err := h.svc.TestConnection(ctx)
if err != nil {
SendError(w, http.StatusInternalServerError, 5000, err.Error())
return
}
SendSuccess(w, map[string]string{"status": msg})
}
func (h *AIHandler) Generate(w http.ResponseWriter, r *http.Request) {
var body struct {
TweetContent string `json:"tweet_content"`
Strategy string `json:"strategy"`
Identity string `json:"identity"`
Provider string `json:"provider,omitempty"`
Model string `json:"model,omitempty"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
SendError(w, http.StatusBadRequest, 4001, "Invalid request body")
return
}
if body.TweetContent == "" {
SendError(w, http.StatusBadRequest, 4002, "Tweet content is required")
return
}
ctx := r.Context()
userID := ctx.Value("userID").(string)
// Fetch Product Profile Context
var productContext string
if profile, err := h.profileSvc.GetProfile(userID); err == nil && profile.IsActive {
productContext = "Product Context: " + profile.ProductName
if profile.Tagline != "" {
productContext += " - " + profile.Tagline
}
if profile.KeyFeatures != "" && profile.KeyFeatures != "[]" {
productContext += ". Key Features: " + profile.KeyFeatures
}
if profile.CustomContext != "" {
productContext += ". Context: " + profile.CustomContext
}
}
// Fetch Custom Strategies Context
if strategies, err := h.strategySvc.ListStrategies(userID); err == nil && len(strategies) > 0 {
productContext += "\n\nAvailable User Custom Strategies:\n"
for _, s := range strategies {
productContext += "- " + s.StrategyKey + " (" + s.Label + "): " + s.Description + "\n"
}
}
replyString, err := h.svc.GenerateReply(ctx, body.TweetContent, productContext, body.Identity, body.Provider, body.Model)
if err != nil {
SendError(w, http.StatusBadGateway, 5002, "Failed to generate AI reply: "+err.Error())
return
}
// Clean up potential markdown wrappers from LLM output
cleanReply := strings.TrimSpace(replyString)
cleanReply = strings.TrimPrefix(cleanReply, "```json")
cleanReply = strings.TrimPrefix(cleanReply, "```")
cleanReply = strings.TrimSuffix(cleanReply, "```")
cleanReply = strings.TrimSpace(cleanReply)
var replies []map[string]interface{}
if err := json.Unmarshal([]byte(cleanReply), &replies); err != nil {
// Fallback: return as single string object if parsing totally fails
replies = []map[string]interface{}{
{
"strategy": "Fallback",
"content": replyString,
},
}
}
SendSuccess(w, map[string]interface{}{
"replies": replies,
})
}