diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 1ef9e6a..087d76a 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -3,5 +3,5 @@ pub mod models; pub mod scoring; pub use affinity::TopicAffinities; -pub use models::{Article, Feed, ReadingEvent, ReadingOutcome}; -pub use scoring::{score_article, RelevanceInputs}; +pub use models::{Article, Feed, ReadingEvent, ReadingOutcome, RelevanceInputs}; +pub use scoring::score_article; diff --git a/crates/core/src/models.rs b/crates/core/src/models.rs index e79bf68..716128b 100644 --- a/crates/core/src/models.rs +++ b/crates/core/src/models.rs @@ -1,3 +1,4 @@ +use crate::affinity::TopicAffinities; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -58,3 +59,16 @@ pub enum ReadingOutcome { /// Explicitly marked not relevant, independent of whether it was opened. Dismissed, } + +/// Inputs to the final blended relevance score for one article, see +/// `scoring::score_article`. +pub struct RelevanceInputs<'a> { + /// Cosine similarity (0.0-1.0, already renormalized from [-1,1] if + /// needed) between article and preference-profile embeddings. + pub embedding_score: f32, + /// LLM judgment (0.0-1.0), `None` if the article didn't clear the + /// embedding shortlist threshold and so was never sent to the LLM. + pub llm_score: Option, + pub topics: &'a [String], + pub affinities: &'a TopicAffinities, +} diff --git a/crates/core/src/scoring.rs b/crates/core/src/scoring.rs index fe64420..8d4a3f9 100644 --- a/crates/core/src/scoring.rs +++ b/crates/core/src/scoring.rs @@ -1,16 +1,4 @@ -use crate::affinity::TopicAffinities; - -/// Inputs to the final blended relevance score for one article. -pub struct RelevanceInputs<'a> { - /// Cosine similarity (0.0-1.0, already renormalized from [-1,1] if - /// needed) between article and preference-profile embeddings. - pub embedding_score: f32, - /// LLM judgment (0.0-1.0), `None` if the article didn't clear the - /// embedding shortlist threshold and so was never sent to the LLM. - pub llm_score: Option, - pub topics: &'a [String], - pub affinities: &'a TopicAffinities, -} +use crate::models::RelevanceInputs; /// Weights are deliberately conservative: the LLM judgment dominates when /// present (it has read the actual content), the embedding score is a diff --git a/crates/db/src/articles.rs b/crates/db/src/articles.rs index 2b4f4ea..aa2fa45 100644 --- a/crates/db/src/articles.rs +++ b/crates/db/src/articles.rs @@ -6,11 +6,11 @@ use chrono::Utc; use diesel::prelude::*; use diesel_async::RunQueryDsl; use feedsignal_core::Article; +use schema::articles::dsl; use uuid::Uuid; impl Db { pub async fn insert_article(&self, article: &Article) -> Result<()> { - use schema::articles::dsl; let mut conn = self.pool.get().await?; let topics = serde_json::to_string(&article.topics)?; diesel::insert_into(dsl::articles) @@ -36,7 +36,6 @@ impl Db { /// Articles above the embedding-similarity threshold that haven't been /// through the (slower) LLM scoring stage yet. pub async fn shortlist_for_llm_scoring(&self, threshold: f32, limit: i64) -> Result> { - use schema::articles::dsl; let mut conn = self.pool.get().await?; let ids: Vec = dsl::articles .filter(dsl::embedding_score.ge(threshold)) @@ -55,7 +54,6 @@ impl Db { &self, article_id: Uuid, ) -> Result, f32)>> { - use schema::articles::dsl; let mut conn = self.pool.get().await?; let row: Option<(String, String, String, Option)> = dsl::articles .filter(dsl::id.eq(article_id.to_string())) @@ -63,13 +61,16 @@ impl Db { .first(&mut conn) .await .optional()?; - Ok(match row { + + let result = match row { Some((title, summary, topics_json, score)) => { let topics: Vec = serde_json::from_str(&topics_json).unwrap_or_default(); Some((title, summary, topics, score.unwrap_or(0.0))) } None => None, - }) + }; + + Ok(result) } pub async fn store_llm_result( @@ -100,7 +101,6 @@ impl Db { feed_id: Option, limit: i64, ) -> Result> { - use schema::articles::dsl; let mut conn = self.pool.get().await?; // SQLite sorts NULL before any value, so `DESC` already puts NULL // `final_score`s last — no separate NULLS LAST clause needed here. diff --git a/crates/web/src/api/mod.rs b/crates/web/src/api/mod.rs index 62211e5..63ca899 100644 --- a/crates/web/src/api/mod.rs +++ b/crates/web/src/api/mod.rs @@ -1,5 +1,5 @@ pub mod articles; -mod dto; pub mod feeds; +mod models; -pub use dto::{ArticleView, FeedView}; +pub use models::{ArticleView, FeedView};