Rip out Ollama: direct_route only, no LLM layer
Pete moves to a remote host without Ollama access. Every source must declare a direct_route channel; the classifier, explainer, semantic dedup, !explain summaries, feed_hint, and the recent_headlines / classification_log tables are gone. Deterministic dedup (canonical URL, headline_norm, per-channel cooldown) remains.
This commit is contained in:
@@ -16,7 +16,7 @@ const userAgent = "Pete/1.0 (newsbot; +https://github.com/reala-misaki/pete)"
|
||||
|
||||
var feedClient = &http.Client{Timeout: 30 * time.Second}
|
||||
|
||||
// FeedItem represents a parsed RSS item ready for classification.
|
||||
// FeedItem represents a parsed RSS item ready for routing.
|
||||
type FeedItem struct {
|
||||
GUID string
|
||||
Headline string
|
||||
@@ -24,9 +24,7 @@ type FeedItem struct {
|
||||
ImageURL string
|
||||
ArticleURL string
|
||||
Source string
|
||||
FeedHint string
|
||||
Tier int
|
||||
DirectRoute *string
|
||||
DirectRoute string
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -11,9 +11,7 @@ import (
|
||||
"pete/internal/storage"
|
||||
)
|
||||
|
||||
// ProcessFunc is called for each new feed item that needs classification.
|
||||
// ctx is the poller's parent context; callbacks should pass it into any
|
||||
// downstream LLM/HTTP work so shutdown aborts in-flight calls.
|
||||
// ProcessFunc is called for each new feed item that needs routing.
|
||||
type ProcessFunc func(ctx context.Context, item *FeedItem)
|
||||
|
||||
// Poller manages per-source RSS polling goroutines.
|
||||
@@ -81,7 +79,6 @@ func (p *Poller) pollSource(ctx context.Context, src config.SourceConfig) {
|
||||
"source", src.Name,
|
||||
"failures", consecutiveFailures,
|
||||
)
|
||||
// TODO: send admin room warning via matrix client
|
||||
}
|
||||
} else {
|
||||
consecutiveFailures = 0
|
||||
@@ -163,11 +160,9 @@ func (p *Poller) pollOnceWithErr(ctx context.Context, src config.SourceConfig) e
|
||||
|
||||
// Stamp source metadata onto the item
|
||||
items[i].Source = src.Name
|
||||
items[i].FeedHint = src.FeedHint
|
||||
items[i].Tier = src.Tier
|
||||
items[i].DirectRoute = src.DirectRoute
|
||||
|
||||
// Store immediately (before classification) to prevent re-ingestion
|
||||
// Store immediately (before routing) to prevent re-ingestion
|
||||
if err := storage.InsertStory(&storage.Story{
|
||||
GUID: items[i].GUID,
|
||||
Headline: items[i].Headline,
|
||||
@@ -177,7 +172,6 @@ func (p *Poller) pollOnceWithErr(ctx context.Context, src config.SourceConfig) e
|
||||
URLCanonical: canonical,
|
||||
HeadlineNorm: headlineNorm,
|
||||
Source: items[i].Source,
|
||||
FeedHint: items[i].FeedHint,
|
||||
SeenAt: time.Now().Unix(),
|
||||
}); err != nil {
|
||||
slog.Error("failed to insert story", "guid", items[i].GUID, "err", err)
|
||||
@@ -192,40 +186,5 @@ func (p *Poller) pollOnceWithErr(ctx context.Context, src config.SourceConfig) e
|
||||
slog.Info("ingested new stories", "source", src.Name, "count", newCount)
|
||||
}
|
||||
|
||||
// Retry unclassified stories from this source only (cap at 20 to avoid runaway retries)
|
||||
unclassified, err := storage.GetUnclassifiedStories(src.Name)
|
||||
if err != nil {
|
||||
slog.Error("failed to get unclassified stories", "err", err)
|
||||
return nil
|
||||
}
|
||||
for _, s := range unclassified {
|
||||
if ctx.Err() != nil {
|
||||
return nil
|
||||
}
|
||||
// Skip stories we just ingested (they're already being processed above)
|
||||
alreadyProcessed := false
|
||||
for _, item := range items {
|
||||
if item.GUID == s.GUID {
|
||||
alreadyProcessed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if alreadyProcessed {
|
||||
continue
|
||||
}
|
||||
|
||||
p.process(ctx, &FeedItem{
|
||||
GUID: s.GUID,
|
||||
Headline: s.Headline,
|
||||
Lede: s.Lede,
|
||||
ImageURL: s.ImageURL,
|
||||
ArticleURL: s.ArticleURL,
|
||||
Source: s.Source,
|
||||
FeedHint: s.FeedHint,
|
||||
Tier: src.Tier,
|
||||
DirectRoute: src.DirectRoute,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user