anthropic-provider-overrides.test.ts 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. import { describe, expect, it } from "vitest";
  2. import {
  3. applyAnthropicProviderOverrides,
  4. applyAnthropicProviderOverridesWithAudit,
  5. } from "@/lib/anthropic/provider-overrides";
  6. describe("Anthropic Provider Overrides", () => {
  7. describe("Provider type filtering", () => {
  8. it("should return unchanged request for non-claude/claude-auth providers (codex)", () => {
  9. const provider = {
  10. providerType: "codex",
  11. anthropicMaxTokensPreference: "32000",
  12. anthropicThinkingBudgetPreference: "10240",
  13. };
  14. const input: Record<string, unknown> = {
  15. model: "claude-3-opus-20240229",
  16. messages: [],
  17. max_tokens: 8000,
  18. };
  19. const output = applyAnthropicProviderOverrides(provider, input);
  20. expect(output).toBe(input);
  21. expect(output).toEqual(input);
  22. });
  23. it("should return unchanged request for non-claude/claude-auth providers (gemini)", () => {
  24. const provider = {
  25. providerType: "gemini",
  26. anthropicMaxTokensPreference: "32000",
  27. anthropicThinkingBudgetPreference: "10240",
  28. };
  29. const input: Record<string, unknown> = {
  30. model: "gemini-pro",
  31. messages: [],
  32. max_tokens: 8000,
  33. };
  34. const output = applyAnthropicProviderOverrides(provider, input);
  35. expect(output).toBe(input);
  36. });
  37. it("should return unchanged request for non-claude/claude-auth providers (openai-compatible)", () => {
  38. const provider = {
  39. providerType: "openai-compatible",
  40. anthropicMaxTokensPreference: "16000",
  41. };
  42. const input: Record<string, unknown> = {
  43. model: "gpt-4",
  44. messages: [],
  45. };
  46. const output = applyAnthropicProviderOverrides(provider, input);
  47. expect(output).toBe(input);
  48. });
  49. it("should apply overrides for 'claude' provider type", () => {
  50. const provider = {
  51. providerType: "claude",
  52. anthropicMaxTokensPreference: "32000",
  53. };
  54. const input: Record<string, unknown> = {
  55. model: "claude-3-opus-20240229",
  56. messages: [],
  57. max_tokens: 8000,
  58. };
  59. const output = applyAnthropicProviderOverrides(provider, input);
  60. expect(output.max_tokens).toBe(32000);
  61. });
  62. it("should apply overrides for 'claude-auth' provider type", () => {
  63. const provider = {
  64. providerType: "claude-auth",
  65. anthropicMaxTokensPreference: "16000",
  66. };
  67. const input: Record<string, unknown> = {
  68. model: "claude-3-sonnet-20240229",
  69. messages: [],
  70. max_tokens: 4000,
  71. };
  72. const output = applyAnthropicProviderOverrides(provider, input);
  73. expect(output.max_tokens).toBe(16000);
  74. });
  75. });
  76. describe("max_tokens override", () => {
  77. it("should not change request when preference is 'inherit'", () => {
  78. const provider = {
  79. providerType: "claude",
  80. anthropicMaxTokensPreference: "inherit",
  81. };
  82. const input: Record<string, unknown> = {
  83. model: "claude-3-opus-20240229",
  84. messages: [],
  85. max_tokens: 8000,
  86. };
  87. const snapshot = structuredClone(input);
  88. const output = applyAnthropicProviderOverrides(provider, input);
  89. expect(output).toEqual(snapshot);
  90. });
  91. it("should not change request when preference is null", () => {
  92. const provider = {
  93. providerType: "claude",
  94. anthropicMaxTokensPreference: null,
  95. };
  96. const input: Record<string, unknown> = {
  97. model: "claude-3-opus-20240229",
  98. messages: [],
  99. max_tokens: 8000,
  100. };
  101. const snapshot = structuredClone(input);
  102. const output = applyAnthropicProviderOverrides(provider, input);
  103. expect(output).toEqual(snapshot);
  104. });
  105. it("should not change request when preference is undefined", () => {
  106. const provider = {
  107. providerType: "claude",
  108. anthropicMaxTokensPreference: undefined,
  109. };
  110. const input: Record<string, unknown> = {
  111. model: "claude-3-opus-20240229",
  112. messages: [],
  113. max_tokens: 8000,
  114. };
  115. const snapshot = structuredClone(input);
  116. const output = applyAnthropicProviderOverrides(provider, input);
  117. expect(output).toEqual(snapshot);
  118. });
  119. it("should set max_tokens to numeric value when preference is valid string '32000'", () => {
  120. const provider = {
  121. providerType: "claude",
  122. anthropicMaxTokensPreference: "32000",
  123. };
  124. const input: Record<string, unknown> = {
  125. model: "claude-3-opus-20240229",
  126. messages: [],
  127. };
  128. const output = applyAnthropicProviderOverrides(provider, input);
  129. expect(output.max_tokens).toBe(32000);
  130. });
  131. it("should overwrite existing max_tokens value", () => {
  132. const provider = {
  133. providerType: "claude",
  134. anthropicMaxTokensPreference: "64000",
  135. };
  136. const input: Record<string, unknown> = {
  137. model: "claude-3-opus-20240229",
  138. messages: [],
  139. max_tokens: 4000,
  140. };
  141. const output = applyAnthropicProviderOverrides(provider, input);
  142. expect(output.max_tokens).toBe(64000);
  143. expect(input.max_tokens).toBe(4000);
  144. });
  145. it("should not change max_tokens for invalid numeric string", () => {
  146. const provider = {
  147. providerType: "claude",
  148. anthropicMaxTokensPreference: "invalid",
  149. };
  150. const input: Record<string, unknown> = {
  151. model: "claude-3-opus-20240229",
  152. messages: [],
  153. max_tokens: 8000,
  154. };
  155. const snapshot = structuredClone(input);
  156. const output = applyAnthropicProviderOverrides(provider, input);
  157. expect(output).toEqual(snapshot);
  158. });
  159. });
  160. describe("thinking.budget_tokens override", () => {
  161. it("should not change request when preference is 'inherit'", () => {
  162. const provider = {
  163. providerType: "claude",
  164. anthropicThinkingBudgetPreference: "inherit",
  165. };
  166. const input: Record<string, unknown> = {
  167. model: "claude-3-opus-20240229",
  168. messages: [],
  169. thinking: { type: "enabled", budget_tokens: 5000 },
  170. };
  171. const snapshot = structuredClone(input);
  172. const output = applyAnthropicProviderOverrides(provider, input);
  173. expect(output).toEqual(snapshot);
  174. });
  175. it("should not change request when preference is null", () => {
  176. const provider = {
  177. providerType: "claude",
  178. anthropicThinkingBudgetPreference: null,
  179. };
  180. const input: Record<string, unknown> = {
  181. model: "claude-3-opus-20240229",
  182. messages: [],
  183. thinking: { type: "enabled", budget_tokens: 5000 },
  184. };
  185. const snapshot = structuredClone(input);
  186. const output = applyAnthropicProviderOverrides(provider, input);
  187. expect(output).toEqual(snapshot);
  188. });
  189. it("should not change request when preference is undefined", () => {
  190. const provider = {
  191. providerType: "claude",
  192. anthropicThinkingBudgetPreference: undefined,
  193. };
  194. const input: Record<string, unknown> = {
  195. model: "claude-3-opus-20240229",
  196. messages: [],
  197. thinking: { type: "enabled", budget_tokens: 5000 },
  198. };
  199. const snapshot = structuredClone(input);
  200. const output = applyAnthropicProviderOverrides(provider, input);
  201. expect(output).toEqual(snapshot);
  202. });
  203. it("should set thinking.budget_tokens and thinking.type when preference is valid '10240'", () => {
  204. const provider = {
  205. providerType: "claude",
  206. anthropicThinkingBudgetPreference: "10240",
  207. };
  208. const input: Record<string, unknown> = {
  209. model: "claude-3-opus-20240229",
  210. messages: [],
  211. max_tokens: 32000,
  212. };
  213. const output = applyAnthropicProviderOverrides(provider, input);
  214. expect(output.thinking).toEqual({
  215. type: "enabled",
  216. budget_tokens: 10240,
  217. });
  218. });
  219. it("should preserve existing thinking properties not overridden", () => {
  220. const provider = {
  221. providerType: "claude",
  222. anthropicThinkingBudgetPreference: "8000",
  223. };
  224. const input: Record<string, unknown> = {
  225. model: "claude-3-opus-20240229",
  226. messages: [],
  227. max_tokens: 32000,
  228. thinking: {
  229. type: "disabled",
  230. budget_tokens: 2000,
  231. custom_field: "preserve_me",
  232. },
  233. };
  234. const output = applyAnthropicProviderOverrides(provider, input);
  235. const thinking = output.thinking as Record<string, unknown>;
  236. expect(thinking.type).toBe("enabled");
  237. expect(thinking.budget_tokens).toBe(8000);
  238. expect(thinking.custom_field).toBe("preserve_me");
  239. });
  240. it("should create thinking object if not present", () => {
  241. const provider = {
  242. providerType: "claude",
  243. anthropicThinkingBudgetPreference: "5000",
  244. };
  245. const input: Record<string, unknown> = {
  246. model: "claude-3-opus-20240229",
  247. messages: [],
  248. max_tokens: 10000,
  249. };
  250. const output = applyAnthropicProviderOverrides(provider, input);
  251. expect(output.thinking).toBeDefined();
  252. const thinking = output.thinking as Record<string, unknown>;
  253. expect(thinking.type).toBe("enabled");
  254. expect(thinking.budget_tokens).toBe(5000);
  255. });
  256. it("should handle non-object thinking value by replacing it", () => {
  257. const provider = {
  258. providerType: "claude",
  259. anthropicThinkingBudgetPreference: "6000",
  260. };
  261. const input: Record<string, unknown> = {
  262. model: "claude-3-opus-20240229",
  263. messages: [],
  264. max_tokens: 32000,
  265. thinking: "invalid_string_value",
  266. };
  267. const output = applyAnthropicProviderOverrides(provider, input);
  268. expect(output.thinking).toEqual({
  269. type: "enabled",
  270. budget_tokens: 6000,
  271. });
  272. });
  273. });
  274. describe("Clamping logic", () => {
  275. it("should clamp budget_tokens to max_tokens - 1 when budget_tokens >= max_tokens (overridden max_tokens)", () => {
  276. const provider = {
  277. providerType: "claude",
  278. anthropicMaxTokensPreference: "10000",
  279. anthropicThinkingBudgetPreference: "15000",
  280. };
  281. const input: Record<string, unknown> = {
  282. model: "claude-3-opus-20240229",
  283. messages: [],
  284. };
  285. const output = applyAnthropicProviderOverrides(provider, input);
  286. expect(output.max_tokens).toBe(10000);
  287. const thinking = output.thinking as Record<string, unknown>;
  288. expect(thinking.budget_tokens).toBe(9999);
  289. });
  290. it("should clamp budget_tokens to max_tokens - 1 when budget_tokens >= max_tokens (request-provided max_tokens)", () => {
  291. const provider = {
  292. providerType: "claude",
  293. anthropicThinkingBudgetPreference: "20000",
  294. };
  295. const input: Record<string, unknown> = {
  296. model: "claude-3-opus-20240229",
  297. messages: [],
  298. max_tokens: 16000,
  299. };
  300. const output = applyAnthropicProviderOverrides(provider, input);
  301. const thinking = output.thinking as Record<string, unknown>;
  302. expect(thinking.budget_tokens).toBe(15999);
  303. });
  304. it("should clamp budget_tokens when exactly equal to max_tokens", () => {
  305. const provider = {
  306. providerType: "claude",
  307. anthropicMaxTokensPreference: "8000",
  308. anthropicThinkingBudgetPreference: "8000",
  309. };
  310. const input: Record<string, unknown> = {
  311. model: "claude-3-opus-20240229",
  312. messages: [],
  313. };
  314. const output = applyAnthropicProviderOverrides(provider, input);
  315. expect(output.max_tokens).toBe(8000);
  316. const thinking = output.thinking as Record<string, unknown>;
  317. expect(thinking.budget_tokens).toBe(7999);
  318. });
  319. it("should not clamp when budget_tokens < max_tokens", () => {
  320. const provider = {
  321. providerType: "claude",
  322. anthropicMaxTokensPreference: "32000",
  323. anthropicThinkingBudgetPreference: "10000",
  324. };
  325. const input: Record<string, unknown> = {
  326. model: "claude-3-opus-20240229",
  327. messages: [],
  328. };
  329. const output = applyAnthropicProviderOverrides(provider, input);
  330. expect(output.max_tokens).toBe(32000);
  331. const thinking = output.thinking as Record<string, unknown>;
  332. expect(thinking.budget_tokens).toBe(10000);
  333. });
  334. it("should not clamp when max_tokens is not set", () => {
  335. const provider = {
  336. providerType: "claude",
  337. anthropicThinkingBudgetPreference: "50000",
  338. };
  339. const input: Record<string, unknown> = {
  340. model: "claude-3-opus-20240229",
  341. messages: [],
  342. };
  343. const output = applyAnthropicProviderOverrides(provider, input);
  344. const thinking = output.thinking as Record<string, unknown>;
  345. expect(thinking.budget_tokens).toBe(50000);
  346. });
  347. it("should skip thinking override when clamped budget_tokens would be below 1024 (API minimum)", () => {
  348. const provider = {
  349. providerType: "claude",
  350. anthropicMaxTokensPreference: "500",
  351. anthropicThinkingBudgetPreference: "10000",
  352. };
  353. const input: Record<string, unknown> = {
  354. model: "claude-3-opus-20240229",
  355. messages: [],
  356. };
  357. const output = applyAnthropicProviderOverrides(provider, input);
  358. expect(output.max_tokens).toBe(500);
  359. expect(output.thinking).toBeUndefined();
  360. });
  361. it("should skip thinking override when budget_tokens preference itself is below 1024", () => {
  362. const provider = {
  363. providerType: "claude",
  364. anthropicThinkingBudgetPreference: "500",
  365. };
  366. const input: Record<string, unknown> = {
  367. model: "claude-3-opus-20240229",
  368. messages: [],
  369. max_tokens: 32000,
  370. };
  371. const output = applyAnthropicProviderOverrides(provider, input);
  372. expect(output.thinking).toBeUndefined();
  373. });
  374. it("should skip thinking override when max_tokens is exactly 1024 (clamped would be 1023)", () => {
  375. const provider = {
  376. providerType: "claude",
  377. anthropicMaxTokensPreference: "1024",
  378. anthropicThinkingBudgetPreference: "2000",
  379. };
  380. const input: Record<string, unknown> = {
  381. model: "claude-3-opus-20240229",
  382. messages: [],
  383. };
  384. const output = applyAnthropicProviderOverrides(provider, input);
  385. expect(output.max_tokens).toBe(1024);
  386. expect(output.thinking).toBeUndefined();
  387. });
  388. it("should apply thinking override when clamped budget_tokens is exactly 1024", () => {
  389. const provider = {
  390. providerType: "claude",
  391. anthropicMaxTokensPreference: "1025",
  392. anthropicThinkingBudgetPreference: "2000",
  393. };
  394. const input: Record<string, unknown> = {
  395. model: "claude-3-opus-20240229",
  396. messages: [],
  397. };
  398. const output = applyAnthropicProviderOverrides(provider, input);
  399. expect(output.max_tokens).toBe(1025);
  400. const thinking = output.thinking as Record<string, unknown>;
  401. expect(thinking.budget_tokens).toBe(1024);
  402. expect(thinking.type).toBe("enabled");
  403. });
  404. it("should apply thinking override when budget_tokens is exactly 1024 and no clamping needed", () => {
  405. const provider = {
  406. providerType: "claude",
  407. anthropicThinkingBudgetPreference: "1024",
  408. };
  409. const input: Record<string, unknown> = {
  410. model: "claude-3-opus-20240229",
  411. messages: [],
  412. max_tokens: 32000,
  413. };
  414. const output = applyAnthropicProviderOverrides(provider, input);
  415. const thinking = output.thinking as Record<string, unknown>;
  416. expect(thinking.budget_tokens).toBe(1024);
  417. expect(thinking.type).toBe("enabled");
  418. });
  419. });
  420. describe("Audit function", () => {
  421. it("should return null audit when provider type is not claude/claude-auth", () => {
  422. const provider = {
  423. id: 123,
  424. name: "codex-provider",
  425. providerType: "codex",
  426. anthropicMaxTokensPreference: "32000",
  427. anthropicThinkingBudgetPreference: "10240",
  428. };
  429. const input: Record<string, unknown> = {
  430. model: "gpt-4",
  431. messages: [],
  432. };
  433. const result = applyAnthropicProviderOverridesWithAudit(provider, input);
  434. expect(result.request).toBe(input);
  435. expect(result.audit).toBeNull();
  436. });
  437. it("should return null audit when all preferences are inherit/null/undefined", () => {
  438. const provider = {
  439. providerType: "claude",
  440. anthropicMaxTokensPreference: "inherit",
  441. anthropicThinkingBudgetPreference: null,
  442. };
  443. const input: Record<string, unknown> = {
  444. model: "claude-3-opus-20240229",
  445. messages: [],
  446. max_tokens: 8000,
  447. };
  448. const result = applyAnthropicProviderOverridesWithAudit(provider, input);
  449. expect(result.request).toBe(input);
  450. expect(result.audit).toBeNull();
  451. });
  452. it("should return null audit when preferences are invalid numeric strings", () => {
  453. const provider = {
  454. providerType: "claude",
  455. anthropicMaxTokensPreference: "invalid",
  456. anthropicThinkingBudgetPreference: "not_a_number",
  457. };
  458. const input: Record<string, unknown> = {
  459. model: "claude-3-opus-20240229",
  460. messages: [],
  461. max_tokens: 8000,
  462. };
  463. const result = applyAnthropicProviderOverridesWithAudit(provider, input);
  464. expect(result.request).toBe(input);
  465. expect(result.audit).toBeNull();
  466. });
  467. it("should return audit with hit=true when max_tokens override is applied", () => {
  468. const provider = {
  469. id: 1,
  470. name: "claude-provider",
  471. providerType: "claude",
  472. anthropicMaxTokensPreference: "32000",
  473. };
  474. const input: Record<string, unknown> = {
  475. model: "claude-3-opus-20240229",
  476. messages: [],
  477. max_tokens: 8000,
  478. };
  479. const result = applyAnthropicProviderOverridesWithAudit(provider, input);
  480. expect(result.audit?.hit).toBe(true);
  481. expect(result.audit?.providerId).toBe(1);
  482. expect(result.audit?.providerName).toBe("claude-provider");
  483. });
  484. it("should return audit with hit=true when thinking override is applied", () => {
  485. const provider = {
  486. id: 2,
  487. name: "anthropic-direct",
  488. providerType: "claude-auth",
  489. anthropicThinkingBudgetPreference: "10240",
  490. };
  491. const input: Record<string, unknown> = {
  492. model: "claude-3-opus-20240229",
  493. messages: [],
  494. max_tokens: 32000,
  495. };
  496. const result = applyAnthropicProviderOverridesWithAudit(provider, input);
  497. expect(result.audit?.hit).toBe(true);
  498. expect(result.audit?.providerId).toBe(2);
  499. expect(result.audit?.providerName).toBe("anthropic-direct");
  500. });
  501. it("should track before/after values correctly for max_tokens", () => {
  502. const provider = {
  503. id: 1,
  504. name: "test-provider",
  505. providerType: "claude",
  506. anthropicMaxTokensPreference: "32000",
  507. };
  508. const input: Record<string, unknown> = {
  509. model: "claude-3-opus-20240229",
  510. messages: [],
  511. max_tokens: 8000,
  512. };
  513. const result = applyAnthropicProviderOverridesWithAudit(provider, input);
  514. const maxTokensChange = result.audit?.changes.find((c) => c.path === "max_tokens");
  515. expect(maxTokensChange?.before).toBe(8000);
  516. expect(maxTokensChange?.after).toBe(32000);
  517. expect(maxTokensChange?.changed).toBe(true);
  518. });
  519. it("should track before/after values correctly for thinking fields", () => {
  520. const provider = {
  521. id: 1,
  522. name: "test-provider",
  523. providerType: "claude",
  524. anthropicThinkingBudgetPreference: "10000",
  525. };
  526. const input: Record<string, unknown> = {
  527. model: "claude-3-opus-20240229",
  528. messages: [],
  529. max_tokens: 32000,
  530. thinking: { type: "disabled", budget_tokens: 5000 },
  531. };
  532. const result = applyAnthropicProviderOverridesWithAudit(provider, input);
  533. const typeChange = result.audit?.changes.find((c) => c.path === "thinking.type");
  534. expect(typeChange?.before).toBe("disabled");
  535. expect(typeChange?.after).toBe("enabled");
  536. expect(typeChange?.changed).toBe(true);
  537. const budgetChange = result.audit?.changes.find((c) => c.path === "thinking.budget_tokens");
  538. expect(budgetChange?.before).toBe(5000);
  539. expect(budgetChange?.after).toBe(10000);
  540. expect(budgetChange?.changed).toBe(true);
  541. });
  542. it("should set changed=false when override value equals existing value", () => {
  543. const provider = {
  544. id: 1,
  545. name: "test-provider",
  546. providerType: "claude",
  547. anthropicMaxTokensPreference: "8000",
  548. };
  549. const input: Record<string, unknown> = {
  550. model: "claude-3-opus-20240229",
  551. messages: [],
  552. max_tokens: 8000,
  553. };
  554. const result = applyAnthropicProviderOverridesWithAudit(provider, input);
  555. const maxTokensChange = result.audit?.changes.find((c) => c.path === "max_tokens");
  556. expect(maxTokensChange?.before).toBe(8000);
  557. expect(maxTokensChange?.after).toBe(8000);
  558. expect(maxTokensChange?.changed).toBe(false);
  559. });
  560. it("should set audit.changed=true only when at least one value actually changed", () => {
  561. const provider = {
  562. id: 1,
  563. name: "test-provider",
  564. providerType: "claude",
  565. anthropicMaxTokensPreference: "32000",
  566. anthropicThinkingBudgetPreference: "10240",
  567. };
  568. const input: Record<string, unknown> = {
  569. model: "claude-3-opus-20240229",
  570. messages: [],
  571. max_tokens: 8000,
  572. };
  573. const result = applyAnthropicProviderOverridesWithAudit(provider, input);
  574. expect(result.audit?.changed).toBe(true);
  575. });
  576. it("should set audit.changed=false when no values actually changed", () => {
  577. const provider = {
  578. id: 1,
  579. name: "test-provider",
  580. providerType: "claude",
  581. anthropicMaxTokensPreference: "8000",
  582. };
  583. const input: Record<string, unknown> = {
  584. model: "claude-3-opus-20240229",
  585. messages: [],
  586. max_tokens: 8000,
  587. };
  588. const result = applyAnthropicProviderOverridesWithAudit(provider, input);
  589. expect(result.audit?.hit).toBe(true);
  590. expect(result.audit?.changed).toBe(false);
  591. });
  592. it("should include correct metadata in audit", () => {
  593. const provider = {
  594. id: 42,
  595. name: "my-claude-provider",
  596. providerType: "claude",
  597. anthropicMaxTokensPreference: "16000",
  598. };
  599. const input: Record<string, unknown> = {
  600. model: "claude-3-opus-20240229",
  601. messages: [],
  602. };
  603. const result = applyAnthropicProviderOverridesWithAudit(provider, input);
  604. expect(result.audit?.type).toBe("provider_parameter_override");
  605. expect(result.audit?.scope).toBe("provider");
  606. expect(result.audit?.providerId).toBe(42);
  607. expect(result.audit?.providerName).toBe("my-claude-provider");
  608. expect(result.audit?.providerType).toBe("claude");
  609. });
  610. it("should handle missing provider id and name gracefully", () => {
  611. const provider = {
  612. providerType: "claude",
  613. anthropicMaxTokensPreference: "16000",
  614. };
  615. const input: Record<string, unknown> = {
  616. model: "claude-3-opus-20240229",
  617. messages: [],
  618. };
  619. const result = applyAnthropicProviderOverridesWithAudit(provider, input);
  620. expect(result.audit?.providerId).toBeNull();
  621. expect(result.audit?.providerName).toBeNull();
  622. });
  623. it("should track null before values when fields do not exist", () => {
  624. const provider = {
  625. id: 1,
  626. name: "test",
  627. providerType: "claude",
  628. anthropicMaxTokensPreference: "32000",
  629. anthropicThinkingBudgetPreference: "10000",
  630. };
  631. const input: Record<string, unknown> = {
  632. model: "claude-3-opus-20240229",
  633. messages: [],
  634. };
  635. const result = applyAnthropicProviderOverridesWithAudit(provider, input);
  636. const maxTokensChange = result.audit?.changes.find((c) => c.path === "max_tokens");
  637. expect(maxTokensChange?.before).toBeNull();
  638. expect(maxTokensChange?.after).toBe(32000);
  639. const typeChange = result.audit?.changes.find((c) => c.path === "thinking.type");
  640. expect(typeChange?.before).toBeNull();
  641. expect(typeChange?.after).toBe("enabled");
  642. const budgetChange = result.audit?.changes.find((c) => c.path === "thinking.budget_tokens");
  643. expect(budgetChange?.before).toBeNull();
  644. expect(budgetChange?.after).toBe(10000);
  645. });
  646. });
  647. });