readysite / website / internal / assist / context.go
3.1 KB
context.go
package assist

import (
	"sort"

	"github.com/readysite/readysite/website/models"
)

// MaxNotesInContext is the maximum number of notes to include in AI context.
const MaxNotesInContext = 10

// noteScore represents a note with its relevance score.
type noteScore struct {
	note  *models.Note
	score int
}

// SelectRelevantNotes returns the most relevant notes for the AI context.
// Notes are scored by category match with current context and recency.
func SelectRelevantNotes(ctx *ConversationContext, maxNotes int) []*models.Note {
	if maxNotes <= 0 {
		maxNotes = MaxNotesInContext
	}

	// Get all active notes
	notes, err := models.Notes.Search("WHERE Active = true ORDER BY UpdatedAt DESC")
	if err != nil || len(notes) == 0 {
		return nil
	}

	// If we have fewer notes than the limit, return all
	if len(notes) <= maxNotes {
		return notes
	}

	// Score each note based on relevance
	scored := make([]noteScore, len(notes))
	for i, note := range notes {
		scored[i] = noteScore{
			note:  note,
			score: scoreNote(note, ctx),
		}
	}

	// Sort by score descending
	sort.Slice(scored, func(i, j int) bool {
		return scored[i].score > scored[j].score
	})

	// Return top notes
	result := make([]*models.Note, maxNotes)
	for i := 0; i < maxNotes; i++ {
		result[i] = scored[i].note
	}

	return result
}

// scoreNote calculates a relevance score for a note.
// Higher scores indicate more relevant notes.
func scoreNote(note *models.Note, ctx *ConversationContext) int {
	score := 0

	// Base score for being active
	score += 10

	// Preference notes are always relevant
	if note.Type == models.NoteTypePreference {
		score += 20
	}

	// Convention notes are generally relevant
	if note.Type == models.NoteTypeConvention {
		score += 15
	}

	// Learned patterns add some value
	if note.Type == models.NoteTypeLearned {
		score += 10
	}

	// Category-based scoring
	if ctx != nil {
		// If working on a page, content and style notes are more relevant
		if ctx.CurrentPageID != "" {
			if note.Category == models.NoteCategoryContent {
				score += 15
			}
			if note.Category == models.NoteCategoryStyle {
				score += 15
			}
		}

		// If working on a collection, structure notes are more relevant
		if ctx.CurrentCollectionID != "" {
			if note.Category == models.NoteCategoryStructure {
				score += 15
			}
		}
	}

	// User-created notes are slightly more important than AI-learned ones
	if note.Source == models.NoteSourceUser {
		score += 5
	}

	return score
}

// GetActiveNotes returns all active notes.
func GetActiveNotes() []*models.Note {
	notes, _ := models.Notes.Search("WHERE Active = true ORDER BY UpdatedAt DESC")
	return notes
}

// GetNotesByType returns active notes of a specific type.
func GetNotesByType(noteType string) []*models.Note {
	notes, _ := models.Notes.Search("WHERE Active = true AND Type = ? ORDER BY UpdatedAt DESC", noteType)
	return notes
}

// GetNotesByCategory returns active notes of a specific category.
func GetNotesByCategory(category string) []*models.Note {
	notes, _ := models.Notes.Search("WHERE Active = true AND Category = ? ORDER BY UpdatedAt DESC", category)
	return notes
}
← Back