Normalize Brave search locale parameters
This commit is contained in:
@@ -12,6 +12,41 @@ from app.config import AIWebSearchConfig
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
SAFESEARCH_VALUES = {"off", "moderate", "strict"}
|
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})$")
|
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
|
MAX_LOG_BODY_CHARS = 1000
|
||||||
|
|
||||||
@@ -76,6 +111,28 @@ def normalize_safesearch(value: Any, *, default: str) -> str:
|
|||||||
return safesearch
|
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:
|
def normalize_freshness(value: Any) -> str | None:
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
@@ -90,14 +147,19 @@ def normalize_freshness(value: Any) -> str | None:
|
|||||||
|
|
||||||
|
|
||||||
def build_query(config: AIWebSearchConfig, arguments: dict[str, Any]) -> WebSearchQuery:
|
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(
|
return WebSearchQuery(
|
||||||
query=sanitize_query(str(arguments.get("query") or arguments.get("q") or "")),
|
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),
|
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),
|
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,
|
country=normalize_country(arguments.get("country"), default=config.country),
|
||||||
search_lang=str(arguments.get("search_lang") or config.search_lang).strip().lower()
|
search_lang=search_lang,
|
||||||
or config.search_lang,
|
ui_lang=normalize_ui_lang(
|
||||||
ui_lang=str(arguments.get("ui_lang") or config.ui_lang).strip() or config.ui_lang,
|
arguments.get("ui_lang"),
|
||||||
|
search_lang=search_lang,
|
||||||
|
default=config.ui_lang,
|
||||||
|
),
|
||||||
safesearch=normalize_safesearch(arguments.get("safesearch"), default=config.safesearch),
|
safesearch=normalize_safesearch(arguments.get("safesearch"), default=config.safesearch),
|
||||||
freshness=normalize_freshness(arguments.get("freshness")),
|
freshness=normalize_freshness(arguments.get("freshness")),
|
||||||
extra_snippets=bool(arguments.get("extra_snippets", config.extra_snippets)),
|
extra_snippets=bool(arguments.get("extra_snippets", config.extra_snippets)),
|
||||||
|
|||||||
@@ -38,6 +38,27 @@ class WebSearchTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
with self.assertRaisesRegex(ValueError, "freshness"):
|
with self.assertRaisesRegex(ValueError, "freshness"):
|
||||||
build_query(AIWebSearchConfig(), {"query": "news", "freshness": "yesterday"})
|
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:
|
def test_parse_and_format_results(self) -> None:
|
||||||
response = parse_response(
|
response = parse_response(
|
||||||
"openai",
|
"openai",
|
||||||
|
|||||||
Reference in New Issue
Block a user