Repository navigation
feat(guardrails): add TypeSafe Jev relevance-based compaction guardrail - #41757
Conversation
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…dling Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
|
|
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…log fail-open status Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…esafe guardrail Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…very finds them Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Backport of upstream BerriAI#41757 (merge 2edda5a). Adds the `typesafe` guardrail (pre_call, fails open by default) that blanks completed tool exchanges Jev scores as no longer relevant, and registers it as an auto-router compression provider alongside headroom/compresr. The only source conflict was the unreachable_fallback docstring in litellm/types/guardrails.py. The OpenAPI snapshot and schema.d.ts are regenerated in a later commit. Drop this commit when rebasing onto a release that contains BerriAI#41757. (cherry picked from commit 2edda5a) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…pes for stable/1.102.x The committed schema.d.ts and lazy OpenAPI snapshot were taken verbatim from main by the #41615 and #41757 picks, so they described classifier types, routing causes, and a guardrail that this line does not ship and lacked the /auto_router/manage permission. Regenerated under Python 3.12, the interpreter the check-ui-api-types workflow uses.
TLDR
Problem this solves:
How it solves it:
typesafeguardrail scores each completed tool exchange with TypeSafe's Jev modelPOST /v1/systemonecall, one yes/no question per exchange, no summarizationguardrail: typesafe,mode: pre_call, or add it from the Admin UI as "TypeSafe (Jev) Compaction"User Flow
Before: an agent developer with a long tool-call history sends the whole thing to the model every turn and pays for all of it
Unsupported guardrail: typesafe"guardrails": ["typesafe-compaction"]and three finished tool exchanges (weather, stock quote, deployment runbook) plus a final question about the runbookprompt_tokens: 604; every stale tool result was billedAfter: the same request drops the two off-topic tool results before it reaches the model
os.environ/TYPESAFE_API_KEYas the api_key, keepspre_call, and clicks Create Guardrailtypesafe-compactionwith provider TypeSafe (Jev) Compaction, mode pre_call, Default Off"guardrails": ["typesafe-compaction"]prompt_tokens: 406; the weather and stock results were replaced by a short removal marker, the runbook result was keptPre-call guardrail: typesafe-compaction PASSED, the raw guardrail responseexchanges_evaluated: 2, exchanges_dropped: 2, chars_removed: 744, and the two replaced tool messages in the request historytypesafe-compactionin the Guardrails selector and every chat sent from there runs through itRelevant issues
Affected release
Linear ticket
Resolves LIT-8088
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/test_litellm/<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)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
Shared setup for both runs: Postgres-backed proxy on port 4000 started with
python litellm/proxy/proxy_cli.py --config ~/typesafe_qa_config.yaml --detailed_debug --reload --use_v2_migration_resolver, Admin UI dev server (npm run devinui/litellm-dashboard) on port 3000 at the PR tip,TYPESAFE_API_KEYandOPENAI_API_KEYexported in the proxy shell. The guardrail is created once through the UI (After, case 1) and stored in the DB; the Before run boots the merge-base proxy against the same DB and config. Real OpenAI (gpt-5.6) and TypeSafe (jev-latest) calls, no mocksThe request payload (
~/typesafe_payload.json) carries a system prompt, a user task, three completed tool exchanges (a Lisbon weather report, an ACME stock quote, and a deployment runbook, each 400 to 600 characters), and a final user question that only the runbook answers. It ends with"tool_choice": "none"so the model answers instead of calling another tooltypesafe_payload.json
{ "model": "gpt-5.6", "guardrails": ["typesafe-compaction"], "max_completion_tokens": 500, "tool_choice": "none", "messages": [ {"role": "system", "content": "You are a helpful operations assistant. Answer the latest user question using the relevant tool result. Be concise and do not invent values."}, {"role": "user", "content": "First check the weather in Lisbon and a stock quote, then read the deployment runbook. I will ask a follow-up about the runbook."}, {"role": "assistant", "content": null, "tool_calls": [{"id": "call_weather", "type": "function", "function": {"name": "get_weather", "arguments": "{\"city\":\"Lisbon\"}"}}]}, {"role": "tool", "tool_call_id": "call_weather", "content": "Lisbon weather station report: temperature 23 C, feels like 24 C, humidity 61 percent, wind from the northwest at 14 km/h. Skies are mostly clear with scattered coastal clouds. The afternoon forecast expects a high of 26 C and less than 5 percent chance of rain. Sunset is at 19:42 local time. Visibility is 12 km and barometric pressure is steady at 1017 hPa. Outdoor walking conditions are comfortable; bring a light jacket for the evening waterfront breeze."}, {"role": "assistant", "content": null, "tool_calls": [{"id": "call_stock", "type": "function", "function": {"name": "get_stock_quote", "arguments": "{\"symbol\":\"ACME\"}"}}]}, {"role": "tool", "tool_call_id": "call_stock", "content": "Market quote for synthetic ticker ACME: last trade 142.35 USD, change +1.12 USD, daily change +0.79 percent. The session opened at 141.20, reached a high of 143.10 and a low of 140.85. Trading volume is 2,430,000 shares, with a bid of 142.34 and ask of 142.36. The prior close was 141.23. The 52-week trading range is 112.40 to 156.80. This delayed quote is intended for a market overview only and does not contain infrastructure settings or deployment instructions."}, {"role": "assistant", "content": null, "tool_calls": [{"id": "call_runbook", "type": "function", "function": {"name": "read_file", "arguments": "{\"path\":\"/runbooks/deployment.md\"}"}}]}, {"role": "tool", "tool_call_id": "call_runbook", "content": "Deployment runbook, revision 12. The production application is deployed in AWS region eu-west-1. Operational request logs must be retained for 30 days and then deleted by the lifecycle policy. Deployments use a blue-green rollout with readiness checks before traffic switches. On-call engineers verify health metrics and error rates during the first fifteen minutes. Roll back to the previous image if the error budget alert fires. Database backups run daily and are verified separately."}, {"role": "user", "content": "Ignore the earlier weather and stock quote. According only to the deployment runbook, which AWS region is production in and how many days are operational request logs retained? Answer in one sentence."} ], "tools": [ {"type": "function", "function": {"name": "get_weather", "description": "Get a city weather report", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}, {"type": "function", "function": {"name": "get_stock_quote", "description": "Get a market quote", "parameters": {"type": "object", "properties": {"symbol": {"type": "string"}}, "required": ["symbol"]}}}, {"type": "function", "function": {"name": "read_file", "description": "Read an operations runbook", "parameters": {"type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"]}}} ] }Before (db37977)
Admin UI: register the guardrail
typesafe-compactionguardrail created in the After run. Startup skipped it:TypeSafeinto the Guardrail Provider dropdown. Observed: "No matching providers"Request logs
Observed: HTTP 200,
prompt_tokens: 604,completion_tokens: 25, answer "Production is in AWS region eu-west-1, and operational request logs are retained for 30 days." The unknown guardrail name is silently ignoredPlayground
/v1/chat/completions, modelgpt-5.6, and opened the Guardrails selector. Observed: the DB-storedtypesafe-compactionname is listed because the dashboard reads the guardrails table, even though the proxy could not load itAfter (0765f6d)
Admin UI: register the guardrail
typesafe-compaction, and picked "TypeSafe (Jev) Compaction" from the provider dropdownpre_calland Always OnNo, enteredos.environ/TYPESAFE_API_KEYas api_key,https://api.typesafe.aias api_base,jev-latestas model,fail_openas unreachable_fallback, and clicked Nextrelevance_threshold0.2,min_chars_to_evaluate200,max_result_chars_in_state4000 and clicked Create Guardrail. Observed: "Guardrail created successfully"typesafe-compaction, provider TypeSafe (Jev) Compaction, mode pre_call, Default OffRequest logs
prompt_tokens: 406,completion_tokens: 25, the same answer "Production is in AWS region eu-west-1, and operational request logs are retained for 30 days." Prompt tokens fell 604 -> 406 (about 33%). The proxy logged:guardrailsfield for comparison. Observed: HTTP 200,prompt_tokens: 604. Both rows in http://localhost:3000/logs/: 629 total tokens (604 + 25) unguarded, 431 (406 + 25) guardedPre-call guardrail: typesafe-compaction PASSEDand the Raw Guardrail Response is{"model": "jev-latest", "chars_removed": 744, "exchanges_dropped": 2, "exchanges_evaluated": 2}[Tool result removed by TypeSafe compaction: judged no longer relevant to the current task], the runbook result, system prompt, and final user question are unchangedPlayground
/v1/chat/completions, modelgpt-5.6, and pickedtypesafe-compactionin the Guardrails selectorPre-call guardrail: typesafe-compaction PASSEDin the lifecycle. The Playground chat has no completed tool exchanges (no MCP server was configured in this environment), so the guardrail had nothing to evaluate and forwarded the request unchangedThe run before this one (tip 22e6947) was uncompacted: the registries had been wrapped in
MappingProxyTypefor the LIT002 gate andguardrail_registry.pyonly discovers plain dicts, so the proxy skipped the guardrail while CI stayed green. 0765f6d reverts that and addstest_typesafe_initializer_discoverable_via_hook_registries, which fails on 22e6947 and passes nowTaxonomy audit
O3: evaluator outage cannot widen anything, it only forwards the request uncompacted and logs
guardrail_failed_to_respond. O4: pre_call only by design, response and streaming paths never compacted. W1: inputstructured_messagesand rows are never mutated, compaction returns new objects (tests assert identity on the no-op paths). X1:is Nonechecks throughout, the onlyorfallback is api_key/api_base where empty means unset. V1:api_baseis operator config, never request controlled. H7:buildAutoRouterCompression.tschanged but the prebuilt_experimental/outbundle is refreshed by the scheduled build-artifacts automation, so the served UI liststypesafeafter the next artifact run. Q3: the Jev call is an unattributed outbound call, same as compresr and headroom. H5:DROPPED_RESULT_TEXTand defaults live in the guardrail module, matching compresrType
🆕 New Feature
Caveats (if any)
Medium
toolrows are blanked; Anthropictool_resultblocks pass through untouchedLow
unreachable_fallbackdefaults tofail_openhere, unlike other guardrails, since compaction is an optimizationexchanges_evaluated: 0entry)Final Attestation
Link to Devin session: https://app.devin.ai/sessions/6e41d4677f5a497289d8e0189b261a63
Open in Devin Desktop: https://app.devin.ai/desktop/session/6e41d4677f5a497289d8e0189b261a63?variant=devin
Requested by: @yassin-berriai