Skip to content

feat(complexity_router): add custom_technical_keywords config - #32262

Merged
mateo-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_lit3237_custom_technical_keywords
Jul 6, 2026
Merged

feat(complexity_router): add custom_technical_keywords config#32262
mateo-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_lit3237_custom_technical_keywords

Conversation

@mateo-berri

@mateo-berri mateo-berri commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Linear ticket

Resolves LIT-3237

Pre-Submission checklist

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

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

Screenshots / Proof of Fix

Verified end to end against a live DB-less proxy hitting the real OpenAI API: fresh Python 3.13 venv with pip install -e ".[proxy]" in a detached worktree, random port 41624, identical config and identical prompt for both runs, so the only variable is the checked out code (base 29035c4a99 vs PR head c5813586). The config maps SIMPLE and MEDIUM to gpt-5.4-mini and COMPLEX and REASONING to gpt-5.5, and sets custom_technical_keywords. The test prompt contains eight custom keywords (kafka, redis, postgresql, mongodb, dns, udp, ssl, ssh) and exactly one built-in keyword ("api"), so it scores 0.150 (MEDIUM, weak model) when the custom list is ignored and 0.400 (COMPLEX, strong model) when it is applied

# qa_config.yaml (identical for both runs)
model_list:
  - model_name: gpt-5.4-mini
    litellm_params:
      model: openai/gpt-5.4-mini
      api_key: os.environ/OPENAI_API_KEY
  - model_name: gpt-5.5
    litellm_params:
      model: openai/gpt-5.5
      api_key: os.environ/OPENAI_API_KEY
  - model_name: smart-router
    litellm_params:
      model: auto_router/complexity_router
      complexity_router_config:
        tiers:
          SIMPLE: gpt-5.4-mini
          MEDIUM: gpt-5.4-mini
          COMPLEX: gpt-5.5
          REASONING: gpt-5.5
        custom_technical_keywords:
          - udp
          - dns
          - ssl
          - tls
          - ssh
          - rest
          - graphql
          - kafka
          - redis
          - postgresql
          - mongodb

general_settings:
  master_key: sk-qa-lit3237

The served deployment is identified by the x-litellm-model-id response header; /v1/model/info on the running proxy maps the ids (same mapping on both SHAs since the hash is derived from litellm_params)

$ curl -s http://localhost:41624/v1/model/info -H "Authorization: Bearer sk-qa-lit3237" | jq -r '.data[] | .model_info.id + " => " + .model_name'
d6a6a3a93b891c90b9b55b0cc0c1131ed3f6f5360df766d5ce701e2ba72ae351 => gpt-5.4-mini
2f96736ffaabf705873de51f7ea8dae984209d6899667a040f612bbf55e2d9cb => gpt-5.5
6e275cdca0dacb085f061e48b7a0ed825735277207aaa9dfee27a9b24adb4da5 => smart-router

Before (base 29035c4a): the prompt full of custom keywords routes to the weak model because custom_technical_keywords is silently ignored

$ git checkout --detach 29035c4a99d595321035def4ca5029786d29838e && git rev-parse --short HEAD
29035c4a99
$ .venv-qa/bin/python litellm/proxy/proxy_cli.py --config qa_config.yaml --port 41624 > proxy_before.log 2>&1 &

$ curl -sS -D - -o before_body.json http://localhost:41624/v1/chat/completions \
    -H "Authorization: Bearer sk-qa-lit3237" -H "Content-Type: application/json" \
    -d '{"model": "smart-router", "messages": [{"role": "user", "content": "Our team runs kafka alongside redis and postgresql, plus mongodb for document storage. Lately the dns lookups over udp appear slow, and ssl handshakes to the ssh bastion behind our internal api gateway keep stalling. Suggest a hardening plan for this stack and recommend tuning for each service so everything stays reliable under heavy load."}]}'
HTTP/1.1 200 OK
x-litellm-model-id: d6a6a3a93b891c90b9b55b0cc0c1131ed3f6f5360df766d5ce701e2ba72ae351
x-litellm-model-group: smart-router
x-litellm-model-api-base: https://api.openai.com
x-litellm-response-cost: 0.0101175

$ jq -r '.choices[0].message.content' before_body.json | head -c 200
Here’s a pragmatic hardening and tuning plan for a mixed Kafka + Redis + PostgreSQL + MongoDB stack when you’re seeing **slow UDP DNS lookups** and **TLS/SSH handshake stalls** through an internal API

Ran the same curl a second time on the base SHA: same x-litellm-model-id d6a6a3a9... (gpt-5.4-mini) since the router is deterministic

After (PR head c5813586): proxy killed, git checkout --detach c5813586334944dcbf1f39da864fc06ed17e2471, proxy restarted with the same config and port, same curl now routes to the strong model

$ git rev-parse --short HEAD
c581358633

