Skip to content

Commit d2f0387

Browse files
stephentoubCopilot
andcommitted
Add experimental multi-provider BYOK registry config across all six SDKs
Surfaces the new named-provider / provider-model registry from copilot-agent-runtime#9864 (providers + models on session create/resume) in the .NET, Node, Python, Go, Rust, and Java SDKs. The whole feature is marked experimental in each language using that SDK's existing convention. Custom agents can now bind to provider-qualified BYOK model ids (e.g. "alpha/sonnet"), letting multiple providers, multiple models per provider, and agents/subagents coexist in one session and route inference to the right provider with the configured wire model and headers. Also adds end-to-end coverage for the surface in every SDK (shared replay snapshots under test/snapshots/multi_provider_registry), and exposes "model" on the Java hand-written rpc.AgentInfo so it matches the agent-info type in the other five SDKs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 8789b28 commit d2f0387

33 files changed

Lines changed: 3137 additions & 6 deletions

dotnet/src/Client.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,8 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig config, Cance
10091009
RequestExtensions: config.RequestExtensions,
10101010
ExtensionSdkPath: config.ExtensionSdkPath,
10111011
ExtensionInfo: config.ExtensionInfo,
1012+
Providers: config.Providers,
1013+
Models: config.Models,
10121014
ToolFilterPrecedence: toolFilter.ToolFilterPrecedence);
10131015

10141016
var rpcTimestamp = Stopwatch.GetTimestamp();
@@ -1207,6 +1209,8 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
12071209
ExtensionSdkPath: config.ExtensionSdkPath,
12081210
ExtensionInfo: config.ExtensionInfo,
12091211
OpenCanvases: config.OpenCanvases,
1212+
Providers: config.Providers,
1213+
Models: config.Models,
12101214
ToolFilterPrecedence: toolFilter.ToolFilterPrecedence);
12111215

12121216
var rpcTimestamp = Stopwatch.GetTimestamp();
@@ -2402,6 +2406,8 @@ internal record CreateSessionRequest(
24022406
bool? RequestExtensions = null,
24032407
string? ExtensionSdkPath = null,
24042408
ExtensionInfo? ExtensionInfo = null,
2409+
IList<NamedProviderConfig>? Providers = null,
2410+
IList<ProviderModelConfig>? Models = null,
24052411
OptionsUpdateToolFilterPrecedence? ToolFilterPrecedence = null);
24062412
#pragma warning restore GHCP001
24072413

@@ -2494,6 +2500,8 @@ internal record ResumeSessionRequest(
24942500
string? ExtensionSdkPath = null,
24952501
ExtensionInfo? ExtensionInfo = null,
24962502
IList<OpenCanvasInstance>? OpenCanvases = null,
2503+
IList<NamedProviderConfig>? Providers = null,
2504+
IList<ProviderModelConfig>? Models = null,
24972505
OptionsUpdateToolFilterPrecedence? ToolFilterPrecedence = null);
24982506
#pragma warning restore GHCP001
24992507

@@ -2569,6 +2577,8 @@ internal record HooksInvokeResponse(
25692577
[JsonSerializable(typeof(EmbeddingCacheStorageMode))]
25702578
[JsonSerializable(typeof(ModelCapabilitiesOverride))]
25712579
[JsonSerializable(typeof(ProviderConfig))]
2580+
[JsonSerializable(typeof(NamedProviderConfig))]
2581+
[JsonSerializable(typeof(ProviderModelConfig))]
25722582
[JsonSerializable(typeof(ResumeSessionRequest))]
25732583
[JsonSerializable(typeof(ResumeSessionResponse))]
25742584
[JsonSerializable(typeof(SessionCapabilities))]

dotnet/src/Types.cs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2070,6 +2070,135 @@ public sealed class AzureOptions
20702070
public string? ApiVersion { get; set; }
20712071
}
20722072

