Support BYOK endpoints for Inline suggestions (fix #318545) - #330816
Support BYOK endpoints for Inline suggestions (fix #318545)#330816unbadfish wants to merge 9 commits into
Conversation
(A) Bridge chatLanguageModels.json custom models into the completions pipeline
- Add byok/common/byokCompletionModels.ts: module-level registry and parser that turns customendpoint/customoai groups into ByokCompletionModel entries (completionsUrl used verbatim, apiKey resolved from secret storage, id disambiguation: id → group/id → vendor/group/id)
- Add extension/src/byokCompletionModelsContribution.ts: triggers core model resolution via lm.selectChatModels({vendor}) and keeps the completions model manager in sync
- Hook abstractLanguageModelChatProvider.provideLanguageModelChatInformation to report the decoded group configuration (vendor, group, configuration)
- Forward `group` in extHostLanguageModels.$provideLanguageModelChatInfo so multiple groups per vendor coexist (typed via an intersection to satisfy tsgo)
(B) Route completions to the custom endpoint instead of the Copilot proxy
- openai/fetch.ts: when a custom model is selected, POST to completionsUrl with FIM fields (prompt/suffix/max_tokens/temperature/top_p/n/stop/stream/model); strip Copilot-only fields (extra/nwo/code_annotations) and headers
- handleError: custom endpoints skip Copilot-specific semantics (466, firewall detection, token reset) and get targeted 401/402/404/429 messages
- nesFetch transport: isCustomEndpoint omits x-policy-id/X-GitHub-Api-Version, sends no Authorization when no apiKey is configured, skips Copilot 402 quota handling
(C) Force n=1 while keeping multi-candidate cycling
- Most OpenAI-compatible FIM endpoints (e.g. DeepSeek) reject n>1; force n=1 after the postOptions merge so it cannot be overridden
- Cycling (Alt+]) still yields multiple candidates: each cycle with ≤1 cached candidate issues a fresh request and merges the new sample into the cache; the cycling sampling temperature (0.2) is preserved so greedy servers still produce different samples
(D) Model selection, picker and configuration schema
- openai/model.ts: validate github.copilot.selectedCompletionModel against BYOK models as well (accepts id, group/id and vendor/group/id forms, e.g. customendpoint/DS-oss/deepseek-v4-flash)
- Model picker lists custom models together with their group name
- package.json: add completionsUrl to customendpoint/customoai models plus a group-level fallback
- completionsCoreContribution: register the inline completion provider without a Copilot token when BYOK models are configured (offline/signed-out)
(E) Tests
- byok/common/test/byokCompletionModels.spec.ts: parsing, group-level fallback, id disambiguation, removal and id-form matching (vitest, 9 cases)
- openai/test/fetch.test.ts: BYOK request construction, header/key handling and error branches (mocked fetch, no network, vendor-neutral URLs)
- openai/test/model.test.ts: model selection validation and picker surfacing
co-author by deepseek-v4-flash&pro, harness by copilot.
|
@microsoft-github-policy-service agree |
There was a problem hiding this comment.
Pull request overview
Adds BYOK FIM endpoints to GitHub Copilot inline completions, including model discovery, routing, selection, and offline support.
Changes:
- Bridges configured BYOK models into the completion model registry and picker.
- Routes FIM requests directly to custom endpoints with BYOK authentication.
- Adds schema entries and tests for custom completion models.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
src/vs/workbench/api/common/extHostLanguageModels.ts |
Forwards language-model group metadata. |
extensions/copilot/src/platform/nesFetch/node/completionsFetchServiceImpl.ts |
Adapts transport headers and quota handling. |
extensions/copilot/src/platform/nesFetch/common/completionsFetchService.ts |
Extends the fetch contract for custom endpoints. |
extensions/copilot/src/extension/completions/vscode-node/completionsCoreContribution.ts |
Enables offline BYOK provider registration. |
extensions/copilot/src/extension/completions-core/vscode-node/lib/src/openai/test/model.test.ts |
Tests BYOK model selection and listing. |
extensions/copilot/src/extension/completions-core/vscode-node/lib/src/openai/test/fetch.test.ts |
Tests custom FIM request behavior. |
extensions/copilot/src/extension/completions-core/vscode-node/lib/src/openai/model.ts |
Integrates BYOK models into model resolution. |
extensions/copilot/src/extension/completions-core/vscode-node/lib/src/openai/fetch.ts |
Constructs and routes custom FIM requests. |
extensions/copilot/src/extension/completions-core/vscode-node/lib/src/openai/config.ts |
Propagates custom model metadata. |
extensions/copilot/src/extension/completions-core/vscode-node/lib/src/ghostText/requestContext.ts |
Carries BYOK models through request context. |
extensions/copilot/src/extension/completions-core/vscode-node/lib/src/ghostText/ghostText.ts |
Adds custom models to ghost-text requests. |
extensions/copilot/src/extension/completions-core/vscode-node/lib/src/ghostText/completionsFromNetwork.ts |
Adapts BYOK candidate sampling. |
extensions/copilot/src/extension/completions-core/vscode-node/extension/src/modelPicker.ts |
Lists custom completion models. |
extensions/copilot/src/extension/completions-core/vscode-node/extension/src/byokCompletionModelsContribution.ts |
Synchronizes configured BYOK models. |
extensions/copilot/src/extension/completions-core/vscode-node/completionsServiceBridges.ts |
Exposes BYOK bridge setup. |
extensions/copilot/src/extension/byok/vscode-node/byokContribution.ts |
Clears models when policy disables BYOK. |
extensions/copilot/src/extension/byok/vscode-node/abstractLanguageModelChatProvider.ts |
Publishes resolved BYOK configuration. |
extensions/copilot/src/extension/byok/common/test/byokCompletionModels.spec.ts |
Tests BYOK configuration parsing. |
extensions/copilot/src/extension/byok/common/byokCompletionModels.ts |
Implements the shared BYOK model registry. |
extensions/copilot/package.json |
Adds completion endpoint schema fields. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| // Bridge BYOK (chatLanguageModels.json) models into the completions pipeline early, | ||
| // before the inline completion provider is registered (signed-out/offline scenarios | ||
| // never produce a Copilot token, yet must still serve custom completions). | ||
| const completionsInstaService = _copilotInlineCompletionItemProviderService.getOrCreateInstantiationService(); | ||
| this._register(completionsInstaService.invokeFunction(setupByokCompletionModels)); |
| if (customModel) { | ||
| // Minimal headers for custom endpoints: Content-Type, X-Request-Id and | ||
| // Authorization (Bearer <apiKey>) are added by the fetch service. | ||
| fullHeaders = {}; |
| // A custom BYOK (OpenAI-compatible) completion model is always valid, even | ||
| // when the CAPI model list is empty (e.g. signed out / fully offline). | ||
| const customModel = getByokCompletionModelById(userSelectedCompletionModel); | ||
| if (customModel) { | ||
| return new ModelRequestInfo(userSelectedCompletionModel, 'modelpicker', customModel); |
| export function updateByokCompletionModelConfig(vendor: string, groupName: string | undefined, configuration: IStringDictionary<unknown> | undefined): void { | ||
| const key = `${vendor}/${groupName ?? ''}`; | ||
| if (!configuration) { | ||
| registeredGroupConfigs.delete(key); | ||
| } else { | ||
| registeredGroupConfigs.set(key, { vendor, groupName: groupName ?? '', configuration }); | ||
| } |
| "completionsUrl": { | ||
| "type": "string", | ||
| "pattern": "^https?://.+", | ||
| "patternErrorMessage": "URL must start with http:// or https://", | ||
| "markdownDescription": "Full URL used **verbatim** for inline code completions (FIM) requests, e.g. `https://api.deepseek.com/beta/completions` or `https://api.siliconflow.cn/v1/completions`. When omitted, this model is only available for chat, not for inline code completions." |
The BYOK model sync lived in CompletionsCoreContribution, which is not instantiated when the joint completions provider is active - custom completion models were never resolved in the default configuration. - Add ByokCompletionBridgeContribution, registered unconditionally, to wire the BYOK model registry into the completions-core instantiation service. - Remove the bridge initialization from CompletionsCoreContribution. - Register completions in the joint provider when custom completion models exist, even without a Copilot token (offline BYOK).
The completions fetcher silently dropped user-configured request headers, breaking endpoints behind APIM gateways or vanity domains. - Extract OpenAIEndpoint's custom header sanitizer into a shared sanitizeCustomRequestHeaders helper in byok/common. - Reuse it in the BYOK completion fetcher so custom request headers (e.g. x-api-key) are forwarded with the same injection protections as chat, minus authentication and transport headers owned by the fetch service. - Extend the reserved set with completions-specific headers (openai-organization, x-policy-id, x-copilot-async, x-copilot-speculative).
A custom model whose id collides with a Copilot model could hijack completion requests, leaking the BYOK apiKey to the Copilot proxy. - Resolve the request model from the Copilot model list first and fall back to custom (BYOK) models only when no Copilot model matches. - Qualify colliding custom models in the picker with a group/id id.
Groups deleted from chatLanguageModels.json while the extension runs previously lingered in the registry and the model picker. Treat a provider invocation without a group as the start of a resolution pass: drop all groups previously registered for that vendor, then re-add the groups that still exist.
Mirror the customendpoint group schema so customoai models can back inline completions as well.
The model-level description only mentioned the "omitted" case. State that the group-level completionsUrl is used as the fallback and the model loses inline completions only when neither level is configured.
fdbfe94 to
1ec285f
Compare
# Conflicts: # extensions/copilot/src/extension/inlineEdits/vscode-node/jointInlineCompletionProvider.ts
|
Sorry for the incorrect conflict handling above. Now I have corrected it. Commit d6712eb is a merge commit that brings in the cherry-picked PR microsoft/vscode#332316 (commit 2ccb7d1). The merge works correctly — both the |
|
Will this inline suggestion support the |
No. As copilot uses the |
Thanks for the clear and detailed response. |
…om) in completionsUrl examples

TL;DR: Fix #318545 by bridge FIM (Fill in Middle) requests to custom endpoint, thus enjoy "vanilla" copilot edit suggestion in custom endpoint.
See "usage" section to edit your settings. NOTE: read endpoint's doc and use FULL URL in
completionsUrlProblem
Chat already supports BYOK (Bring Your Own Key) models via
chatLanguageModels.json, but inline code completions always require GitHub's cloud. Settinggithub.copilot.selectedCompletionModelto a custom model ID is silently ignored because the ID is not in the list returned by GitHub's API — the extension falls back to the default Copilot cloud model and every completion goes throughproxy.business.githubcopilot.com. This makes self-hosted local models (llama.cpp, FastFlowLM, vLLM) and BYOK endpoints cannot use "vanilla" copilot edit suggestion in this situation.Solution
Reuse the existing
chatLanguageModels.jsonconfiguration as the single source of truth for BYOK completion models. A model becomes eligible for inline completions when it declares acompletionsUrl(a full OpenAI-compatible FIM endpoint, used verbatim). When such a model is selected, the completions pipeline POSTs a FIM request to that endpoint with the user's own API key. Enterprise policy (isClientBYOKAllowed) is inherited automatically, exactly as it is for chat.Highlight
By bridge FIM requests to custom endpoint, users can enjoy "vanilla" copilot edit suggestion (pop-up window, key bounding, not extra extension, etc.) in custom endpoint.
Changes
customendpoint/customoaigroups reported by the BYOK chat providers (which receive the decoded configuration, including API keys resolved from secret storage). A small contribution triggers core model resolution vialm.selectChatModels({vendor})and keeps the completions model manager in sync. One line in the coreextHostLanguageModelsbridge forwards thegroupname so multiple groups per vendor coexist.LiveOpenAIFetcherbranches on the selected custom model: the request goes tocompletionsUrlverbatim with FIM fields only (prompt,suffix,max_tokens,temperature,top_p,n,stop,stream,model); Copilot-only fields (extra,nwo,code_annotations) and headers are stripped. The transport layer omits Copilot-specific headers, sendsAuthorization: Bearer <apiKey>only when a key is configured.nis forced to 1 because most OpenAI-compatible FIM endpoints rejectn > 1. Cycling still produces multiple candidates: eachAlt+]with ≤1 cached candidate issues a fresh request whose sample is merged (deduplicated) with the cache, and the cycling sampling temperature (0.2) is preserved so greedy servers yield different completions per request.github.copilot.selectedCompletionModelaccepts the bare id,group/idandvendor/group/idforms; the model picker lists custom models with their group name;package.jsonschemas forcustomendpoint/customoaigaincompletionsUrl(model-level and group-level). The inline completion provider is registered without a Copilot token when BYOK models are configured, enabling signed-out/offline usage.Validation
tsc --noEmit(extension) and TS7 native--noEmitontsconfig.jsonboth passhttps://api.deepseek.com/beta/completions): all completion requests route to the configured endpoint with 200 responses; multi-line/single-line stop sequences and Alt+] cycling verified; offline usage verified (no Copilot token required)Usage
completionsUrlinchatLanguageModels.json.NOTE: read endpoint's doc and use FULL URL in
completionsUrl.GitHub Copilot: Change Completions Model, select your BYOK model.Notes
completionsUrlis used exactly "as is"; URL correctness (e.g. appending something as/beta/completions) is the user's responsibility. READ the provider's DOC!completionsUrlremain chat-only.n > 1are fully supported; cycling falls back to repeated single-sample requests.co-author by deepseek-v4-flash&pro, harness by copilot.