feat(captain): align playground fallback
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gochat/gochat/internal/llm"
|
||||
"github.com/gochat/gochat/internal/model"
|
||||
@@ -59,6 +60,17 @@ type UpdateAssistantRequest struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type PlaygroundMessage struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
AgentName string `json:"agent_name,omitempty"`
|
||||
}
|
||||
|
||||
type PlaygroundRequest struct {
|
||||
MessageContent string `json:"message_content"`
|
||||
MessageHistory []PlaygroundMessage `json:"message_history"`
|
||||
}
|
||||
|
||||
// --- CRUD Operations ---
|
||||
|
||||
// Create creates a new CaptainAssistant.
|
||||
@@ -309,6 +321,118 @@ func (s *CaptainAssistantService) GenerateResponse(ctx context.Context, assistan
|
||||
return resp.Choices[0].Message.Content, nil
|
||||
}
|
||||
|
||||
const captainPlaygroundFallbackMessage = "Captain assistant response generation is not configured for this account."
|
||||
|
||||
// GeneratePlaygroundResponse follows Chatwoot Captain assistant playground behavior.
|
||||
func (s *CaptainAssistantService) GeneratePlaygroundResponse(ctx context.Context, accountID, assistantID uint, req PlaygroundRequest) (map[string]any, error) {
|
||||
assistant, err := s.assistantRepo.GetByAccountAndID(ctx, accountID, assistantID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("assistant not found: %w", err)
|
||||
}
|
||||
|
||||
if s.captainV2Enabled(ctx, accountID) {
|
||||
history := playgroundMessageHistory(req.MessageHistory, req.MessageContent)
|
||||
content, err := s.generatePlaygroundLLMResponse(ctx, assistant, history)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]any{"response": content}, nil
|
||||
}
|
||||
|
||||
history := append([]PlaygroundMessage{}, req.MessageHistory...)
|
||||
content, err := s.generatePlaygroundLLMResponse(ctx, assistant, appendAdditionalPlaygroundMessage(history, req.MessageContent))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]any{"content": content}, nil
|
||||
}
|
||||
|
||||
func (s *CaptainAssistantService) generatePlaygroundLLMResponse(ctx context.Context, assistant *model.CaptainAssistant, history []PlaygroundMessage) (string, error) {
|
||||
if s.llmProvider == nil {
|
||||
return captainPlaygroundFallbackMessage, nil
|
||||
}
|
||||
|
||||
cfg, _ := assistant.GetConfig()
|
||||
messages := []llm.ChatMessage{{Role: "system", Content: buildSystemPrompt(assistant, cfg)}}
|
||||
for _, message := range history {
|
||||
if message.Role == "" || message.Content == "" {
|
||||
continue
|
||||
}
|
||||
messages = append(messages, llm.ChatMessage{Role: message.Role, Content: message.Content})
|
||||
}
|
||||
|
||||
resp, err := s.llmProvider.ChatCompletion(ctx, llm.ChatRequest{
|
||||
Model: cfg.Model,
|
||||
Messages: messages,
|
||||
Temperature: cfg.Temperature,
|
||||
MaxTokens: 1024,
|
||||
})
|
||||
if err != nil {
|
||||
applogger.L().Errorf("GeneratePlaygroundResponse LLM call: %v", err)
|
||||
return "", fmt.Errorf("llm generation failed: %w", err)
|
||||
}
|
||||
if len(resp.Choices) == 0 {
|
||||
return "", fmt.Errorf("no response from LLM")
|
||||
}
|
||||
return resp.Choices[0].Message.Content, nil
|
||||
}
|
||||
|
||||
func (s *CaptainAssistantService) captainV2Enabled(ctx context.Context, accountID uint) bool {
|
||||
flags, err := s.assistantRepo.GetAccountFeatureFlags(ctx, accountID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return featureFlagStringEnabled(flags, "captain_integration_v2")
|
||||
}
|
||||
|
||||
func featureFlagStringEnabled(raw, flag string) bool {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return false
|
||||
}
|
||||
var objectFlags map[string]bool
|
||||
if err := json.Unmarshal([]byte(raw), &objectFlags); err == nil {
|
||||
return objectFlags[flag]
|
||||
}
|
||||
var arrayFlags []string
|
||||
if err := json.Unmarshal([]byte(raw), &arrayFlags); err == nil {
|
||||
for _, item := range arrayFlags {
|
||||
if item == flag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
for _, item := range strings.Split(raw, ",") {
|
||||
if strings.TrimSpace(item) == flag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func playgroundMessageHistory(history []PlaygroundMessage, current string) []PlaygroundMessage {
|
||||
result := append([]PlaygroundMessage{}, history...)
|
||||
if strings.TrimSpace(current) == "" {
|
||||
return result
|
||||
}
|
||||
currentMessage := PlaygroundMessage{Role: "user", Content: current}
|
||||
if len(result) > 0 {
|
||||
last := result[len(result)-1]
|
||||
if last.Role == currentMessage.Role && last.Content == currentMessage.Content && last.AgentName == "" {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return append(result, currentMessage)
|
||||
}
|
||||
|
||||
func appendAdditionalPlaygroundMessage(history []PlaygroundMessage, current string) []PlaygroundMessage {
|
||||
if strings.TrimSpace(current) == "" {
|
||||
return history
|
||||
}
|
||||
return append(history, PlaygroundMessage{Role: "user", Content: current})
|
||||
}
|
||||
|
||||
// buildSystemPrompt constructs the system prompt from assistant config and guidelines.
|
||||
func buildSystemPrompt(assistant *model.CaptainAssistant, cfg *model.AssistantConfig) string {
|
||||
prompt := fmt.Sprintf("You are %s, an AI assistant.", assistant.Name)
|
||||
|
||||
Reference in New Issue
Block a user