fix(exa): add Lite mode and fallback pricing

This commit is contained in:
kesku 2026-10-22 14:50:38 -07:00
parent 789b23d36a
commit f691d27d5f
5 changed files with 37 additions and 1 deletions

View file

@ -9,6 +9,7 @@ EXA_AI_ADDITIONAL_RESULT_COST: Final = 0.001
EXA_AI_STANDARD_SEARCH_MODEL: Final = "exa_ai/search"
EXA_AI_PRICING_MODEL_BY_TYPE: Final[Mapping[str, str]] = MappingProxyType(
{
"lite": "exa_ai/search-lite",
"instant": "exa_ai/search-instant",
"deep-lite": "exa_ai/search-deep",
"deep": "exa_ai/search-deep",

View file

@ -5,7 +5,7 @@ from typing import Literal, TypeAlias
from typing_extensions import ReadOnly, Required, TypedDict
ExaSearchMode: TypeAlias = Literal["instant", "fast", "auto", "deep-lite", "deep", "deep-reasoning"]
ExaSearchMode: TypeAlias = Literal["lite", "instant", "fast", "auto", "deep-lite", "deep", "deep-reasoning"]
ExaPageSection: TypeAlias = Literal["header", "navigation", "banner", "body", "sidebar", "footer", "metadata"]

View file

@ -24437,6 +24437,14 @@
},
"mode": "search"
},
"exa_ai/search-lite": {
"input_cost_per_query": 0.004,
"litellm_provider": "exa_ai",
"metadata": {
"notes": "Base price per /search request with type lite, covering up to 10 results. Source: https://exa.ai/pricing (2026-10-22)"
},
"mode": "search"
},
"exa_ai/search-instant": {
"input_cost_per_query": 0.004,
"litellm_provider": "exa_ai",

View file

@ -24437,6 +24437,14 @@
},
"mode": "search"
},
"exa_ai/search-lite": {
"input_cost_per_query": 0.004,
"litellm_provider": "exa_ai",
"metadata": {
"notes": "Base price per /search request with type lite, covering up to 10 results. Source: https://exa.ai/pricing (2026-10-22)"
},
"mode": "search"
},
"exa_ai/search-instant": {
"input_cost_per_query": 0.004,
"litellm_provider": "exa_ai",

View file

@ -519,6 +519,7 @@ def test_exa_estimate_prices_one_request_however_many_queries_it_joins() -> None
@pytest.mark.parametrize(
("search_type", "pricing_model"),
[
("lite", "exa_ai/search-lite"),
("instant", "exa_ai/search-instant"),
("auto", "exa_ai/search"),
("fast", "exa_ai/search"),
@ -557,3 +558,21 @@ def test_exa_estimate_ignores_invalid_result_counts(count: object) -> None:
assert search_provider_cost_per_query(
"exa_ai/search", "exa_ai", optional_params={"numResults": count}
) == search_provider_cost_per_query("exa_ai/search", "exa_ai")
@pytest.mark.parametrize("asynchronous", [False, True])
@pytest.mark.usefixtures("exa_api_key")
@pytest.mark.asyncio
async def test_lite_search_preserves_mode_and_highlights(respx_mock: respx.MockRouter, asynchronous: bool) -> None:
route: Final = respx_mock.post(EXA_SEARCH_URL).respond(json=FULL_RESPONSE)
response: Final = (
await litellm.asearch(query="question", search_provider="exa_ai", type="lite", contents={"highlights": True})
if asynchronous
else litellm.search(query="question", search_provider="exa_ai", type="lite", contents={"highlights": True})
)
assert json.loads(route.calls.last.request.content) == {
"query": "question",
"type": "lite",
"contents": {"highlights": True},
}
assert response.results[0].snippet == "\n\n".join(FULL_RESULT["highlights"])