From 78acd12678af67b0cf2967f9aa1492e8ae6bc632 Mon Sep 17 00:00:00 2001 From: kesku Date: Thu, 15 Oct 2026 20:52:53 -0400 Subject: [PATCH] fix(exa): retain stream metadata and price instant searches --- litellm/llms/base_llm/search/streaming_iterator.py | 6 +++++- litellm/llms/exa_ai/search/cost_calculator.py | 1 + litellm/model_prices_and_context_window_backup.json | 8 ++++++++ model_prices_and_context_window.json | 8 ++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/litellm/llms/base_llm/search/streaming_iterator.py b/litellm/llms/base_llm/search/streaming_iterator.py index 97ece56c8b9..f4e22f671ad 100644 --- a/litellm/llms/base_llm/search/streaming_iterator.py +++ b/litellm/llms/base_llm/search/streaming_iterator.py @@ -137,11 +137,15 @@ class SearchStreamWrapper: raise self._error("Malformed search stream event") from error payload: Final = TypeAdapter(Mapping[str, object]).validate_python(event.model_dump()) if "requestId" in payload: - self._request_metadata = {"requestId": payload["requestId"]} + self._request_metadata = {**self._request_metadata, "requestId": payload["requestId"]} if event.type == "error": raise self._error(f"Search stream error: {payload.get('error')}", body=payload) if event.type == "results": self._results = payload.get("results", []) + self._request_metadata = { + **self._request_metadata, + **{key: value for key, value in payload.items() if key not in ("type", "results")}, + } if event.type == "done": self._finished = True completed: Final = {key: value for key, value in payload.items() if key != "type"} diff --git a/litellm/llms/exa_ai/search/cost_calculator.py b/litellm/llms/exa_ai/search/cost_calculator.py index 80a8d1b5fb1..2ad188d837d 100644 --- a/litellm/llms/exa_ai/search/cost_calculator.py +++ b/litellm/llms/exa_ai/search/cost_calculator.py @@ -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( { + "instant": "exa_ai/search-instant", "deep-lite": "exa_ai/search-deep", "deep": "exa_ai/search-deep", "deep-reasoning": "exa_ai/search-deep-reasoning", diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 86f8840551e..773a1f280f1 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -23892,6 +23892,14 @@ }, "mode": "search" }, + "exa_ai/search-instant": { + "input_cost_per_query": 0.004, + "litellm_provider": "exa_ai", + "metadata": { + "notes": "Base price per /search request with type instant, covering up to 10 results. Source: https://exa.ai/pricing (2026-10-15)" + }, + "mode": "search" + }, "exa_ai/search-deep": { "input_cost_per_query": 0.012, "litellm_provider": "exa_ai", diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 86f8840551e..773a1f280f1 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -23892,6 +23892,14 @@ }, "mode": "search" }, + "exa_ai/search-instant": { + "input_cost_per_query": 0.004, + "litellm_provider": "exa_ai", + "metadata": { + "notes": "Base price per /search request with type instant, covering up to 10 results. Source: https://exa.ai/pricing (2026-10-15)" + }, + "mode": "search" + }, "exa_ai/search-deep": { "input_cost_per_query": 0.012, "litellm_provider": "exa_ai",