2073+
/// <summary>
2074+
/// A named BYOK provider connection (transport + credentials only), referenced by
2075+
/// <see cref="ProviderModelConfig"/> entries via <see cref="Name"/>.
2076+
/// <para>
2077+
/// Unlike the singular, whole-session <see cref="ProviderConfig"/> — which bypasses
2078+
/// Copilot API authentication — named providers are additive and coexist with Copilot
2079+
/// API auth, so models from CAPI and one or more BYOK providers can be mixed within a
2080+
/// single session and across sub-agents. Combining named providers/models with
2081+
/// <see cref="SessionConfigBase.Provider"/> is rejected.
2082+
/// </para>
2083+
/// </summary>
2084+
[Experimental(Diagnostics.Experimental)]
2085+
public sealed class NamedProviderConfig
2086+
{
2087+
/// <summary>
2088+
/// Stable identifier referenced by <see cref="ProviderModelConfig.Provider"/>.
2089+
/// Must not contain '/'.
2090+
/// </summary>
2091+
[JsonPropertyName("name")]
2092+
public string Name { get; set; } = string.Empty;
2093+
2094+
/// <summary>
2095+
/// Provider type. Defaults to "openai" for generic OpenAI-compatible APIs.
2096+
/// </summary>
2097+
[JsonPropertyName("type")]
2098+
public string? Type { get; set; }
2099+
2100+
/// <summary>
2101+
/// Wire API format (openai/azure only). Defaults to "completions".
2102+
/// </summary>
2103+
[JsonPropertyName("wireApi")]
2104+
public string? WireApi { get; set; }
2105+
2106+
/// <summary>
2107+
/// API endpoint URL.
2108+
/// </summary>
2109+
[JsonPropertyName("baseUrl")]
2110+
public string BaseUrl { get; set; } = string.Empty;
2111+
2112+
/// <summary>
2113+
/// API key. Optional for local providers like Ollama.
2114+
/// </summary>
2115+
[JsonPropertyName("apiKey")]
2116+
public string? ApiKey { get; set; }
2117+
2118+
/// <summary>
2119+
/// Bearer token for authentication. Sets the Authorization header directly.
2120+
/// Takes precedence over <see cref="ApiKey"/> when both are set.
2121+
/// </summary>
2122+
[JsonPropertyName("bearerToken")]
2123+
public string? BearerToken { get; set; }
2124+
2125+
/// <summary>
2126+
/// Azure-specific configuration options.
2127+
/// </summary>
2128+
[JsonPropertyName("azure")]
2129+
public AzureOptions? Azure { get; set; }
2130+
2131+
/// <summary>
2132+
/// Custom HTTP headers to include in all outbound requests to the provider.
2133+
/// </summary>
2134+
[JsonPropertyName("headers")]
2135+
public IDictionary<string, string>? Headers { get; set; }
2136+
}
2137+
2138+
/// <summary>
2139+
/// A BYOK model definition that references a <see cref="NamedProviderConfig"/> by name
2140+
/// and is added to the session's selectable model list. The session-wide selection id
2141+
/// (shown in the model list and passed to model switching) is the provider-qualified
2142+
/// <c>provider/id</c>, so BYOK ids never collide with bare CAPI ids.
2143+
/// </summary>
2144+
[Experimental(Diagnostics.Experimental)]
2145+
public sealed class ProviderModelConfig
2146+
{
2147+
/// <summary>
2148+
/// Provider-local model id, unique within its provider.
2149+
/// </summary>
2150+
[JsonPropertyName("id")]
2151+
public string Id { get; set; } = string.Empty;
2152+
2153+
/// <summary>
2154+
/// Name of the <see cref="NamedProviderConfig"/> that serves this model.
2155+
/// </summary>
2156+
[JsonPropertyName("provider")]
2157+
public string Provider { get; set; } = string.Empty;
2158+
2159+
/// <summary>
2160+
/// The model name sent to the provider API for inference. Defaults to <see cref="Id"/>.
2161+
/// </summary>
2162+
[JsonPropertyName("wireModel")]
2163+
public string? WireModel { get; set; }
2164+
2165+
/// <summary>
2166+
/// Well-known base model id used for behavior/capability/config lookup. Defaults to <see cref="Id"/>.
2167+
/// </summary>
2168+
[JsonPropertyName("modelId")]
2169+
public string? ModelId { get; set; }
2170+
2171+
/// <summary>
2172+
/// Display name for model pickers. Defaults to the provider-qualified selection id.
2173+
/// </summary>
2174+
[JsonPropertyName("name")]
2175+
public string? Name { get; set; }
2176+
2177+
/// <summary>
2178+
/// Maximum prompt/input tokens for the model.
2179+
/// </summary>
2180+
[JsonPropertyName("maxPromptTokens")]
2181+
public int? MaxPromptTokens { get; set; }
2182+
2183+
/// <summary>
2184+
/// Maximum context window tokens for the model.
2185+
/// </summary>
2186+
[JsonPropertyName("maxContextWindowTokens")]
2187+
public int? MaxContextWindowTokens { get; set; }
2188+
2189+
/// <summary>
2190+
/// Maximum output tokens for the model.
2191+
/// </summary>
2192+
[JsonPropertyName("maxOutputTokens")]
2193+
public int? MaxOutputTokens { get; set; }
2194+
2195+
/// <summary>
2196+
/// Optional capability overrides (vision, tool_calls, reasoning, etc.) for the synthesized model.
2197+
/// </summary>
2198+
[JsonPropertyName("capabilities")]
2199+
public ModelCapabilitiesOverride? Capabilities { get; set; }
2200+
}
2201+
20732202
// ============================================================================
20742203
// MCP Server Configuration Types
20752204
// ============================================================================
@@ -2494,6 +2623,8 @@ protected SessionConfigBase(SessionConfigBase? other)
24942623
OnPermissionRequest = other.OnPermissionRequest;
24952624
OnUserInputRequest = other.OnUserInputRequest;
24962625
Provider = other.Provider;
2626+
Providers = other.Providers is not null ? [.. other.Providers] : null;
2627+
Models = other.Models is not null ? [.. other.Models] : null;
24972628
EnableSessionTelemetry = other.EnableSessionTelemetry;
24982629
SkipCustomInstructions = other.SkipCustomInstructions;
24992630
CustomAgentsLocalOnly = other.CustomAgentsLocalOnly;
@@ -2649,6 +2780,21 @@ protected SessionConfigBase(SessionConfigBase? other)
26492780
/// <summary>Custom model provider configuration for the session.</summary>
26502781
public ProviderConfig? Provider { get; set; }
26512782

2783+
/// <summary>
2784+
/// Named BYOK provider connections (transport + credentials). Additive to Copilot
2785+
/// API authentication (unlike <see cref="Provider"/>); combine with <see cref="Models"/>.
2786+
/// Cannot be combined with <see cref="Provider"/>.
2787+
/// </summary>
2788+
[Experimental(Diagnostics.Experimental)]
2789+
public IList<NamedProviderConfig>? Providers { get; set; }
2790+
2791+
/// <summary>
2792+
/// BYOK model definitions added to the session's selectable model list, each
2793+
/// referencing a <see cref="Providers"/> entry by name.
2794+
/// </summary>
2795+
[Experimental(Diagnostics.Experimental)]
2796+
public IList<ProviderModelConfig>? Models { get; set; }
2797+
26522798
/// <summary>
26532799
/// Enables or disables internal session telemetry for this session.
26542800
/// When <c>false</c>, disables session telemetry. When <c>null</c> (the default) or <c>true</c>,

0 commit comments

Comments
 (0)