Repository navigation
Conversation
|
| stream: Final = ctx.stream | ||
| timeout: Final = ctx.timeout | ||
|
|
||
| api_key = api_key or get_secret_str("CLINEPASS_API_KEY") or litellm.api_key |
There was a problem hiding this comment.
Global credential sent to ClinePass
If litellm.api_key contains a credential for another provider and no ClinePass key is supplied, this fallback uses that credential for a clinepass/... chat completion. The inherited header builder then sends it as a Bearer token to the ClinePass host, disclosing an unrelated credential.
How this was verified: The dispatch passes the global-key fallback to the ClinePass HTTP handler, whose inherited configuration puts it in the outbound Authorization header.
| api_key = api_key or get_secret_str("CLINEPASS_API_KEY") or litellm.api_key | |
| api_key = api_key or get_secret_str("CLINEPASS_API_KEY") |
| api_key = api_key or get_secret_str("CLINEPASS_API_KEY") or litellm.api_key | ||
|
|
||
| api_base = ( | ||
| api_base or litellm.api_base or get_secret_str("CLINEPASS_API_BASE") or "https://api.cline.bot/api/v1" | ||
| ) |
There was a problem hiding this comment.
Provider policy in shared dispatcher
This wrapper selects ClinePass-specific credentials and URLs in main.py, alongside a dedicated ClinePass dispatch branch. The repository requires provider-specific code to stay under llms/. Move this setup into the provider configuration before merging.
Rule Used: What: Avoid writing provider-specific code outside of the llms/ directory. Why: This practice ensures better maintainability and reduces complexity over time. Good: ```python # Handle provider-specific logic within llms/vertex_ai/transformation.py ... (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| | [Bytez (`bytez`)](https://docs.litellm.ai/docs/providers/bytez) | ✅ | ✅ | ✅ | | | | | | | | | ||
| | [Cerebras (`cerebras`)](https://docs.litellm.ai/docs/providers/cerebras) | ✅ | ✅ | ✅ | | | | | | | | | ||
| | [Clarifai (`clarifai`)](https://docs.litellm.ai/docs/providers/clarifai) | ✅ | ✅ | ✅ | | | | | | | | | ||
| | [ClinePass (`clinepass`)](https://docs.litellm.ai/docs/providers/clinepass) | ✅ | ✅ | ✅ | | | | | | | | |
There was a problem hiding this comment.
The PR description says the linked ClinePass documentation page is only drafted for a separate repository. If this change merges before that page is published, readers following the new provider link will reach a missing page. Publish the companion page before exposing the link.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
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: 3 · PR risk: 0/10 |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
The Companion PR opened: BerriAI/litellm-docs#2049 (adds The other three failures from the first run are fixed in 1561abd:
|
ClinePass (the Cline API) is OpenAI-compatible apart from two quirks:
1. Non-streaming completions are nested under a `data` envelope --
`{"data": {"choices": [...]}, "success": true}` -- rather than returning
`choices` at the top level. Against the openai SDK this surfaces as
`r.choices` being None, not as an error. Streaming responses are *not*
enveloped, so SSE needs no special handling.
2. A bare model id is rejected with HTTP 400 "invalid model format. Expected
format: modelType/model", but LiteLLM strips its own `clinepass/` routing
prefix before the request is built, so it has to be restored.
Both are handled in ClinePassConfig, which inherits OpenAIGPTConfig and
overrides only transform_request/async_transform_request (prefix) and
transform_response (unwrap). Unwrapping rebuilds the httpx.Response around the
inner object, so the inherited OpenAI response transform -- including its
`reasoning` -> `reasoning_content` mapping, which Cline populates -- is reused
rather than duplicated.
A response transform is the reason this cannot be a declarative entry in
litellm/llms/openai_like/providers.json: JSON-configured providers are
dispatched to `_complete_custom_openai`, which builds the request via
provider_config but parses the response with convert_to_model_response_object
and never calls provider_config.transform_response. Under that dispatch the
unwrap is unreachable, so ClinePass gets an explicit branch in main.py routing
it to base_llm_http_handler.completion.
`clinepass` is still listed in openai_compatible_providers, which is what
routes an upstream 401 through _map_openai_exception to AuthenticationError;
the explicit dispatch branch precedes that list's catch-all, so membership does
not send it back to the SDK path.
Tests drive litellm.completion()/acompletion() against a mocked transport
rather than calling the transforms directly, so dispatch itself is covered --
a unit test that calls transform_response() proves the function is correct but
not that anything invokes it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 86b3486)
Addresses findings from three independent code reviews (Claude Opus 5, GPT-5.6-sol xhigh via codex, Grok 4.6 xhigh via cursor) ahead of proposing this branch upstream. Correctness: * The restored model qualifier was `clinepass/`, a namespace that does not exist. The catalog namespace is `cline-pass/` (hyphenated); `clinepass/` is LiteLLM's own routing prefix, which is stripped before the request is built. This failed silently because the API validates only the *shape* of a model id -- `totallybogus/deepseek-v4-flash` also returns HTTP 200. It is not inert, though: verified against the live API, `cline-pass/deepseek-v4-flash` resolves to `deepseek/deepseek-v4-flash` while any unrecognised namespace falls back to the date-pinned `deepseek/deepseek-v4-flash-0731`. * `_correct_truncated_finish_reason` compared an aggregate `usage.completion_tokens` against a per-choice `max_tokens`. With `n > 1` that relabels naturally-finished choices as truncated: two 60-token choices under a cap of 100 aggregate to 120 and both became `length`. Restricted to single-choice responses, where the inference is sound. * Zero, negative and `bool` caps are now rejected. `bool` subclasses `int`, so `max_tokens=True` was read as a cap of 1 and would relabel everything. Float usage/caps are now accepted rather than silently skipped. * `_unwrap_response_envelope` reached for the private `_request` attribute because `httpx.Response.request` raises `RuntimeError` instead of returning `None`. Ask for the public attribute defensively instead. * `get_models()` is overridden to return an empty catalog. ClinePass has no `/models` endpoint (404), and the inherited OpenAI implementation asked for it at the wrong path. * Dropped the `async_transform_request` override. `BaseLLMHTTPHandler` builds the body with the synchronous `transform_request` on both the sync and async paths, so it was dead code -- the same shape of bug this provider shipped once already. Pinned with a test. Packaging / CI: * Added the `clinepass` entry to `provider_endpoints_support.json` and its backup. Without it `check_provider_folders_documented.py` fails, which is a required Code Quality job -- verified failing before, passing after. * `transformation.py` did not satisfy `ruff format` under the repo's pinned ruff 0.15.3, which the changed-file CI gate runs. Reformatted. * This commit replaces the previous branch tip, which had accidentally swept in ~435 regenerated Next.js artifacts under `litellm/proxy/_experimental/`. The branch is now +883/-0 across 12 files. Honesty note on the truncation fix: re-probing the live API (streaming and non-streaming, caps of 2000 and 4000, both namespaces) could NOT reproduce the `stop`-instead-of-`length` misreport that motivated it. The correction is kept as a conservative safety net and documented as such rather than as a workaround for a currently-observable defect. Streaming is deliberately not covered; see the docstring. Tests: 41 pass (was 32). openai_like + cometapi regressions: 126 passed, 8 skipped. (cherry picked from commit c2dda4f)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit 890301f)
The tests were under tests/test_litellm/llms/clinepass/chat/, but .circleci/scripts/unit_selection.sh:62 builds the llm-other-providers shard with `find tests/unit/llms -name 'test_*.py'`, and GitHub Actions consumes the same script. Nothing errored -- the 482 lines of tests simply never ran in CI, so the PR would have looked green on tests that never executed. Moved to tests/unit/llms/clinepass/chat/, matching the layout cometapi and deepseek already use for an OpenAI-compatible provider, including the __init__.py files those directories carry, and renamed to test_clinepass_chat_transformation.py to match that same convention. 41 passed at the new location, where tests/unit/conftest.py applies rather than tests/test_litellm/conftest.py. The CI selector now lists the file.
… the OpenAI key
ClinePass implements chat completions only, but it was listed in
`openai_compatible_providers` to reach `_map_openai_exception`. That list is not
inert for routing:
* the speech branch in `main.py` matched it, and sends the request to the
provider's own `api_base` while reading the credential from `OPENAI_API_KEY` --
so `litellm.speech(model="clinepass/...")` POSTed the caller's OpenAI key to
the Cline host for an endpoint that does not exist there;
* `OPENAI_AUDIO_TRANSCRIPTION_PROVIDERS` is derived from the same list, opening
the identical hole for transcription;
* `litellm/images/main.py` consults it for image generation;
* `_add_provider_specific_params` packs unknown kwargs into `extra_body`, an
OpenAI *SDK* concept the SDK unwraps client-side. ClinePass dispatches through
`BaseLLMHTTPHandler`, which serialises optional params straight into the JSON
body -- so membership put a literal `"extra_body": {}` on the wire on every
chat request and buried genuine vendor kwargs one level deep.
Drop the membership and register ClinePass explicitly for `_map_openai_exception`
alongside `mistral` and `runwayml`, which is the established shape for a provider
with its own module. Exception mapping is unchanged and still covered by
`test_upstream_401_maps_to_authentication_error`.
Verified: speech, transcription and image generation now make zero outbound
requests and transmit no credential; unknown chat kwargs are flattened instead of
wrapped. Image generation returns an empty `ImageResponse` rather than raising,
which is pre-existing upstream behaviour for a provider with no image support --
`mistral` behaves identically.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e, UI, README Fixes from two independent reviews of the provider addition: * `main.py`: drop the provider's own `logging.post_call`. The shared `BaseLLMHTTPHandler` already logs post-call, so this double-fired callbacks and overwrote raw-response metadata; on the async path `response` is still a coroutine there, so callbacks ran before the request completed. Also adopt the `Final` annotations and `_dispatch_client_http(ctx)` used by the sibling `_complete_*` blocks. * `transformation.py`: `json.dumps(..., ensure_ascii=False)` when rebuilding the unwrapped body -- the default escapes non-ASCII to `\uXXXX`, and `OpenAIGPTConfig.transform_response` hands `raw_response.text` straight to `logging.post_call`, so observability backends received mangled text. * `transformation.py`: remove two unreachable branches. `httpx.StreamError` cannot arrive, because `BaseLLMHTTPHandler` returns `get_model_response_iterator()` without calling `transform_response` when streaming (the only paths that do call it set `stream=False`, so the body is fully buffered). The `max_completion_tokens` fallback cannot fire either, because `request_data` is the post-`map_openai_params` payload and the mapping has already rewritten that key to `max_tokens`. * `transformation.py`: modernise typing to builtin generics and PEP 604 unions, matching the repo's UP006/UP007/UP035/UP045 rules and the CometAPI house style. * UI: add `CLINEPASS` to the `Providers` enum and `provider_map`. `CredentialModal` builds its options from `Object.entries(Providers)`, so backend credential metadata alone left the provider unselectable. * README: add the provider table row, ticking only the endpoints `provider_endpoints_support.json` declares. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`model_prices_and_context_window.json` had no ClinePass entry, so provider-specific model info could not resolve: a DeepSeek row declares `"litellm_provider": "deepseek"`, which `_check_provider_match` rejects when the requested provider is `clinepass`. Prices and limits are deliberately omitted rather than guessed. ClinePass is a flat-rate monthly subscription, so inventing per-token rates would report an invented allocation as provider spend; `null` prices are not schema-valid, and the schema requires only `litellm_provider`. Omitting them follows the existing subscription precedent -- several `github_copilot/*` entries carry metadata with no price fields. Context and output limits are left unset because nothing in this repo establishes them, and DeepSeek's limits are not evidence for ClinePass's. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…g truncation `_correct_truncated_finish_reason` rewrote a single-choice response's `finish_reason` from `stop` to `length` whenever completion usage reached the requested cap. Two independent reviewers flagged it, and it is unsound: * usage reaching the cap is a token count, not a reason for stopping. A natural completion, or a stop-sequence match, can land exactly on the cap, and the heuristic then mislabels a successful response as truncated -- which can provoke spurious continuation requests in callers; * it read no explicit provider truncation signal, so it was pure inference; * it was inconsistent. Real streaming never reaches `transform_response` -- `BaseLLMHTTPHandler` delegates to `get_model_response_iterator()` -- so a streamed response kept the provider's `stop` while the identical non-streamed response was rewritten to `length`. The original incident is recorded in a comment on `transform_response` so it is not lost: ClinePass was once observed returning `stop` on a completion cut off at 4000 tokens, and follow-up probes on 2026-08-22 did not reproduce it. Tests: the three end-to-end rewrite tests become regression tests asserting an explicit `stop` survives at and above the cap, including through the `max_completion_tokens` -> `max_tokens` mapping path. The streaming test now appends a terminal finish-reason chunk and asserts it survives, which closes the streaming/non-streaming asymmetry that motivated the change. Ten tests that only exercised the removed helper's internals (non-positive cap filtering, missing usage, multi-choice guard, float coercion) are deleted with it: 46 -> 36 passing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Closes the last external-review finding (codex P2 BerriAI#9), which noted the only streaming test checked concatenated text and that there was no async-stream, tool-call-fragment, usage-preservation or outgoing-Authorization assertion, and asked for the registry/`__dict__` introspection to become behaviour checks. Added: an async streaming test; tool-call fragments split across SSE chunks reassembling intact; usage (prompt/completion/total) surviving the envelope unwrap into ModelResponse.usage; the request actually carrying `Authorization: Bearer <clinepass key>`; an explicit `api_key=` argument taking precedence over CLINEPASS_API_KEY in the environment; and an upstream 429 mapping to RateLimitError, mirroring the existing 401 test. Replaced three introspection assertions with observable behaviour: the JSON provider-registry check now proves the transform layer is actually reached, and the async-transform check proves `acompletion` evaluates the synchronous `transform_request` rather than reading `__dict__`. Kept the structural membership assertion rather than replacing it. The behavioural test proves the consequence; the one-line structural test proves the cause, with no mocking that could itself drift into passing for the wrong reason. Both are wanted, and its docstring carries why the membership was dangerous: the list is read by the speech branch (which pairs the provider's api_base with OPENAI_API_KEY), by the transcription set derived from it, by image generation, and by the extra_body wrapping that BaseLLMHTTPHandler never unwraps. 36 -> 43 passing; ruff check and format green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three failures from the first CI run on the PR, all genuine and all local: - `cost-map-guard`: litellm/model_prices_and_context_window_backup.json is a mirror of the root cost map and must be copied over whenever the root changes. I did not know that second copy existed. - `ui-unit-tests`: provider_info_helpers.test.tsx asserts every provider maps to a bundled logo *except* a known-logoless set. ClinePass intentionally ships no logo -- the `<Logo>` component falls back to a first-letter circle rather than an invented asset -- so it belongs in that set, not in the logo map. - `lint`: `ruff format` on litellm/main.py and the endpoint-guard test. Not fixed here, because it cannot be: `documentation` and `code-quality` both fail with "Environment variables read under ./litellm but mentioned nowhere in the docs: ['CLINEPASS_API_BASE', 'CLINEPASS_API_KEY']". That check resolves docs by checking out BerriAI/litellm-docs into docs/my-website (the test raises "check out BerriAI/litellm-docs into docs/my-website" when the directory is absent), so those two keys stay undocumented until a companion PR lands there. It is a genuine cross-repo ordering dependency, not something this branch can satisfy alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two lint failures, both mine, found by CI rather than locally because I had been running ruff from the repo root while the job runs it from `litellm/` and with `ruff-tests.toml` for the test tree. - PIE790: `ClinePassException` had a `pass` after its docstring. - PT011 x2: `pytest.raises(Exception)` is too broad. Replaced with an explicit try/except that is also more precise about the contract: which exception litellm raises for an unsupported endpoint is its business and may change, whereas "nothing was transmitted" is what the test exists to prove. The fixture's own AssertionError (meaning the network WAS reached) is re-raised rather than swallowed as "some exception happened", which the previous form only caught via an isinstance check afterwards. 43 tests still pass; `ruff check` clean under both of the configurations CI uses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The `lint` job's failing step was not plain ruff but `scripts/ruff_strict_gate.py`, a strict-rule budget gate that compares totals against the base commit: "ANN401: total 361 over limit 119 (this change added 2)", pointing at transformation.py:192 and :197. Those two were `logging_obj: Any` and `encoding: Any`. The base class already types them (`litellm/llms/base_llm/chat/transformation.py:350,355`), so they now use `LiteLLMLoggingObj` and `"Tokenizer | None"`, imported under TYPE_CHECKING with an `Any` fallback exactly as litellm/llms/openai/chat/gpt_transformation.py does -- importing litellm_logging at runtime from a provider module risks a circular import, which is presumably why that pattern exists. This was also codex review finding BerriAI#6, which said to "use the existing logging/tokenizer types as current CometAPI and Perplexity transformations do". I had read that as satisfied by the builtin-generics work and it was not. `scripts/ruff_strict_gate.py --base upstream/main` now reports "OK: every strict rule is within its codebase ceiling"; ruff clean under both CI configurations; 43 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two upstream guards did not know about ClinePass, and both caught something real. `test_every_cost_map_provider_is_registered[main|backup]`: its failure message says exactly what a new provider owes — "a `<provider>_models` set in litellm/__init__.py, filled in _populate_provider_model_sets and listed in _build_models_by_provider". Adding the cost-map entry without that leaves the provider's models unreachable through `models_by_provider`. Wired at all four sites, mirroring `deepseek`. Verified with LITELLM_LOCAL_MODEL_COST_MAP=True, since `model_cost` is fetched remotely by default and the remote map naturally has no clinepass yet: `clinepass_models` and `models_by_provider["clinepass"]` both resolve to `clinepass/deepseek-v4-flash`. `test_a_provider_without_a_handler_maps_by_the_upstream_status[clinepass-403]`: expected PermissionDeniedError, got APIError. The test derives its no-handler list as `LlmProviders - PROVIDERS_WITH_A_HANDLER - aliases - openai_compatible_providers`. Removing clinepass from `openai_compatible_providers` (the P1 fix) dropped it into the no-handler bucket while the explicit `_map_openai_exception` registration gave it a handler, so the test's model of reality went stale rather than the code being wrong. `clinepass` now sits in `PROVIDERS_WITH_A_HANDLER` beside `mistral` and `runwayml` — the two providers whose registration shape this change copied. Not addressed, and not mine: `proxy-infra`'s test_every_model_in_the_prisma_schema_is_a_renderable_span_table. This branch touches no prisma schema, span, or proxy-db file — the full changed-file list is the clinepass provider, its tests, the two cost maps, the provider-support JSONs, the UI helpers and README. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
clinepass is mapped by _map_openai_exception, the same path as openai, mistral and runwayml, so a full context window and a content-policy block reach the caller as ContextWindowExceededError / ContentPolicyViolationError. The per-provider tests added upstream pin that set, and clinepass was missing from it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
_complete_clinepass fell back to litellm.api_key when no ClinePass credential was configured, so a caller's general-purpose (usually OpenAI) key was sent to the Cline host as a Bearer token. get_llm_provider already resolves the key without that fallback; use what it resolved. Adds a regression test that asserts the sentinel key never leaves the process, and checks the request actually reached the transport so it cannot pass vacuously. Also suppress three basedpyright reportUnknownArgumentType diagnostics this change introduced (the untyped model cost map key and the dispatch context's messages/headers), which tipped the per-rule budget by +3. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The docstrings said BaseLLMHTTPHandler never unwraps extra_body and that it would reach the API verbatim. It pops extra_body and merges it back into the request body, so the wire body is identical whether or not clinepass is in openai_compatible_providers, and the body-level assertions cannot detect that drift. Say so, and point at the checks that do: the structural membership test, the transcription half, and the params-level test (which fails when the provider is listed, because the kwarg lands nested under extra_body). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
5068b9a to
08408fe
Compare
…ine budget The mutable-collection annotations in the ClinePass chat config are all dictated by the OpenAIGPTConfig signatures they override (and the request body handed to its transform_request). Mark each with the repo's own '# mutable-ok: matches the dict-typed base-class signature' so the change adds none to the LIT001 budget. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…der policy Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…n routing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve conflicts with upstream's public-name refactor: - get_llm_provider_logic: call ClinePassConfig().get_openai_compatible_provider_info, matching the renamed Perplexity call beside it. - main: _complete_http_provider takes CompletionDispatchContext. - ClinePassConfig gains the public get_openai_compatible_provider_info forwarder that upstream added to every other config defining the private one. - test_exception_mapping_utils: clinepass maps 402 through _map_openai_exception like openai/mistral, so it joins their APIError deviation for 402. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Upstream's strict ruff gate bans typing.Any (TID251) and fails when the total grows past the merge base. The alias is only meaningful to type checkers, so the runtime fallback can be object. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The merge of upstream/main brought the LIT010/LIT011 gates and a per-rule basedpyright ceiling. Declare ClinePass locals Final, stop rebinding parameters, replace bare dict annotations with dict[str, object], and parse the response envelope through a pydantic TypeAdapter instead of Any.
|
The only red check here is |
Adds ClinePass (the Cline API) as a LiteLLM provider, routed as
clinepass/<model>.ClinePass is an OpenAI-compatible endpoint with two wrinkles that need real code rather than a declarative entry: it nests non-streaming completions under a
dataenvelope, and it expects acline-pass/qualifier on outbound model ids. The JSON provider system can't express the first, because its OpenAI-SDK dispatch path never callstransform_response, so this is a provider module with an explicit route to the generic HTTP dispatcher. Provider-specific credentials, URLs and transforms live inllms/clinepass/What's here
litellm/llms/clinepass/—ClinePassConfig(OpenAIGPTConfig), envelope unwrap, model-prefix handlingbase_llm_http_handler.completionProviders/provider_mapentries, README provider rowmodel_prices_and_context_window.jsonregistrationtests/unit/llms/clinepass/(the CI-selected tree)Notes a reviewer may want up front
clinepassis deliberately not inopenai_compatible_providers. An earlier revision added it there to reach_map_openai_exception, assuming membership was inert for routing. It isn't. That list is also read by the speech branch inmain.py— which pairs the provider's ownapi_basewith a key chain that falls back toOPENAI_API_KEY— solitellm.speech(model="clinepass/...")sent the caller's OpenAI credential to a third-party host, for an endpoint ClinePass doesn't implement.OPENAI_AUDIO_TRANSCRIPTION_PROVIDERSis derived from the same list, andlitellm/images/main.pyreads it too. Exception mapping is instead registered explicitly besidemistralandrunwayml.tests/unit/llms/clinepass/test_clinepass_endpoint_guard.pyasserts speech/transcription/image make zero outbound requests and transmit no credential.Chat authentication uses only the request's
api_keyorCLINEPASS_API_KEY, never global or other-provider keys. The provider rejects moderation, realtime client secrets, transcription sessions and SDP calls before HTTP dispatch, including when a ClinePass key is supplied. The managed Responses WebSocket-to-chat bridge no longer borrows global or OpenAI credentials, and provider-switching frames do not inherit another provider's connection key or URL. Authenticated managed sockets are pinned to the connection-authorized model or alias; changing models requires a new authorized connection. Sync, async, streaming and real managed-WebSocket tests inspect outbound authorization; unsupported-endpoint tests require a specific error and zero outbound requests. Mutation checks detect restored credential fallbacks, removed endpoint rejection and removed model pinningUnknown kwargs remain flat at the optional-params level. This is distinct from the wire body:
BaseLLMHTTPHandleralso mergesextra_bodybefore serializationNo pricing is claimed. ClinePass is a flat-rate subscription, so the catalogue entry carries
litellm_providerandmodeonly. Inventing per-token rates would report fabricated spend, andnullprices aren't schema-valid. This follows the existinggithub_copilot/*precedent of metadata without price fields.Docs: the README row and
provider_endpoints_support.jsonpoint atdocs.litellm.ai/docs/providers/clinepass, the final URL for the page in BerriAI/litellm-docs#2049. The documentation and code-quality jobs fail only because upstream docs do not yet documentCLINEPASS_API_KEYandCLINEPASS_API_BASE. All documentation validators pass locally with the companion page present. Merge the companion PR before this oneVerifying
uv run --no-sync pytest tests/unit/llms/clinepass/ -q # 105 passedThe commits are fine to squash-merge
The change was reviewed by two independent tools before submission; their findings are all addressed, including dropping a
finish_reasonheuristic that inferred truncation from usage reaching the cap — unsound, since a natural completion can land exactly on the cap, and it didn't apply to streaming.