Skip to content

Commit 960c40c

Browse files
author
MAN
authored
Merge branch 'main' into patch-2
2 parents 6ba7647 + 5cc4248 commit 960c40c

11 files changed

Lines changed: 606 additions & 18 deletions

File tree

content/copilot/how-tos/copilot-cli/customize-copilot/use-byok-models.md

Lines changed: 156 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ shortTitle: Use your own model provider
44
intro: 'Use a model from an external provider of your choice in {% data variables.product.prodname_copilot_short %} by supplying your own API key.'
55
allowTitleToDifferFromFilename: true
66
versions:
7-
feature: copilot
7+
fpt: '*'
8+
ghec: '*'
9+
ghes: '>=3.22'
810
contentType: how-tos
911
category:
1012
- Configure Copilot
@@ -16,7 +18,9 @@ docsTeamMetrics:
1618
You can configure {% data variables.copilot.copilot_cli_short %} to use your own LLM provider, also called BYOK (Bring Your Own Key), instead of {% data variables.product.github %}-hosted models. This lets you connect to OpenAI-compatible endpoints, Azure OpenAI, or Anthropic, including locally running models such as Ollama.
1719

1820
> [!NOTE]
19-
> This article is for users who want to configure their own LLM provider API key on their local machine. To set up custom models for users in an enterprise, see [AUTOTITLE](/copilot/how-tos/administer-copilot/manage-for-enterprise/use-your-own-api-keys).
21+
> This article is for users who want to configure their own LLM provider API key on their local machine. To set up custom models for users in an enterprise, see [AUTOTITLE](/copilot/how-tos/administer-copilot/manage-for-enterprise/use-your-own-api-keys).{% ifversion ghes %}
22+
>
23+
> This article is also for administrators who want to configure their own LLM provider API key in GHES and for users who want to use those models in {% data variables.copilot.copilot_cli_short %}.{% endif %}
2024
2125
## Prerequisites
2226

@@ -123,5 +127,154 @@ You can run {% data variables.copilot.copilot_cli_short %} in offline mode to pr
123127
```shell
124128
export COPILOT_OFFLINE=true
125129
```
130+
131+
1. {% data reusables.copilot.copilot-cli.start-cli %}
126132

