feat(ai-providers): add OpenCode Zen provider (#1400)#1417
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds OpenCode Zen (https://opencode.ai/docs/zen) as a first-class AI provider, and fixes the base-URL handling that made it (and other gateways) fail as a Custom Provider. Closes #1400.
OpenCode Zen is a clean OpenAI-compatible gateway:
POST /zen/v1/chat/completions(Bearer auth) serves every model family it offers, andGET /zen/v1/modelsreturns the list. So it reuses the existingOpenAICompatibleProviderwith no new transport.AIProviderType.openCode— display name "OpenCode Zen", default endpointhttps://opencode.ai/zen, API-key auth,sparklesicon.AIProviderRegistration(same wiring as OpenRouter); the model picker auto-loads from/v1/models, so there's no hardcoded model list to go stale.Why the reporter's Custom Provider failed (root cause)
OpenCode documents its base URL as
https://opencode.ai/zen/v1, butOpenAICompatibleProviderappended/v1/...to the stored endpoint, so a pasted.../zen/v1became.../zen/v1/v1/chat/completionsand 404'd. This is a footgun for every gateway whose documented base ends in/v1.Fix: a new
String.openAIPath(_:)appends/v1/<resource>only when the endpoint doesn't already end in/v1. Used in the three URL builds (chat, test-connection, models). Backward-compatible: the built-in endpoints (https://api.openai.com,https://openrouter.ai/api) don't end in/v1, so their behavior is unchanged; and any custom gateway now tolerates a pasted/v1base URL.Verified against the live API
GET https://opencode.ai/zen/v1/modelsreturns a standard OpenAI list with bare ids (gpt-5.5,claude-opus-4-7,big-pickle, ...).POST .../v1/chat/completionswith a Claude/GPT/Gemini id returns401 Missing API key(accepted, just needs a key), and the freebig-picklemodel returns a fullchat.completionwith no key. So one OpenAI-compatible transport covers all of Zen's models.Files
Models/AI/AIModels.swift— new.openCodecase + displayName / defaultEndpoint / symbolName.Core/AI/Registry/AIProviderRegistration.swift—.openCodeadded to the OpenAI-compatible loop.Views/Settings/AISettingsView.swift— added to the addable provider list.Core/AI/String+AIEndpoint.swift— newopenAIPath(_:).Core/AI/OpenAICompatibleProvider.swift— chat / test / models URLs useopenAIPath.TableProTests/Core/AI/StringAIEndpointTests.swift— new.docs/features/ai-assistant.mdx,CHANGELOG.md.Notes
Test plan
/v1/modelsafter pasting a key.StringAIEndpointTestspasses: endpoint without/v1gets one appended; endpoint ending in/v1is not doubled; trailing slash normalized.https://opencode.ai/zen/v1now works (no doubled/v1).