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 <noreply@anthropic.com>
master
oabrivard 3 months ago
parent fa03c60339
commit f414ff0f58

@ -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 (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` 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 @@
ALTER TABLE settings ADD COLUMN use_brave_search BOOLEAN NOT NULL DEFAULT false;

@ -19,6 +19,7 @@ struct SettingsRow {
max_items_per_category: i32, max_items_per_category: i32,
max_articles_per_source: i32, max_articles_per_source: i32,
use_llm_for_source_links: bool, use_llm_for_source_links: bool,
use_brave_search: bool,
article_history_days: i32, article_history_days: i32,
batch_size: i32, batch_size: i32,
search_agent_behavior: String, search_agent_behavior: String,
@ -46,6 +47,7 @@ impl TryFrom<SettingsRow> for UserSettings {
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,
use_llm_for_source_links: row.use_llm_for_source_links, 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, article_history_days: row.article_history_days,
batch_size: row.batch_size, batch_size: row.batch_size,
search_agent_behavior: row.search_agent_behavior, search_agent_behavior: row.search_agent_behavior,
@ -74,10 +76,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_websearch, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, use_llm_for_source_links, article_history_days, batch_size) 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) 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 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) .bind(user_id)
@ -93,6 +95,7 @@ pub async fn get_or_create_default(
.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.use_llm_for_source_links) .bind(defaults.use_llm_for_source_links)
.bind(defaults.use_brave_search)
.bind(defaults.article_history_days) .bind(defaults.article_history_days)
.bind(defaults.batch_size) .bind(defaults.batch_size)
.fetch_one(pool) .fetch_one(pool)
@ -113,8 +116,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_websearch, rate_limit_max_requests, rate_limit_time_window_seconds, max_articles_per_source, use_llm_for_source_links, article_history_days, batch_size) 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) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
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,
@ -128,10 +131,11 @@ pub async fn upsert(
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,
use_llm_for_source_links = EXCLUDED.use_llm_for_source_links, 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, article_history_days = EXCLUDED.article_history_days,
batch_size = EXCLUDED.batch_size, batch_size = EXCLUDED.batch_size,
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_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) .bind(user_id)
@ -147,6 +151,7 @@ pub async fn upsert(
.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.use_llm_for_source_links) .bind(req.use_llm_for_source_links)
.bind(req.use_brave_search)
.bind(req.article_history_days) .bind(req.article_history_days)
.bind(req.batch_size) .bind(req.batch_size)
.fetch_one(pool) .fetch_one(pool)

@ -14,6 +14,7 @@ pub struct UserSettings {
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 use_llm_for_source_links: bool, pub use_llm_for_source_links: bool,
pub use_brave_search: bool,
pub article_history_days: i32, pub article_history_days: i32,
pub batch_size: i32, pub batch_size: i32,
pub search_agent_behavior: String, pub search_agent_behavior: String,
@ -34,6 +35,7 @@ pub struct SettingsResponse {
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 use_llm_for_source_links: bool, pub use_llm_for_source_links: bool,
pub use_brave_search: bool,
pub article_history_days: i32, pub article_history_days: i32,
pub batch_size: i32, pub batch_size: i32,
pub search_agent_behavior: String, pub search_agent_behavior: String,
@ -53,6 +55,7 @@ impl From<UserSettings> for SettingsResponse {
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,
use_llm_for_source_links: s.use_llm_for_source_links, 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, article_history_days: s.article_history_days,
batch_size: s.batch_size, batch_size: s.batch_size,
search_agent_behavior: s.search_agent_behavior, search_agent_behavior: s.search_agent_behavior,
@ -74,6 +77,7 @@ pub struct UpdateSettingsRequest {
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 use_llm_for_source_links: bool, pub use_llm_for_source_links: bool,
pub use_brave_search: bool,
pub article_history_days: i32, pub article_history_days: i32,
pub batch_size: i32, pub batch_size: i32,
pub search_agent_behavior: String, pub search_agent_behavior: String,
@ -171,6 +175,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,
use_llm_for_source_links: false, use_llm_for_source_links: false,
use_brave_search: false,
article_history_days: 90, article_history_days: 90,
batch_size: 5, batch_size: 5,
search_agent_behavior: String::new(), search_agent_behavior: String::new(),
@ -197,6 +202,7 @@ mod tests {
max_items_per_category: 4, max_items_per_category: 4,
max_articles_per_source: 3, max_articles_per_source: 3,
use_llm_for_source_links: false, use_llm_for_source_links: false,
use_brave_search: false,
article_history_days: 90, article_history_days: 90,
batch_size: 5, batch_size: 5,
search_agent_behavior: String::new(), search_agent_behavior: String::new(),

@ -198,6 +198,7 @@ mod tests {
max_items_per_category: 4, max_items_per_category: 4,
max_articles_per_source: 3, max_articles_per_source: 3,
use_llm_for_source_links: false, use_llm_for_source_links: false,
use_brave_search: false,
article_history_days: 90, article_history_days: 90,
batch_size: 5, batch_size: 5,
search_agent_behavior: String::new(), search_agent_behavior: String::new(),

@ -632,6 +632,7 @@ async fn generate_pipeline_resolves_model_from_admin_config() {
"ai_model": "", "ai_model": "",
"ai_model_websearch": "", "ai_model_websearch": "",
"use_llm_for_source_links": false, "use_llm_for_source_links": false,
"use_brave_search": false,
"use_llm_for_article_extraction": false, "use_llm_for_article_extraction": false,
"article_history_days": 90, "article_history_days": 90,
"batch_size": 5 "batch_size": 5

@ -142,6 +142,7 @@ test.describe('Live generation with OpenAI', () => {
ai_model: 'gpt-4o-mini', ai_model: 'gpt-4o-mini',
ai_model_websearch: 'gpt-4o-mini', ai_model_websearch: 'gpt-4o-mini',
use_llm_for_source_links: false, use_llm_for_source_links: false,
use_brave_search: false,
article_history_days: 90, article_history_days: 90,
batch_size: 5, batch_size: 5,
}); });

@ -152,6 +152,12 @@ const fr = {
'settings.rateLimitReset': 'Reinitialiser', 'settings.rateLimitReset': 'Reinitialiser',
'settings.advancedExtraction': 'Extraction avancee', 'settings.advancedExtraction': 'Extraction avancee',
'settings.useLlmForSourceLinks': "Utiliser l'IA pour extraire les liens", '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.articleHistoryDays': 'Historique des articles (jours)',
'settings.batchSize': 'Taille du lot de traitement', 'settings.batchSize': 'Taille du lot de traitement',
'settings.batchSizeHelp': 'Nombre d\'articles traites en parallele lors de la generation (defaut: 5).', 'settings.batchSizeHelp': 'Nombre d\'articles traites en parallele lors de la generation (defaut: 5).',

@ -45,6 +45,7 @@ export interface UserSettings {
max_items_per_category: number; max_items_per_category: number;
max_articles_per_source: number; max_articles_per_source: number;
use_llm_for_source_links: boolean; use_llm_for_source_links: boolean;
use_brave_search: boolean;
article_history_days: number; article_history_days: number;
batch_size: number; batch_size: number;
search_agent_behavior: string; search_agent_behavior: string;
@ -62,6 +63,7 @@ export const DEFAULT_SETTINGS: UserSettings = {
max_items_per_category: 4, max_items_per_category: 4,
max_articles_per_source: 3, max_articles_per_source: 3,
use_llm_for_source_links: false, use_llm_for_source_links: false,
use_brave_search: false,
article_history_days: 90, article_history_days: 90,
batch_size: 5, batch_size: 5,
search_agent_behavior: search_agent_behavior:

Loading…
Cancel
Save