1
>
/*---------------------------------------------------------------------------------------------
byokLmBridgeRegistry.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 { Disposable, DisposableStore, IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
7
>
import { createDecorator } from '../../instantiation/common/instantiation.js';
8
>
import { IByokLmBridgeConnection, IByokLmModelInfo } from '../common/agentHostByokLm.js';
9
>
10
>
export const IByokLmBridgeRegistry = createDecorator<IByokLmBridgeRegistry>('byokLmBridgeRegistry');
11
>
12
>
/**
13
>
* Node-side registry of renderer {@link IByokLmBridgeConnection}s keyed by
14
>
* client id. Populated by the agent host's connection lifecycle (one entry per
15
>
* connected renderer) and consumed by {@link IByokLmProxyService} (inference
16
>
* routing) and {@link CopilotAgent} (model catalogue).
17
>
*
18
>
* **Single serving window, multiple connections.** BYOK is serviced by the
19
>
* renderer LM API, whose BYOK models are a property of the user's installed
20
>
* extensions, not of a particular window — so every window that registers the
21
>
* handler exposes the same set. Both the main workbench and the dedicated Agents
22
>
* app register it (each runs a full extension host whose LM API holds the same
23
>
* BYOK models), so either can serve. A connection that connects without binding
24
>
* the handler never pushes a snapshot and is treated as non-serving. The registry
25
>
* therefore does NOT aggregate per-window model sets; it surfaces the models from
26
>
* any one *serving* window (preferring one that actually has models) and routes
27
>
* inference there, automatically excluding non-serving windows.
28
>
*
29
>
* **Push, not pull.** Each connection pushes its current model snapshot over
30
>
* {@link IByokLmBridgeConnection.onDidChangeModels} (on subscribe and on every
31
>
* change); the registry subscribes on {@link register}, caches each snapshot, and
32
>
* fires {@link onDidChangeModels} when the serving model set changes. A connection
33
>
* becomes "serving" once it pushes its first snapshot (even an empty one).
34
>
*/
35
>
export interface IByokLmBridgeRegistry {
36
>
readonly _serviceBrand: undefined;
37
>
38
>
/** Register a renderer connection. Disposing the result removes it. */
39
>
register(clientId: string, connection: IByokLmBridgeConnection): IDisposable;
40
>
41
>
/**
42
>
* The serving window's BYOK models, read synchronously from the cache (no
43
>
* enumeration). Use this for fast reads driven by {@link onDidChangeModels}.
44
>
*/
45
>
getModels(): readonly IByokLmModelInfo[];
46
>
47
>
/**
48
>
* A connection that can serve BYOK inference, or `undefined` when none can.
49
>
* All serving windows expose the same models, so any one is a valid target.
50
>
*/
51
>
getServingConnection(): IByokLmBridgeConnection | undefined;
52
>
53
>
/**
54
>
* Subscribe to changes in the set of registered connections (a renderer
55
>
* connecting or disconnecting) or in the serving window's pushed models, so
56
>
* consumers can re-read {@link getModels}. Disposing the result removes the
57
>
* listener.
58
>
*/
59
>
onDidChangeModels(listener: () => void): IDisposable;
60
>
}
61
>
62
>
/**
63
>
* Per-connection registry entry. `models` is `undefined` until the connection
64
>
* pushes its first snapshot; a connection with defined `models` is "serving"
65
>
* (it pushed, even an empty list). Non-serving windows (those that did not
66
>
* register the BYOK handler and therefore never push) keep `models === undefined`.
67
>
*/
68
>
interface IConnectionEntry {
69
>
readonly connection: IByokLmBridgeConnection;
70
>
models: readonly IByokLmModelInfo[] | undefined;
71
>
readonly store: DisposableStore;
72
>
}
73
>
74
>
export class ByokLmBridgeRegistry implements IByokLmBridgeRegistry {
75
76
declare readonly _serviceBrand: undefined;