Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/link-check-internal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ jobs:

- name: Create Copilot redirect issue
if: inputs.create_copilot_issue
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3
with:
github-token: ${{ secrets.DOCS_BOT_PAT_BASE }}
script: |
Expand Down
21 changes: 11 additions & 10 deletions content/admin/data-residency/github-copilot-with-data-residency.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,17 @@ The models available for {% data variables.product.prodname_copilot_short %} var

### European Union

* GPT-4o mini
* GPT-4.1
* GPT-5 mini
* GPT-5.2
* GPT-5.4
* Claude Haiku 4.5
* Claude Sonnet 4.5
* Claude Opus 4.5
* Claude Sonnet 4.6
* Claude Opus 4.6
* {% data variables.copilot.copilot_gpt_4o_mini %}
* {% data variables.copilot.copilot_gpt_41 %}
* {% data variables.copilot.copilot_gpt_5_mini %}
* {% data variables.copilot.copilot_gpt_52 %}
* {% data variables.copilot.copilot_gpt_53_codex %}
* {% data variables.copilot.copilot_gpt_54 %}
* {% data variables.copilot.copilot_claude_haiku_45 %}
* {% data variables.copilot.copilot_claude_sonnet_45 %}
* {% data variables.copilot.copilot_claude_opus_45 %}
* {% data variables.copilot.copilot_claude_sonnet_46 %}
* {% data variables.copilot.copilot_claude_opus_46 %}

## Pricing changes

Expand Down
2 changes: 1 addition & 1 deletion content/copilot/concepts/auto-model-selection.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Experience less rate limiting and reduce the mental load of choosing a model by
{% data variables.copilot.copilot_auto_model_selection %} intelligently chooses models based on real time system health and model performance. You benefit from:
* Reduced rate limiting
* Lower latency and errors
* Discounted multipliers for paid plans ({% data variables.copilot.copilot_chat_short %} only)
* Discounted multipliers for paid plans

{% data variables.copilot.copilot_auto_model_selection_short_cap_a %} **won't** include these models:
* Models excluded by administrator policies. See [AUTOTITLE](/copilot/how-tos/copilot-on-github/set-up-copilot/configure-access-to-ai-models).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ category:
- Manage Copilot for a team
---

{% data reusables.copilot.byok-intro %}
{% data reusables.copilot.byok-intro %} {% data reusables.copilot.byok-no-subscription-required %}

## Why bring your own API keys?

Expand All @@ -25,6 +25,8 @@ As an enterprise owner, you may have specific requirements for governance, data

After you've added your key and selected one or more models, you and members of your organizations will be able to use them with {% data variables.copilot.copilot_byok_supported_features %}. Your models will appear at the bottom of the model picker, under the enterprise name.

