



























@@ -2932,6 +2932,53 @@ describe("openai transport stream", () => {
29322932expect(params.tool_choice).toBe("required");
29332933});
293429342935+it("sorts Responses tools by name for stable prompt-cache payloads", () => {
2936+const model = {
2937+id: "gpt-5.4",
2938+name: "GPT-5.4",
2939+api: "openai-responses",
2940+provider: "openai",
2941+baseUrl: "https://api.openai.com/v1",
2942+reasoning: true,
2943+input: ["text"],
2944+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
2945+contextWindow: 200000,
2946+maxTokens: 8192,
2947+} satisfies Model<"openai-responses">;
2948+const zetaTool = {
2949+name: "zeta",
2950+description: "Z",
2951+parameters: { type: "object", properties: {}, additionalProperties: false },
2952+};
2953+const alphaTool = {
2954+name: "alpha",
2955+description: "A",
2956+parameters: { type: "object", properties: {}, additionalProperties: false },
2957+};
2958+2959+const first = buildOpenAIResponsesParams(
2960+model,
2961+{
2962+systemPrompt: "system",
2963+messages: [],
2964+tools: [zetaTool, alphaTool],
2965+} as never,
2966+{ sessionId: "session-123" } as never,
2967+) as { tools?: Array<{ name?: string }> };
2968+const second = buildOpenAIResponsesParams(
2969+model,
2970+{
2971+systemPrompt: "system",
2972+messages: [],
2973+tools: [alphaTool, zetaTool],
2974+} as never,
2975+{ sessionId: "session-123" } as never,
2976+) as { tools?: Array<{ name?: string }> };
2977+2978+expect(first.tools?.map((tool) => tool.name)).toEqual(["alpha", "zeta"]);
2979+expect(first.tools).toEqual(second.tools);
2980+});
2981+29352982it("falls back to strict:false when a native OpenAI tool schema is not strict-compatible", () => {
29362983const params = buildOpenAIResponsesParams(
29372984{
@@ -3809,6 +3856,54 @@ describe("openai transport stream", () => {
38093856expect(notOptedIn.prompt_cache_key).toBeUndefined();
38103857});
381138583859+it("sorts Chat Completions tools by function name for stable prompt-cache payloads", () => {
3860+const model = {
3861+id: "custom-model",
3862+name: "Custom Model",
3863+api: "openai-completions",
3864+provider: "custom-cpa",
3865+baseUrl: "https://proxy.example.com/v1",
3866+compat: { supportsPromptCacheKey: true },
3867+reasoning: false,
3868+input: ["text"],
3869+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
3870+contextWindow: 32768,
3871+maxTokens: 8192,
3872+} as unknown as Model<"openai-completions">;
3873+const zetaTool = {
3874+name: "zeta",
3875+description: "Z",
3876+parameters: { type: "object", properties: {} },
3877+};
3878+const alphaTool = {
3879+name: "alpha",
3880+description: "A",
3881+parameters: { type: "object", properties: {} },
3882+};
3883+3884+const first = buildOpenAICompletionsParams(
3885+model,
3886+{
3887+systemPrompt: "system",
3888+messages: [],
3889+tools: [zetaTool, alphaTool],
3890+} as never,
3891+{ sessionId: "session-123" },
3892+) as { tools?: Array<{ function?: { name?: string } }> };
3893+const second = buildOpenAICompletionsParams(
3894+model,
3895+{
3896+systemPrompt: "system",
3897+messages: [],
3898+tools: [alphaTool, zetaTool],
3899+} as never,
3900+{ sessionId: "session-123" },
3901+) as { tools?: Array<{ function?: { name?: string } }> };
3902+3903+expect(first.tools?.map((tool) => tool.function?.name)).toEqual(["alpha", "zeta"]);
3904+expect(first.tools).toEqual(second.tools);
3905+});
3906+38123907it("disables developer-role-only compat defaults for configured custom proxy completions providers", () => {
38133908const params = buildOpenAICompletionsParams(
38143909{
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。