From f414ff0f58539cbac4203a6b1f34c31906d6dfa4 Mon Sep 17 00:00:00 2001 From: oabrivard Date: Wed, 25 Mar 2026 22:51:19 +0100 Subject: [PATCH] feat: add use_brave_search setting Add use_brave_search boolean field to all settings structs, DB layer, SQL queries, frontend types, i18n labels, and test fixtures following the same pattern as use_llm_for_source_links. Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 2 +- .../20260325000022_add_brave_search_setting.sql | 1 + backend/src/db/settings.rs | 17 +++++++++++------ backend/src/models/settings.rs | 6 ++++++ backend/src/services/prompts.rs | 1 + backend/tests/api_syntheses_test.rs | 1 + e2e/tests/generation-live.spec.ts | 1 + frontend/src/i18n/fr.ts | 6 ++++++ frontend/src/types.ts | 2 ++ 9 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 backend/migrations/20260325000022_add_brave_search_setting.sql diff --git a/CLAUDE.md b/CLAUDE.md index 84a8223..8569279 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 (21 migrations) +## Database (22 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/20260325000022_add_brave_search_setting.sql b/backend/migrations/20260325000022_add_brave_search_setting.sql new file mode 100644 index 0000000..dde0851 --- /dev/null +++ b/backend/migrations/20260325000022_add_brave_search_setting.sql @@ -0,0 +1 @@ +ALTER TABLE settings ADD COLUMN use_brave_search BOOLEAN NOT NULL DEFAULT false; diff --git a/backend/src/db/settings.rs b/backend/src/db/settings.rs index c049090..93290ce 100644 --- a/backend/src/db/settings.rs +++ b/backend/src/db/settings.rs @@ -19,6 +19,7 @@ struct SettingsRow { max_items_per_category: i32, max_articles_per_source: i32, use_llm_for_source_links: bool, + use_brave_search: bool, article_history_days: i32, batch_size: i32, search_agent_behavior: String, @@ -46,6 +47,7 @@ impl TryFrom for UserSettings { max_items_per_category: row.max_items_per_category, max_articles_per_source: row.max_articles_per_source, use_llm_for_source_links: row.use_llm_for_source_links, + use_brave_search: row.use_brave_search, article_history_days: row.article_history_days, batch_size: row.batch_size, search_agent_behavior: row.search_agent_behavior, @@ -74,10 +76,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, article_history_days, batch_size) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) + 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) 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, 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, updated_at "#, ) .bind(user_id) @@ -93,6 +95,7 @@ pub async fn get_or_create_default( .bind(defaults.rate_limit_time_window_seconds) .bind(defaults.max_articles_per_source) .bind(defaults.use_llm_for_source_links) + .bind(defaults.use_brave_search) .bind(defaults.article_history_days) .bind(defaults.batch_size) .fetch_one(pool) @@ -113,8 +116,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, article_history_days, batch_size) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) + 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) ON CONFLICT (user_id) DO UPDATE SET theme = EXCLUDED.theme, max_age_days = EXCLUDED.max_age_days, @@ -128,10 +131,11 @@ pub async fn upsert( rate_limit_time_window_seconds = EXCLUDED.rate_limit_time_window_seconds, max_articles_per_source = EXCLUDED.max_articles_per_source, use_llm_for_source_links = EXCLUDED.use_llm_for_source_links, + use_brave_search = EXCLUDED.use_brave_search, article_history_days = EXCLUDED.article_history_days, batch_size = EXCLUDED.batch_size, 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, 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, updated_at "#, ) .bind(user_id) @@ -147,6 +151,7 @@ pub async fn upsert( .bind(req.rate_limit_time_window_seconds) .bind(req.max_articles_per_source) .bind(req.use_llm_for_source_links) + .bind(req.use_brave_search) .bind(req.article_history_days) .bind(req.batch_size) .fetch_one(pool) diff --git a/backend/src/models/settings.rs b/backend/src/models/settings.rs index 2ffb585..6162a72 100644 --- a/backend/src/models/settings.rs +++ b/backend/src/models/settings.rs @@ -14,6 +14,7 @@ pub struct UserSettings { pub max_items_per_category: i32, pub max_articles_per_source: i32, pub use_llm_for_source_links: bool, + pub use_brave_search: bool, pub article_history_days: i32, pub batch_size: i32, pub search_agent_behavior: String, @@ -34,6 +35,7 @@ pub struct SettingsResponse { pub max_items_per_category: i32, pub max_articles_per_source: i32, pub use_llm_for_source_links: bool, + pub use_brave_search: bool, pub article_history_days: i32, pub batch_size: i32, pub search_agent_behavior: String, @@ -53,6 +55,7 @@ impl From for SettingsResponse { max_items_per_category: s.max_items_per_category, max_articles_per_source: s.max_articles_per_source, use_llm_for_source_links: s.use_llm_for_source_links, + use_brave_search: s.use_brave_search, article_history_days: s.article_history_days, batch_size: s.batch_size, search_agent_behavior: s.search_agent_behavior, @@ -74,6 +77,7 @@ pub struct UpdateSettingsRequest { pub max_items_per_category: i32, pub max_articles_per_source: i32, pub use_llm_for_source_links: bool, + pub use_brave_search: bool, pub article_history_days: i32, pub batch_size: i32, pub search_agent_behavior: String, @@ -171,6 +175,7 @@ impl Default for UserSettings { max_items_per_category: 4, max_articles_per_source: 3, use_llm_for_source_links: false, + use_brave_search: false, article_history_days: 90, batch_size: 5, search_agent_behavior: String::new(), @@ -197,6 +202,7 @@ mod tests { max_items_per_category: 4, max_articles_per_source: 3, use_llm_for_source_links: false, + use_brave_search: false, article_history_days: 90, batch_size: 5, search_agent_behavior: String::new(), diff --git a/backend/src/services/prompts.rs b/backend/src/services/prompts.rs index 9dbbe2d..999fda4 100644 --- a/backend/src/services/prompts.rs +++ b/backend/src/services/prompts.rs @@ -198,6 +198,7 @@ mod tests { max_items_per_category: 4, max_articles_per_source: 3, use_llm_for_source_links: false, + use_brave_search: false, article_history_days: 90, batch_size: 5, search_agent_behavior: String::new(), diff --git a/backend/tests/api_syntheses_test.rs b/backend/tests/api_syntheses_test.rs index dff8ed8..a6ee9ff 100644 --- a/backend/tests/api_syntheses_test.rs +++ b/backend/tests/api_syntheses_test.rs @@ -632,6 +632,7 @@ async fn generate_pipeline_resolves_model_from_admin_config() { "ai_model": "", "ai_model_websearch": "", "use_llm_for_source_links": false, + "use_brave_search": false, "use_llm_for_article_extraction": false, "article_history_days": 90, "batch_size": 5 diff --git a/e2e/tests/generation-live.spec.ts b/e2e/tests/generation-live.spec.ts index 3ff9109..38f5fcc 100644 --- a/e2e/tests/generation-live.spec.ts +++ b/e2e/tests/generation-live.spec.ts @@ -142,6 +142,7 @@ test.describe('Live generation with OpenAI', () => { ai_model: 'gpt-4o-mini', ai_model_websearch: 'gpt-4o-mini', use_llm_for_source_links: false, + use_brave_search: false, article_history_days: 90, batch_size: 5, }); diff --git a/frontend/src/i18n/fr.ts b/frontend/src/i18n/fr.ts index efcc5bf..65a0854 100644 --- a/frontend/src/i18n/fr.ts +++ b/frontend/src/i18n/fr.ts @@ -152,6 +152,12 @@ const fr = { 'settings.rateLimitReset': 'Reinitialiser', 'settings.advancedExtraction': 'Extraction avancee', 'settings.useLlmForSourceLinks': "Utiliser l'IA pour extraire les liens", + 'settings.useBraveSearch': 'Utiliser Brave Search pour la recherche web', + 'settings.useBraveSearchHelp': "Remplace la recherche web par IA par l'API Brave Search pour des resultats plus precis.", + 'settings.braveSearch': 'Brave Search', + 'settings.braveSearchKey': 'Cle API Brave Search', + 'settings.braveSearchKeyHelp': 'Obtenez une cle sur api-dashboard.search.brave.com. Le plan gratuit offre 2000 requetes/mois.', + 'settings.braveSearchNotConfigured': 'Configurez une cle API Brave Search pour activer cette option.', 'settings.articleHistoryDays': 'Historique des articles (jours)', 'settings.batchSize': 'Taille du lot de traitement', 'settings.batchSizeHelp': 'Nombre d\'articles traites en parallele lors de la generation (defaut: 5).', diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 93562fe..d80ad89 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -45,6 +45,7 @@ export interface UserSettings { max_items_per_category: number; max_articles_per_source: number; use_llm_for_source_links: boolean; + use_brave_search: boolean; article_history_days: number; batch_size: number; search_agent_behavior: string; @@ -62,6 +63,7 @@ export const DEFAULT_SETTINGS: UserSettings = { max_items_per_category: 4, max_articles_per_source: 3, use_llm_for_source_links: false, + use_brave_search: false, article_history_days: 90, batch_size: 5, search_agent_behavior: