refactor: centralize provider stream fallback ownership

This commit is contained in:
Peter Steinberger
2026-04-27 12:11:29 +01:00
parent 8200d878a3
commit e98f976a70
9 changed files with 99 additions and 44 deletions

View File

@@ -3,6 +3,7 @@ import { describe, expect, it } from "vitest";
import {
buildCopilotDynamicHeaders,
createHtmlEntityToolCallArgumentDecodingWrapper,
createPayloadPatchStreamWrapper,
defaultToolStreamExtraParams,
decodeHtmlEntitiesInObject,
hasCopilotVisionInput,
@@ -182,3 +183,47 @@ describe("createHtmlEntityToolCallArgumentDecodingWrapper", () => {
});
});
});
describe("createPayloadPatchStreamWrapper", () => {
it("passes stream call options to payload patches", () => {
let captured: Record<string, unknown> = {};
const baseStreamFn: StreamFn = (_model, _context, options) => {
const payload: Record<string, unknown> = {};
options?.onPayload?.(payload, _model);
captured = payload;
return {} as ReturnType<StreamFn>;
};
const wrapped = createPayloadPatchStreamWrapper(baseStreamFn, ({ payload, options }) => {
payload.reasoning = (options as { reasoning?: unknown } | undefined)?.reasoning;
});
void wrapped(
{ id: "model" } as never,
{ messages: [] } as never,
{
reasoning: "medium",
} as never,
);
expect(captured).toEqual({ reasoning: "medium" });
});
it("calls the underlying stream directly when shouldPatch rejects the model", () => {
let onPayloadWasInstalled = false;
const baseStreamFn: StreamFn = (_model, _context, options) => {
onPayloadWasInstalled = typeof options?.onPayload === "function";
return {} as ReturnType<StreamFn>;
};
const wrapped = createPayloadPatchStreamWrapper(
baseStreamFn,
({ payload }) => {
payload.unexpected = true;
},
{ shouldPatch: () => false },
);
void wrapped({ id: "model" } as never, { messages: [] } as never, {});
expect(onPayloadWasInstalled).toBe(false);
});
});

View File

@@ -132,13 +132,26 @@ export function createPayloadPatchStreamWrapper(
patchPayload: (params: {
payload: Record<string, unknown>;
model: Parameters<StreamFn>[0];
context: Parameters<StreamFn>[1];
options: Parameters<StreamFn>[2];
}) => void,
wrapperOptions?: {
shouldPatch?: (params: {
model: Parameters<StreamFn>[0];
context: Parameters<StreamFn>[1];
options: Parameters<StreamFn>[2];
}) => boolean;
},
): StreamFn {
const underlying = baseStreamFn ?? streamSimple;
return (model, context, options) =>
streamWithPayloadPatch(underlying, model, context, options, (payload) =>
patchPayload({ payload, model }),
return (model, context, options) => {
if (wrapperOptions?.shouldPatch && !wrapperOptions.shouldPatch({ model, context, options })) {
return underlying(model, context, options);
}
return streamWithPayloadPatch(underlying, model, context, options, (payload) =>
patchPayload({ payload, model, context, options }),
);
};
}
export type DeepSeekV4ThinkingLevel = ProviderWrapStreamFnContext["thinkingLevel"];