> [!NOTE] For members of your organizations to use third-party models in {% data variables.product.prodname_vscode %}, the **Bring Your Own Language Model Key in {% data variables.product.prodname_vscode_shortname %}** policy must be enabled. For more information, see the [{% data variables.product.prodname_copilot_short %} settings page](https://github.com/settings/copilot/features) in {% data variables.product.prodname_dotcom_the_website %}.

{% data reusables.enterprise-accounts.access-enterprise %}
{% data reusables.enterprise-accounts.ai-controls-tab %}
1. In the sidebar, click **{% octicon "copilot" aria-hidden="true" aria-label="copilot" %} {% data variables.product.prodname_copilot_short %}**.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ category:
- Manage Copilot for a team
---

{% data reusables.copilot.byok-intro %}
{% data reusables.copilot.byok-intro %} {% data reusables.copilot.byok-no-subscription-required %}

## Why bring your own API keys?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ If you have a {% data variables.copilot.copilot_free_short %}, {% data variables

As an enterprise or organization owner, you can enable or disable access to AI models for members with a {% data variables.copilot.copilot_enterprise_short %} or {% data variables.copilot.copilot_business_short %} seat. See [AUTOTITLE](/copilot/managing-copilot/managing-github-copilot-in-your-organization/setting-policies-for-copilot-in-your-organization/managing-policies-for-copilot-in-your-organization) and [AUTOTITLE](/copilot/managing-copilot/managing-copilot-for-your-enterprise/managing-policies-and-features-for-copilot-in-your-enterprise).

> [!NOTE] Models available in {% data variables.copilot.copilot_auto_model_selection %} will follow the policies set for an organization or enterprise. See [AUTOTITLE](/copilot/concepts/auto-model-selection).
> [!NOTE]
> * Models available in {% data variables.copilot.copilot_auto_model_selection %} will follow the policies set for an organization or enterprise. See [AUTOTITLE](/copilot/concepts/auto-model-selection).
> * {% data reusables.copilot.byok-no-subscription-required %}

{% ifversion copilot-byok %}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ redirect_from:
- /copilot/how-tos/ai-models/changing-the-ai-model-for-copilot-chat
- /copilot/how-tos/ai-models/change-the-chat-model
contentType: how-tos
category:
category:
- Configure Copilot
---

Expand Down Expand Up @@ -75,6 +75,7 @@ You can expand the model options that are available to power {% data variables.c

* Depending on the provider or model you choose, you may need to supply an API key, or model ID, from the provider, or a {% data variables.product.github %} {% data variables.product.pat_generic %} (PAT).
* To add models from the AI Toolkit for {% data variables.product.prodname_vscode %}, you must <a href="vscode:extension/ms-windows-ai-studio.windows-ai-studio?ref_product=copilot&ref_type=engagement&ref_style=text">install the AI Toolkit extension</a>.
* If you are a {% data variables.copilot.copilot_business_short %} or {% data variables.copilot.copilot_enterprise_short %} customer and want to use third-party models in {% data variables.product.prodname_vscode %}, the **Bring Your Own Language Model Key in {% data variables.product.prodname_vscode_shortname %}** policy must be enabled. For more information, see the [{% data variables.product.prodname_copilot_short %} settings page](https://github.com/settings/copilot/features) in {% data variables.product.prodname_dotcom_the_website %}.

### Adding models

Expand Down
1 change: 1 addition & 0 deletions data/reusables/copilot/byok-no-subscription-required.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Using your own API keys does not require a {% data variables.product.prodname_copilot_short %} subscription. However, without a subscription, you won't have access to other {% data variables.product.prodname_copilot_short %} capabilities such as mobile access, automation, and remote server features.
2 changes: 1 addition & 1 deletion data/variables/copilot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,4 @@ copilot_workspace: 'Copilot Workspace'
copilot_workspace_short: 'Workspace'

# BYOK
copilot_byok_supported_features: '{% data variables.copilot.copilot_chat %} and {% data variables.copilot.copilot_cli %}'
copilot_byok_supported_features: '{% data variables.copilot.copilot_chat_short %}, {% data variables.copilot.copilot_cli_short %}, and {% data variables.product.prodname_vscode_shortname %}'
1 change: 0 additions & 1 deletion eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ export default [
'src/article-api/**/*.{ts,js}',
'src/audit-logs/**/*.{ts,js}',
'src/color-schemes/**/*.{ts,js}',
'src/content-render/**/*.{ts,js}',
'src/data-directory/**/*.{ts,js}',
'src/dev-toc/**/*.{ts,js}',
'src/events/**/*.{ts,js}',
Expand Down
5 changes: 4 additions & 1 deletion src/content-render/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { renderLiquid } from './liquid/index'
import { renderMarkdown, renderUnified } from './unified/index'
import { engine } from './liquid/engine'
import type { Context } from '@/types'
import { createLogger } from '@/observability/logger'

const logger = createLogger(import.meta.url)

interface RenderOptions {
cache?: boolean | ((template: string, context: Context) => string)
Expand Down Expand Up @@ -53,7 +56,7 @@ export async function renderContent(
return html
} catch (error) {
if (options.filename) {
console.error(`renderContent failed on file: ${options.filename}`)
logger.error('renderContent failed on file', { filename: options.filename })
}
throw error
}
Expand Down
5 changes: 4 additions & 1 deletion src/content-render/liquid/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import type { TagToken, Liquid, Template } from 'liquidjs'

import { THROW_ON_EMPTY, DataReferenceError } from './error-handling'
import { getDataByLanguage } from '@/data-directory/lib/get-data'
import { createLogger } from '@/observability/logger'

const logger = createLogger(import.meta.url)

const Syntax = /([a-z0-9/\\_.\-[\]]+)/i
const SyntaxHelp = "Syntax Error in 'data' - Valid syntax: data [path]"
Expand Down Expand Up @@ -42,7 +45,7 @@ export default {
if (THROW_ON_EMPTY) {
throw new DataReferenceError(message)
}
console.warn(message)
logger.warn(message)
}
return
}
Expand Down
17 changes: 5 additions & 12 deletions src/content-render/liquid/ifversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import versionSatisfiesRange from '@/versions/lib/version-satisfies-range'
import supportedOperators, {
type IfversionSupportedOperator,
} from './ifversion-supported-operators'
import { createLogger } from '@/observability/logger'

const logger = createLogger(import.meta.url)

interface Branch {
cond: string
Expand Down Expand Up @@ -174,17 +177,7 @@ export default class Ifversion extends Tag {
}

if (!this.currentVersionObj) {
console.warn(
`
If this happens, it means the context prepared for rendering Liquid
did not supply an object called 'currentVersionObj'.
To fix the error, find the code that prepares the context before
calling 'liquid.parseAndRender' and make sure there's an object
called 'currentVersionObj' included there.
`
.replace(/\n\s+/g, ' ')
.trim(),
)
logger.warn('Context missing currentVersionObj for Liquid rendering')
throw new Error('currentVersionObj not found in environment context.')
}

Expand Down Expand Up @@ -222,7 +215,7 @@ export default class Ifversion extends Tag {

handleVersionNames(resolvedBranchCond: string): string {
if (!this.currentVersionObj) {
console.warn('currentVersionObj not found in ifversion context.')
logger.warn('currentVersionObj not found in ifversion context')
return resolvedBranchCond
}

Expand Down
5 changes: 4 additions & 1 deletion src/content-render/liquid/indented-data-reference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import assert from 'assert'
import { type TagToken, type Liquid } from 'liquidjs'
import { THROW_ON_EMPTY, IndentedDataReferenceError } from './error-handling'
import { getDataByLanguage } from '@/data-directory/lib/get-data'
import { createLogger } from '@/observability/logger'

const logger = createLogger(import.meta.url)

interface LiquidScope {
environments: {
Expand Down Expand Up @@ -55,7 +58,7 @@ const IndentedDataReference = {
if (THROW_ON_EMPTY) {
throw new IndentedDataReferenceError(message)
}
console.warn(message)
logger.warn(message)
}
return
}
Expand Down
24 changes: 16 additions & 8 deletions src/content-render/tests/copilot-code-blocks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import { describe, it, expect, vi } from 'vitest'
import { renderContent } from '@/content-render/index'

const { mockWarn } = vi.hoisted(() => ({ mockWarn: vi.fn() }))
vi.mock('@/observability/logger', () => ({
createLogger: () => ({
info: vi.fn(),
warn: mockWarn,
error: vi.fn(),
debug: vi.fn(),
}),
}))

describe('code-header plugin', () => {
describe('copilot language code blocks', () => {
it('should render basic copilot code block without header (no copy meta)', async () => {
Expand Down Expand Up @@ -126,18 +136,17 @@ Improve the variable names in this function

describe('edge cases', () => {
it('should handle missing reference gracefully and fall back to current code only', async () => {
// Mock console.warn to capture warning
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
mockWarn.mockClear()

const markdown =
'```copilot copy prompt ref=nonexistent-id\nImprove the variable names in this function\n```'

const html = await renderContent(markdown)

// Should warn about missing reference
expect(consoleWarnSpy).toHaveBeenCalledWith(
expect.stringContaining("Can't find referenced code block with id=nonexistent-id"),
)
// Should warn about missing reference via structured logger
expect(mockWarn).toHaveBeenCalledWith('Cannot find referenced code block', {
ref: 'nonexistent-id',
})

// Should still render with prompt button using current code only
expect(html).toContain('https://github.com/copilot?prompt=')
Expand All @@ -149,8 +158,7 @@ Improve the variable names in this function
// Should not crash or fail
expect(html).toContain('code-example')

// Restore console.warn
consoleWarnSpy.mockRestore()
mockWarn.mockClear()
})

it('should not process annotated code blocks', async () => {
Expand Down
7 changes: 4 additions & 3 deletions src/content-render/unified/alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { visit } from 'unist-util-visit'
import { h } from 'hastscript'
import octicons from '@primer/octicons'
import type { Element, Root, ElementContent } from 'hast'
import { createLogger } from '@/observability/logger'

const logger = createLogger(import.meta.url)

interface AlertType {
icon: string
Expand Down Expand Up @@ -33,9 +36,7 @@ export default function alerts({ alertTitles = {} }: { alertTitles?: Record<stri
return
const key = getAlertKey(el)
if (!(key in alertTypes)) {
console.warn(
`Alert key '${key}' should be all uppercase (change it to '${key.toUpperCase()}')`,
)
logger.warn('Alert key should be all uppercase', { key, expected: key.toUpperCase() })
}
const alertType = alertTypes[getAlertKey(el).toUpperCase()]
el.tagName = 'div'
Expand Down
11 changes: 7 additions & 4 deletions src/content-render/unified/annotate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ import { toHast } from 'mdast-util-to-hast'
import type { Root } from 'mdast'
import { header } from './code-header'
import findPage from '@/frame/lib/find-page'
import { createLogger } from '@/observability/logger'

const logger = createLogger(import.meta.url)

interface LanguageConfig {
comment: 'number' | 'slash' | 'xml' | 'percent' | 'hyphen'
Expand Down Expand Up @@ -280,10 +283,10 @@ function processAutotitleInMdast(mdast: Root, context: any): void {
child.value = page.rawTitle || 'AUTOTITLE'
} catch (error) {
// Keep AUTOTITLE if we can't get the title
console.warn(
`Could not resolve AUTOTITLE for ${node.url}:`,
error instanceof Error ? error.message : String(error),
)
logger.warn('Could not resolve AUTOTITLE', {
url: node.url,
error: error instanceof Error ? error.message : String(error),
})
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/content-render/unified/copilot-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ import octicons from '@primer/octicons'
import { parse } from 'parse5'
import { fromParse5 } from 'hast-util-from-parse5'
import { getPreMeta } from './code-header'
import { createLogger } from '@/observability/logger'
import { generatePromptId } from '../lib/prompt-id'
import type { Element, Root } from 'hast'

const logger = createLogger(import.meta.url)

export function getPrompt(
node: Element,
tree: Root,
Expand Down Expand Up @@ -55,7 +58,7 @@ function buildPromptData(
// If the 'ref=<id>' meta is found, find a matching code block to include as context in the prompt link.
const matchingCodeEl = findMatchingCode(ref as string, tree)
if (!matchingCodeEl) {
console.warn(`Can't find referenced code block with id=${ref}`)
logger.warn('Cannot find referenced code block', { ref })
return promptOnly(code)
}
// AST structure: element -> code -> text node with value property
Expand Down
8 changes: 4 additions & 4 deletions src/content-render/unified/rewrite-asset-urls.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import fs from 'fs'
import type { Element, Node } from 'hast'
import { visit } from 'unist-util-visit'
import { createLogger } from '@/observability/logger'

const logger = createLogger(import.meta.url)

// Process-level cache for stat results — file sizes don't change between deploys.
const statCache = new Map<string, number | null>()
Expand Down Expand Up @@ -59,9 +62,6 @@ function getNewSrc(node: Element): string | undefined {
return split.join('/')
} catch {
statCache.set(filePath, null)
console.warn(
`Failed to get a hash for ${src} ` +
'(This is mostly harmless and can happen with outdated translations).',
)
logger.warn('Failed to get a hash for asset URL', { src })
}
}
Loading
Loading