Skip to content

feat(search): add Keenable as a search provider - #44808

Open
ilya-bogin-keenable wants to merge 3 commits into
BerriAI:mainfrom
keenableai:feat/keenable-search-provider
Open

ilya-bogin-keenable wants to merge 3 commits into
BerriAI:mainfrom
keenableai:feat/keenable-search-provider

Conversation

@ilya-bogin-keenable

@ilya-bogin-keenable ilya-bogin-keenable commented Oct 6, 2026 •

Copy link
Copy Markdown

I work at Keenable

TLDR

Problem this solves:

  • search_provider: keenable is rejected, so Keenable search is unreachable

How it solves it:

  • New keenable search provider that works with no API key
  • A caller api_key or KEENABLE_API_KEY switches to the keyed endpoint
  • Unified params mapped; date filters and other params pass through
  • Docs page in docs(search): add Keenable litellm-docs#2105

User Flow

Before: a gateway admin who adds a Keenable search tool gets a 500 on every search

  1. They add a keenable-search tool with search_provider: keenable to search_tools and start the proxy
  2. They send POST http://localhost:4000/v1/search/keenable-search with {"query": "python asyncio tutorial", "max_results": 3}
  3. The proxy answers 500 with 'keenable' is not a valid SearchProviders

After: the same request returns ranked results, with no Keenable account or key

  1. They add a keenable-search tool with search_provider: keenable to search_tools and start the proxy
  2. They send POST http://localhost:4000/v1/search/keenable-search with {"query": "python asyncio tutorial", "max_results": 3}
  3. The proxy answers 200 with three results, each with a title, URL, page text snippet and publish date
  4. Adding "search_domain_filter": ["docs.python.org"] to the body returns only docs.python.org pages

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/unit/<your_test_file>.py -v. Leave the suites (make test-unit-*, make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Screenshots / Proof of Fix

Shared setup: no KEENABLE_API_KEY in the environment, the master key read from LITELLM_MASTER_KEY, and this config.yaml, started with python litellm/proxy/proxy_cli.py --config config.yaml --port 4000

search_tools:
  - search_tool_name: keenable-search
    litellm_params:
      search_provider: keenable

general_settings:
  master_key: os.environ/LITELLM_MASTER_KEY

Before (c13746c)

  1. curl -s http://localhost:4000/v1/search/keenable-search -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H "Content-Type: application/json" -d '{"query": "python asyncio tutorial", "max_results": 3}'
  2. HTTP 500:
{"error":{"message":"litellm.APIConnectionError: 'keenable' is not a valid SearchProviders","type":"internal_server_error","param":null,"code":"500"}}

After (12f6731)

  1. The same curl as Before
  2. HTTP 200, snippets cut to 60 characters here for length:
{
  "object": "search",
  "results": [
    {
      "title": "Async Programming with asyncio",
      "url": "https://pyguides.dev/tutorials/intermediate-python/asyncio-tutorial",
      "date": "2026-03-07T00:00:00Z",
      "snippet": "Asynchronous programming lets your Python code handle multip..."
    },
    {
      "title": "Getting Started with asyncio",
      "url": "https://pyguides.dev/guides/asyncio-basics",
      "date": "2026-03-13T00:00:00Z",
      "snippet": "Asynchronous programming lets your code handle multiple oper..."
    },
    {
      "title": "Python's asyncio: A Hands-On Walkthrough",
      "url": "https://realpython.com/async-io-python/",
      "date": "2025-07-30T14:00:00Z",
      "snippet": "Python’s `asyncio` library enables you to write concurrent c..."
    }
  ]
}
  1. curl -s http://localhost:4000/v1/search/keenable-search -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H "Content-Type: application/json" -d '{"query": "python asyncio tutorial", "max_results": 3, "search_domain_filter": ["docs.python.org"]}'
  2. HTTP 200 with three docs.python.org results: /3/howto/a-conceptual-overview-of-asyncio.html, /3/library/asyncio.html and /3/library/asyncio-dev.html

Type

🆕 New Feature

Caveats (if any)

Low

  • Keyless limits are per IP, so a busy gateway should set KEENABLE_API_KEY
  • The keyed endpoint is covered by unit tests, not by the live run above
  • Keyed searches are priced at the $4 per 1,000 list rate and keyless ones at $0; the 100,000 free keyed requests a month are not tracked

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

@CLAassistant

CLAassistant commented Oct 6, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@greptile-apps

greptile-apps Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

[Medium risk] Adds Keenable as a new search provider.

The PR should not merge until free public searches stop consuming paid-search budgets.

Findings

  1. P1 Free searches accrue paid spend ▶
  2. P2 API errors lose their message ▶

Summary

Adds Keenable as a keyless or keyed search provider, with request and response transformations, pricing, registry entries, tests, and a dashboard logo.

  • Keyless requests currently accrue the keyed per-query price.
  • Non-2xx POST responses lose Keenable’s actionable error message.

Reviews (1) · Last reviewed commit: "feat(search): add Keenable as a search p..."

}
},
"keenable/search": {
"input_cost_per_query": 0.004,

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 Free searches accrue paid spend

When a request has no Keenable API key, it uses the free /search/public endpoint. This price still charges $0.004 per query because search cost calculation does not distinguish public from keyed requests. Keyless searches will therefore inflate reported spend and can consume proxy budgets without incurring a Keenable charge.

Comment on lines +193 to +197
try:
parsed: Final = _KeenableSearchResponse.model_validate_json(raw_response.content)
except ValidationError as e:
raise self.get_error_class(
error_message=f"response does not match the documented /v1/search schema: {e}",

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 API errors lose their message

For a non-2xx POST response, the search handler passes Keenable’s error body to this success-response parser. An error such as “Public API hourly limit reached” has no results field, so callers see a response-schema mismatch instead of the useful rate-limit message. This makes failed searches harder to diagnose.

@codspeed

codspeed Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing keenableai:feat/keenable-search-provider (7f08afa) with main (8adb8f3)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (b91b2fc) during the generation of this report, so 8adb8f3 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

@codecov

codecov Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.89474% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
litellm/utils.py 0.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

…s reach the caller

Keyless calls are free and keyed accounts start with 100,000 free requests a
month, so a flat list price overstated spend.
Comment thread model_prices_and_context_window.json Outdated
@veria-ai

veria-ai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

Keyless requests to /v1/search/public are free, keyed requests to /v1/search are
billed per query. The response transform records which endpoint answered, and the
search cost calculator prices a keyless search at $0 and a keyed one at the
$4 per 1,000 list rate, so keyed spend counts against proxy budgets again.

This branch has not been deployed

No deployments
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