Files
2025-09-02 08:01:09 +00:00

148 lines
3.9 KiB
Go

package llm
import (
"context"
"fmt"
"strings"
"github.com/0xJacky/Nginx-UI/settings"
"github.com/sashabaranov/go-openai"
"github.com/uozi-tech/cosy/logger"
)
// GenerateSessionTitle generates a concise title for an LLM session based on the conversation context
func GenerateSessionTitle(messages []openai.ChatCompletionMessage) (string, error) {
client, err := GetClient()
if err != nil {
return "", fmt.Errorf("failed to get LLM client: %w", err)
}
// Create a summarized context from the first few messages
messageContext := extractContextForTitleGeneration(messages)
if messageContext == "" {
return "New Session", nil
}
// Prepare the system message for title generation
systemMessage := openai.ChatCompletionMessage{
Role: openai.ChatMessageRoleSystem,
Content: `You are a helpful assistant that generates concise, descriptive titles for chat sessions.
Based on the conversation context provided, generate a short title (2-6 words) that captures the main topic or purpose.
The title should be clear, specific, and professional.
Respond only with the title, no additional text or formatting.`,
}
userMessage := openai.ChatCompletionMessage{
Role: openai.ChatMessageRoleUser,
Content: fmt.Sprintf("Generate a title for this conversation:\n\n%s", messageContext),
}
req := openai.ChatCompletionRequest{
Model: settings.OpenAISettings.Model,
Messages: []openai.ChatCompletionMessage{systemMessage, userMessage},
MaxTokens: 20, // Keep it short
Temperature: 0.3, // Lower temperature for more consistent titles
}
resp, err := client.CreateChatCompletion(context.Background(), req)
if err != nil {
logger.Error("Failed to generate session title:", err)
return "", fmt.Errorf("failed to generate title: %w", err)
}
if len(resp.Choices) == 0 {
return "New Session", nil
}
title := strings.TrimSpace(resp.Choices[0].Message.Content)
// Sanitize the title
title = sanitizeTitle(title)
if title == "" {
return "New Session", nil
}
return title, nil
}
// extractContextForTitleGeneration extracts relevant context from messages for title generation
func extractContextForTitleGeneration(messages []openai.ChatCompletionMessage) string {
if len(messages) == 0 {
return ""
}
var contextBuilder strings.Builder
messageCount := 0
maxMessages := 3 // Only use the first few messages for context
maxLength := 800 // Limit total context length
for _, message := range messages {
if messageCount >= maxMessages {
break
}
// Skip system messages for title generation
if message.Role == openai.ChatMessageRoleSystem {
continue
}
content := strings.TrimSpace(message.Content)
if content == "" {
continue
}
// Add role prefix for clarity
rolePrefix := ""
switch message.Role {
case openai.ChatMessageRoleUser:
rolePrefix = "User: "
case openai.ChatMessageRoleAssistant:
rolePrefix = "Assistant: "
}
// Truncate very long messages
if len(content) > 200 {
content = content[:200] + "..."
}
newContent := fmt.Sprintf("%s%s\n", rolePrefix, content)
// Check if adding this message would exceed the max length
if contextBuilder.Len()+len(newContent) > maxLength {
break
}
contextBuilder.WriteString(newContent)
messageCount++
}
return contextBuilder.String()
}
// sanitizeTitle cleans up the generated title
func sanitizeTitle(title string) string {
// Remove quotes if present
title = strings.Trim(title, `"'`)
// Remove any prefix like "Title: " if present
if strings.HasPrefix(strings.ToLower(title), "title:") {
title = strings.TrimSpace(title[6:])
}
// Limit length
if len(title) > 50 {
title = title[:47] + "..."
}
// Replace any problematic characters
title = strings.ReplaceAll(title, "\n", " ")
title = strings.ReplaceAll(title, "\r", " ")
// Collapse multiple spaces
for strings.Contains(title, " ") {
title = strings.ReplaceAll(title, " ", " ")
}
return strings.TrimSpace(title)
}