Normalize Brave search locale parameters

This commit is contained in:
2026-05-19 21:52:28 +02:00
parent cc783ab8ed
commit 36170f83d1
2 changed files with 87 additions and 4 deletions

View File

@@ -12,6 +12,41 @@ from app.config import AIWebSearchConfig
log = logging.getLogger(__name__)
SAFESEARCH_VALUES = {"off", "moderate", "strict"}
COUNTRY_VALUES = {
"AR", "AU", "AT", "BE", "BR", "CA", "CL", "DK", "FI", "FR", "DE", "GR", "HK",
"IN", "ID", "IT", "JP", "KR", "MY", "MX", "NL", "NZ", "NO", "CN", "PL", "PT",
"PH", "RU", "SA", "ZA", "ES", "SE", "CH", "TW", "TR", "GB", "US", "ALL",
}
UI_LANG_VALUES = {
"es-AR", "en-AU", "de-AT", "nl-BE", "fr-BE", "pt-BR", "en-CA", "fr-CA",
"es-CL", "da-DK", "fi-FI", "fr-FR", "de-DE", "el-GR", "zh-HK", "en-IN",
"en-ID", "it-IT", "ja-JP", "ko-KR", "en-MY", "es-MX", "nl-NL", "en-NZ",
"no-NO", "zh-CN", "pl-PL", "en-PH", "ru-RU", "en-ZA", "es-ES", "sv-SE",
"fr-CH", "de-CH", "zh-TW", "tr-TR", "en-GB", "en-US", "es-US",
}
LANG_TO_UI_LANG = {
"da": "da-DK",
"de": "de-DE",
"el": "el-GR",
"en": "en-US",
"es": "es-ES",
"fi": "fi-FI",
"fr": "fr-FR",
"it": "it-IT",
"ja": "ja-JP",
"jp": "ja-JP",
"ko": "ko-KR",
"nl": "nl-NL",
"no": "no-NO",
"pl": "pl-PL",
"pt": "pt-BR",
"pt-br": "pt-BR",
"ru": "ru-RU",
"sv": "sv-SE",
"tr": "tr-TR",
"zh-hans": "zh-CN",
"zh-hant": "zh-TW",
}
FRESHNESS_RE = re.compile(r"^(pd|pw|pm|py|\d{4}-\d{2}-\d{2}to\d{4}-\d{2}-\d{2})$")
MAX_LOG_BODY_CHARS = 1000
@@ -76,6 +111,28 @@ def normalize_safesearch(value: Any, *, default: str) -> str:
return safesearch
def normalize_country(value: Any, *, default: str) -> str:
country = str(value or default).strip().upper() or default
if country in COUNTRY_VALUES:
return country
log.info("Unsupported Brave search country %r; using ALL", country)
return "ALL"
def normalize_ui_lang(value: Any, *, search_lang: str, default: str) -> str:
ui_lang = str(value or default).strip() or default
if ui_lang in UI_LANG_VALUES:
return ui_lang
normalized = ui_lang.replace("_", "-")
language = normalized.split("-", 1)[0].lower()
fallback = LANG_TO_UI_LANG.get(language) or LANG_TO_UI_LANG.get(search_lang) or "en-US"
if fallback not in UI_LANG_VALUES:
fallback = "en-US"
log.info("Unsupported Brave search ui_lang %r; using %s", ui_lang, fallback)
return fallback
def normalize_freshness(value: Any) -> str | None:
if value is None:
return None
@@ -90,14 +147,19 @@ def normalize_freshness(value: Any) -> str | None:
def build_query(config: AIWebSearchConfig, arguments: dict[str, Any]) -> WebSearchQuery:
search_lang = str(arguments.get("search_lang") or config.search_lang).strip().lower()
search_lang = search_lang or config.search_lang
return WebSearchQuery(
query=sanitize_query(str(arguments.get("query") or arguments.get("q") or "")),
count=clamp_int(arguments.get("count"), default=config.count, minimum=1, maximum=20),
offset=clamp_int(arguments.get("offset"), default=0, minimum=0, maximum=9),
country=str(arguments.get("country") or config.country).strip().upper() or config.country,
search_lang=str(arguments.get("search_lang") or config.search_lang).strip().lower()
or config.search_lang,
ui_lang=str(arguments.get("ui_lang") or config.ui_lang).strip() or config.ui_lang,
country=normalize_country(arguments.get("country"), default=config.country),
search_lang=search_lang,
ui_lang=normalize_ui_lang(
arguments.get("ui_lang"),
search_lang=search_lang,
default=config.ui_lang,
),
safesearch=normalize_safesearch(arguments.get("safesearch"), default=config.safesearch),
freshness=normalize_freshness(arguments.get("freshness")),
extra_snippets=bool(arguments.get("extra_snippets", config.extra_snippets)),

View File

@@ -38,6 +38,27 @@ class WebSearchTests(unittest.IsolatedAsyncioTestCase):
with self.assertRaisesRegex(ValueError, "freshness"):
build_query(AIWebSearchConfig(), {"query": "news", "freshness": "yesterday"})
def test_build_query_normalizes_unsupported_brave_country_and_ui_lang(self) -> None:
config = AIWebSearchConfig(
api_key="key",
country="UA",
search_lang="ru",
ui_lang="ru-UA",
)
query = build_query(config, {"query": "crimea news"})
self.assertEqual(query.country, "ALL")
self.assertEqual(query.search_lang, "ru")
self.assertEqual(query.ui_lang, "ru-RU")
def test_build_query_falls_back_unknown_ui_lang_to_english(self) -> None:
config = AIWebSearchConfig(api_key="key", ui_lang="zz-ZZ")
query = build_query(config, {"query": "python"})
self.assertEqual(query.ui_lang, "en-US")
def test_parse_and_format_results(self) -> None:
response = parse_response(
"openai",