Skip to content

feat(search): add TinyFish as search provider#28385

Closed
simantak-dabhade wants to merge 2 commits into
BerriAI:shin_agent_oss_staging_05_20_2026from
simantak-dabhade:feat/add-tinyfish-search-provider
Closed

feat(search): add TinyFish as search provider#28385
simantak-dabhade wants to merge 2 commits into
BerriAI:shin_agent_oss_staging_05_20_2026from
simantak-dabhade:feat/add-tinyfish-search-provider

Conversation

@simantak-dabhade

Copy link
Copy Markdown

Summary

Adds TinyFish as the 14th search provider in LiteLLM, following the same GET-based pattern as Brave/Serper.

  • Implements TinyfishSearchConfig(BaseSearchConfig) with full request/response transformation
  • Maps unified LiteLLM params (countrylocation, search_domain_filtersite: operators)
  • Auth via X-API-Key header (from api_key param or TINYFISH_API_KEY env var)
  • 6 unit tests covering: basic search, country mapping, domain filter injection, language passthrough, empty results, missing API key

Files changed (8)

File Change
litellm/llms/tinyfish/search/__init__.py New module export
litellm/llms/tinyfish/search/transformation.py Core TinyfishSearchConfig implementation
litellm/types/utils.py Add TINYFISH to SearchProviders enum
litellm/utils.py Register config in get_provider_search_config()
model_prices_and_context_window.json Add tinyfish/search pricing entry
provider_endpoints_support.json Add tinyfish endpoint metadata
tests/code_coverage_tests/enforce_llms_folder_style.py Add tinyfish to SEARCH_PROVIDERS
tests/search_tests/test_tinyfish_search.py 6 unit tests

Test plan

  • uv run pytest tests/search_tests/test_tinyfish_search.py -v — all 6 tests pass
  • Lint passes (make lint-ruff)
  • Black formatting applied

🤖 Generated with Claude Code

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-apps

greptile-apps Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This 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 (countrylocation, max_results capped and applied client-side) and proper auth via X-API-Key.

  • TinyfishSearchConfig correctly extends BaseSearchConfig, encodes query params via urlencode in get_complete_url, and uses the same _tinyfish_params wrapper dict pattern as Brave uses for _brave_params.
  • Six unit tests cover all major code paths using properly mocked HTTP calls — no real network requests.
  • Plumbing changes (enum, provider map, pricing JSON, folder style test) are minimal and consistent with how other search-only providers are registered.

Confidence Score: 5/5

This 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.

Important Files Changed

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

Comment on lines +84 to +87
request_data: TinyfishSearchRequest = {"query": query}

if "country" in optional_params:
request_data["location"] = optional_params["country"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

Suggested change
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"]

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b251f35max_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.

Comment on lines +27 to +31
class TinyfishSearchRequest(_TinyfishSearchRequestRequired, total=False):
location: str
language: str
page: int
include_thumbnail: str

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Suggested change
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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b251f35 — changed to 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>
@simantak-dabhade

Copy link
Copy Markdown
Author

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:

  • Added client-side max_results truncation (TinyFish API doesn't have a count param, so we handle it like Brave does — pass it as a query param and truncate in transform_search_response)
  • Fixed include_thumbnail type annotation (bool not str)
  • Added a 7th test for max_results truncation

cc @ishaan-berri @yuneng-berri

@simantak-dabhade

Copy link
Copy Markdown
Author

@greptileai review

@oss-pr-review-agent-shin

Copy link
Copy Markdown
Contributor

🤖 litellm-agent: This PR is currently BLOCKED from merge.

Score: 3/5

Why blocked:

  • 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 screenshot-exempt label if this PR has no visible output (e.g. pure docs, CI config).) (pr_related_failures, -2 pts)

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 screenshot-exempt label if this PR has no visible output (e.g. pure docs, CI config).).

Fix the issues above and push an update — the bot will re-review automatically.

Note: This bot is still in beta and might not always work as expected. Please share any feedback via Slack.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants