mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
514f630904
The header propagation chain (middleware -> context -> policy) had two bugs that combined to threaten D5/D6 header-forwarding: 1. AimockHeaderMiddleware.ToDictionary used the default ORDINAL case-sensitive comparer. ASP.NET's IHeaderDictionary is case-insensitive, but iterating the underlying store can yield case-variant duplicates (e.g., a misbehaving proxy injecting both `X-Foo` and `x-foo`). Default-comparer ToDictionary throws ArgumentException on duplicates and fails the request. 2. AimockHeaderContext.Set lowercased all keys via ToLowerInvariant. AimockHeaderMiddleware captured original case; the context then mutated the casing; AimockHeaderPolicy.TryGetValue then matched against whatever case the OpenAI SDK happened to use. This is inconsistent and aimock fixture matching can be case-sensitive depending on configuration. Canonical strategy: preserve original header casing as captured by the middleware, but compare case-insensitively throughout via StringComparer.OrdinalIgnoreCase. The middleware now also groups variants defensively so duplicate keys cannot blow up the dictionary build. AimockHeaderPolicy's add-if-absent TryGetValue then works correctly under case-insensitive comparison without any further changes.
24 lines
1.2 KiB
C#
24 lines
1.2 KiB
C#
// STOPGAP: This integration-level header propagation replaces once copilotkit-sdk-dotnet
|
|
// ships (Microsoft contribution, ETA mid-2026). When that SDK lands, delete this code
|
|
// and use the SDK's built-in header propagation.
|
|
// See: https://www.notion.so/copilotkit/3543aa3818528150b6acc5b872ad7fe5
|
|
|
|
// TODO(copilotkit-sdk-dotnet): migrate to SDK-level header propagation
|
|
public static class AimockHeaderContext
|
|
{
|
|
private static readonly AsyncLocal<Dictionary<string, string>> _headers = new();
|
|
|
|
public static void Set(Dictionary<string, string> headers)
|
|
{
|
|
// Canonical strategy: preserve the original header casing captured upstream by
|
|
// AimockHeaderMiddleware, but compare keys case-insensitively throughout.
|
|
// AimockHeaderPolicy relies on case-insensitive TryGetValue to add-if-absent.
|
|
var filtered = headers
|
|
.Where(h => h.Key.StartsWith("x-", StringComparison.OrdinalIgnoreCase))
|
|
.ToDictionary(h => h.Key, h => h.Value, StringComparer.OrdinalIgnoreCase);
|
|
_headers.Value = filtered;
|
|
}
|
|
|
|
public static Dictionary<string, string> Get() => _headers.Value ?? new(StringComparer.OrdinalIgnoreCase);
|
|
}
|