package ai import ( "context" "fmt" "time" "git.pengzhan.dev/haopengzhan/rsspipe/internal/ai" "git.pengzhan.dev/haopengzhan/rsspipe/internal/db" "git.pengzhan.dev/haopengzhan/rsspipe/internal/models" "git.pengzhan.dev/haopengzhan/rsspipe/internal/worker" "go.uber.org/zap" ) // NewTriageHandler creates a worker action handler for AI autonomous triage decisions. func NewTriageHandler(database *db.DB, client *ai.Client, pool *worker.Pool, log *zap.Logger) worker.ActionHandler { return func(ctx context.Context, entry models.FeedEntry, params map[string]interface{}) (*worker.ActionResult, error) { cfg, _ := LoadConfig(database) if !cfg.Enabled { log.Warn("AI triage skipped: AI features are disabled in Settings", zap.String("entry_id", entry.ID)) return nil, nil } if cfg.APIKey == "" && cfg.Provider != "ollama" { log.Warn("AI triage action skipped: API key is not configured in Settings > AI & Integrations", zap.String("entry_id", entry.ID), ) return nil, nil } policyPrompt, _ := params["prompt"].(string) var allowedActions []string if rawAllowed, ok := params["allowed_actions"].([]interface{}); ok { for _, a := range rawAllowed { allowedActions = append(allowedActions, fmt.Sprintf("%v", a)) } } else if strList, ok := params["allowed_actions"].([]string); ok { allowedActions = strList } if len(allowedActions) == 0 { allowedActions = []string{"mark_read", "mark_starred", "set_variant"} } decision, err := client.GenerateAgentDecision(ctx, cfg, policyPrompt, allowedActions, entry) if err != nil { log.Error("AI triage decision failed", zap.String("entry_id", entry.ID), zap.Error(err), ) return nil, err } log.Info("AI Triage agent executed successfully", zap.String("entry_id", entry.ID), zap.String("reasoning", decision.Reasoning), zap.Int("actions_approved", len(decision.Actions)), ) res := &worker.ActionResult{} for _, act := range decision.Actions { switch act.Type { case "mark_read": trueVal := true res.MarkRead = &trueVal case "mark_starred": trueVal := true res.MarkStarred = &trueVal case "set_variant": vKey, _ := act.Params["variant_key"].(string) if vKey == "" { vKey, _ = act.Params["key"].(string) } vVal, _ := act.Params["value"].(string) if vKey != "" { v := models.EntryVariant{ EntryID: entry.ID, VariantKey: vKey, ContentType: "text", TextValue: vVal, CreatedAt: time.Now(), UpdatedAt: time.Now(), } res.ResultVariant = &v } default: pool.Dispatch(ctx, act, entry, 0) } } return res, nil } }