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:
@@ -29,9 +29,9 @@ func InsertStory(s *Story) error {
|
||||
classified = 1
|
||||
}
|
||||
_, err := Get().Exec(
|
||||
`INSERT INTO stories (guid, headline, lede, image_url, article_url, url_canonical, headline_norm, source, feed_hint, platforms, channel, classified, seen_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
s.GUID, s.Headline, s.Lede, s.ImageURL, s.ArticleURL, nullIfEmpty(s.URLCanonical), nullIfEmpty(s.HeadlineNorm), s.Source, s.FeedHint, s.Platforms, s.Channel, classified, s.SeenAt,
|
||||
`INSERT INTO stories (guid, headline, lede, image_url, article_url, url_canonical, headline_norm, source, platforms, channel, classified, seen_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
s.GUID, s.Headline, s.Lede, s.ImageURL, s.ArticleURL, nullIfEmpty(s.URLCanonical), nullIfEmpty(s.HeadlineNorm), s.Source, s.Platforms, s.Channel, classified, s.SeenAt,
|
||||
)
|
||||
return err
|
||||
}
|
||||
@@ -99,73 +99,18 @@ func MarkClassified(guid, channel, platforms string) {
|
||||
channel, platforms, guid)
|
||||
}
|
||||
|
||||
// GetUnclassifiedStories returns stories from a specific source that were ingested but not yet classified.
|
||||
func GetUnclassifiedStories(source string) ([]Story, error) {
|
||||
rows, err := Get().Query(
|
||||
`SELECT guid, headline, lede, image_url, article_url, source, feed_hint, platforms, seen_at
|
||||
FROM stories WHERE classified = 0 AND source = ? ORDER BY seen_at ASC LIMIT 20`, source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var stories []Story
|
||||
for rows.Next() {
|
||||
var s Story
|
||||
if err := rows.Scan(&s.GUID, &s.Headline, &s.Lede, &s.ImageURL, &s.ArticleURL, &s.Source, &s.FeedHint, &s.Platforms, &s.SeenAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stories = append(stories, s)
|
||||
}
|
||||
return stories, rows.Err()
|
||||
}
|
||||
|
||||
// GetStoryByGUID returns the full story record for a GUID, or nil if not found.
|
||||
func GetStoryByGUID(guid string) (*Story, error) {
|
||||
row := Get().QueryRow(
|
||||
`SELECT guid, headline, lede, image_url, article_url, source, feed_hint, platforms, channel, seen_at
|
||||
`SELECT guid, headline, lede, image_url, article_url, source, platforms, channel, seen_at
|
||||
FROM stories WHERE guid = ?`, guid)
|
||||
var s Story
|
||||
if err := row.Scan(&s.GUID, &s.Headline, &s.Lede, &s.ImageURL, &s.ArticleURL, &s.Source, &s.FeedHint, &s.Platforms, &s.Channel, &s.SeenAt); err != nil {
|
||||
if err := row.Scan(&s.GUID, &s.Headline, &s.Lede, &s.ImageURL, &s.ArticleURL, &s.Source, &s.Platforms, &s.Channel, &s.SeenAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
// InsertRecentHeadline adds a headline to the dedup context window.
|
||||
func InsertRecentHeadline(guid, headline, source string) {
|
||||
exec("insert recent_headline",
|
||||
`INSERT OR REPLACE INTO recent_headlines (guid, headline, source, seen_at) VALUES (?, ?, ?, ?)`,
|
||||
guid, headline, source, nowUnix())
|
||||
}
|
||||
|
||||
// GetRecentHeadlines returns the most recent headlines for LLM dedup context, capped at limit.
|
||||
func GetRecentHeadlines(limit int) ([]RecentHeadline, error) {
|
||||
rows, err := Get().Query(
|
||||
`SELECT guid, headline, source, seen_at FROM recent_headlines ORDER BY seen_at DESC LIMIT ?`, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var headlines []RecentHeadline
|
||||
for rows.Next() {
|
||||
var h RecentHeadline
|
||||
if err := rows.Scan(&h.GUID, &h.Headline, &h.Source, &h.SeenAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headlines = append(headlines, h)
|
||||
}
|
||||
return headlines, rows.Err()
|
||||
}
|
||||
|
||||
// InsertClassificationLog stores a classification result for tuning review.
|
||||
func InsertClassificationLog(guid, resultJSON string) {
|
||||
exec("insert classification_log",
|
||||
`INSERT OR REPLACE INTO classification_log (guid, result, logged_at) VALUES (?, ?, ?)`,
|
||||
guid, resultJSON, nowUnix())
|
||||
}
|
||||
|
||||
// InsertPostLog records that a story was posted to a channel.
|
||||
// Uses OR IGNORE to prevent duplicate posts for the same story+channel.
|
||||
func InsertPostLog(guid, channel, eventID, urlCanonical string) {
|
||||
@@ -228,7 +173,7 @@ func InsertReaction(postGUID, channel, eventID, emoji, userID string, reactedAt
|
||||
// row in post_log). Returns (nil, nil) when nothing qualifies.
|
||||
func GetNewestPostableStory(source string) (*Story, error) {
|
||||
row := Get().QueryRow(
|
||||
`SELECT guid, headline, lede, image_url, article_url, source, feed_hint, platforms, channel, seen_at
|
||||
`SELECT guid, headline, lede, image_url, article_url, source, platforms, channel, seen_at
|
||||
FROM stories
|
||||
WHERE classified = 1
|
||||
AND source = ?
|
||||
@@ -238,7 +183,7 @@ func GetNewestPostableStory(source string) (*Story, error) {
|
||||
ORDER BY seen_at DESC
|
||||
LIMIT 1`, source)
|
||||
var s Story
|
||||
if err := row.Scan(&s.GUID, &s.Headline, &s.Lede, &s.ImageURL, &s.ArticleURL, &s.Source, &s.FeedHint, &s.Platforms, &s.Channel, &s.SeenAt); err != nil {
|
||||
if err := row.Scan(&s.GUID, &s.Headline, &s.Lede, &s.ImageURL, &s.ArticleURL, &s.Source, &s.Platforms, &s.Channel, &s.SeenAt); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -253,7 +198,7 @@ func GetNewestPostableStory(source string) (*Story, error) {
|
||||
// between ticks). Returns (nil, nil) when nothing qualifies.
|
||||
func GetNewestPostableStoryByChannel(channel string) (*Story, error) {
|
||||
row := Get().QueryRow(
|
||||
`SELECT guid, headline, lede, image_url, article_url, source, feed_hint, platforms, channel, seen_at
|
||||
`SELECT guid, headline, lede, image_url, article_url, source, platforms, channel, seen_at
|
||||
FROM stories
|
||||
WHERE classified = 1
|
||||
AND channel = ?
|
||||
@@ -261,7 +206,7 @@ func GetNewestPostableStoryByChannel(channel string) (*Story, error) {
|
||||
ORDER BY seen_at DESC
|
||||
LIMIT 1`, channel)
|
||||
var s Story
|
||||
if err := row.Scan(&s.GUID, &s.Headline, &s.Lede, &s.ImageURL, &s.ArticleURL, &s.Source, &s.FeedHint, &s.Platforms, &s.Channel, &s.SeenAt); err != nil {
|
||||
if err := row.Scan(&s.GUID, &s.Headline, &s.Lede, &s.ImageURL, &s.ArticleURL, &s.Source, &s.Platforms, &s.Channel, &s.SeenAt); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -275,7 +220,7 @@ func GetNewestPostableStoryByChannel(channel string) (*Story, error) {
|
||||
// channels (_discarded, _duplicate) are excluded.
|
||||
func ListClassifiedByChannel(channel string, limit, offset int) ([]Story, error) {
|
||||
rows, err := Get().Query(
|
||||
`SELECT guid, headline, lede, image_url, article_url, source, feed_hint, platforms, channel, seen_at
|
||||
`SELECT guid, headline, lede, image_url, article_url, source, platforms, channel, seen_at
|
||||
FROM stories
|
||||
WHERE classified = 1
|
||||
AND channel = ?
|
||||
@@ -288,7 +233,7 @@ func ListClassifiedByChannel(channel string, limit, offset int) ([]Story, error)
|
||||
var out []Story
|
||||
for rows.Next() {
|
||||
var s Story
|
||||
if err := rows.Scan(&s.GUID, &s.Headline, &s.Lede, &s.ImageURL, &s.ArticleURL, &s.Source, &s.FeedHint, &s.Platforms, &s.Channel, &s.SeenAt); err != nil {
|
||||
if err := rows.Scan(&s.GUID, &s.Headline, &s.Lede, &s.ImageURL, &s.ArticleURL, &s.Source, &s.Platforms, &s.Channel, &s.SeenAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, s)
|
||||
|
||||
Reference in New Issue
Block a user