$ curl -sS -D - -o after_body.json http://localhost:41624/v1/chat/completions \
    -H "Authorization: Bearer sk-qa-lit3237" -H "Content-Type: application/json" \
    -d @same_payload_as_above
HTTP/1.1 200 OK
x-litellm-model-id: 2f96736ffaabf705873de51f7ea8dae984209d6899667a040f612bbf55e2d9cb
x-litellm-model-group: smart-router
x-litellm-response-cost: 0.16249000000000002

$ jq -r '.choices[0].message.content' after_body.json | head -c 200
Below is a practical hardening and tuning plan for Kafka, Redis, PostgreSQL, and MongoDB, with specific attention to slow UDP DNS lookups and stalled TLS/SSH handshakes through an internal gateway.

Ran the main prompt three times total on the PR head (including across a proxy restart): all three returned x-litellm-model-id 2f96736f... (gpt-5.5). A control prompt with no technical keywords ("Hello, please share a fun fact about the Eiffel Tower.") routed to d6a6a3a9... (gpt-5.4-mini) on both SHAs, confirming the flip is attributable to custom_technical_keywords and not a general routing change

Type

🆕 New Feature

Changes

Adds a custom_technical_keywords option to the complexity router. Unlike technical_keywords, which replaces the built-in list entirely, the new field appends domain-specific terms to the effective base list (technical_keywords if set, otherwise DEFAULT_TECHNICAL_KEYWORDS), preserving order and deduplicating case-insensitively against the base list and within itself. Custom keywords go through the same case-insensitive word boundary matching as the built-ins. This came out of a report from a customer whose technical prompts routed to weaker models because the default list misses common terms; it contains "tcp" but not "udp", for example

On the backend, ComplexityRouterConfig gains the new field and ComplexityRouter.__init__ resolves the merged list through a small pure helper. On the Admin UI, the complexity router form in the Add Auto Router flow gets a tags input for custom technical keywords, and the entered terms are submitted as custom_technical_keywords: string[] inside complexity_router_config when non-empty

Unit tests cover appending to the defaults, appending to a technical_keywords override, case-insensitive dedup, unchanged behavior when the option is absent or null, and a scoring-level check that a prompt matching only custom keywords scores higher on the technicalTerms dimension. UI tests cover rendering the new field, displaying existing keywords as tags, and firing the change callback when a keyword is entered

@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a custom_technical_keywords config field to the complexity router, allowing users to append domain-specific terms to the built-in technical keyword list without replacing it. The implementation is well-scoped, safe (custom keywords go through re.escape), and ships with thorough unit and UI tests.

  • Backend: A pure _append_custom_keywords helper merges and deduplicates keywords case-insensitively, then ComplexityRouterConfig exposes the new field; existing behavior when the field is absent or None is unchanged.
  • UI: ComplexityRouterConfig.tsx gains an Ant Design tags-input card; add_auto_router_tab.tsx manages the state and conditionally includes it in the submission payload only when non-empty.

Confidence Score: 5/5

Additive feature with no changes to existing routing logic; safe to merge.

The change is purely additive — existing routers with no custom_technical_keywords field follow the identical code path as before. Custom keywords are escaped via re.escape before regex use, so no injection risk. Tests cover append, override, dedup, no-op, and scoring behavior without any real network calls.

No files require special attention.

Important Files Changed

Filename Overview
litellm/router_strategy/complexity_router/complexity_router.py Added _append_custom_keywords helper and wired custom_technical_keywords into ComplexityRouter.__init__. Logic is correct: uses re.escape in matching, preserves insertion order, and deduplicates case-insensitively.
litellm/router_strategy/complexity_router/config.py Added custom_technical_keywords: Optional[list[str]] field with a clear description to ComplexityRouterConfig; additive change with no breaking impact.
tests/test_litellm/router_strategy/test_complexity_router.py Added TestCustomTechnicalKeywords with 5 tests covering append-to-defaults, append-to-override, case-insensitive dedup, absent/null no-op, and scoring behavior. No real network calls; all mocked.
ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx Added optional customTechnicalKeywords / onCustomTechnicalKeywordsChange props and a new Ant Design Select tags input UI card; correctly falls back to [] and uses optional chaining for the callback.
ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx Adds customTechnicalKeywords state, passes it to ComplexityRouterConfig, and conditionally spreads it into the submission payload only when non-empty.
ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx Added 3 tests: field rendering, existing keyword tag display, and change-callback firing. Uses userEvent and within scoping correctly.

Reviews (2): Last reviewed commit: "feat(complexity_router): add custom_tech..." | Re-trigger Greptile

Comment thread litellm/router_strategy/complexity_router/config.py
Comment thread litellm/router_strategy/complexity_router/complexity_router.py
@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

@mateo-berri
mateo-berri merged commit f628b41 into litellm_internal_staging Jul 6, 2026
126 checks passed
@mateo-berri
mateo-berri deleted the litellm_lit3237_custom_technical_keywords branch July 6, 2026 20:00
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