feat(search): add TinyFish as search provider#28385
Conversation
Add TinyFish (https://tinyfish.ai) as a new search provider for the LiteLLM Search API. TinyFish provides web search results via GET https://api.search.tinyfish.ai with X-API-Key authentication. Supports unified params (query, country, search_domain_filter) and TinyFish-specific passthrough params (language, page, include_thumbnail). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Greptile SummaryThis PR adds TinyFish as a 14th search provider in LiteLLM, following the established GET-based pattern of Brave and Serper. The implementation is clean and well-structured, with correct parameter mappings (
Confidence Score: 5/5This is a self-contained new provider addition with no changes to existing code paths; all required plumbing is in place and follows established patterns. The implementation is additive-only, uses LiteLLM's existing HTTP handler infrastructure, and mirrors the Brave/Serper GET-provider pattern faithfully. The two observations are cosmetic style differences, not functional defects. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/llms/tinyfish/search/transformation.py | Core TinyFish search config — follows the GET-based Brave/Serper pattern; explicit mappings for country→location and max_results; passthrough loop for provider-specific extras |
| litellm/llms/tinyfish/search/init.py | Minimal module init exporting TinyfishSearchConfig — no issues |
| litellm/types/utils.py | Adds TINYFISH to SearchProviders enum following existing pattern |
| litellm/utils.py | Registers TinyfishSearchConfig in get_provider_search_config() alongside other search providers |
| model_prices_and_context_window.json | Adds tinyfish/search pricing entry with input_cost_per_query=0.0 and subscription-based pricing note |
| provider_endpoints_support.json | Adds tinyfish provider metadata; the docs URL points to a page that doesn't exist yet |
| tests/code_coverage_tests/enforce_llms_folder_style.py | Adds tinyfish to SEARCH_PROVIDERS allowlist — correct placement to pass the llms folder style check |
| tests/search_tests/test_tinyfish_search.py | 6 unit tests with properly mocked HTTP calls; covers basic search, country mapping, domain filter injection, language passthrough, max_results truncation, empty results, and missing API key |
Reviews (2): Last reviewed commit: "fix(search/tinyfish): handle max_results..." | Re-trigger Greptile
| request_data: TinyfishSearchRequest = {"query": query} | ||
|
|
||
| if "country" in optional_params: | ||
| request_data["location"] = optional_params["country"] |
There was a problem hiding this comment.
The
max_results unified parameter is a member of get_supported_perplexity_optional_params(), which means it is intentionally excluded from the passthrough loop. But there is no explicit mapping of max_results to a TinyFish-specific parameter, so passing max_results=5 to litellm.asearch(…, search_provider="tinyfish") is silently swallowed — the API receives no limit hint. Every other provider that supports result limiting (Brave → count, Serper → num) has an explicit mapping; TinyFish needs the same treatment once the correct API parameter name is confirmed from the TinyFish docs.
| request_data: TinyfishSearchRequest = {"query": query} | |
| if "country" in optional_params: | |
| request_data["location"] = optional_params["country"] | |
| request_data: TinyfishSearchRequest = {"query": query} | |
| if "max_results" in optional_params: | |
| # TODO: replace "num_results" with the actual TinyFish API parameter name | |
| request_data["num_results"] = optional_params["max_results"] | |
| if "country" in optional_params: | |
| request_data["location"] = optional_params["country"] |
There was a problem hiding this comment.
Fixed in b251f35 — max_results is now mapped in transform_search_request and used for client-side truncation in transform_search_response (reading it back from the URL query params, same pattern as Brave). Added a dedicated test as well.
| class TinyfishSearchRequest(_TinyfishSearchRequestRequired, total=False): | ||
| location: str | ||
| language: str | ||
| page: int | ||
| include_thumbnail: str |
There was a problem hiding this comment.
include_thumbnail is an include/exclude flag and should be typed as bool. Keeping it as str is a type-checking gap — static analysis tools will not catch callers passing a non-string value, and urlencode will serialize a Python bool as "True"/"False" (capital T/F) rather than the lowercased "true"/"false" most REST APIs expect.
| class TinyfishSearchRequest(_TinyfishSearchRequestRequired, total=False): | |
| location: str | |
| language: str | |
| page: int | |
| include_thumbnail: str | |
| class TinyfishSearchRequest(_TinyfishSearchRequestRequired, total=False): | |
| location: str | |
| language: str | |
| page: int | |
| include_thumbnail: bool |
…thumbnail type Address Greptile review feedback: - Map max_results to query param and truncate results client-side (TinyFish API has no count param) - Fix include_thumbnail type annotation from str to bool - Add test for max_results truncation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Hey team — looking for a review on this PR when you get a chance! This adds TinyFish as a new search provider (the 14th), following the same GET-based pattern as Brave/Serper. Clean 8-file diff, 341 additions. Update: Pushed a second commit addressing Greptile's feedback:
|
|
@greptileai review |
|
🤖 litellm-agent: This PR is currently BLOCKED from merge. Score: 3/5 ❌ Why blocked:
Details: Score docked for: 1 PR-related CI failure (This PR will be auto-closed as it lacks a screenshot for proof of fix. Please include one in the PR description. Add the Fix the issues above and push an update — the bot will re-review automatically.
|
Summary
Adds TinyFish as the 14th search provider in LiteLLM, following the same GET-based pattern as Brave/Serper.
TinyfishSearchConfig(BaseSearchConfig)with full request/response transformationcountry→location,search_domain_filter→site:operators)X-API-Keyheader (fromapi_keyparam orTINYFISH_API_KEYenv var)Files changed (8)
litellm/llms/tinyfish/search/__init__.pylitellm/llms/tinyfish/search/transformation.pyTinyfishSearchConfigimplementationlitellm/types/utils.pyTINYFISHtoSearchProvidersenumlitellm/utils.pyget_provider_search_config()model_prices_and_context_window.jsontinyfish/searchpricing entryprovider_endpoints_support.jsontests/code_coverage_tests/enforce_llms_folder_style.pySEARCH_PROVIDERStests/search_tests/test_tinyfish_search.pyTest plan
uv run pytest tests/search_tests/test_tinyfish_search.py -v— all 6 tests passmake lint-ruff)🤖 Generated with Claude Code