127-
{% data reusables.copilot.copilot-cli.start-cli %}
133+
{% ifversion ghes %}
134+
135+
## Using Copilot CLI with GitHub Enterprise Server
136+
137+
> [!NOTE]
138+
> This feature is in {% data variables.release-phases.technical_preview %} and subject to change. Additionally, GHES 3.22, the first version to support this functionality, is in the release candidate phase. We recommend waiting until GHES 3.22 reaches GA before validating and using this capability. We are publishing these docs early to provide visibility into what is coming.
139+
140+
{% data variables.copilot.copilot_cli_short %} can be configured to work with {% data variables.product.prodname_ghe_server %} for enterprises that operate in disconnected or air-gapped environments without connectivity to {% data variables.product.github %} Cloud. Your {% data variables.product.prodname_ghe_server %} administrator configures a model provider once, and users across the enterprise can use {% data variables.copilot.copilot_cli_short %} with their {% data variables.product.prodname_ghe_server %} credentials.
141+
142+
Setting up this feature involves two roles:
143+
144+
* **Administrator**: Configures the model provider on the {% data variables.product.prodname_ghe_server %} instance using `ghe-config`. This is a one-time setup that requires administrative SSH access.
145+
* **End user**: Sets environment variables on their local machine to connect {% data variables.copilot.copilot_cli_short %} to the instance.
146+
147+
### Prerequisites
148+
149+
* Your {% data variables.product.prodname_ghe_server %} administrator has configured a model provider on the instance. See [Configuring your {% data variables.product.prodname_ghe_server %} instance](#configuring-your-github-enterprise-server-instance).
150+
* {% data variables.copilot.copilot_cli_short %} is installed on client machines. See [AUTOTITLE](/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli).
151+
* {% data variables.product.company_short %} CLI (`gh`) is installed on client machines. See [Installing gh](https://cli.github.com/manual/installation).
152+
153+
The same [supported providers](#supported-providers) and [model requirements](#model-requirements) apply.
154+
155+
### Configuring your {% data variables.product.prodname_ghe_server %} instance
156+
157+
This step is for the operator or administrator of the {% data variables.product.prodname_ghe_server %} instance.
158+
159+
With administrative SSH access to the {% data variables.product.prodname_ghe_server %} instance, configure the model provider using the following `ghe-config` values. After configuring, run `ghe-config-apply` to apply the changes.
160+
161+
| Variable name | Required | Options | Description |
162+
|---|---|---|---|
163+
| `app.copilot-proxy.enabled` | Yes | `true`, `false` | Enables or disables the feature. |
164+
| `app.copilot-proxy.endpoint-url` | Yes | URI | The full upstream base URL including any version prefix (for example, `https://api.openai.com/v1`). |
165+
| `secrets.copilot-proxy.endpoint-key` | Yes | String | The API key for the upstream provider. |
166+
| `app.copilot-proxy.provider-model-id` | Yes | String | The provider model ID that {% data variables.copilot.copilot_cli_short %} uses to look up the model internally. |
167+
| `app.copilot-proxy.provider-type` | Yes | `openai`, `azure`, `anthropic` | The provider type. OpenAI includes OpenAI, Ollama, vLLM, Foundry Local, and any other OpenAI Chat Completions API-compatible endpoint. |
168+
| `app.copilot-proxy.upstream-timeout` | No | Integer (seconds) | Read/send timeout in seconds for upstream requests. If not set, falls back to the default timeout. |
169+
| `app.copilot-proxy.provider-wire-api` | No | `completions`, `responses` | The wire API format for the provider. |
170+
| `app.copilot-proxy.provider-wire-model` | No | String | Overrides the model identifier sent to the upstream provider if it differs from the internal model ID. |
171+
| `app.copilot-proxy.enable-upstream-probe` | No | `true`, `false` | Enables or disables the startup upstream probe. Defaults to enabled. When disabled, the startup probe is skipped. |
172+
173+
### Examples
174+
175+
```shell
176+
ghe-config app.copilot-proxy.enabled true
177+
ghe-config app.copilot-proxy.endpoint-url 'https://api.openai.com/v1'
178+
ghe-config secrets.copilot-proxy.endpoint-key 'YOUR-API-KEY'
179+
ghe-config app.copilot-proxy.provider-model-id 'gpt-5.5'
180+
ghe-config app.copilot-proxy.provider-wire-model 'gpt-5.5'
181+
ghe-config app.copilot-proxy.provider-type openai
182+
ghe-config app.copilot-proxy.upstream-timeout 300
183+
ghe-config app.copilot-proxy.enable-upstream-probe false
184+
ghe-config-apply
185+
```
186+
187+
Replace `YOUR-API-KEY` with the real API key before applying.
188+
189+
### Configuring your {% data variables.copilot.copilot_cli_short %} client (end user)
190+
191+
Configure {% data variables.copilot.copilot_cli_short %} to connect to your {% data variables.product.prodname_ghe_server %} instance by setting the following environment variables before starting {% data variables.copilot.copilot_cli_short %}.
192+
193+
| Environment variable | Required | Description |
194+
|---|---|---|
195+
| `COPILOT_PROVIDER_GHES_HOST` | Yes | The hostname of your {% data variables.product.prodname_ghe_server %} instance. |
196+
| `COPILOT_PROVIDER_GHES_TOKEN` | Yes | A {% data variables.product.pat_generic %} for the {% data variables.product.prodname_ghe_server %} instance. This token authenticates requests to the instance. |
197+
| `COPILOT_OFFLINE=true` | Yes | Enables offline mode. The {% data variables.product.prodname_ghe_server %} provider is only active when offline mode is enabled. |
198+
199+
### Understanding client (end user) tokens
200+
201+
{% data variables.copilot.copilot_cli_short %} needs access to LLM inference, so `COPILOT_PROVIDER_GHES_TOKEN` is always required. You will also very likely want {% data variables.copilot.copilot_cli_short %} to perform {% data variables.product.github %} operations such as create issues, pull requests, and search repositories. Such operations can be done via the {% data variables.product.github %} CLI.
202+
203+
It is recommended and preferred that you run `gh auth login --hostname YOUR-GHES-HOSTNAME`. After it succeeds, next step is to set COPILOT_PROVIDER_GHES_TOKEN to the token generated in `gh auth login --hostname YOUR-GHES-HOSTNAME`. It is more secure to retrieve the token dynamically rather than copying it from `~/.config/gh/hosts.yml`. You can do so by using `COPILOT_PROVIDER_GHES_TOKEN="$(gh auth token --hostname YOUR-GHES-HOSTNAME)"`.
204+
205+
Alternatively, you can generate a {% data variables.product.pat_generic %} on your {% data variables.product.prodname_ghe_server %} instance, set that token as `COPILOT_PROVIDER_GHES_TOKEN`, and use the same token when running `gh auth login --hostname YOUR-GHES-HOSTNAME`.
206+
207+
The above approach works when you are using {% data variables.copilot.copilot_cli_short %} interactively. For automation, you need to do a few things differently:
208+
* Set `GH_ENTERPRISE_TOKEN` (or `GITHUB_ENTERPRISE_TOKEN`) to the {% data variables.product.pat_generic %}.
209+
* Set `GH_HOST` to your server's hostname.
210+
* When both `GH_ENTERPRISE_TOKEN` and `gh auth login` credentials exist for the same host, the environment variable takes precedence.
211+
212+
### Recommended end user setup
213+
214+
1. Authenticate the {% data variables.product.company_short %} CLI.
215+
216+
```shell
217+
gh auth login --hostname YOUR-GHES-HOSTNAME
218+
```
219+
220+
1. Set the following environment variables:
221+
222+
```shell
223+
export COPILOT_PROVIDER_GHES_HOST=YOUR-GHES-HOSTNAME
224+
export COPILOT_PROVIDER_GHES_TOKEN="$(gh auth token --hostname YOUR-GHES-HOSTNAME)"
225+
export COPILOT_OFFLINE=true
226+
```
227+
228+
If you are authenticated with `gh auth login` to multiple accounts, you can set `GH_HOST` to your server's hostname and set `GH_ENTERPRISE_TOKEN` (or `GITHUB_ENTERPRISE_TOKEN`) to `"$(gh auth token --hostname YOUR-GHES-HOSTNAME)"`. This ensures {% data variables.product.prodname_cli %} targets your {% data variables.product.prodname_ghe_server %} instance.
229+
230+
```shell
231+
export GH_HOST=YOUR-GHES-HOSTNAME
232+
export GH_ENTERPRISE_TOKEN="$(gh auth token --hostname YOUR-GHES-HOSTNAME)"
233+
```
234+
235+
1. {% data reusables.copilot.copilot-cli.start-cli %}
236+
237+
You can run this entire set-up as a script.
238+
239+
### Examples
240+
241+
If both {% data variables.product.prodname_ghe_server %} and your {% data variables.copilot.copilot_cli_short %} configurations are correct, then you should see responses like the following in your {% data reusables.copilot.copilot-cli.start-cli %} session.
242+
243+
```shell
244+
• fabric-core-mcp — disabled
245+
• powerbi-mcp — disabled
246+
• slack — connected
247+
248+
● Current model: gpt-5.5
249+
250+
❯ Hello 13:31
251+
252+
● Hello!
253+
254+
❯ what is going on in github/codeql-action repo? 13:33
255+
256+
● I’ll check recent repository activity on the GHES host: repo metadata, open
257+
PRs/issues, and latest commits.
258+
259+
$ Shell Fetch repo metadata 2 lines… 5s
260+
gh api --hostname "$GH_HOST" repos/github/codeql-action --jq '{name_with_own…
261+
```
262+
263+
### Supported capabilities on {% data variables.product.prodname_ghe_server %}
264+
265+
For the most up-to-date information on {% data variables.copilot.copilot_cli_short %} features, refer to the [AUTOTITLE](/copilot/how-tos/copilot-cli) as the primary source of truth. In general, any capability that relies on connectivity to {% data variables.product.github %} cloud services is not available in the {% data variables.product.prodname_ghe_server %} offline configuration.
266+
267+
The following table provides a directional overview of what is available in {% data variables.product.prodname_ghe_server %} offering.
268+
269+
| Capability | {% data variables.product.prodname_dotcom %} / {% data variables.product.prodname_ghe_cloud %} | {% data variables.product.prodname_ghe_server %} |
270+
|---|---|---|
271+
| AI-assisted coding (prompts, code generation, debugging) | {% octicon "check-circle" aria-label="Available" %} | {% octicon "check-circle" aria-label="Available" %} |
272+
| Shell commands and file operations | {% octicon "check-circle" aria-label="Available" %} | {% octicon "check-circle" aria-label="Available" %} |
273+
| {% data variables.product.github %} operations (issues, PRs, repos) via `gh` CLI | {% octicon "check-circle" aria-label="Available" %} | {% octicon "check-circle" aria-label="Available" %} (requires `gh` CLI authenticated to the instance) |
274+
| {% data variables.product.github %} MCP server tools | {% octicon "check-circle" aria-label="Available" %} | {% octicon "x-circle" aria-label="Not available" %} |
275+
| Web search and web fetch | {% octicon "check-circle" aria-label="Available" %} | {% octicon "x-circle" aria-label="Not available" %} |
276+
| {% data variables.product.prodname_copilot_short %} model selection ({% data variables.product.github %}-hosted models) | {% octicon "check-circle" aria-label="Available" %} | {% octicon "x-circle" aria-label="Not available" %} |
277+
| Telemetry and usage reporting | {% octicon "check-circle" aria-label="Available" %} | {% octicon "x-circle" aria-label="Not available" %} |
278+
| Auto-update | {% octicon "check-circle" aria-label="Available" %} | {% octicon "x-circle" aria-label="Not available" %} |
279+
280+
{% endif %}

data/features/security-delegated-alert-dismissal.yml

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/automated-pipelines/components/AutomatedPageContext.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ export const useAutomatedPageContext = (): AutomatedPageContextT => {
3131
return context
3232
}
3333

34+
// Non-throwing variant: returns null when there is no provider. For components that render
35+
// both inside and outside an AutomatedPageContext.Provider (e.g. the product sidebar, shared
36+
// across automated REST reference pages and conceptual REST pages). Call it unconditionally.
37+
export const useAutomatedPageContextOptional = (): AutomatedPageContextT | null => {
38+
return useContext(AutomatedPageContext)
39+
}
40+
3441
type AutomatedPageContextRequest = { context?: Partial<Context> } | IncomingMessage
3542

3643
type AutomatedPage = {

src/fixtures/tests/playwright-rendering.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,35 @@ test.describe('test nav at different viewports', () => {
609609
await expect(page.getByTestId('breadcrumbs-bar')).toBeVisible()
610610
})
611611

612+
test('mobile nav opens even when the desktop rail was collapsed', async ({ page }) => {
613+
// Collapse the desktop rail at the xxl breakpoint so the persisted
614+
// `collapsed` state is set (the collapse toggle only exists at 1400px+).
615+
page.setViewportSize({
616+
width: 1400,
617+
height: 700,
618+
})
619+
await page.goto('/get-started/foo/bar')
620+
await page.getByTestId('sidebar-collapse-toggle').click()
621+
// With the rail collapsed the sidebar is not rendered on desktop.
622+
await expect(page.getByTestId('sidebar')).toHaveCount(0)
623+
624+
// Drop below xxl where the inline mobile nav lives. `collapsed` persists.
625+
page.setViewportSize({
626+
width: 1013,
627+
height: 700,
628+
})
629+
630+
// Opening the mobile nav must still render the doc-tree drawer -- before the
631+
// fix, `collapsed` short-circuited the sidebar to null while the open state
632+
// hid the content column, leaving a blank area with no drawer.
633+
await page.getByTestId('sidebar-mobile-toggle').click()
634+
await expect(page.getByTestId('sidebar')).toBeVisible()
635+
636+
// Closing it restores the content column (main content visible again).
637+
await page.getByTestId('sidebar-mobile-toggle').click()
638+
await expect(page.locator('#main-content')).toBeVisible()
639+
})
640+
612641
test('large -> x-large viewports - 1012+', async ({ page }) => {
613642
page.setViewportSize({
614643
width: 1013,

src/frame/components/DefaultLayout.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,12 @@ const LayoutBody = ({ children, scrollToTopLabel }: LayoutBodyProps) => {
265265
const { collapsed, mobileNavOpen } = useSidebarCollapsed()
266266
return (
267267
<div className="d-lg-flex">
268-
{collapsed ? null : <SidebarNav mobileOpen={mobileNavOpen} />}
268+
{/* `collapsed` is the desktop rail-collapse state (persisted). The inline
269+
mobile nav is independent, so still render the sidebar when it's open —
270+
otherwise opening the mobile nav while the desktop rail is collapsed
271+
hides the content column (contentHiddenForNav) with no drawer to show,
272+
so the open nav displays a blank area instead of the doc tree. */}
273+
{collapsed && !mobileNavOpen ? null : <SidebarNav mobileOpen={mobileNavOpen} />}
269274
{/* Need to set an explicit height for sticky elements since we also
270275
set overflow to auto */}
271276
<div

src/landings/components/ProductSelectionCard.module.scss

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// A single "All Docs" grid cell: category heading at the top, product links
2-
// bottom-aligned (space-between) so every cell in a row shares the tallest
3-
// height — matching the Docs 2026 design. Internal dividers are the cell's left
2+
// top-aligned directly beneath it. Cells in a row still stretch to the tallest
3+
// cell's height via the grid. Internal dividers are the cell's left
44
// border (skipped on the first column of each row, per breakpoint, so they don't
55
// double the container rail) plus a bottom border for row dividers. The column
66
// count steps 1 -> 2 -> 3 -> 4, so each breakpoint re-applies the left border to
77
// every cell and then clears it on the new first-of-row.
88
.cell {
99
display: flex;
1010
flex-direction: column;
11-
justify-content: space-between;
11+
justify-content: flex-start;
1212
gap: 2.25rem;
1313
padding: 2rem;
1414
border-bottom: var(--brand-borderWidth-thin, 1px) solid

src/landings/components/SidebarProduct.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
import { NavList } from '@primer/react-brand'
1515

1616
import { ProductTreeNode, useMainContext } from '@/frame/components/context/MainContext'
17-
import { useAutomatedPageContext } from '@/automated-pipelines/components/AutomatedPageContext'
17+
import { useAutomatedPageContextOptional } from '@/automated-pipelines/components/AutomatedPageContext'
1818
import { nonAutomatedRestPaths } from '@/rest/lib/config'
1919
import { usePrefetchOnInteraction } from '@/frame/components/lib/prefetch'
2020
import { SidebarExpandStateProvider, useSidebarExpandState } from './useSidebarExpandState'
@@ -414,12 +414,16 @@ function RestNavListItem({ category }: { category: ProductTreeNode }) {
414414
const { routePath, navigate, prefetch } = nav
415415
const { asPath, query } = useRestNav()
416416
const [visibleAnchor, setVisibleAnchor] = useState('')
417+
// Read the automated-page context unconditionally so hook order is stable across route
418+
// changes. It is null on conceptual REST pages (no provider), which is fine — those pages
419+
// use `[]` anyway.
420+
const automatedPage = useAutomatedPageContextOptional()
417421
const miniTocItems =
418422
query.productId === 'rest' ||
419423
// These pages need the Article Page mini tocs instead of the Rest Pages
420424
nonAutomatedRestPaths.some((item: string) => asPath.includes(item))
421425
? []
422-
: useAutomatedPageContext().miniTocItems
426+
: (automatedPage?.miniTocItems ?? [])
423427

424428
useEffect(() => {
425429
if (nonAutomatedRestPaths.every((item: string) => !asPath.includes(item))) {

0 commit comments

Comments
 (0)