diff --git a/CLAUDE.md b/CLAUDE.md index 8569279..7e67899 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -117,7 +117,7 @@ cd frontend && npx tsc --noEmit - `GET /api/v1/admin/users` — user list - `PUT /api/v1/admin/users/:id/role` — role management -## Database (22 migrations) +## Database (23 migrations) Tables: `users`, `sessions`, `magic_link_tokens`, `user_settings`, `sources`, `syntheses`, `admin_providers`, `admin_rate_limits`, `user_api_keys`, `audit_log` ## Environment Variables diff --git a/backend/migrations/20260326000023_add_summary_length.sql b/backend/migrations/20260326000023_add_summary_length.sql new file mode 100644 index 0000000..74c4221 --- /dev/null +++ b/backend/migrations/20260326000023_add_summary_length.sql @@ -0,0 +1 @@ +ALTER TABLE settings ADD COLUMN summary_length INTEGER NOT NULL DEFAULT 3; diff --git a/backend/src/db/settings.rs b/backend/src/db/settings.rs index 93290ce..a704a3d 100644 --- a/backend/src/db/settings.rs +++ b/backend/src/db/settings.rs @@ -22,6 +22,7 @@ struct SettingsRow { use_brave_search: bool, article_history_days: i32, batch_size: i32, + summary_length: i32, search_agent_behavior: String, ai_provider: String, ai_model: String, @@ -50,6 +51,7 @@ impl TryFrom for UserSettings { use_brave_search: row.use_brave_search, article_history_days: row.article_history_days, batch_size: row.batch_size, + summary_length: row.summary_length, search_agent_behavior: row.search_agent_behavior, ai_provider: row.ai_provider, ai_model: row.ai_model, @@ -76,10 +78,10 @@ pub async fn get_or_create_default( let row = sqlx::query_as::<_, SettingsRow>( r#" - INSERT INTO settings (user_id, theme, max_age_days, categories, max_items_per_category, search_agent_behavior, ai_provider, ai_model, ai_model_websearch, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, use_llm_for_source_links, use_brave_search, article_history_days, batch_size) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) + INSERT INTO settings (user_id, theme, max_age_days, categories, max_items_per_category, search_agent_behavior, ai_provider, ai_model, ai_model_websearch, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, use_llm_for_source_links, use_brave_search, article_history_days, batch_size, summary_length) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) 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_websearch, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, use_llm_for_source_links, use_brave_search, article_history_days, batch_size, updated_at + RETURNING user_id, theme, max_age_days, categories, max_items_per_category, search_agent_behavior, ai_provider, ai_model, ai_model_websearch, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, use_llm_for_source_links, use_brave_search, article_history_days, batch_size, summary_length, updated_at "#, ) .bind(user_id) @@ -98,6 +100,7 @@ pub async fn get_or_create_default( .bind(defaults.use_brave_search) .bind(defaults.article_history_days) .bind(defaults.batch_size) + .bind(defaults.summary_length) .fetch_one(pool) .await?; @@ -116,8 +119,8 @@ pub async fn upsert( let row = sqlx::query_as::<_, SettingsRow>( r#" - INSERT INTO settings (user_id, theme, max_age_days, categories, max_items_per_category, search_agent_behavior, ai_provider, ai_model, ai_model_websearch, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, use_llm_for_source_links, use_brave_search, article_history_days, batch_size) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) + INSERT INTO settings (user_id, theme, max_age_days, categories, max_items_per_category, search_agent_behavior, ai_provider, ai_model, ai_model_websearch, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, use_llm_for_source_links, use_brave_search, article_history_days, batch_size, summary_length) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) ON CONFLICT (user_id) DO UPDATE SET theme = EXCLUDED.theme, max_age_days = EXCLUDED.max_age_days, @@ -134,8 +137,9 @@ pub async fn upsert( use_brave_search = EXCLUDED.use_brave_search, article_history_days = EXCLUDED.article_history_days, batch_size = EXCLUDED.batch_size, + summary_length = EXCLUDED.summary_length, updated_at = now() - RETURNING user_id, theme, max_age_days, categories, max_items_per_category, search_agent_behavior, ai_provider, ai_model, ai_model_websearch, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, use_llm_for_source_links, use_brave_search, article_history_days, batch_size, updated_at + RETURNING user_id, theme, max_age_days, categories, max_items_per_category, search_agent_behavior, ai_provider, ai_model, ai_model_websearch, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, use_llm_for_source_links, use_brave_search, article_history_days, batch_size, summary_length, updated_at "#, ) .bind(user_id) @@ -154,6 +158,7 @@ pub async fn upsert( .bind(req.use_brave_search) .bind(req.article_history_days) .bind(req.batch_size) + .bind(req.summary_length) .fetch_one(pool) .await?; diff --git a/backend/src/models/settings.rs b/backend/src/models/settings.rs index 0539b31..a5955fc 100644 --- a/backend/src/models/settings.rs +++ b/backend/src/models/settings.rs @@ -18,6 +18,7 @@ pub struct UserSettings { pub use_brave_search: bool, pub article_history_days: i32, pub batch_size: i32, + pub summary_length: i32, pub search_agent_behavior: String, pub ai_provider: String, pub ai_model: String, @@ -40,6 +41,7 @@ pub struct UpdateSettingsRequest { pub use_brave_search: bool, pub article_history_days: i32, pub batch_size: i32, + pub summary_length: i32, pub search_agent_behavior: String, pub ai_provider: String, pub ai_model: String, @@ -92,6 +94,9 @@ impl UpdateSettingsRequest { if !(1..=20).contains(&self.batch_size) { return Err("batch_size must be between 1 and 20".into()); } + if !(1..=3).contains(&self.summary_length) { + return Err("summary_length must be between 1 and 3".into()); + } if self.search_agent_behavior.len() > 2000 { return Err("search_agent_behavior must be at most 2000 characters".into()); } @@ -138,6 +143,7 @@ impl Default for UserSettings { use_brave_search: false, article_history_days: 90, batch_size: 5, + summary_length: 3, search_agent_behavior: String::new(), ai_provider: String::new(), ai_model: String::new(), @@ -165,6 +171,7 @@ mod tests { use_brave_search: false, article_history_days: 90, batch_size: 5, + summary_length: 3, search_agent_behavior: String::new(), ai_provider: String::new(), ai_model: String::new(), diff --git a/backend/src/services/prompts.rs b/backend/src/services/prompts.rs index 999fda4..8149a54 100644 --- a/backend/src/services/prompts.rs +++ b/backend/src/services/prompts.rs @@ -201,6 +201,7 @@ mod tests { use_brave_search: false, article_history_days: 90, batch_size: 5, + summary_length: 3, search_agent_behavior: String::new(), ai_provider: String::new(), ai_model: String::new(), diff --git a/backend/tests/api_syntheses_test.rs b/backend/tests/api_syntheses_test.rs index bb9bc77..2f3bda7 100644 --- a/backend/tests/api_syntheses_test.rs +++ b/backend/tests/api_syntheses_test.rs @@ -635,6 +635,7 @@ async fn generate_pipeline_resolves_model_from_admin_config() { "use_brave_search": false, "article_history_days": 90, "batch_size": 5, + "summary_length": 3, "search_agent_behavior": "", "ai_provider": "openai", "ai_model": "", diff --git a/backend/tests/pipeline_test.rs b/backend/tests/pipeline_test.rs index 449cf89..b4ab149 100644 --- a/backend/tests/pipeline_test.rs +++ b/backend/tests/pipeline_test.rs @@ -61,6 +61,7 @@ async fn setup_user_with_settings( "use_brave_search": false, "article_history_days": 90, "batch_size": 5, + "summary_length": 3, "search_agent_behavior": "", "ai_provider": "", "ai_model": "",