Repository navigation
feat(search): add Keenable as a search provider - #44808
ilya-bogin-keenable wants to merge 3 commits into
Conversation
|
| } | ||
| }, | ||
| "keenable/search": { | ||
| "input_cost_per_query": 0.004, |
There was a problem hiding this comment.
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.
| 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}", |
There was a problem hiding this comment.
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.
Codecov Report❌ Patch coverage is
📢 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.
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo 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.
I work at Keenable
TLDR
Problem this solves:
search_provider: keenableis rejected, so Keenable search is unreachableHow it solves it:
keenablesearch provider that works with no API keyapi_keyorKEENABLE_API_KEYswitches to the keyed endpointUser Flow
Before: a gateway admin who adds a Keenable search tool gets a 500 on every search
keenable-searchtool withsearch_provider: keenabletosearch_toolsand start the proxy{"query": "python asyncio tutorial", "max_results": 3}'keenable' is not a valid SearchProvidersAfter: the same request returns ranked results, with no Keenable account or key
keenable-searchtool withsearch_provider: keenabletosearch_toolsand start the proxy{"query": "python asyncio tutorial", "max_results": 3}"search_domain_filter": ["docs.python.org"]to the body returns only docs.python.org pagesPre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
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@greptileaito re-request a review after pushing changes)Screenshots / Proof of Fix
Shared setup: no
KEENABLE_API_KEYin the environment, the master key read fromLITELLM_MASTER_KEY, and this config.yaml, started withpython litellm/proxy/proxy_cli.py --config config.yaml --port 4000Before (c13746c)
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}'{"error":{"message":"litellm.APIConnectionError: 'keenable' is not a valid SearchProviders","type":"internal_server_error","param":null,"code":"500"}}After (12f6731)
curlas Before{ "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..." } ] }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"]}'/3/howto/a-conceptual-overview-of-asyncio.html,/3/library/asyncio.htmland/3/library/asyncio-dev.htmlType
🆕 New Feature
Caveats (if any)
Low
KEENABLE_API_KEYFinal Attestation