diff --git a/internal/digest/digest.go b/internal/digest/digest.go index 86de34a..8565dba 100644 --- a/internal/digest/digest.go +++ b/internal/digest/digest.go @@ -23,7 +23,7 @@ const ( ) const ( - LLMTimeout = 2 * time.Minute + LLMTimeout = 5 * time.Minute DayGroupLayout = "2006-01-02" DayGroupTitleLayout = "Jan 2, 2006" ) @@ -171,7 +171,9 @@ type llmEntry struct { FeedTitle string `json:"feed_title"` } -const llmPrompt = `You are an expert news editor with a talent for identifying the most important and interesting information from a large volume of content. Your primary goal is to save the user time by providing a high-level, insightful overview of their news feeds. +const llmPrompt = `You are an expert content curator with a talent for identifying the most important and interesting information from a large volume of content. Your primary goal is to save the user time by providing a high-level, insightful overview of their content feeds. + +The content can come from a variety of sources, including news sites, blogs, forums like Reddit, and social media like Bluesky. Your tone should be that of a helpful assistant, not a news editor. Given the following list of entries, your task is to perform two distinct functions: @@ -185,6 +187,7 @@ Given the following list of entries, your task is to perform two distinct functi * The number of groups should be driven by the content itself. **Do not create a group for a single entry unless it represents a major, unique event.** The ideal number of groups is one that best helps a user skim the content. A group can contain many entries if they are all highly related. * Group titles should be short, descriptive, and useful for skimming (e.g., "AI Industry News," "Project Updates," "Global Politics"). * Within each group, you must rank the 'entries' by importance, with the most significant or actionable item appearing first. + * The goal of grouping is to help with reading, so if a few entries are about some similar topic, those are worth grouping because the user can just read one and skim the rest. Return the response as a JSON object according to the desired responseSchema. @@ -304,5 +307,12 @@ func (g *LLMGrouper) GroupEntries(entries *miniflux.Entries) ([]*models.EntryGro } } - return entryGroups, response.Summary + feedIDs := make(map[int64]bool) + for _, entry := range *entries { + feedIDs[entry.FeedID] = true + } + + statsSummary := fmt.Sprintf("You have %d entries from %d feeds.", len(*entries), len(feedIDs)) + + return entryGroups, fmt.Sprintf("%s\n\n%s", response.Summary, statsSummary) } diff --git a/internal/digest/digest_test.go b/internal/digest/digest_test.go index b3d16ac..53f4531 100644 --- a/internal/digest/digest_test.go +++ b/internal/digest/digest_test.go @@ -34,29 +34,33 @@ func (m *mockLLMService) GenerateContent(ctx context.Context, prompt string, sch func createDayGrouperMockEntries() *miniflux.Entries { return &miniflux.Entries{ { - ID: 1, - Title: "Entry 1 - Jan 2", - Date: time.Date(2024, time.January, 2, 10, 0, 0, 0, time.UTC), - Feed: &miniflux.Feed{ID: 100, Title: "Feed A"}, + ID: 1, + Title: "Entry 1 - Jan 2", + Date: time.Date(2024, time.January, 2, 10, 0, 0, 0, time.UTC), + FeedID: 100, + Feed: &miniflux.Feed{ID: 100, Title: "Feed A"}, }, { - ID: 2, - Title: "Entry 2 - Jan 1", - Date: time.Date(2024, time.January, 1, 10, 0, 0, 0, time.UTC), - Feed: &miniflux.Feed{ID: 200, Title: "Feed B"}, + ID: 2, + Title: "Entry 2 - Jan 1", + Date: time.Date(2024, time.January, 1, 10, 0, 0, 0, time.UTC), + FeedID: 200, + Feed: &miniflux.Feed{ID: 200, Title: "Feed B"}, }, { - ID: 3, - Title: "Entry 3 - Jan 2", - Date: time.Date(2024, time.January, 2, 11, 0, 0, 0, time.UTC), - Feed: &miniflux.Feed{ID: 100, Title: "Feed A"}, + ID: 3, + Title: "Entry 3 - Jan 2", + Date: time.Date(2024, time.January, 2, 11, 0, 0, 0, time.UTC), + FeedID: 100, + Feed: &miniflux.Feed{ID: 100, Title: "Feed A"}, }, { - ID: 4, - Title: "Entry 4 - Jan 1", + ID: 4, + Title: "Entry 4 - Jan 1", Content: "Content of entry 4 about Go concurrency.", - Date: time.Date(2024, time.January, 1, 12, 0, 0, 0, time.UTC), - Feed: &miniflux.Feed{ID: 200, Title: "Feed B"}, + Date: time.Date(2024, time.January, 1, 12, 0, 0, 0, time.UTC), + FeedID: 200, + Feed: &miniflux.Feed{ID: 200, Title: "Feed B"}, }, } } @@ -199,8 +203,9 @@ func TestLLMGrouper_GroupEntries(t *testing.T) { grouper := &LLMGrouper{LLMService: mockLLM} groups, summary := grouper.GroupEntries(entries) - if summary != "This is a summary of all entries." { - t.Errorf("Expected summary \"This is a summary of all entries.\", got \"%s\"", summary) + expectedSummary := "This is a summary of all entries.\n\nYou have 4 entries from 2 feeds." + if summary != expectedSummary { + t.Errorf("Expected summary \"%s\", got \"%s\"", expectedSummary, summary) } if len(groups) != 2 { @@ -287,8 +292,9 @@ func TestLLMGrouper_GroupEntries_WithDuplicateEntries(t *testing.T) { grouper := &LLMGrouper{LLMService: mockLLM} groups, summary := grouper.GroupEntries(entries) - if summary != "This is a summary of all entries." { - t.Errorf("Expected summary \"This is a summary of all entries.\", got \"%s\"", summary) + expectedSummary := "This is a summary of all entries.\n\nYou have 4 entries from 2 feeds." + if summary != expectedSummary { + t.Errorf("Expected summary \"%s\", got \"%s\"", expectedSummary, summary) } if len(groups) != 2 {