codex-provider-overrides.test.ts 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import { describe, expect, it } from "vitest";
  2. import {
  3. applyCodexProviderOverrides,
  4. applyCodexProviderOverridesWithAudit,
  5. } from "@/lib/codex/provider-overrides";
  6. describe("Codex 供应商级参数覆写", () => {
  7. it("当 providerType 不是 codex 时,应直接返回原对象且不做任何处理", () => {
  8. const provider = {
  9. providerType: "claude",
  10. codexReasoningEffortPreference: "high",
  11. codexParallelToolCallsPreference: "false",
  12. };
  13. const input: Record<string, unknown> = {
  14. model: "gpt-5-codex",
  15. input: [],
  16. parallel_tool_calls: true,
  17. reasoning: { effort: "low", summary: "auto" },
  18. };
  19. const output = applyCodexProviderOverrides(provider as any, input);
  20. expect(output).toBe(input);
  21. expect(output).toEqual(input);
  22. });
  23. it("当所有偏好均为 inherit/null 时,应保持请求不变", () => {
  24. const provider = {
  25. providerType: "codex",
  26. codexReasoningEffortPreference: null,
  27. codexReasoningSummaryPreference: null,
  28. codexTextVerbosityPreference: null,
  29. codexParallelToolCallsPreference: null,
  30. };
  31. const input: Record<string, unknown> = {
  32. model: "gpt-5-codex",
  33. input: [],
  34. parallel_tool_calls: false,
  35. reasoning: { effort: "low", summary: "auto" },
  36. text: { verbosity: "medium" },
  37. };
  38. const snapshot = structuredClone(input);
  39. const output = applyCodexProviderOverrides(provider as any, input);
  40. expect(output).toEqual(snapshot);
  41. expect(input).toEqual(snapshot);
  42. });
  43. it("当偏好值为字符串 inherit 时,应视为不覆写", () => {
  44. const provider = {
  45. providerType: "codex",
  46. codexReasoningEffortPreference: "inherit",
  47. codexReasoningSummaryPreference: "inherit",
  48. codexTextVerbosityPreference: "inherit",
  49. codexParallelToolCallsPreference: "inherit",
  50. };
  51. const input: Record<string, unknown> = {
  52. model: "gpt-5-codex",
  53. input: [],
  54. parallel_tool_calls: false,
  55. reasoning: { effort: "low", summary: "auto" },
  56. text: { verbosity: "medium", other: "keep" },
  57. };
  58. const snapshot = structuredClone(input);
  59. const output = applyCodexProviderOverrides(provider as any, input);
  60. expect(output).toEqual(snapshot);
  61. expect(input).toEqual(snapshot);
  62. });
  63. it("当强制 parallel_tool_calls 时,应覆写为对应布尔值", () => {
  64. const provider = {
  65. providerType: "codex",
  66. codexParallelToolCallsPreference: "false",
  67. };
  68. const input: Record<string, unknown> = {
  69. model: "gpt-5-codex",
  70. input: [],
  71. parallel_tool_calls: true,
  72. };
  73. const output = applyCodexProviderOverrides(provider as any, input);
  74. expect(output.parallel_tool_calls).toBe(false);
  75. expect(input.parallel_tool_calls).toBe(true);
  76. });
  77. it("当强制 reasoning.effort/summary 时,应覆写并保留 reasoning 的其他字段", () => {
  78. const provider = {
  79. providerType: "codex",
  80. codexReasoningEffortPreference: "high",
  81. codexReasoningSummaryPreference: "detailed",
  82. };
  83. const input: Record<string, unknown> = {
  84. model: "gpt-5-codex",
  85. input: [],
  86. reasoning: { effort: "low", summary: "auto", extra: "keep" },
  87. };
  88. const output = applyCodexProviderOverrides(provider as any, input);
  89. expect(output.reasoning).toEqual({ effort: "high", summary: "detailed", extra: "keep" });
  90. expect((input.reasoning as any).effort).toBe("low");
  91. });
  92. it("当请求缺少 reasoning/text 时,强制值应自动补齐对象结构", () => {
  93. const provider = {
  94. providerType: "codex",
  95. codexReasoningEffortPreference: "minimal",
  96. codexReasoningSummaryPreference: "auto",
  97. codexTextVerbosityPreference: "high",
  98. };
  99. const input: Record<string, unknown> = {
  100. model: "gpt-5-codex",
  101. input: [],
  102. };
  103. const output = applyCodexProviderOverrides(provider as any, input);
  104. expect(output.reasoning).toEqual({ effort: "minimal", summary: "auto" });
  105. expect(output.text).toEqual({ verbosity: "high" });
  106. });
  107. it("当强制 service_tier 时,应覆写顶层 service_tier 字段", () => {
  108. const provider = {
  109. providerType: "codex",
  110. codexServiceTierPreference: "priority",
  111. };
  112. const input: Record<string, unknown> = {
  113. model: "gpt-5-codex",
  114. input: [],
  115. service_tier: "default",
  116. };
  117. const output = applyCodexProviderOverrides(provider as any, input);
  118. expect(output.service_tier).toBe("priority");
  119. expect(input.service_tier).toBe("default");
  120. });
  121. it("审计:当 providerType 不是 codex 时,应返回 audit=null 且保持引用不变", () => {
  122. const provider = {
  123. id: 123,
  124. name: "P",
  125. providerType: "claude",
  126. codexParallelToolCallsPreference: "false",
  127. };
  128. const input: Record<string, unknown> = {
  129. model: "gpt-5-codex",
  130. input: [],
  131. parallel_tool_calls: true,
  132. };
  133. const result = applyCodexProviderOverridesWithAudit(provider as any, input);
  134. expect(result.request).toBe(input);
  135. expect(result.audit).toBeNull();
  136. });
  137. it("审计:当所有偏好均为 inherit/null 时,应返回 audit=null 且不做覆写", () => {
  138. const provider = {
  139. providerType: "codex",
  140. codexReasoningEffortPreference: "inherit",
  141. codexReasoningSummaryPreference: null,
  142. codexTextVerbosityPreference: "inherit",
  143. codexParallelToolCallsPreference: null,
  144. };
  145. const input: Record<string, unknown> = {
  146. model: "gpt-5-codex",
  147. input: [],
  148. parallel_tool_calls: false,
  149. reasoning: { effort: "low", summary: "auto" },
  150. text: { verbosity: "medium" },
  151. };
  152. const result = applyCodexProviderOverridesWithAudit(provider as any, input);
  153. expect(result.request).toBe(input);
  154. expect(result.audit).toBeNull();
  155. });
  156. it("审计:当偏好命中但值未变化时,应标记 changed=false 并记录 before/after", () => {
  157. const provider = {
  158. id: 1,
  159. name: "codex-provider",
  160. providerType: "codex",
  161. codexParallelToolCallsPreference: "false",
  162. };
  163. const input: Record<string, unknown> = {
  164. model: "gpt-5-codex",
  165. input: [],
  166. parallel_tool_calls: false,
  167. };
  168. const result = applyCodexProviderOverridesWithAudit(provider as any, input);
  169. expect(result.audit?.hit).toBe(true);
  170. expect(result.audit?.changed).toBe(false);
  171. expect(result.audit?.providerId).toBe(1);
  172. expect(result.audit?.providerName).toBe("codex-provider");
  173. const parallelChange = result.audit?.changes.find((c) => c.path === "parallel_tool_calls");
  174. expect(parallelChange).toEqual({
  175. path: "parallel_tool_calls",
  176. before: false,
  177. after: false,
  178. changed: false,
  179. });
  180. });
  181. it("审计:当偏好命中且值变化时,应标记 changed=true 并记录变化明细", () => {
  182. const provider = {
  183. id: 2,
  184. name: "codex-provider",
  185. providerType: "codex",
  186. codexReasoningEffortPreference: "high",
  187. codexReasoningSummaryPreference: "detailed",
  188. codexTextVerbosityPreference: "high",
  189. codexParallelToolCallsPreference: "true",
  190. };
  191. const input: Record<string, unknown> = {
  192. model: "gpt-5-codex",
  193. input: [],
  194. parallel_tool_calls: false,
  195. reasoning: { effort: "low", summary: "auto" },
  196. text: { verbosity: "low" },
  197. };
  198. const result = applyCodexProviderOverridesWithAudit(provider as any, input);
  199. expect(result.audit?.hit).toBe(true);
  200. expect(result.audit?.changed).toBe(true);
  201. const changedPaths = (result.audit?.changes ?? []).filter((c) => c.changed).map((c) => c.path);
  202. expect(changedPaths).toEqual([
  203. "parallel_tool_calls",
  204. "reasoning.effort",
  205. "reasoning.summary",
  206. "text.verbosity",
  207. ]);
  208. });
  209. it("审计:当客户端原本就携带 priority service_tier 时,也应保留 fast 命中记录", () => {
  210. const provider = {
  211. id: 2,
  212. name: "codex-provider",
  213. providerType: "codex",
  214. codexReasoningEffortPreference: "inherit",
  215. codexReasoningSummaryPreference: null,
  216. codexTextVerbosityPreference: null,
  217. codexParallelToolCallsPreference: null,
  218. codexServiceTierPreference: null,
  219. };
  220. const input: Record<string, unknown> = {
  221. model: "gpt-5-codex",
  222. input: [],
  223. service_tier: "priority",
  224. };
  225. const result = applyCodexProviderOverridesWithAudit(provider as any, input);
  226. expect(result.audit?.hit).toBe(true);
  227. expect(result.audit?.changed).toBe(false);
  228. expect(result.audit?.changes).toContainEqual({
  229. path: "service_tier",
  230. before: "priority",
  231. after: "priority",
  232. changed: false,
  233. });
  234. });
  235. });