refactor: simplify provider detection and resolution - #21
Merged
emilsvennesson merged 3 commits intoApr 29, 2026
Merged
Conversation
Detect providers by canonical OpenCode ID instead of layering npm-package matching, ID-substring hints, and per-model uniformity heuristics. The provider type now comes from a single flat ID-to-type lookup, with moonshotai-cn added alongside moonshotai. Collapses the 6-helper resolution chain to two functions, moves the ChatGPT-vs-OpenAI routing into detection, drops the redundant ProviderResolution.providerType field and ProviderState wrapper, scans providers once per cold path instead of three times, and extracts the duplicated chatgpt/copilot auth.json reading into a shared helper. Net -549 lines, no behavior change for any supported provider.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR refactors the web-search plugin’s provider detection and model resolution pipeline to rely on canonical OpenCode provider IDs and a simplified scan/resolve flow, while also de-duplicating shared auth.json reading logic.
Changes:
- Replaced multi-strategy provider detection with a canonical provider-id → adapter-type map (
detectProviderType) and consolidated scan state (scanProviders). - Simplified model selection/resolution logic to a small set of functions (
resolveActiveType,findModelByKey,pickModel) and reduced repeated provider scanning. - Extracted shared OpenCode
auth.jsonreading intosrc/providers/shared/auth.tsand updated provider adapters to acceptquery: stringdirectly.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/index.ts | Simplifies resolution loading, active-provider routing (incl. OpenAI→ChatGPT), and model selection. |
| src/config.ts | Collapses provider scanning and resolution map building; removes npm-based detection inputs. |
| src/providers/registry.ts | Introduces canonical provider-id lookup map and exports scan/type priority constants. |
| src/types.ts | Refactors shared types (adds ScannableProviderType, removes SearchArgs, simplifies resolution map typing). |
| src/providers/shared/auth.ts | New shared helper to locate and read OpenCode auth.json entries. |
| src/providers/chatgpt/auth.ts | Uses shared auth reader; trims and simplifies OAuth credential extraction. |
| src/providers/copilot/auth.ts | Uses shared auth reader; refactors enterprise URL normalization and credential extraction. |
| src/providers/index.ts | Updates adapter interface to accept query: string and streamlines dispatch functions. |
| src/providers/anthropic/index.ts | Updates adapter signature to executeSearch(config, query). |
| src/providers/openai/index.ts | Updates adapter signature to executeSearch(config, query). |
| src/providers/moonshot/index.ts | Updates adapter signature to executeSearch(config, query). |
| src/providers/chatgpt/index.ts | Updates adapter signature to executeSearch(config, query). |
| src/providers/copilot/index.ts | Updates adapter signature to executeSearch(config, query). |
| AGENTS.md | Updates style guidance to allow ternaries in simple cases. |
| .oxlintrc.json | Disables no-ternary to match updated style guidance. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
131
to
137
| const copilot = await resolveCopilotCredentials(client, directory); | ||
| if (copilot) { | ||
| resolutions.copilot = { | ||
| credentials: copilotCredentials, | ||
| fallbackModel: modelOverrides.fallbackModel, | ||
| lockedModel: modelOverrides.lockedModel, | ||
| providerType: "copilot", | ||
| credentials: copilot, | ||
| fallbackModel: scan.copilot.fallbackModel, | ||
| lockedModel: scan.copilot.lockedModel, | ||
| }; |
Comment on lines
+22
to
+24
| * Provider types that are scannable from OpenCode provider config, | ||
| * in the order they should be preferred when multiple providers offer | ||
| * a `lockedModel` or `fallbackModel`. |
Comment on lines
+13
to
+19
| const PROVIDER_TYPES_BY_ID: Record<string, ScannableProviderType> = { | ||
| anthropic: "anthropic", | ||
| "github-copilot": "copilot", | ||
| moonshotai: "moonshot", | ||
| "moonshotai-cn": "moonshot", | ||
| openai: "openai", | ||
| }; |
Switch from a type-keyed resolution map to a list of resolutions keyed by OpenCode provider ID. This lets users have multiple providers of the same type (e.g. `openai-prod` and `openai-staging`) with their own credentials and baseURLs, all auto-detected via npm-package matching on `@ai-sdk/openai`, `@ai-sdk/anthropic`, or `@ai-sdk/github-copilot`. Detection now falls back to npm matching when the provider ID is not canonical. Active model routing matches by provider ID, so each renamed provider serves traffic on its own credentials. ChatGPT OAuth shadowing is scoped to the canonical `openai` provider with no baseURL; renamed openai-typed providers keep their explicit credentials.
…feedback Allow scanProvider to emit credential-less scan results when a provider exposes websearch flags but no apiKey, so an OAuth-only github-copilot config that pins a model via `"websearch": "always"` is no longer silently dropped. The OAuth attachment phase fills in credentials with a unified baseURL guard that also preserves any explicit Copilot proxy configuration. Use the canonical `moonshotai` provider ID in the formatNoProviderError example block.
emilsvennesson
deleted the
refactor/simplify-provider-detection-and-resolution
branch
April 29, 2026 20:29
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
Replaces the multi-strategy provider detection logic and the deeply-layered
resolution helpers with a single canonical-ID lookup and two small functions.
Net −549 lines; bundle size drops from 37.18 KB → 31.91 KB. No behavior
change for any supported provider.
Why
Provider detection layered three independent strategies (npm-package matching,
ID-substring hints, per-model uniformity heuristics) plus five fallback
helpers. OpenCode already exposes canonical, well-known provider IDs
(
anthropic,openai,github-copilot,moonshotai,moonshotai-cn) viaprovider.idand thechat.messagehook'smodel.providerID. A flat ID mapcovers every supported case and makes the rest of the pipeline obvious.
The resolution side had its own bloat: a 6-helper chain for what is a 3-step
priority lookup, a
ProviderStatewrapper around a single field, ahasAnyProviderguard that listed every provider type by hand, and threefull provider scans per cold path.
What changed
Detection
src/providers/registry.ts: replaceddetectProviderTypeFromNpm,detectProviderTypeFromProviderID,detectProviderTypeFromModel, hintarrays, and helpers with a flat
PROVIDER_TYPES_BY_IDmap and a singledetectProviderTypelookup. Addedmoonshotai-cn.model.api.npmis no longer consulted anywhere.my-anthropic-proxy) are intentionally notauto-detected; users must use the canonical provider ID. Custom
baseURLon canonical providers continues to work.
Resolution (
src/index.ts)resolveSearchProvider→resolveLockedModel/resolveActiveModel/resolveFallbackModel→resolveModelByPriority+buildSearchConfig) withfindModelByKey+pickModel.openai → chatgptredirect intoresolveActiveType, soresolution no longer needs a special branch.
hasAnyProvider(23 lines) withObject.keys(resolutions).length === NO_PROVIDERS.hasConfiguredOpenAIBaseURL+shouldAttachChatGPTResolution(17 lines) as
resolutions.openai?.credentials.baseURL?.trim().ProviderStatewrapper andResolvedProviderinterface.clientwithPluginInput["client"].Scanning (
src/config.ts)scanProvidersnow runs once per cold path;loadResolutionsthreadsthe scan state through chatgpt/copilot attachment instead of calling
resolveModelOverrides(deleted) twice more.createInitialScanState,buildResolutionMap,ScanState) collapsed via aSCANNABLE_TYPESarray.isScannableProviderTypeguard (detectProviderTypereturnsScannableProviderType | nulldirectly).resolveGeneralModelHint/resolveCopilotModelHint→ constants.Auth (
src/providers/{chatgpt,copilot}/auth.ts)readAuthEntry<Entry>(client, directory, key),PathClient, and path helpers intosrc/providers/shared/auth.ts.auth.tsis now ~50–75 lines focused on the entryshape and
buildCredentials. Removes ~170 lines of duplication.Types (
src/types.ts)providerTypefield fromProviderResolution(redundant withthe map key).
SearchArgsinterface; adapters now takequery: stringdirectly. Updated all 5 adapter signatures and
dispatchSearch.ScannableProviderTypefromconfig.tstotypes.ts.ProviderResolutionMapis nowPartial<Record<ProviderType, ...>>.Lint
no-ternary: off); updatedAGENTS.mdaccordingly.File-by-file impact
src/index.tssrc/config.tssrc/providers/registry.tssrc/providers/chatgpt/auth.tssrc/providers/copilot/auth.tssrc/types.tssrc/providers/shared/auth.tsdist/index.js)Behavior preserved
anthropic,openai,github-copilot,moonshotai(now alsomoonshotai-cn).baseURLon canonical provider IDs (provider-level options andper-model
api.url), including Anthropic/v1$stripping.openaihas no configuredbaseURL).enterpriseUrl.websearch: "always"/"auto"flags.Behavior intentionally removed
my-anthropic-proxyconfigured withnpm: "@ai-sdk/anthropic"). Usersmust use the canonical OpenCode provider ID.
Verification
No tests exist yet.