feat: drop source_diversity_window and use_llm_for_article_extraction settings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
master
oabrivard 3 months ago
parent d3b63295f6
commit 825b793387

@ -117,7 +117,7 @@ cd frontend && npx tsc --noEmit
- `GET /api/v1/admin/users` — user list - `GET /api/v1/admin/users` — user list
- `PUT /api/v1/admin/users/:id/role` — role management - `PUT /api/v1/admin/users/:id/role` — role management
## Database (17 migrations) ## Database (18 migrations)
Tables: `users`, `sessions`, `magic_link_tokens`, `user_settings`, `sources`, `syntheses`, `admin_providers`, `admin_rate_limits`, `user_api_keys`, `audit_log` Tables: `users`, `sessions`, `magic_link_tokens`, `user_settings`, `sources`, `syntheses`, `admin_providers`, `admin_rate_limits`, `user_api_keys`, `audit_log`
## Environment Variables ## Environment Variables

@ -0,0 +1,2 @@
ALTER TABLE settings DROP COLUMN source_diversity_window;
ALTER TABLE settings DROP COLUMN use_llm_for_article_extraction;

@ -18,9 +18,7 @@ struct SettingsRow {
categories: serde_json::Value, categories: serde_json::Value,
max_items_per_category: i32, max_items_per_category: i32,
max_articles_per_source: i32, max_articles_per_source: i32,
source_diversity_window: i32,
use_llm_for_source_links: bool, use_llm_for_source_links: bool,
use_llm_for_article_extraction: bool,
article_history_days: i32, article_history_days: i32,
search_agent_behavior: String, search_agent_behavior: String,
ai_provider: String, ai_provider: String,
@ -46,9 +44,7 @@ impl TryFrom<SettingsRow> for UserSettings {
categories, categories,
max_items_per_category: row.max_items_per_category, max_items_per_category: row.max_items_per_category,
max_articles_per_source: row.max_articles_per_source, max_articles_per_source: row.max_articles_per_source,
source_diversity_window: row.source_diversity_window,
use_llm_for_source_links: row.use_llm_for_source_links, use_llm_for_source_links: row.use_llm_for_source_links,
use_llm_for_article_extraction: row.use_llm_for_article_extraction,
article_history_days: row.article_history_days, article_history_days: row.article_history_days,
search_agent_behavior: row.search_agent_behavior, search_agent_behavior: row.search_agent_behavior,
ai_provider: row.ai_provider, ai_provider: row.ai_provider,
@ -76,10 +72,10 @@ pub async fn get_or_create_default(
let row = sqlx::query_as::<_, SettingsRow>( let row = sqlx::query_as::<_, SettingsRow>(
r#" r#"
INSERT INTO settings (user_id, theme, max_age_days, categories, max_items_per_category, search_agent_behavior, ai_provider, ai_model, ai_model_writing, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, source_diversity_window, use_llm_for_source_links, use_llm_for_article_extraction, article_history_days) INSERT INTO settings (user_id, theme, max_age_days, categories, max_items_per_category, search_agent_behavior, ai_provider, ai_model, ai_model_writing, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, use_llm_for_source_links, article_history_days)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
ON CONFLICT (user_id) DO UPDATE SET user_id = settings.user_id ON CONFLICT (user_id) DO UPDATE SET user_id = settings.user_id
RETURNING user_id, theme, max_age_days, categories, max_items_per_category, search_agent_behavior, ai_provider, ai_model, ai_model_writing, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, source_diversity_window, use_llm_for_source_links, use_llm_for_article_extraction, article_history_days, updated_at RETURNING user_id, theme, max_age_days, categories, max_items_per_category, search_agent_behavior, ai_provider, ai_model, ai_model_writing, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, use_llm_for_source_links, article_history_days, updated_at
"#, "#,
) )
.bind(user_id) .bind(user_id)
@ -94,9 +90,7 @@ pub async fn get_or_create_default(
.bind(defaults.rate_limit_max_requests) .bind(defaults.rate_limit_max_requests)
.bind(defaults.rate_limit_time_window_seconds) .bind(defaults.rate_limit_time_window_seconds)
.bind(defaults.max_articles_per_source) .bind(defaults.max_articles_per_source)
.bind(defaults.source_diversity_window)
.bind(defaults.use_llm_for_source_links) .bind(defaults.use_llm_for_source_links)
.bind(defaults.use_llm_for_article_extraction)
.bind(defaults.article_history_days) .bind(defaults.article_history_days)
.fetch_one(pool) .fetch_one(pool)
.await?; .await?;
@ -116,8 +110,8 @@ pub async fn upsert(
let row = sqlx::query_as::<_, SettingsRow>( let row = sqlx::query_as::<_, SettingsRow>(
r#" r#"
INSERT INTO settings (user_id, theme, max_age_days, categories, max_items_per_category, search_agent_behavior, ai_provider, ai_model, ai_model_writing, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, source_diversity_window, use_llm_for_source_links, use_llm_for_article_extraction, article_history_days) INSERT INTO settings (user_id, theme, max_age_days, categories, max_items_per_category, search_agent_behavior, ai_provider, ai_model, ai_model_writing, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, use_llm_for_source_links, article_history_days)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
ON CONFLICT (user_id) DO UPDATE SET ON CONFLICT (user_id) DO UPDATE SET
theme = EXCLUDED.theme, theme = EXCLUDED.theme,
max_age_days = EXCLUDED.max_age_days, max_age_days = EXCLUDED.max_age_days,
@ -130,12 +124,10 @@ pub async fn upsert(
rate_limit_max_requests = EXCLUDED.rate_limit_max_requests, rate_limit_max_requests = EXCLUDED.rate_limit_max_requests,
rate_limit_time_window_seconds = EXCLUDED.rate_limit_time_window_seconds, rate_limit_time_window_seconds = EXCLUDED.rate_limit_time_window_seconds,
max_articles_per_source = EXCLUDED.max_articles_per_source, max_articles_per_source = EXCLUDED.max_articles_per_source,
source_diversity_window = EXCLUDED.source_diversity_window,
use_llm_for_source_links = EXCLUDED.use_llm_for_source_links, use_llm_for_source_links = EXCLUDED.use_llm_for_source_links,
use_llm_for_article_extraction = EXCLUDED.use_llm_for_article_extraction,
article_history_days = EXCLUDED.article_history_days, article_history_days = EXCLUDED.article_history_days,
updated_at = now() updated_at = now()
RETURNING user_id, theme, max_age_days, categories, max_items_per_category, search_agent_behavior, ai_provider, ai_model, ai_model_writing, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, source_diversity_window, use_llm_for_source_links, use_llm_for_article_extraction, article_history_days, updated_at RETURNING user_id, theme, max_age_days, categories, max_items_per_category, search_agent_behavior, ai_provider, ai_model, ai_model_writing, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, use_llm_for_source_links, article_history_days, updated_at
"#, "#,
) )
.bind(user_id) .bind(user_id)
@ -150,9 +142,7 @@ pub async fn upsert(
.bind(req.rate_limit_max_requests) .bind(req.rate_limit_max_requests)
.bind(req.rate_limit_time_window_seconds) .bind(req.rate_limit_time_window_seconds)
.bind(req.max_articles_per_source) .bind(req.max_articles_per_source)
.bind(req.source_diversity_window)
.bind(req.use_llm_for_source_links) .bind(req.use_llm_for_source_links)
.bind(req.use_llm_for_article_extraction)
.bind(req.article_history_days) .bind(req.article_history_days)
.fetch_one(pool) .fetch_one(pool)
.await?; .await?;

@ -13,9 +13,7 @@ pub struct UserSettings {
pub categories: Vec<String>, pub categories: Vec<String>,
pub max_items_per_category: i32, pub max_items_per_category: i32,
pub max_articles_per_source: i32, pub max_articles_per_source: i32,
pub source_diversity_window: i32,
pub use_llm_for_source_links: bool, pub use_llm_for_source_links: bool,
pub use_llm_for_article_extraction: bool,
pub article_history_days: i32, pub article_history_days: i32,
pub search_agent_behavior: String, pub search_agent_behavior: String,
pub ai_provider: String, pub ai_provider: String,
@ -34,9 +32,7 @@ pub struct SettingsResponse {
pub categories: Vec<String>, pub categories: Vec<String>,
pub max_items_per_category: i32, pub max_items_per_category: i32,
pub max_articles_per_source: i32, pub max_articles_per_source: i32,
pub source_diversity_window: i32,
pub use_llm_for_source_links: bool, pub use_llm_for_source_links: bool,
pub use_llm_for_article_extraction: bool,
pub article_history_days: i32, pub article_history_days: i32,
pub search_agent_behavior: String, pub search_agent_behavior: String,
pub ai_provider: String, pub ai_provider: String,
@ -54,9 +50,7 @@ impl From<UserSettings> for SettingsResponse {
categories: s.categories, categories: s.categories,
max_items_per_category: s.max_items_per_category, max_items_per_category: s.max_items_per_category,
max_articles_per_source: s.max_articles_per_source, max_articles_per_source: s.max_articles_per_source,
source_diversity_window: s.source_diversity_window,
use_llm_for_source_links: s.use_llm_for_source_links, use_llm_for_source_links: s.use_llm_for_source_links,
use_llm_for_article_extraction: s.use_llm_for_article_extraction,
article_history_days: s.article_history_days, article_history_days: s.article_history_days,
search_agent_behavior: s.search_agent_behavior, search_agent_behavior: s.search_agent_behavior,
ai_provider: s.ai_provider, ai_provider: s.ai_provider,
@ -76,9 +70,7 @@ pub struct UpdateSettingsRequest {
pub categories: Vec<String>, pub categories: Vec<String>,
pub max_items_per_category: i32, pub max_items_per_category: i32,
pub max_articles_per_source: i32, pub max_articles_per_source: i32,
pub source_diversity_window: i32,
pub use_llm_for_source_links: bool, pub use_llm_for_source_links: bool,
pub use_llm_for_article_extraction: bool,
pub article_history_days: i32, pub article_history_days: i32,
pub search_agent_behavior: String, pub search_agent_behavior: String,
pub ai_provider: String, pub ai_provider: String,
@ -126,9 +118,6 @@ impl UpdateSettingsRequest {
if !(1..=10).contains(&self.max_articles_per_source) { if !(1..=10).contains(&self.max_articles_per_source) {
return Err("max_articles_per_source must be between 1 and 10".into()); return Err("max_articles_per_source must be between 1 and 10".into());
} }
if !(0..=10).contains(&self.source_diversity_window) {
return Err("source_diversity_window must be between 0 and 10".into());
}
if !(0..=365).contains(&self.article_history_days) { if !(0..=365).contains(&self.article_history_days) {
return Err("article_history_days must be between 0 and 365".into()); return Err("article_history_days must be between 0 and 365".into());
} }
@ -174,9 +163,7 @@ impl Default for UserSettings {
], ],
max_items_per_category: 4, max_items_per_category: 4,
max_articles_per_source: 3, max_articles_per_source: 3,
source_diversity_window: 3,
use_llm_for_source_links: false, use_llm_for_source_links: false,
use_llm_for_article_extraction: false,
article_history_days: 90, article_history_days: 90,
search_agent_behavior: String::new(), search_agent_behavior: String::new(),
ai_provider: String::new(), ai_provider: String::new(),
@ -201,9 +188,7 @@ mod tests {
categories: vec!["Category 1".into(), "Category 2".into()], categories: vec!["Category 1".into(), "Category 2".into()],
max_items_per_category: 4, max_items_per_category: 4,
max_articles_per_source: 3, max_articles_per_source: 3,
source_diversity_window: 3,
use_llm_for_source_links: false, use_llm_for_source_links: false,
use_llm_for_article_extraction: false,
article_history_days: 90, article_history_days: 90,
search_agent_behavior: String::new(), search_agent_behavior: String::new(),
ai_provider: String::new(), ai_provider: String::new(),
@ -410,31 +395,4 @@ mod tests {
assert!(err.contains("ai_model_writing")); assert!(err.contains("ai_model_writing"));
} }
#[test]
fn test_source_diversity_window_zero_is_valid() {
let mut req = valid_request();
req.source_diversity_window = 0;
assert!(req.validate().is_ok());
}
#[test]
fn test_source_diversity_window_ten_is_valid() {
let mut req = valid_request();
req.source_diversity_window = 10;
assert!(req.validate().is_ok());
}
#[test]
fn test_source_diversity_window_below_range() {
let mut req = valid_request();
req.source_diversity_window = -1;
assert!(req.validate().is_err());
}
#[test]
fn test_source_diversity_window_above_range() {
let mut req = valid_request();
req.source_diversity_window = 11;
assert!(req.validate().is_err());
}
} }

@ -280,9 +280,7 @@ mod tests {
], ],
max_items_per_category: 4, max_items_per_category: 4,
max_articles_per_source: 3, max_articles_per_source: 3,
source_diversity_window: 3,
use_llm_for_source_links: false, use_llm_for_source_links: false,
use_llm_for_article_extraction: false,
article_history_days: 90, article_history_days: 90,
search_agent_behavior: String::new(), search_agent_behavior: String::new(),
ai_provider: String::new(), ai_provider: String::new(),

@ -306,11 +306,7 @@ async fn run_generation_inner(
let user_rate_limiter = get_user_rate_limiter(state, &settings, user_id); let user_rate_limiter = get_user_rate_limiter(state, &settings, user_id);
let llm_for_scraping: Option<(std::sync::Arc<dyn crate::services::llm::LlmProvider>, String)> = if settings.use_llm_for_article_extraction { let llm_for_scraping: Option<(std::sync::Arc<dyn crate::services::llm::LlmProvider>, String)> = None;
Some((std::sync::Arc::clone(&provider), model_research.clone()))
} else {
None
};
// Build categories list with "Autre" appended for classification // Build categories list with "Autre" appended for classification
let mut classification_categories = settings.categories.clone(); let mut classification_categories = settings.categories.clone();
@ -621,39 +617,8 @@ async fn run_generation_inner(
// Rate limit check before search pass // Rate limit check before search pass
check_rate_limit(state, &user_rate_limiter, &provider_name)?; check_rate_limit(state, &user_rate_limiter, &provider_name)?;
// Load recently-used domains for diversity (Phase 2 only) // Source diversity tracking removed (source_diversity_window setting dropped)
let recent_domains = if settings.source_diversity_window > 0 { let recent_domains: Vec<String> = Vec::new();
let recent = db::syntheses::list_for_user(
&state.pool,
user_id,
settings.source_diversity_window as i64,
0,
)
.await
.unwrap_or_default();
let mut domains: Vec<String> = recent
.iter()
.filter_map(|s| {
serde_json::from_value::<Vec<crate::models::synthesis::NewsSection>>(
s.sections.clone(),
)
.ok()
})
.flat_map(|sections| {
sections
.into_iter()
.flat_map(|sec| sec.items.into_iter())
.filter_map(|item| extract_domain(&item.url))
})
.collect();
domains.sort();
domains.dedup();
domains
} else {
Vec::new()
};
// Build search schema for gap categories // Build search schema for gap categories
let search_schema = build_category_schema(&settings.categories, settings.max_items_per_category); let search_schema = build_category_schema(&settings.categories, settings.max_items_per_category);

Loading…
Cancel
Save