feat: route scanners through LiteLLM SDK for multi-provider support - #598
feat: route scanners through LiteLLM SDK for multi-provider support#598prodmanpd wants to merge 1 commit into
Conversation
|
Thanks @prodmanpd for this — routing every scanner through LiteLLM is a great capability win (100+ providers, native Bedrock/Vertex auth, one config via The design is solid: the
None of these are blockers for the design. My main ask is (1)/(2): confirm CI install + import works under the pinned range. Nice work overall! |
|
I'm concerned about the supply-chain boundary of this LiteLLM integration. The upstream incident report confirms compromised PyPI releases 1.82.7/1.82.8 in March 2026; those releases were removed. At reviewed head That upper bound permits later 1.x releases beyond a single tested version when dependencies are resolved afresh or upgraded. Please document tested versions, artifact verification, and whether installation of the SDK is opt-in. A disabled application feature cannot prevent an installed malicious Python-startup hook. This is one of 49 observed LiteLLM integration PRs from the same account, whose author acknowledged the cross-project effort. The pattern prompted this review; it does not establish malicious intent or connect the author to the incident. The consolidated questions and corrections are in YouDub #130; this PR's review snapshot preserves the revision and scope. |
boy-hack
left a comment
There was a problem hiding this comment.
Thanks @prodmanpd — the design holds up: the openai/-prefix + api_base trick keeps the OpenRouter default byte-for-byte compatible, drop_params=True is the right lever for cross-provider configs, and the LiteLLMAsyncClient shim is a minimal, clean surface. Tests covering both sync and async paths plus a live E2E through a proxy are strong.
Re my earlier asks (1)-(4): the transitive openai/litellm versions resolve cleanly (1.98.0) and the out-of-scope openai consumers are correctly left alone, so those are satisfied. Two things I'd like tightened before merge, prompted partly by @liuzhao1225's supply-chain note:
-
Pin the tested version (supply-chain hygiene). The compromised PyPI releases (1.82.7/1.82.8, March 2026) were removed, and your floor
>=1.89.0never reaches them — so this PR does not pull the bad versions. But the upper bound<2.0.0still permits any future 1.x release to be resolved fresh, which is exactly the drift the supply-chain reviewer is worried about. Recommendation: pin to the version you actually tested and verified — e.g.litellm==1.98.0(or at least>=1.98.0,<1.99.0) — so installs are reproducible, and add a one-line note in each scanner's README that thelitellmdependency should be installed from a verified index. That directly answers the "document tested versions / artifact verification" ask without weakening the feature. (For deployment you could also recommend hash-pinning.) -
Confirm minimal-install import + cold-start.
import litellmat module top now runs in every scanner process startup. Please confirmimport litellmdoesn't fail or add noticeable latency under each scanner's minimalrequirements.txtinstall (litellm pulls a lot of transitive deps) — especially formcp-scan/skill-scanCLI cold starts. The CI install job passing is the proof point here.
On the broader "49 cross-project LiteLLM PRs" framing: that's context about the reviewer's scope, not evidence of malicious intent in this change. The integration itself is legitimate and a clear capability win. Acting on point 1 keeps us safe regardless.
Also still worth a quick confirm: the non-streaming chat() path still calls litellm.completion inside asyncio.to_thread alongside chat_async — is chat() still exercised, or is it dead weight now? If unused, dropping it reduces surface.
Nice work overall — let's tighten the pin and confirm the install path, then this is good to go.
|
@boy-hack thanks for the detailed analysis, will tighten this. |
|
Thanks @prodmanpd — following up on the supply-chain thread raised by @liuzhao1225 and my earlier review. The good news: the floor Since you said you'd tighten this, here's the concrete ask before I'm comfortable approving:
Two smaller things from my first pass that still stand:
Net: the design is good and the (Review comment only — not merging.) |
Summary
Routes every Python scanner's LLM calls through the LiteLLM SDK instead of a raw
openaiclient, so AI-Infra-Guard can talk to 100+ providers (OpenAI, Anthropic, Gemini, Bedrock, Vertex, Azure, Groq, Mistral, self hosted, or a LiteLLM proxy) through one interface.base_urlkeep working byte for byte.openaiclient plusbase_urlcannot reach without standing up a separate gateway.drop_params=Trueso one config works across providers that reject each other's generation params.How routing works (
to_litellm_params)base_urlset (default, for example OpenRouter or any OpenAI compatible gateway): the request is sent as a custom OpenAI compatible endpoint (model="openai/<model>",api_base=<base_url>). The wire request is identical to the previousopenai.OpenAI(base_url)call, so existing configs are unaffected.base_urlempty: the model string is passed through for native provider routing (anthropic/...,bedrock/...,gemini/...), resolving credentials from that provider's own env vars, with no gateway or proxy.Changes
agent-scan/agent_scan/utils/llm.py:LLMnow callslitellm.completion(stream=True); LiteLLM exception handling;to_litellm_paramshelper.mcp-scan/mcp_scan/utils/llm.py: same for the syncLLM; addsLiteLLMAsyncClient, a shim compatible with the OpenAIAsyncClientoverlitellm.acompletionfor the red team engine.mcp-scan/mcp_scan/redteam/{orchestrator,evaluator,attacker,target}.py: the async red team path now usesLiteLLMAsyncClient(call sites unchanged; response shape identical).skill-scan/skill_scan/utils/llm.py: same for the syncLLM(preservesstream_optionsusage and custom headers viaextra_headers).*/pyproject.toml,*/requirements.txt: addlitellm>=1.89.0,<2.0.0*/pytests/test_llm_request.pyupdated to the LiteLLM seam;mcp-scan/pytests/test_redteam_litellm_client.pyadded.Tests
1. Unit tests, 15 pass (litellm 1.98.0, openai 2.54.0 transitive):
Coverage: OpenAI compatible routing (
openai/prefix plusapi_base), native passthrough whenbase_urlblank,drop_params=Truepresent, blank API key sent asNone(so LiteLLM falls back to provider env vars),stream_optionsandextra_headerspreserved, and the async red team shim forwarding tolitellm.acompletion.2. Lint (repo ruff config, py312, line length 100): authored files clean:
3. Pin resolves cleanly:
pip install "litellm>=1.89.0,<2.0.0"gives litellm 1.98.0 plus openai 2.54.0.4. Live E2E (real code path through a LiteLLM proxy): all three scanner LLM paths exercised end to end against a live model (
gpt-4.1-minivia a LiteLLM proxy), deterministic prompt "Reply with exactly: OK":This proves the full chain for both the sync
litellm.completionpath (agent-scan, mcp-scan, and skill-scan share it) and the asynclitellm.acompletionred team shim: request routed through LiteLLM, provider returned, response and usage parsed back through each scanner's existing code.Risk and compatibility
openairemains available (LiteLLM depends on it); no scanner imports it directly anymore.Example usage