feat(opencode): add opencode_go and opencode_zen first-class providers - #1
feat(opencode): add opencode_go and opencode_zen first-class providers#1streber42 wants to merge 1106 commits into
Conversation
db3e8a3 to
6e00597
Compare
6e00597 to
58b27a6
Compare
Greptile SummaryThe PR adds first-class OpenCode Go and Zen providers, selecting OpenAI Chat Completions, Anthropic Messages, or OpenAI Responses according to model metadata.
Confidence Score: 5/5The PR appears safe to merge because no blocking failure remains. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| litellm/main.py | Adds the central OpenCode dispatch path that selects the provider surface and wire-format handler. |
| litellm/llms/opencode/chat/messages_transformation.py | Implements the Anthropic Messages-compatible transformation and model classification. |
| litellm/llms/opencode/chat/transformation.py | Implements the OpenAI Chat Completions-compatible OpenCode transformation. |
| litellm/llms/opencode/config.py | Selects the appropriate OpenCode configuration according to surface and model. |
| litellm/llms/opencode/go/responses/transformation.py | Implements OpenCode Go’s OpenAI Responses-compatible transformation. |
| litellm/llms/opencode/zen/responses/transformation.py | Implements OpenCode Zen’s OpenAI Responses-compatible transformation. |
| ui/litellm-dashboard/src/components/add_model/AddModelForm.tsx | Adds both OpenCode surfaces to the dashboard’s model-creation flow. |
| model_prices_and_context_window.json | Registers OpenCode model capabilities, pricing metadata, and response modes. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[opencode_go or opencode_zen request] --> B{Model classification}
B -->|supports_anthropic_messages| C[Anthropic Messages transformation]
B -->|mode: responses| D[OpenAI Responses transformation]
B -->|default| E[OpenAI Chat Completions transformation]
C --> F[OpenCode gateway]
D --> F
E --> F
Reviews (4): Last reviewed commit: "fix(anthropic): gate mid-turn system pre..." | Re-trigger Greptile
| from litellm.llms.base_llm.chat.transformation import BaseLLMException | ||
| from litellm.llms.opencode.common_utils import OpenCodeException | ||
| from litellm.secret_managers.main import get_secret_str | ||
| from litellm.types.router import GenericLiteLLMParams |
There was a problem hiding this comment.
Tighten provider boundary typing
The new provider modules use Any, unparameterized dict and list annotations, and an untyped configuration factory across request and configuration boundaries. This prevents static checking of substantial portions of the provider implementation; replace the coarse annotations with the existing concrete request, parameter, header, and configuration types, and remove the unnecessary explanatory comments covered by the same repository guideline.
Context Used: CLAUDE.md (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!
58b27a6 to
35554ea
Compare
…l monitors The monitors are separate servers with their own password, so the data node's Entra or IAM token has no standing there. Dropping the provider only when a Sentinel password was configured left it in place for unauthenticated monitors, where redis-py sends it as an AUTH the monitor rejects and async Sentinel discovery fails.
* test(ci): serve /moderations from the canned OpenAI mock The otel proxy E2E job points its `openai/*` wildcard deployment at the canned mock, and BerriAI#37492 made `get_model_list` agree with `get_available_deployment` on bare model names. /moderations now resolves `omni-moderation-latest` to that wildcard deployment the way /chat/completions already did, so the request lands on the mock, which never implemented the route and answers a bare 404. Add /moderations and /v1/moderations to the mock, returning an OpenAI-shaped response with one result per input item. * style(ci): annotate the new moderations locals as Final
…/litellm into litellm_lit5879_semantic_cache_embedding_timeout
The edit model is reached through the image generation path with fal's image_urls param; /v1/images/edits is not wired for fal_ai and errors. Point supported_endpoints at /v1/images/generations and say so in the entry notes.
…ls that still exist (BerriAI#37733) * test: point the live web search, groq and vertex image suites at models that still exist Three CircleCI jobs on the staging-to-main promotion are red because the models their live suites call have been retired by the providers, not because anything in litellm changed. openai/gpt-4o-search-preview now answers "has been deprecated" (its dated id gpt-4o-search-preview-2025-03-11 carries deprecation_date 2026-07-23), so the two web search conformance tests and the web search cost tracking test move to gpt-5-search-api, the current search model. It keeps mode chat, supports_web_search and a search_context_cost_per_query map, so the cost assertion still resolves. groq/llama-3.1-8b-instant reached its deprecation_date of 2026-08-16 and Groq answers "does not exist or you do not have access to it". It follows groq/llama-3.3-70b-versatile to groq/openai/gpt-oss-120b, the same replacement PR BerriAI#37422 already picked. The proxy config that job boots routes on a */* wildcard, so no config change is needed. vertex_ai/imagen-3.0-fast-generate-001 404s with "was not found or your project does not have access to it". Google retired the whole Imagen family across Vertex and the Gemini API, so there is no Imagen id left to point at. The class is removed rather than repointed: Vertex image generation is already covered live by TestVertexAIGeminiImageGeneration on vertex_ai/gemini-2.5-flash-image, and the Imagen request and response transformations keep their offline coverage in tests/test_litellm/llms/vertex_ai/image_generation/. Only live call sites move. Remaining references to the old ids sit in offline cost-map and transformation tests, where the string is a lookup key and no request leaves the process. * chore(lint): ratchet the TQ005 ceiling down to the count this branch reached Removing the retired TestVertexImageGeneration class cleared one TQ005 violation, so the gate demands the limit come down with it. make lint-budget-update only lowers a limit by the delta a branch cleared, and this ceiling already sat 2 above the base count, so the tool landed on 2834 while the gate wants the limit at or below the 2832 this branch reached. The remaining 2 are that stale headroom, which is exactly what the gate is asking to reclaim.
Cognition serves an OpenAI-compatible /v1/chat/completions endpoint, so it has been onboarded as custom_llm_provider: openai. That books its traffic as OpenAI, which means OpenAI-specific cost discounts and provider-level reporting apply to it. Registers cognition through the JSON provider registry: a providers.json entry with COGNITION_API_KEY and COGNITION_API_BASE, LlmProviders.COGNITION, the constants.py provider lists, cost map entries for swe-1.6 and swe-1.7, the provider endpoints matrix, the dashboard provider fields, and tests. JSON providers can now also be resolved from their base url alone, so an api_base pointing at a known provider no longer falls through to an unresolved provider.
…_tokens fix(model-costs): correct gpt-5.6 max input tokens to 922k
…RL (BerriAI#37691) The read replica never received the operator's DB pool settings, so its Prisma pool fell back to `num_physical_cpus * 2 + 1` and the configured cap was not enforced. Both startup paths now pass the same params to the reader: the CLI, and the componentized entrypoints that go through `DatabaseURLSettings.apply_to_env`. Only pool and timeout params are inherited, through a single allowlist both paths share. Anything that decides which tables a query resolves against stays on the writer, including entries smuggled in through `database_extra_connection_params`, so a writer `search_path` cannot repoint reader queries. Params the operator pinned on the replica URL still win. Co-authored-by: Yassin Kortam <yassin.kortam@gmail.com> Co-authored-by: yassin <yassin@berri.ai> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
feat(fal_ai): add gpt-image-2 image generation support
…tes (BerriAI#37700) SCIM roster writes were swallowed, so a group or user push returned 200 while the team roster never received the membership. Surfacing the failure fixes that, but aborting on the first failed write leaves the rest of the batch unattempted on top of unrolled-back, which is worse than what it replaces. Every roster write in a reconciliation is now attempted, and the ones that did not land are reported together, naming each failed add and remove. Rollback would be the other option and it is not safe here: the compensating write can fail too, and it can strip a membership that pre-dated the push. SCIM reconciliation is idempotent, so a named partial failure is what the IdP's next push needs to close the gap. The reported status still follows the failures, so a unanimous 404 stays a 404 and only a batch whose failures disagree falls back to 500. Co-authored-by: yassin <yassin@berri.ai> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…credential_provider fix(redis): apply Azure AD and GCP IAM auth to every async client path
…erriAI#37744) a369cb0 made the chat-to-responses bridge return the routing prefix on the model it passes to responses(), so responses() re-resolving the provider is a no-op instead of stripping a second prefix. It updated the bridge's own unit tests but not this one, which still asserted the stripped id and has been failing llm_translation_testing since that change landed. The provider still receives gpt-5.4: responses() strips the openai/ prefix on its own resolve, one layer later than this assertion used to sit. The stale comment claiming the prefix is stripped before routing goes with it.
…ow scan (BerriAI#36497) Every pod schedules the budget reset job, so a fleet re-read the whole due population and wrote it back against one Postgres at the same calendar boundary, multiplying a single sweep by its replica count. The job now takes the shared PodLockManager lease, so one pod sweeps per tick. A deployment with no Redis keeps its previous behavior, and a Redis that cannot answer sweeps unguarded rather than stranding every expired budget at its cap. The per-window scan read every row carrying budget_limits in one statement, so its cost grew with the deployment's key count. It is now keyset-paginated and walks to the end of the table on every sweep. A per-run cap would need a resume position, and no pod can hold one because the lease rotates between ticks, so the strictly advancing cursor is what terminates the walk. Found and updated rows were also JSON-serialized into the service hook's metadata and into debug lines on every chunk, on the event loop, whether or not anything consumed them. The hooks now carry counts, and the debug payload is deferred until a record is actually emitted. Resolves LIT-4793 Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…models feat(proxy): add admin toggle to block requests for models without pricing
…tring The passthrough tests and their coverage registry rows pointed at the internal ticket id, which does not resolve for anyone following a link from status.litellm.ai. Each test docstring and registry rationale now names the GitHub issue it pins: BerriAI#36086 for the two prefix routing cases, BerriAI#36087 for the file list cursors, BerriAI#36523 for streamed Responses cost, and BerriAI#36646 for embeddings spend.
…itellm_lit_5870_passthrough_e2e_pins_wt
The swe-1.7 rates were carried over from the closed prior attempt and match SWE-1.7 Lightning, 5x the SWE-1.7 Max and Medium rates the vendor publishes. swe-1.6 was already on the standard tier, so the two entries disagreed with each other. Both now read 0.5 in, 2.5 out, 0.2 cached per million tokens. Also drops the redundant registry comment in constants.py.
…consolidated_20260820 fix(model_prices): consolidate eleven open registry audits into one changeset
…upport The swe-1.7 rates were briefly lowered to the standard tier. The docs page records the API-served swe-1.7 as the Cerebras-served Lightning tier, so put the matching rates back rather than have the cost map and the docs disagree. Cognition also answers /v1/responses through the chat-completions bridge, the same as every other provider in the JSON registry, so the endpoints support matrix should say so instead of under-declaring it.
cache_read_input_tokens and cache_creation_input_tokens are pydantic extras on Usage, not declared fields, so filling them in created keys that were not there before rather than replacing a None. Readers that test for presence then took the new zero as authoritative: the spend log writer skipped its own copy from prompt_tokens_details, turning a real cache read of 500 into 0, and the prometheus provider cache counters stopped incrementing. Carry the prompt_tokens_details counts up before defaulting to zero, so a partial row reports the same cache numbers a complete one does. Renamed the helper to say what it now does.
…ed_session_lookup fix(responses): keep the conversation when chaining previous_response_id on the bridge
The lint job installs its dependencies from scratch on every run. That step measures 2.8 minutes of a job whose p50 is 9.5, and lint is the slowest required check on 9 of the last 10 merged staging PRs, so it sets the critical path for the whole PR. _test-unit-base.yml already caches ~/.cache/uv and .venv keyed on uv.lock. This mirrors that block. The key carries its own `lint` namespace rather than sharing the unit tier's: the two jobs sync different group sets (proxy-dev + e2e-dev here, ci + proxy-dev + four extras there), so a shared .venv entry would be pruned and rebuilt on alternating runs.
* perf(ci): fan the budget checkers out across cores check_type_discipline.py and check_test_quality.py each walk a few thousand files and parse every one, single-threaded. In the lint job those two steps measure 2.3 and 1.6 minutes, second and third behind dependency install, and lint is the slowest required check on 9 of the last 10 merged staging PRs. check_file is already pure per-file work, so the walk fans out over a process pool with no change to what either rule reports. Callers sort, which is what keeps output order stable when results land out of order. Runs below PARALLEL_MIN_PATHS stay serial rather than pay for process startup, and the worker count is capped so a large runner does not oversubscribe. Measured locally over the same trees, output byte-identical both times: type-discipline 17.8s -> 3.0s over litellm/ (78,768 report lines), test-quality 14.4s -> 2.3s over tests/ (6,321 report lines), per-rule counts unchanged. * test(ci): type the fan-out helpers and skip the comparison on one core
…rriAI#37785) * ci: port the Postgres suites off CircleCI onto service containers proxy_behavior_tests, proxy_security_tests and schema_migration_check were near-identical CircleCI jobs: a Postgres sidecar, a schema seed, and one pytest tree each. They ran nowhere else, and CircleCI holds none of the branch ruleset's required checks, so the signal they produced gated nothing. test-postgres.yml runs the same three trees on a Postgres service container as one matrix, keeping each suite's own seeding rather than normalising it: the behavior and security trees keep `prisma db push`, and the migration tree keeps an empty database, which is what it needs to apply every committed migration itself. Their CircleCI definitions and workflow entries go with them, taking the config from 47 jobs to 44. assert_ci_coverage.py stays green: dropping the new workflow fails the census on exactly these trees, so the coverage moved rather than went missing. auth_ui_unit_tests is deliberately left behind. Ported, two of its tests fail because prepare_metadata_fields refuses enterprise-only keys without LITELLM_LICENSE, which exists as a CircleCI project variable and has no GitHub Actions secret. Creating that secret is a human action, so the job stays on CircleCI until it exists rather than shipping a red shard or quietly deselecting the two tests. * chore(ci): drop the narrative header from test-postgres.yml
…7787) * feat(ci): gate patching of SDK internals in tests as TQ008 TQ002 catches the narrowest symptom of the suite's dominant mocking idiom, patch X then assert only that X was called. The idiom itself is wider: tests reach for litellm's own functions instead of faking the wire, so they pin how the code is wired rather than what it does, and a test that patches internals but makes weak real assertions trips nothing today. TQ008 counts patch targets rooted at `litellm`, both the dotted string form and the attribute chain handed to patch.object, and ratchets like every other rule. Mocking anything outside the SDK is untouched: respx, httpx transports and third-party clients do not trip it, which is the point, since those are the patterns this is meant to move the suite toward. Seeded at 9,643, in line with the ~9.4k patch sites an independent grep found in the mirror. The burn-down horizon is long; the value here is stopping the flow rather than clearing the stock. Five existing rule tests patched `litellm.completion` incidentally and now report TQ008 alongside what they were pinning. Their expected values are updated to the accurate pair rather than loosened, so they keep failing on a regression in either rule. * test: add TQ008 to the shipped-budget rule canary * fix(ci): resolve imported SDK names in TQ008 patch.object(handler.OpenAIChatCompletion, ...) after a from-import reaches the same internal as the dotted string form, but the rule only saw the bare local name and let it through. Import bindings are now resolved to the path they stand for, so the aliased, renamed and from-imported forms all read alike and the reported target is the real one. That is 1,496 patches the ratchet could not see, so the TQ008 limit moves from 9,643 to 11,139. Third-party names and locals with no SDK import behind them stay unflagged.
codecov.yaml has carried an `Enterprise` component scoped to `enterprise/**` since it was written, and it has never received a line of data. Every one of the 19 coverage invocations across the unit base, the MCP workflow and the CircleCI config passes `--cov=./litellm` and nothing else, so 11,203 lines of paid-customer code sat outside the measured universe while the reported number described only the rest. litellm-enterprise is a uv workspace member and a direct dependency, so every job that syncs already has it installed and importable; only the measurement was missing. Measured on tests/test_litellm/enterprise, the shard that exercises this code: 0 enterprise files in the report before, 142 after, at `enterprise/...` paths that match the component's existing glob. That shard alone puts enterprise at 30.8%, which nudged its own total from 24.09% to 24.20% rather than down. The aggregate direction across every shard is not knowable until they all report, and a drop there is the instrument working, not a regression.
The allowlist recorded eight files in tests/local_testing, 118 tests, that every job globbing that directory then deselects: local_testing_part1 and part2 carry `-k "... and not caching and not cache"`, and the other three keep one unrelated keyword each. They counted as covered while running nowhere. Five of the eight need nothing. Measured with no provider credentials and no Redis: test_cache_preset_key, test_caching_handler, test_prompt_caching, test_responses_stream_cache_keys and test_unit_test_caching pass, 45 tests together, and they now run as a caching-local shard. The other three stay allowlisted with what they actually need recorded rather than a question: test_caching wants Redis and a provider key for 37 of its 65, disk-cache wants OPENAI_API_KEY for 2 of 4, gcs-cache wants GCS credentials for all 4. Taking them off the allowlist exposed a gap in the slice guard itself: it reasoned only about CircleCI `-k` expressions, so a file every slice drops read as unrun even when a workflow names it outright. It now credits workflow test-paths the way the census already does, and only workflows, so a tree only CircleCI globs is still reported.
… SQL (BerriAI#37791) * fix(ci): make the migration DDL guard run, and stop it reading comments as SQL TestMigrationSQLIdempotency requires guarded DDL across litellm-proxy-extras and has never run in any job, so the convention eroded quietly. Four of its assertions fail today, and it was allowlisted rather than wired up because fixing the migrations is not an option: Prisma checksums an applied migration, so editing one breaks `migrate deploy` for every existing install. Two things were wrong with the guard itself. It scanned raw lines, so Prisma's own `-- CREATE INDEX CONCURRENTLY ...` explanations counted as the statements they describe, which is two of the reported migrations. And it had no way to say "these predate the rule", so the only options were editing immutable files or leaving the whole file unrun. Comments are now stripped before matching, on the drop-column rule too, and the migrations that already violate are named once in _PRE_GUARD_MIGRATIONS. The rules bind everything after them, so a new migration with bare CREATE TABLE, ADD COLUMN, CREATE INDEX or an unguarded ADD CONSTRAINT now fails a check instead of landing unnoticed. That set is 14 migrations, not the 13 previously recorded, measured after comment-stripping. It can only shrink: a test fails if an entry names no migration on disk, and another fails if an entry no longer violates anything. The file now runs as a proxy-extras shard and comes off the coverage allowlist. * fix(ci): strip block comments in the migration guard too Prisma opens a destructive migration with a /* Warnings: You are about to drop the column ... */ header. Nothing in the tree trips a rule on that text today, but it is prose about a statement rather than the statement, and the line-comment fix left the class open. Bodies are blanked rather than removed so the reported line number still points at the real statement.
tests/enterprise is 13 files and 244 tests that only CircleCI runs, and CircleCI gates nothing: it triggers on PR labeled events, none of its jobs are required, and red runs get merged past. So the suite that covers the enterprise package's guardrails, auth and management endpoints has had no say in whether a change lands. Measured on 2026-08-21 with every credential stripped from the environment: 240 passed, 4 skipped, nothing failed. It needs no provider key, so it can be a required shard rather than a scheduled lane, unlike the other CircleCI suites in this group, which each carry a live-API minority. The CircleCI job is removed in the same commit so the suite runs once, not twice.
…erriAI#37804) proxy-endpoints and proxy-infra are the unit tier's critical path at 358s and 325s of pytest, measured on staging 2026-08-21, and both run two xdist workers on a four-vCPU runner. proxy-server already runs four. This is the cheaper half of splitting them: no second job, so no second setup to pay for.
…rovider maps to (BerriAI#37807) `exception_type` decides the class and status a caller sees for every provider failure, across 190 raise sites, and the tests for it were written one incident at a time. Nothing said what a plain 401 from any given provider should be, so mutating a raise site went unnoticed: swapping the class at each of the 190 in turn, the mapped test file caught 15. Adds two tables asserted end to end through `exception_type`: 25 providers by the 9 upstream statuses, and the three error shapes the router branches on (a full context window, a content policy block, a timeout). The same 190 mutants now fail 97 of them. The tables record today's behavior, uneven where it is uneven. cloudflare, ollama and vllm map no status at all, so every failure reaches the caller as a 500. A full context window is recognised by 15 of the 25, and a content policy block by 11, which bounds where `context_window_fallbacks` and the content policy retry policy can fire.
e464d3e to
9feeaef
Compare
…s to litellm_team (BerriAI#37918) * fix(terraform): add soft_budget, tags, and soft_budget_alerting_emails to litellm_team The team resource rejected soft_budget and tags at plan time and had no way to express the list-valued metadata.soft_budget_alerting_emails the proxy reads for soft-budget alerts, even though /team/new and /team/update accept all three. Add the attributes, forward them in buildTeamData (alert emails merged under metadata, where the proxy stores them), and send the full metadata map whenever either half changes because /team/update replaces metadata wholesale. Read was decoding /team/info as if the team fields were top-level, but the proxy nests them under team_info, so every attribute silently fell back to prior state. Decode the envelope and split the proxy's metadata back into tags / soft_budget_alerting_emails / string metadata, dropping the server-managed team_member_budget_id. Verified with OpenTofu plan/apply against a live proxy: the attributes are accepted, land on the proxy, refresh into state, re-plan clean, propagate on update, and clear when removed from HCL. * fix(terraform): clear litellm_team.soft_budget in state when the proxy returns null Read only wrote soft_budget when the proxy returned a value, so a soft budget cleared outside Terraform stayed in state and never surfaced as drift. Set it from the response unconditionally so a null clears it.
…7985) The virtual key shown after creating a key sits in a div with a hardcoded #f8f8f8 inline background, so in dark mode the box keeps the light background while the key text inherits the light foreground color, leaving the key nearly unreadable. Swap the inline styles for the bg-muted and text-foreground tokens, which resolve per theme.
…l flow (BerriAI#37986) The tooltip popup is an inline-flex row, so the four sibling blocks passed as a fragment laid out side by side in four columns. Wrap them in a single flex-col container instead. The inline code samples also used bg-muted, which is defined against the page surface, not the inverted tooltip surface, so they rendered as near-white chips carrying near-white text. Tint them from the popup's own token instead.
…mited (BerriAI#37916) * fix(ui): render team and org tpm/rpm limits of 0 as 0 instead of Unlimited A tpm_limit or rpm_limit of 0 is a hard block on the backend (every request 429s) and only null means unlimited, but the team and organization views rendered both as "Unlimited" (and a team-member limit of 0 as "No Limit") because every display site used a falsy || fallback. The team member edit dialog also seeded its form with `tpm_limit || null`, so opening Edit Member on a member stored with 0 and clicking Save sent null to /team/member_update and silently turned the hard block into unlimited Every limit display site in TeamInfo, organization_view, the organizations list cell and the team members table now uses a nullish check, and both member form seeding paths keep 0 for max_budget_in_team, tpm_limit and rpm_limit. Regression tests cover each site and the existing memberFormValues test that asserted 0 -> null is flipped to assert 0 survives Resolves LIT-5760 * test(ui): assert a stored 0 member limit survives an untouched save The EditMembership integration test named the old 0 -> null collapse as the expected payload, so the related-tests CI job went red once the form kept 0. It now asserts 0 survives and only the empty budget_duration collapses to null. The TeamMemberTab fixture is built with a map instead of mutating the nested membership
…ating (BerriAI#37968) Regenerating a key from the key info page left the ?key= query param on the old hash, so dismissing the dialog or reloading landed on a key that no longer exists and the page rendered "Key not found". Two defects had to line up. POST /key/{key}/regenerate returns the rotated hash in token_id and leaves token null, but RegenerateKeyModal read response.token || response.key_id, neither of which the endpoint populates, so it always reported the old hash back to its parent. And KeyInfoView's onKeyDataUpdate prop had no caller anywhere in the tree: VirtualKeysTable owns the ?key= param and mounts the view but never passed it, so even a correct hash went nowhere. VirtualKeysTable now handles the update by pointing ?key= at the rotated hash and refetching. KeyInfoView holds that callback until the regenerate dialog is dismissed rather than firing it on the API response, because swapping the selected key mid-dialog unmounts the view and tears down the one-time plaintext key before the user can copy it.
The playground message bubble painted its fill, border and avatar circle from inline hex values, so in dark mode both bubbles stayed near-white while the text inherited the dark foreground: the message body was unreadable. The MCP-events placeholder bubble in ChatUI carried the same three fills. They move onto the tokens the rest of the sweep already uses, so the assistant surface is bg-card over border-border and the user surface is the info tint at the same weight the other selected-state surfaces take. Light mode keeps the same colour family it had. The regression test asserts the token classes and that no inline style survives on either surface, which is the exact shape the bug took.
…tplace_commands_v2 fix(UI): correct skill install command and marketplace setup UX
9feeaef to
ede15ea
Compare
|
Too many files changed for review (3000 files, 100 file limit). |
Add two new first-class OpenCode provider surfaces that route LLM requests through the opencode.ai gateway: - opencode_zen: supports chat completions, Anthropic Messages, and Responses arms across 36+ models including Claude, GPT-5 series, Grok, Gemini, and more - opencode_go: similar 3-arm routing for Go-flavored models (minimax, qwen3.5+) Each surface has its own API key resolution chain (module attr > env var > shared fallback), cost-map entries, and model-list wildcards. The chat arm uses Bearer token auth to /chat/completions. The messages arm uses x-api-key to /v1/messages (Anthropic wire format). The responses arm bridges to /v1/responses for GPT-5 models. Includes: transformation configs, error handling, streaming handlers, cost map entries, model wildcard registration, and test suite exercising all three arms.
ede15ea to
a3bd79d
Compare
… rebind-ok: CI lint and schema validation fixes
- Add supports_adaptive_thinking, prompt_cache_min_tokens, and supports_sampling_params to opencode_zen/claude-opus-5 - Add supports_adaptive_thinking, thinking_always_on, and supports_sampling_params to opencode_zen/claude-fable-5 - Add supports_sampling_params to opencode_zen/claude-opus-4-7 and opus-4-8 - Fix import sorting in zen/responses/transformation.py - Regenerate model_prices_and_context_window.schema.json
The notice interpolates each candidate's title, and the sweep scanned the whole comment for issue references and took the lowest. Titles are attacker-controlled, so filing a candidate titled "... see #1" redirected the closure: any later report matching that candidate would be closed as a duplicate of #1 instead. The detector now emits the candidate numbers as a digits-only field inside the marker, built from the API's number field, and the sweep reads only that. Prose is never parsed, so nothing a reporter can type reaches the target selection.
TLDR
Problem this solves:
How it solves it:
opencode_go,opencode_zen) that share one codebaseUser Flow
Before: a developer wants to send traffic to an OpenCode model through the proxy, but OpenCode isn't a selectable provider
opencode_go/...oropencode_zen/...model by hand, but the proxy rejects it as an unknown providerAfter: OpenCode Go and Zen are first-class providers
opencode_go/*andopencode_zen/*modelsopencode_zen/gpt-5.6-sol) and send a request through the proxyRelevant issues
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@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
Live verification against the OpenCode gateway (real API calls, commit
abb8e2e452):Both return 200 with a completion, proving the provider routes to the correct OpenCode surface and wire format end to end. The Admin UI Add Model panel at
http://localhost:4000/ui/models-and-endpoints/listsopencode_go/*andopencode_zen/*models when "OpenCode Go" / "OpenCode Zen" is selected.Type
🆕 New Feature
Caveats (if any)
supports_anthropic_messagesflag; only messages models (13 zen, 11 go) carry itmode: responses); adding one only requires a cost-map entrydocumentation_test_env_keysand docs checks stay red until the env vars are documented in BerriAI/litellm-docs (PR #908)Final Attestation