Add Brave web search support
This commit is contained in:
67
tests/test_config_web_search.py
Normal file
67
tests/test_config_web_search.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from app.config import _parse_ai_config
|
||||
|
||||
|
||||
def _raw_ai_config() -> dict:
|
||||
return {
|
||||
"default_model": "test",
|
||||
"context_messages": 1,
|
||||
"prompt": {"root": "prompts/ai"},
|
||||
"providers": {
|
||||
"test": {
|
||||
"api_key": "model-key",
|
||||
"base_url": "https://example.test/v1",
|
||||
}
|
||||
},
|
||||
"models": {
|
||||
"test": {
|
||||
"provider": "test",
|
||||
"model": "test-model",
|
||||
}
|
||||
},
|
||||
"web_search": {
|
||||
"api_key": "config-key",
|
||||
"count": 99,
|
||||
"safesearch": "strict",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class ConfigWebSearchTests(unittest.TestCase):
|
||||
def test_web_search_config_clamps_count(self) -> None:
|
||||
with patch.dict("os.environ", {}, clear=True):
|
||||
config = _parse_ai_config(_raw_ai_config(), root=Path.cwd())
|
||||
|
||||
assert config is not None
|
||||
self.assertEqual(config.web_search.api_key, "config-key")
|
||||
self.assertEqual(config.web_search.count, 20)
|
||||
self.assertEqual(config.web_search.safesearch, "strict")
|
||||
|
||||
def test_web_search_env_overrides(self) -> None:
|
||||
env = {
|
||||
"BRAVE_API_KEY": "env-key",
|
||||
"BRAVE_SEARCH_COUNT": "3",
|
||||
"BRAVE_SEARCH_COUNTRY": "pl",
|
||||
"BRAVE_SEARCH_LANG": "pl",
|
||||
"BRAVE_SEARCH_UI_LANG": "pl-PL",
|
||||
"BRAVE_SEARCH_SAFESEARCH": "off",
|
||||
}
|
||||
with patch.dict("os.environ", env, clear=True):
|
||||
config = _parse_ai_config(_raw_ai_config(), root=Path.cwd())
|
||||
|
||||
assert config is not None
|
||||
self.assertEqual(config.web_search.api_key, "env-key")
|
||||
self.assertEqual(config.web_search.count, 3)
|
||||
self.assertEqual(config.web_search.country, "PL")
|
||||
self.assertEqual(config.web_search.search_lang, "pl")
|
||||
self.assertEqual(config.web_search.ui_lang, "pl-PL")
|
||||
self.assertEqual(config.web_search.safesearch, "off")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
84
tests/test_web_search.py
Normal file
84
tests/test_web_search.py
Normal file
@@ -0,0 +1,84 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
import httpx
|
||||
|
||||
from app.ai.web_search import (
|
||||
_format_error_response,
|
||||
build_query,
|
||||
format_results,
|
||||
parse_response,
|
||||
)
|
||||
from app.config import AIWebSearchConfig
|
||||
|
||||
|
||||
class WebSearchTests(unittest.TestCase):
|
||||
def test_build_query_clamps_and_defaults(self) -> None:
|
||||
config = AIWebSearchConfig(api_key="key", count=5)
|
||||
|
||||
query = build_query(
|
||||
config,
|
||||
{
|
||||
"query": " python web framework ",
|
||||
"count": 99,
|
||||
"offset": -1,
|
||||
"freshness": "pw",
|
||||
},
|
||||
)
|
||||
|
||||
self.assertEqual(query.query, "python web framework")
|
||||
self.assertEqual(query.count, 20)
|
||||
self.assertEqual(query.offset, 0)
|
||||
self.assertEqual(query.freshness, "pw")
|
||||
self.assertEqual(query.safesearch, "moderate")
|
||||
|
||||
def test_build_query_rejects_invalid_freshness(self) -> None:
|
||||
with self.assertRaisesRegex(ValueError, "freshness"):
|
||||
build_query(AIWebSearchConfig(), {"query": "news", "freshness": "yesterday"})
|
||||
|
||||
def test_parse_and_format_results(self) -> None:
|
||||
response = parse_response(
|
||||
"openai",
|
||||
{
|
||||
"query": {"altered": "open ai", "more_results_available": True},
|
||||
"web": {
|
||||
"results": [
|
||||
{
|
||||
"title": "OpenAI",
|
||||
"url": "https://openai.com",
|
||||
"description": "AI research and products.",
|
||||
"age": "May 18, 2026",
|
||||
"extra_snippets": ["Models and APIs.", ""],
|
||||
},
|
||||
{"title": "", "url": ""},
|
||||
]
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
self.assertEqual(len(response.results), 1)
|
||||
self.assertTrue(response.more_results_available)
|
||||
|
||||
formatted = format_results(response)
|
||||
self.assertIn("Search results for: openai", formatted)
|
||||
self.assertIn("https://openai.com", formatted)
|
||||
self.assertIn("Models and APIs.", formatted)
|
||||
|
||||
def test_format_empty_results(self) -> None:
|
||||
formatted = format_results(parse_response("nothing", {"web": {"results": []}}))
|
||||
self.assertIn("No results found.", formatted)
|
||||
|
||||
def test_format_error_response_uses_brave_detail(self) -> None:
|
||||
request = httpx.Request("GET", "https://api.search.brave.com/res/v1/web/search")
|
||||
response = httpx.Response(
|
||||
429,
|
||||
request=request,
|
||||
json={"error": {"detail": "Rate limit exceeded"}},
|
||||
)
|
||||
|
||||
self.assertIn("Rate limit exceeded", _format_error_response(response))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user