byokLmProxyService.ts ×11

Frontier kind: Code frontier

unlabeled · c_024d831cf893

435 tests · 8394 LOC · 42 files · introduces 0 tests · 133 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
11 ranges133 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
1293 ranges8394 lines · 42 files · Browse complete extent
All tests (intent)
435 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

1 file ranked by introduced lines: 133 introduced LOC across 11 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/agentHost/node/copilot/byokLmProxyService.ts 133 introduced LOC · 11 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- byokLmProxyService.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import type * as http from 'http';
7 > import { createDecorator } from '../../../instantiation/common/instantiation.js';
8 > import { ILogService } from '../../../log/common/log.js';
9 > import { IByokLmBridgeRegistry } from '../byokLmBridgeRegistry.js';
10 > import { parseProxyBearer } from '../claude/claudeProxyAuth.js';
11 > import {
12 > ILoopbackProxyHandle,
13 > ILoopbackProxyRuntime,
14 > IProxyInFlight,
15 > LoopbackProxyServer,
16 > readProxyRequestBody,
17 > } from '../shared/loopbackProxyServer.js';
18 > import {
19 > IOpenAiChatRequest,
20 > OpenAiTranslationError,
21 > bridgeResultToSseFrames,
22 > openAiErrorBody,
23 > openAiRequestToBridge,
24 > } from './byokOpenAiTranslation.js';
25 >
26 > // #region Public types
27 >
28 > /**
29 > * Handle returned by {@link IByokLmProxyService.start}. Refcounts the shared
30 > * loopback server (see {@link LoopbackProxyServer}): when every handle is
31 > * disposed the listener closes and the nonce is destroyed; the next `start()`
32 > * rebinds with a fresh port and nonce.
33 > *
34 > * **Subprocess ownership invariant.** Callers that hand `baseUrl`/`nonce` to
35 > * the Copilot SDK runtime subprocess MUST kill that subprocess before calling
36 > * `dispose()` — after disposal the proxy may rebind on a different port and the
37 > * subprocess would silently lose its endpoint (same contract as the Claude and
38 > * Codex proxies).
39 > */
40 > export interface IByokLmProxyHandle extends ILoopbackProxyHandle {
41 > /** e.g. `http://127.0.0.1:54321` — no trailing slash. */
42 > readonly baseUrl: string;
43 > /** 256-bit hex string. Combine with a session id as `Bearer <nonce>.<sessionId>`. */
44 > readonly nonce: string;
45 > /**
46 > * Build the provider `baseUrl` for a given BYOK vendor. The vendor is
47 > * encoded into the path so a single proxy can serve every vendor; the
48 > * runtime appends `/chat/completions` to this URL.
49 > */
50 > providerBaseUrl(vendor: string): string;
51 > }
52 >
53 > export const IByokLmProxyService = createDecorator<IByokLmProxyService>('byokLmProxyService');
54 >
55 > export interface IByokLmProxyService {
56 > readonly _serviceBrand: undefined;
57 >
58 > /** Start the proxy (if not already running) and return a refcounted handle. */
59 > start(): Promise<IByokLmProxyHandle>;
60 >
61 > /**
62 > * Force-close the proxy regardless of refcount and abort in-flight
63 > * requests. Idempotent; subsequent `start()` calls rebind.
64 > */
65 > dispose(): void;
66 > }
67 >
68 > // #endregion
69 >
70 > const PROXY_USER_FACING_NAME = 'ByokLmProxyService';
71 > const VENDOR_PATH_PREFIX = '/v/';
72 > const CHAT_COMPLETIONS_SUFFIX = '/chat/completions';
73 >
74 > /**
75 > * The BYOK proxy keeps no per-bind mutable state: the active renderer bridge is
76 > * resolved from {@link IByokLmBridgeRegistry} at request time, and the nonce
77 > * lives on the runtime owned by {@link LoopbackProxyServer}.
78 > */
79 > type ByokLmProxyState = undefined;
80 >
81 > /**
82 > * Local OpenAI-compatible HTTP proxy that lets the Copilot SDK runtime run
83 > * BYOK models provided by VS Code extensions. The runtime is configured with a
84 > * `type: 'openai'`, `wireApi: 'completions'` provider whose `baseUrl` points
85 > * here; inbound `POST /v/<vendor>/chat/completions` requests are authenticated,
86 > * translated, and forwarded to the renderer LM API via
87 > * {@link IByokLmBridgeRegistry}, and the buffered completion is streamed back
88 > * as OpenAI Chat Completions SSE.
89 > *
90 > * The server lifecycle — lazy bind on `127.0.0.1`, nonce minting, refcounted
91 > * handles, in-flight tracking, and teardown — is inherited from
92 > * {@link LoopbackProxyServer}; this subclass only implements request routing.
93 > */
94 > export class ByokLmProxyService extends LoopbackProxyServer<ByokLmProxyState> implements IByokLmProxyService {
95 >
96 > declare readonly _serviceBrand: undefined;
97 >
98 > constructor(
99 @ILogService logService: ILogService,
100 @IByokLmBridgeRegistry private readonly _bridgeRegistry: IByokLmBridgeRegistry,
102 super(PROXY_USER_FACING_NAME, logService);
103 }
105 > protected createState(): ByokLmProxyState {
106 // No per-bind state — the bridge is resolved from the registry per request.
107 return undefined;
108 }
110 > async start(): Promise<IByokLmProxyHandle> {
111 const { runtime, release } = await this.acquire();
112
125 };
126 }
128 > /** Emit the base's fallback failure using the OpenAI error envelope. */
129 > protected override writeInternalError(res: http.ServerResponse): void {
130 this._writeJsonError(res, 500, 'Internal proxy error');
131 }
133 > protected override async handleRequest(req: http.IncomingMessage, res: http.ServerResponse, runtime: ILoopbackProxyRuntime<ByokLmProxyState>): Promise<void> {
134 const method = req.method ?? 'GET';
135 const pathname = new URL(req.url ?? '/', 'http://127.0.0.1').pathname;
158 this._writeJsonError(res, 404, `No route for ${method} ${pathname}`, 'not_found_error');
159 }
161 > /**
162 > * Extract the vendor from a `/v/<vendor>/chat/completions` path, or return
163 > * `undefined` when the path is not a chat-completions route.
164 > */
165 > private _parseVendorFromChatPath(pathname: string): string | undefined {
166 if (!pathname.startsWith(VENDOR_PATH_PREFIX) || !pathname.endsWith(CHAT_COMPLETIONS_SUFFIX)) {
167 return undefined;
185 return vendor;
186 }
188 > private async _handleChatCompletions(req: http.IncomingMessage, res: http.ServerResponse, runtime: ILoopbackProxyRuntime<ByokLmProxyState>, vendor: string): Promise<void> {
189 let body: IOpenAiChatRequest;
190 try {
256 }
257 }
259 > private _writeJsonError(res: http.ServerResponse, status: number, message: string, type = 'api_error'): void {
260 if (res.headersSent || res.writableEnded) {
261 return;
264 res.end(openAiErrorBody(message, type));
265 }
267 >
268 > /**
269 > * No-op {@link IByokLmProxyService} for agent host entrypoints that do not
270 > * support BYOK — e.g. the remote agent host, where no extension host runs
271 > * alongside the agent host to serve the renderer LM API.
272 > *
273 > */
274 > export class NullByokLmProxyService implements IByokLmProxyService {
275 >
276 > declare readonly _serviceBrand: undefined;
277 >
278 > start(): Promise<IByokLmProxyHandle> {
279 return Promise.reject(new Error('BYOK is not supported in this agent host'));
280 }
282 > dispose(): void {
283 // No-op: the null proxy never binds a socket, so there is nothing to close.
284 }