Files
gochat/internal/model/copilot_models.go
T

116 lines
4.3 KiB
Go

package model
import (
"encoding/json"
)
// --- Copilot Thread Model ---
// Reference: Chatwoot enterprise/app/models/copilot_thread.rb
// Table: copilot_threads
//
// CopilotThread represents a conversation thread between a user
// and a Captain Assistant in the Copilot UI (side-panel chat).
// Linked to Account, User, and optionally an Assistant.
type CopilotThread struct {
Base
AccountID uint `gorm:"index;not null" json:"account_id"`
UserID uint `gorm:"index;not null" json:"user_id"`
AssistantID *uint `gorm:"index" json:"assistant_id,omitempty"`
Title string `gorm:"size:255;not null" json:"title"`
// Relationships
Messages []CopilotMessage `gorm:"foreignKey:CopilotThreadID" json:"messages,omitempty"`
User User `gorm:"foreignKey:UserID" json:"user,omitempty"`
Assistant CaptainAssistant `gorm:"foreignKey:AssistantID" json:"assistant,omitempty"`
}
func (CopilotThread) TableName() string { return "copilot_threads" }
// PreviousHistory returns the conversation history as role/content pairs
// for LLM context building. Reference: Chatwoot CopilotThread#previous_history
func (t *CopilotThread) PreviousHistory(messages []CopilotMessage) []ChatMessage {
var history []ChatMessage
for _, m := range messages {
if m.MessageType == CopilotMessageTypeUser || m.MessageType == CopilotMessageTypeAssistant {
content := m.GetMessageContent()
history = append(history, ChatMessage{
Role: string(m.MessageType),
Content: content,
})
}
}
return history
}
// --- Copilot Message Model ---
// Reference: Chatwoot enterprise/app/models/copilot_message.rb
// Table: copilot_messages
//
// CopilotMessage represents a single message within a CopilotThread.
// Message content is stored as JSONB (supports structured content,
// tool results, thinking traces). MessageType: user/assistant/assistant_thinking.
type CopilotMessage struct {
Base
AccountID uint `gorm:"index;not null" json:"account_id"`
CopilotThreadID uint `gorm:"index;not null" json:"copilot_thread_id"`
MessageType CopilotMessageType `gorm:"size:50;default:user;not null" json:"message_type"`
Message json.RawMessage `gorm:"type:jsonb;not null" json:"message"`
CopilotThread CopilotThread `gorm:"foreignKey:CopilotThreadID" json:"copilot_thread,omitempty"`
}
func (CopilotMessage) TableName() string { return "copilot_messages" }
// GetMessageContent extracts the "content" field from the JSONB message.
func (m *CopilotMessage) GetMessageContent() string {
var msgMap map[string]interface{}
if err := json.Unmarshal(m.Message, &msgMap); err != nil {
return ""
}
if val, ok := msgMap["content"]; ok {
s, _ := val.(string)
return s
}
return ""
}
// CopilotSuggestionMessage represents a Copilot suggestion/reply tied to a conversation.
// These are "side-panel" suggestions the Copilot generates for the agent,
// distinct from the thread-based CopilotMessage (which tracks the full chat history).
// Reference: Chatwoot enterprise/app/controllers/api/v1/copilot_messages_controller.rb
// Table: copilot_suggestion_messages
type CopilotSuggestionType string
const (
CopilotSuggestionTypeReply CopilotSuggestionType = "reply"
CopilotSuggestionTypeSuggestion CopilotSuggestionType = "suggestion"
CopilotSuggestionTypeSummary CopilotSuggestionType = "summary"
)
type CopilotSuggestionStatus string
const (
CopilotSuggestionStatusPending CopilotSuggestionStatus = "pending"
CopilotSuggestionStatusAccepted CopilotSuggestionStatus = "accepted"
CopilotSuggestionStatusRejected CopilotSuggestionStatus = "rejected"
)
type CopilotSuggestionMessage struct {
Base
AccountID uint `gorm:"index;not null" json:"account_id"`
ConversationID uint `gorm:"index;not null" json:"conversation_id"`
Content string `gorm:"type:text;not null" json:"content"`
SuggestionType CopilotSuggestionType `gorm:"size:50;not null;default:suggestion" json:"suggestion_type"`
Status CopilotSuggestionStatus `gorm:"size:50;not null;default:pending" json:"status"`
}
func (CopilotSuggestionMessage) TableName() string { return "copilot_suggestion_messages" }
// ChatMessage is a simplified LLM message format (role + content).
// Used for building LLM conversation context.
type ChatMessage struct {
Role string `json:"role"`
Content string `json:"content"`
}