mirror of
https://github.com/Dvorinka/Trackeep.git
synced 2026-06-03 20:12:58 +00:00
812 lines
24 KiB
Go
812 lines
24 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/trackeep/backend/models"
|
|
"github.com/trackeep/backend/services"
|
|
)
|
|
|
|
// SummarizeContentRequest represents a request to summarize content
|
|
type SummarizeContentRequest struct {
|
|
ContentType string `json:"content_type" binding:"required"` // "bookmark", "note", "file"
|
|
ContentID uint `json:"content_id" binding:"required"`
|
|
Provider string `json:"provider"` // "mistral", "longcat", "" for default
|
|
ModelType string `json:"model_type"` // "standard", "thinking", "upgraded_thinking"
|
|
Options struct {
|
|
Length string `json:"length"` // "short", "medium", "long"
|
|
Style string `json:"style"` // "bullet", "paragraph", "executive"
|
|
IncludeKey bool `json:"include_key"` // Include key points
|
|
} `json:"options"`
|
|
}
|
|
|
|
// GenerateTaskSuggestionsRequest represents a request for task suggestions
|
|
type GenerateTaskSuggestionsRequest struct {
|
|
Context string `json:"context"` // "calendar", "deadlines", "habits", "all"
|
|
Timeframe string `json:"timeframe"` // "today", "week", "month"
|
|
Limit int `json:"limit"` // Max number of suggestions
|
|
Provider string `json:"provider"` // "mistral", "longcat", "" for default
|
|
ModelType string `json:"model_type"` // "standard", "thinking", "upgraded_thinking"
|
|
}
|
|
|
|
// GenerateTagsRequest represents a request for tag suggestions
|
|
type GenerateTagsRequest struct {
|
|
ContentType string `json:"content_type" binding:"required"`
|
|
ContentID uint `json:"content_id" binding:"required"`
|
|
Content string `json:"content" binding:"required"`
|
|
ExistingTag string `json:"existing_tags"`
|
|
Provider string `json:"provider"` // "mistral", "longcat", "" for default
|
|
ModelType string `json:"model_type"` // "standard", "thinking", "upgraded_thinking"
|
|
}
|
|
|
|
// GenerateContentRequest represents a request for content generation
|
|
type GenerateContentRequest struct {
|
|
Prompt string `json:"prompt" binding:"required"`
|
|
ContentType string `json:"content_type" binding:"required"`
|
|
Context string `json:"context"`
|
|
Temperature float64 `json:"temperature"`
|
|
MaxLength int `json:"max_length"`
|
|
Provider string `json:"provider"` // "mistral", "longcat", "" for default
|
|
ModelType string `json:"model_type"` // "standard", "thinking", "upgraded_thinking"
|
|
}
|
|
|
|
// SummarizeContent generates AI summary for content
|
|
func SummarizeContent(c *gin.Context) {
|
|
userID := c.GetUint("user_id")
|
|
|
|
var req SummarizeContentRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Get content based on type
|
|
var content string
|
|
var title string
|
|
switch req.ContentType {
|
|
case "bookmark":
|
|
var bookmark models.Bookmark
|
|
if err := models.DB.Where("id = ? AND user_id = ?", req.ContentID, userID).First(&bookmark).Error; err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Content not found"})
|
|
return
|
|
}
|
|
content = bookmark.Content
|
|
title = bookmark.Title
|
|
case "note":
|
|
var note models.Note
|
|
if err := models.DB.Where("id = ? AND user_id = ?", req.ContentID, userID).First(¬e).Error; err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Content not found"})
|
|
return
|
|
}
|
|
content = note.Content
|
|
title = note.Title
|
|
default:
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Unsupported content type"})
|
|
return
|
|
}
|
|
|
|
if content == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "No content to summarize"})
|
|
return
|
|
}
|
|
|
|
// Check if summary already exists
|
|
var existingSummary models.AISummary
|
|
if err := models.DB.Where("user_id = ? AND content_type = ? AND content_id = ?", userID, req.ContentType, req.ContentID).First(&existingSummary).Error; err == nil {
|
|
c.JSON(http.StatusOK, existingSummary)
|
|
return
|
|
}
|
|
|
|
// Generate summary using AI
|
|
summary, err := generateAISummary(content, title, req.Options, req.Provider, req.ModelType)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate summary: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
// Save summary
|
|
aiSummary := models.AISummary{
|
|
UserID: userID,
|
|
ContentType: req.ContentType,
|
|
ContentID: req.ContentID,
|
|
Title: summary.Title,
|
|
Summary: summary.Summary,
|
|
KeyPoints: summary.KeyPoints,
|
|
Tags: summary.Tags,
|
|
ReadTime: summary.ReadTime,
|
|
Complexity: summary.Complexity,
|
|
ModelUsed: getProviderModel(req.Provider),
|
|
Confidence: summary.Confidence,
|
|
LastAnalyzed: time.Now(),
|
|
}
|
|
|
|
if err := models.DB.Create(&aiSummary).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save summary"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, aiSummary)
|
|
}
|
|
|
|
// GetTaskSuggestions generates AI task suggestions
|
|
func GetTaskSuggestions(c *gin.Context) {
|
|
userID := c.GetUint("user_id")
|
|
|
|
var req GenerateTaskSuggestionsRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Build context from user data
|
|
contextData, err := buildTaskContext(userID, req.Context, req.Timeframe)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to build context"})
|
|
return
|
|
}
|
|
|
|
// Generate suggestions
|
|
suggestions, err := generateTaskSuggestions(contextData, req.Limit, req.Provider, req.ModelType)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate suggestions: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
// Save suggestions
|
|
var aiSuggestions []models.AITaskSuggestion
|
|
for _, suggestion := range suggestions {
|
|
aiSuggestion := models.AITaskSuggestion{
|
|
UserID: userID,
|
|
Title: suggestion.Title,
|
|
Description: suggestion.Description,
|
|
Priority: suggestion.Priority,
|
|
Category: suggestion.Category,
|
|
Reasoning: suggestion.Reasoning,
|
|
ContextType: req.Context,
|
|
ContextData: suggestion.ContextData,
|
|
Deadline: suggestion.Deadline,
|
|
EstimatedTime: suggestion.EstimatedTime,
|
|
ModelUsed: getProviderModel(req.Provider),
|
|
Confidence: suggestion.Confidence,
|
|
}
|
|
models.DB.Create(&aiSuggestion)
|
|
aiSuggestions = append(aiSuggestions, aiSuggestion)
|
|
}
|
|
|
|
c.JSON(http.StatusOK, aiSuggestions)
|
|
}
|
|
|
|
// GenerateTagSuggestions generates AI tag suggestions
|
|
func GenerateTagSuggestions(c *gin.Context) {
|
|
userID := c.GetUint("user_id")
|
|
|
|
var req GenerateTagsRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Generate tags
|
|
tags, err := generateTagSuggestions(req.Content, req.ExistingTag, req.Provider, req.ModelType)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate tags: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
// Save suggestion
|
|
tagSuggestion := models.AITagSuggestion{
|
|
UserID: userID,
|
|
ContentType: req.ContentType,
|
|
ContentID: req.ContentID,
|
|
SuggestedTags: tags.Suggested,
|
|
ExistingTags: req.ExistingTag,
|
|
Relevance: tags.Relevance,
|
|
ModelUsed: getProviderModel(req.Provider),
|
|
Confidence: tags.Confidence,
|
|
}
|
|
|
|
if err := models.DB.Create(&tagSuggestion).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save tag suggestion"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, tagSuggestion)
|
|
}
|
|
|
|
// GenerateContent generates AI content
|
|
func GenerateContent(c *gin.Context) {
|
|
userID := c.GetUint("user_id")
|
|
|
|
var req GenerateContentRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Generate content
|
|
content, err := generateAIContent(req.Prompt, req.ContentType, req.Context, req.Temperature, req.MaxLength, req.Provider, req.ModelType)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate content: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
// Save generation
|
|
aiContent := models.AIContentGeneration{
|
|
UserID: userID,
|
|
Prompt: req.Prompt,
|
|
ContentType: req.ContentType,
|
|
Context: req.Context,
|
|
Title: content.Title,
|
|
Content: content.Content,
|
|
WordCount: content.WordCount,
|
|
ReadTime: content.ReadTime,
|
|
ModelUsed: getProviderModel(req.Provider),
|
|
ProcessingMs: content.ProcessingMs,
|
|
TokenCount: content.TokenCount,
|
|
Confidence: content.Confidence,
|
|
Temperature: req.Temperature,
|
|
}
|
|
|
|
if err := models.DB.Create(&aiContent).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save content"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, aiContent)
|
|
}
|
|
|
|
// GetAIProviders returns available AI providers
|
|
func GetAIProviders(c *gin.Context) {
|
|
providers := services.GetAvailableProviders()
|
|
|
|
providerInfo := make([]map[string]interface{}, 0)
|
|
for _, provider := range providers {
|
|
info := map[string]interface{}{
|
|
"id": string(provider),
|
|
"name": getProviderDisplayName(provider),
|
|
}
|
|
|
|
// Add model info
|
|
switch provider {
|
|
case services.ProviderMistral:
|
|
standardModel := os.Getenv("MISTRAL_MODEL")
|
|
thinkingModel := os.Getenv("MISTRAL_MODEL_THINKING")
|
|
|
|
info["models"] = []map[string]string{
|
|
{"id": "standard", "name": standardModel, "type": "Standard"},
|
|
{"id": "thinking", "name": thinkingModel, "type": "Thinking"},
|
|
}
|
|
info["description"] = "Mistral AI - Fast and efficient European AI"
|
|
info["icon"] = "🇪🇺"
|
|
|
|
case services.ProviderLongCat:
|
|
standardModel := os.Getenv("LONGCAT_MODEL")
|
|
thinkingModel := os.Getenv("LONGCAT_MODEL_THINKING")
|
|
upgradedModel := os.Getenv("LONGCAT_MODEL_THINKING_UPGRADED")
|
|
|
|
models := []map[string]string{
|
|
{"id": "standard", "name": standardModel, "type": "Standard"},
|
|
{"id": "thinking", "name": thinkingModel, "type": "Thinking"},
|
|
}
|
|
|
|
if upgradedModel != "" {
|
|
models = append(models, map[string]string{"id": "upgraded_thinking", "name": upgradedModel, "type": "Upgraded Thinking"})
|
|
}
|
|
|
|
info["models"] = models
|
|
info["description"] = "LongCat AI - High-performance AI models"
|
|
info["icon"] = "🐱"
|
|
|
|
case services.ProviderGrok:
|
|
standardModel := os.Getenv("GROK_MODEL")
|
|
thinkingModel := os.Getenv("GROK_MODEL_THINKING")
|
|
|
|
models := []map[string]string{
|
|
{"id": "standard", "name": standardModel, "type": "Standard"},
|
|
}
|
|
|
|
if thinkingModel != "" && thinkingModel != standardModel {
|
|
models = append(models, map[string]string{"id": "thinking", "name": thinkingModel, "type": "Thinking"})
|
|
}
|
|
|
|
info["models"] = models
|
|
info["description"] = "Grok AI - Real-time information from X"
|
|
info["icon"] = "🐦"
|
|
|
|
case services.ProviderDeepSeek:
|
|
standardModel := os.Getenv("DEEPSEEK_MODEL")
|
|
thinkingModel := os.Getenv("DEEPSEEK_MODEL_THINKING")
|
|
|
|
models := []map[string]string{
|
|
{"id": "standard", "name": standardModel, "type": "Standard"},
|
|
}
|
|
|
|
if thinkingModel != "" && thinkingModel != standardModel {
|
|
models = append(models, map[string]string{"id": "thinking", "name": thinkingModel, "type": "Reasoning"})
|
|
}
|
|
|
|
info["models"] = models
|
|
info["description"] = "DeepSeek - Advanced reasoning AI"
|
|
info["icon"] = "🔍"
|
|
|
|
case services.ProviderOllama:
|
|
standardModel := os.Getenv("OLLAMA_MODEL")
|
|
thinkingModel := os.Getenv("OLLAMA_MODEL_THINKING")
|
|
|
|
models := []map[string]string{
|
|
{"id": "standard", "name": standardModel, "type": "Standard"},
|
|
}
|
|
|
|
if thinkingModel != "" && thinkingModel != standardModel {
|
|
models = append(models, map[string]string{"id": "thinking", "name": thinkingModel, "type": "Local"})
|
|
}
|
|
|
|
info["models"] = models
|
|
info["description"] = "Ollama - Local AI models"
|
|
info["icon"] = "🦙"
|
|
|
|
case services.ProviderOpenRouter:
|
|
standardModel := os.Getenv("OPENROUTER_MODEL")
|
|
thinkingModel := os.Getenv("OPENROUTER_MODEL_THINKING")
|
|
|
|
models := []map[string]string{
|
|
{"id": "standard", "name": standardModel, "type": "Standard"},
|
|
}
|
|
|
|
if thinkingModel != "" && thinkingModel != standardModel {
|
|
models = append(models, map[string]string{"id": "thinking", "name": thinkingModel, "type": "Thinking"})
|
|
}
|
|
|
|
info["models"] = models
|
|
info["description"] = "OpenRouter - Unified access to many models"
|
|
info["icon"] = "🌀"
|
|
}
|
|
|
|
providerInfo = append(providerInfo, info)
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"providers": providerInfo})
|
|
}
|
|
|
|
// Helper function to get display name for provider
|
|
func getProviderDisplayName(provider services.AIProvider) string {
|
|
switch provider {
|
|
case services.ProviderMistral:
|
|
return "Mistral AI"
|
|
case services.ProviderLongCat:
|
|
return "LongCat AI"
|
|
case services.ProviderGrok:
|
|
return "Grok AI"
|
|
case services.ProviderDeepSeek:
|
|
return "DeepSeek"
|
|
case services.ProviderOllama:
|
|
return "Ollama"
|
|
case services.ProviderOpenRouter:
|
|
return "OpenRouter"
|
|
default:
|
|
return string(provider)
|
|
}
|
|
}
|
|
|
|
// GetAISummaries retrieves AI summaries for user
|
|
func GetAISummaries(c *gin.Context) {
|
|
userID := c.GetUint("user_id")
|
|
|
|
var summaries []models.AISummary
|
|
models.DB.Where("user_id = ?", userID).Order("created_at desc").Find(&summaries)
|
|
|
|
c.JSON(http.StatusOK, summaries)
|
|
}
|
|
|
|
// GetTaskSuggestions retrieves task suggestions for user
|
|
func GetTaskSuggestionsList(c *gin.Context) {
|
|
userID := c.GetUint("user_id")
|
|
|
|
var suggestions []models.AITaskSuggestion
|
|
models.DB.Where("user_id = ? AND accepted = false AND dismissed = false", userID).Order("created_at desc").Find(&suggestions)
|
|
|
|
c.JSON(http.StatusOK, suggestions)
|
|
}
|
|
|
|
// AcceptTaskSuggestion accepts a task suggestion
|
|
func AcceptTaskSuggestion(c *gin.Context) {
|
|
userID := c.GetUint("user_id")
|
|
suggestionID := c.Param("id")
|
|
|
|
var suggestion models.AITaskSuggestion
|
|
if err := models.DB.Where("id = ? AND user_id = ?", suggestionID, userID).First(&suggestion).Error; err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Suggestion not found"})
|
|
return
|
|
}
|
|
|
|
// Create actual task
|
|
task := models.Task{
|
|
UserID: userID,
|
|
Title: suggestion.Title,
|
|
Description: suggestion.Description,
|
|
Priority: models.TaskPriority(suggestion.Priority),
|
|
Status: models.TaskStatusPending,
|
|
DueDate: suggestion.Deadline,
|
|
}
|
|
|
|
if err := models.DB.Create(&task).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create task"})
|
|
return
|
|
}
|
|
|
|
// Mark suggestion as accepted
|
|
suggestion.Accepted = true
|
|
models.DB.Save(&suggestion)
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "Task created successfully", "task_id": task.ID})
|
|
}
|
|
|
|
// DismissTaskSuggestion dismisses a task suggestion
|
|
func DismissTaskSuggestion(c *gin.Context) {
|
|
userID := c.GetUint("user_id")
|
|
suggestionID := c.Param("id")
|
|
|
|
var suggestion models.AITaskSuggestion
|
|
if err := models.DB.Where("id = ? AND user_id = ?", suggestionID, userID).First(&suggestion).Error; err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Suggestion not found"})
|
|
return
|
|
}
|
|
|
|
suggestion.Dismissed = true
|
|
models.DB.Save(&suggestion)
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "Suggestion dismissed"})
|
|
}
|
|
|
|
// Helper structs for AI responses
|
|
type AISummaryResponse struct {
|
|
Title string `json:"title"`
|
|
Summary string `json:"summary"`
|
|
KeyPoints string `json:"key_points"`
|
|
Tags string `json:"tags"`
|
|
ReadTime int `json:"read_time"`
|
|
Complexity string `json:"complexity"`
|
|
Confidence float64 `json:"confidence"`
|
|
}
|
|
|
|
type TaskSuggestionResponse struct {
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
Priority string `json:"priority"`
|
|
Category string `json:"category"`
|
|
Reasoning string `json:"reasoning"`
|
|
ContextData string `json:"context_data"`
|
|
Deadline *time.Time `json:"deadline"`
|
|
EstimatedTime int `json:"estimated_time"`
|
|
Confidence float64 `json:"confidence"`
|
|
}
|
|
|
|
type TagSuggestionResponse struct {
|
|
Suggested string `json:"suggested"`
|
|
Relevance float64 `json:"relevance"`
|
|
Confidence float64 `json:"confidence"`
|
|
}
|
|
|
|
type ContentGenerationResponse struct {
|
|
Title string `json:"title"`
|
|
Content string `json:"content"`
|
|
WordCount int `json:"word_count"`
|
|
ReadTime int `json:"read_time"`
|
|
ProcessingMs int64 `json:"processing_ms"`
|
|
TokenCount int `json:"token_count"`
|
|
Confidence float64 `json:"confidence"`
|
|
}
|
|
|
|
// AI generation functions (simplified - would call actual AI models)
|
|
func generateAISummary(content, title string, options struct {
|
|
Length string `json:"length"`
|
|
Style string `json:"style"`
|
|
IncludeKey bool `json:"include_key"`
|
|
}, provider string, modelType string) (*AISummaryResponse, error) {
|
|
// Build prompt for summarization
|
|
prompt := fmt.Sprintf(`Please summarize the following content:
|
|
Title: %s
|
|
Content: %s
|
|
|
|
Length: %s
|
|
Style: %s
|
|
Include key points: %t
|
|
|
|
Provide a JSON response with:
|
|
- title: Brief title
|
|
- summary: Main summary
|
|
- key_points: Array of key points (if requested)
|
|
- tags: Array of relevant tags
|
|
- read_time: Estimated reading time in minutes
|
|
- complexity: "low", "medium", or "high"
|
|
- confidence: Confidence score 0-1`, title, content, options.Length, options.Style, options.IncludeKey)
|
|
|
|
messages := []services.Message{
|
|
{Role: "system", Content: "You are an expert content summarizer. Always respond with valid JSON."},
|
|
{Role: "user", Content: prompt},
|
|
}
|
|
|
|
// Determine provider
|
|
aiProvider := services.ProviderMistral // default
|
|
if provider == "longcat" {
|
|
aiProvider = services.ProviderLongCat
|
|
}
|
|
|
|
aiService := services.NewAIService(aiProvider)
|
|
|
|
req := services.AIRequest{
|
|
Messages: messages,
|
|
MaxTokens: 2000,
|
|
Temperature: 0.3,
|
|
ModelType: modelType,
|
|
}
|
|
|
|
var resp *services.AIResponse
|
|
var err error
|
|
|
|
// Choose the appropriate method based on model type
|
|
switch req.ModelType {
|
|
case "thinking":
|
|
resp, err = aiService.ChatCompletionWithThinking(req)
|
|
case "upgraded_thinking":
|
|
resp, err = aiService.ChatCompletionWithUpgradedThinking(req)
|
|
default:
|
|
resp, err = aiService.ChatCompletion(req)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse the response content properly for thinking models
|
|
actualContent := services.ParseThinkingResponse(resp, aiProvider, modelType)
|
|
|
|
var summary AISummaryResponse
|
|
if err := json.Unmarshal([]byte(actualContent), &summary); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &summary, nil
|
|
}
|
|
|
|
func generateTaskSuggestions(contextData map[string]interface{}, limit int, provider string, modelType string) ([]TaskSuggestionResponse, error) {
|
|
// Build prompt for task suggestions
|
|
prompt := fmt.Sprintf(`Based on the following user context, suggest %d tasks:
|
|
Context: %+v
|
|
|
|
Provide a JSON array of task objects with:
|
|
- title: Task title
|
|
- description: Task description
|
|
- priority: "low", "medium", "high", "urgent"
|
|
- category: Task category
|
|
- reasoning: Why this task is suggested
|
|
- context_data: Additional context
|
|
- deadline: Suggested deadline (ISO date or null)
|
|
- estimated_time: Estimated time in minutes
|
|
- confidence: Confidence score 0-1`, limit, contextData)
|
|
|
|
messages := []services.Message{
|
|
{Role: "system", Content: "You are a productivity assistant. Always respond with valid JSON array."},
|
|
{Role: "user", Content: prompt},
|
|
}
|
|
|
|
// Determine provider
|
|
aiProvider := services.ProviderMistral // default
|
|
if provider == "longcat" {
|
|
aiProvider = services.ProviderLongCat
|
|
}
|
|
|
|
aiService := services.NewAIService(aiProvider)
|
|
|
|
req := services.AIRequest{
|
|
Messages: messages,
|
|
MaxTokens: 2000,
|
|
Temperature: 0.7,
|
|
ModelType: modelType,
|
|
}
|
|
|
|
var resp *services.AIResponse
|
|
var err error
|
|
|
|
// Choose the appropriate method based on model type
|
|
switch req.ModelType {
|
|
case "thinking":
|
|
resp, err = aiService.ChatCompletionWithThinking(req)
|
|
case "upgraded_thinking":
|
|
resp, err = aiService.ChatCompletionWithUpgradedThinking(req)
|
|
default:
|
|
resp, err = aiService.ChatCompletion(req)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse the response content properly for thinking models
|
|
actualContent := services.ParseThinkingResponse(resp, aiProvider, modelType)
|
|
|
|
var suggestions []TaskSuggestionResponse
|
|
if err := json.Unmarshal([]byte(actualContent), &suggestions); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return suggestions, nil
|
|
}
|
|
|
|
func generateTagSuggestions(content, existingTags string, provider string, modelType string) (*TagSuggestionResponse, error) {
|
|
prompt := fmt.Sprintf(`Suggest relevant tags for this content:
|
|
Content: %s
|
|
Existing tags: %s
|
|
|
|
Provide JSON response with:
|
|
- suggested: Array of suggested tags
|
|
- relevance: Relevance score 0-1
|
|
- confidence: Confidence score 0-1`, content, existingTags)
|
|
|
|
messages := []services.Message{
|
|
{Role: "system", Content: "You are a tagging expert. Always respond with valid JSON."},
|
|
{Role: "user", Content: prompt},
|
|
}
|
|
|
|
// Determine provider
|
|
aiProvider := services.ProviderMistral // default
|
|
if provider == "longcat" {
|
|
aiProvider = services.ProviderLongCat
|
|
}
|
|
|
|
aiService := services.NewAIService(aiProvider)
|
|
|
|
req := services.AIRequest{
|
|
Messages: messages,
|
|
MaxTokens: 1000,
|
|
Temperature: 0.5,
|
|
ModelType: modelType,
|
|
}
|
|
|
|
var resp *services.AIResponse
|
|
var err error
|
|
|
|
// Choose the appropriate method based on model type
|
|
switch req.ModelType {
|
|
case "thinking":
|
|
resp, err = aiService.ChatCompletionWithThinking(req)
|
|
case "upgraded_thinking":
|
|
resp, err = aiService.ChatCompletionWithUpgradedThinking(req)
|
|
default:
|
|
resp, err = aiService.ChatCompletion(req)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse the response content properly for thinking models
|
|
actualContent := services.ParseThinkingResponse(resp, aiProvider, modelType)
|
|
|
|
var tags TagSuggestionResponse
|
|
if err := json.Unmarshal([]byte(actualContent), &tags); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &tags, nil
|
|
}
|
|
|
|
func generateAIContent(prompt, contentType, context string, temperature float64, maxLength int, provider string, modelType string) (*ContentGenerationResponse, error) {
|
|
fullPrompt := fmt.Sprintf(`Generate %s content based on this prompt:
|
|
%s
|
|
Additional context: %s
|
|
Max length: %d words
|
|
|
|
Provide JSON response with:
|
|
- title: Generated title
|
|
- content: Generated content
|
|
- word_count: Word count
|
|
- read_time: Estimated reading time in minutes
|
|
- confidence: Confidence score 0-1`, contentType, prompt, context, maxLength)
|
|
|
|
messages := []services.Message{
|
|
{Role: "system", Content: "You are a content generation expert. Always respond with valid JSON."},
|
|
{Role: "user", Content: fullPrompt},
|
|
}
|
|
|
|
// Determine provider
|
|
aiProvider := services.ProviderMistral // default
|
|
if provider == "longcat" {
|
|
aiProvider = services.ProviderLongCat
|
|
}
|
|
|
|
aiService := services.NewAIService(aiProvider)
|
|
|
|
// Adjust temperature if provided
|
|
temp := 0.7
|
|
if temperature > 0 {
|
|
temp = temperature
|
|
}
|
|
|
|
req := services.AIRequest{
|
|
Messages: messages,
|
|
MaxTokens: maxLength * 2, // Rough estimate
|
|
Temperature: temp,
|
|
ModelType: modelType,
|
|
}
|
|
|
|
var resp *services.AIResponse
|
|
var err error
|
|
|
|
// Choose the appropriate method based on model type
|
|
switch req.ModelType {
|
|
case "thinking":
|
|
resp, err = aiService.ChatCompletionWithThinking(req)
|
|
case "upgraded_thinking":
|
|
resp, err = aiService.ChatCompletionWithUpgradedThinking(req)
|
|
default:
|
|
resp, err = aiService.ChatCompletion(req)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse the response content properly for thinking models
|
|
actualContent := services.ParseThinkingResponse(resp, aiProvider, modelType)
|
|
|
|
var content ContentGenerationResponse
|
|
if err := json.Unmarshal([]byte(actualContent), &content); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
content.ProcessingMs = 0 // Would track actual processing time
|
|
content.TokenCount = resp.Usage.TotalTokens
|
|
|
|
return &content, nil
|
|
}
|
|
|
|
func buildTaskContext(userID uint, contextType, timeframe string) (map[string]interface{}, error) {
|
|
ctx := make(map[string]interface{})
|
|
|
|
// Get upcoming tasks
|
|
var tasks []models.Task
|
|
query := models.DB.Where("user_id = ?", userID)
|
|
|
|
if timeframe == "today" {
|
|
query = query.Where("deadline <= ?", time.Now().AddDate(0, 0, 1))
|
|
} else if timeframe == "week" {
|
|
query = query.Where("deadline <= ?", time.Now().AddDate(0, 0, 7))
|
|
}
|
|
|
|
query.Find(&tasks)
|
|
ctx["tasks"] = tasks
|
|
|
|
// Get calendar events
|
|
var events []models.CalendarEvent
|
|
models.DB.Where("user_id = ? AND start_time >= ?", userID, time.Now()).Find(&events)
|
|
ctx["events"] = events
|
|
|
|
return ctx, nil
|
|
}
|
|
|
|
// Helper function to get model name based on provider
|
|
func getProviderModel(provider string) string {
|
|
switch provider {
|
|
case "mistral":
|
|
return os.Getenv("MISTRAL_MODEL")
|
|
case "longcat":
|
|
return os.Getenv("LONGCAT_MODEL")
|
|
case "grok":
|
|
return os.Getenv("GROK_MODEL")
|
|
case "deepseek":
|
|
return os.Getenv("DEEPSEEK_MODEL")
|
|
case "ollama":
|
|
return os.Getenv("OLLAMA_MODEL")
|
|
case "openrouter":
|
|
return os.Getenv("OPENROUTER_MODEL")
|
|
default:
|
|
return os.Getenv("MISTRAL_MODEL")
|
|
}
|
|
}
|