feat(anthropic): add adaptive thinking override support#758
Conversation
Add adaptive thinking as a new option in the Anthropic thinking override
settings on the provider form, supporting the new API format:
- thinking: { type: "adaptive" } + output_config: { effort: "low"|"medium"|"high"|"max" }
- Model-level targeting: apply to all models or specific models with prefix matching
- Full audit trail tracking output_config.effort changes
- Rectifier safety: skip budget rectification for adaptive mode
- DB: new JSONB column anthropic_adaptive_thinking on providers table
- i18n: all 5 locales (en, zh-CN, zh-TW, ja, ru)
- Tests: 10 override tests + 5 rectifier tests (56/56 passing)
📝 WalkthroughWalkthrough新增 Anthropic 自适应思考配置(anthropic_adaptive_thinking),涵盖数据库列、类型与验证、后端持久化、覆盖逻辑、前端表单/状态、多语言文案及单元测试。 Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @ding113, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances Anthropic provider configuration by introducing 'Adaptive Thinking' capabilities. This feature allows providers to dynamically adjust the reasoning effort for Anthropic models, either globally or for specific models, based on predefined effort levels. It provides a more flexible and intelligent way to manage model behavior, moving beyond static budget token settings. The changes span database schema, API logic, validation, and the user interface, ensuring a robust and user-friendly implementation. This enables users to optimize model performance and resource usage more effectively. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
🧪 测试结果
总体结果: ✅ 所有测试通过 |
There was a problem hiding this comment.
Code Review
This pull request introduces support for Anthropic's adaptive thinking override, allowing providers to configure dynamic reasoning effort based on model matching. The changes include database schema updates, new types, UI controls for configuring adaptive thinking, and modifications to the Anthropic provider override logic to apply these settings. Additionally, the thinking budget rectifier has been updated to correctly skip rectification when adaptive thinking is enabled. Comprehensive unit tests have been added to ensure the correctness of the new functionality across various scenarios.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/repository/provider.ts (1)
216-218:⚠️ Potential issue | 🔴 Critical在
findProviderList、findAllProvidersFresh、findProviderById三个查询函数的 select 对象中缺少anthropicAdaptiveThinking列。写入路径(
createProvider和updateProvider)正确地包含了anthropicAdaptiveThinking,但所有读取路径都遗漏了该列。这导致:
- 运行时 provider override 逻辑无法获取自适应思考配置,功能完全失效
- 编辑已有 provider 时表单无法回显自适应思考配置
- Provider 列表/缓存中该字段始终为 null
需要在各函数的 select 对象中补充该列:
修复位置
findProviderList(~Line 218):在anthropicThinkingBudgetPreference和geminiGoogleSearchPreference之间添加findAllProvidersFresh(~Line 296):同上findProviderById(~Line 378):同上
🤖 Fix all issues with AI agents
In `@src/actions/providers.ts`:
- Line 284: The addProvider and editProvider functions are missing the
anthropic_adaptive_thinking field in their data parameter type signatures;
update the data types for addProvider and editProvider to include an optional
anthropic_adaptive_thinking?: AnthropicAdaptiveThinkingConfig | null so
TypeScript reflects the CreateProviderSchema/UpdateProviderSchema and matches
the ProviderDisplay mapping; ensure the new property name and type match the Zod
schema (CreateProviderSchema/UpdateProviderSchema) and existing
AnthropicAdaptiveThinkingConfig type to restore IDE autocomplete and
compile-time checks.
In `@src/lib/validation/schemas.ts`:
- Around line 59-66: The ANTHROPIC_ADAPTIVE_THINKING_CONFIG schema must enforce
that when modelMatchMode === "specific" the models array is non-empty; update
the schema (using zod's superRefine or a conditional refinement on the
ANTHROPIC_ADAPTIVE_THINKING_CONFIG object) to check modelMatchMode and then
require models?.length > 0, adding a clear validation error message if models is
empty or undefined; make sure this logic respects the schema's
nullable()/optional() wrapping so null/undefined remains valid but specific mode
always requires at least one model.
🧹 Nitpick comments (7)
src/app/v1/_lib/proxy/thinking-budget-rectifier.ts (1)
64-81: 自适应模式的跳过逻辑正确。当
thinking.type === "adaptive"时,整流器正确地返回applied: false且before/after状态一致,避免对自适应模式的请求进行不必要的修改。有一个小的代码重复:
budget_tokens的提取逻辑(Lines 71-72, 77-78)与下方 Lines 82-83 重复。可以考虑提取为局部变量后再做分支判断。可选:提取公共变量减少重复
const currentThinkingType = thinking && typeof thinking.type === "string" ? thinking.type : null; + const currentThinkingBudgetTokens = + thinking && typeof thinking.budget_tokens === "number" ? thinking.budget_tokens : null; if (currentThinkingType === "adaptive") { return { applied: false, before: { maxTokens: currentMaxTokens, thinkingType: currentThinkingType, - thinkingBudgetTokens: - thinking && typeof thinking.budget_tokens === "number" ? thinking.budget_tokens : null, + thinkingBudgetTokens: currentThinkingBudgetTokens, }, after: { maxTokens: currentMaxTokens, thinkingType: currentThinkingType, - thinkingBudgetTokens: - thinking && typeof thinking.budget_tokens === "number" ? thinking.budget_tokens : null, + thinkingBudgetTokens: currentThinkingBudgetTokens, }, }; } - const currentThinkingBudgetTokens = - thinking && typeof thinking.budget_tokens === "number" ? thinking.budget_tokens : null;tests/unit/proxy/thinking-budget-rectifier.test.ts (1)
1-53: 测试覆盖整体合理,建议补充边界场景。adaptive 路径的核心行为(
applied: false、before/after一致性)已覆盖。但rectifyThinkingBudget还有几个分支未被测试:
- 入参不含
thinking对象时(会创建新的 thinking 并强制enabled)max_tokens < MIN_MAX_TOKENS_FOR_BUDGET时的max_tokens自动拉升- adaptive 模式下
thinking对象包含budget_tokens的情况(验证返回值中thinkingBudgetTokens仍为该数值)这些分支对于确保 rectifier 在生产中正确工作比较重要。
src/lib/anthropic/provider-overrides.ts (1)
63-81: adaptive 分支在config为 null 时静默跳过所有 thinking 覆盖。当
anthropicThinkingBudgetPreference === "adaptive"但anthropicAdaptiveThinking为null(管理员选择了「自适应思考」但未完成配置)时,Line 80 的return output会跳过后续的thinkingBudget数值分支,导致 thinking 完全不被覆盖。这个行为可能是有意为之(passthrough),但如果管理员误操作,请求将静默透传而无任何提示。建议考虑是否需要在此处添加日志以便排查。
另外,当
modelMatchMode为"specific"而models数组为空时,some()永远返回false,即所有模型都不会命中——行为正确但管理员可能不容易意识到。src/app/[locale]/settings/providers/_components/forms/provider-form/sections/routing-section.tsx (1)
707-833: 自适应思考 UI 区块实现良好,与现有 Codex/Anthropic override 的 UI 模式保持一致。guard condition (
budgetPreference === "adaptive" && anthropicAdaptiveThinking !== null) 确保了安全渲染,effort/modelMatchMode/models 三个控件都正确绑定了对应 action。建议关注一个边缘场景:当modelMatchMode为"specific"但models数组为空时,用户可能无意中保存了一个不匹配任何模型的配置(相当于自适应思考不生效)。可以考虑在 UI 或提交时加一个提示。src/app/[locale]/settings/providers/_components/forms/provider-form/provider-form-context.tsx (1)
189-191: 默认模型名"claude-opus-4-6"是硬编码值。作为 UI 初始默认值可以接受,但随着 Anthropic 发布新模型,这个默认值可能变得过时。考虑将其提取为常量或从配置中读取,方便后续维护。
tests/unit/proxy/anthropic-provider-overrides.test.ts (1)
886-907: 测试标题与实际机制轻微不符(可选 nit)。标题 "should remove budget_tokens from existing thinking when applying adaptive" 暗示选择性移除
budget_tokens,但实现实际上是用{ type: "adaptive" }完全替换thinking对象。测试断言本身是正确的,只是标题可以更精确,例如 "should replace thinking object with adaptive type"。src/lib/validation/schemas.ts (1)
595-604:superRefine中 adaptive 校验逻辑在 Create 和 Update schema 中完全重复Lines 595-604 和 Lines 805-814 的校验逻辑完全相同。可以考虑提取为一个共享的 refinement 函数,减少维护成本。
♻️ 建议的重构方案
在文件顶部定义共享函数:
function refineAnthropicThinkingBudget( data: { anthropic_max_tokens_preference?: string; anthropic_thinking_budget_preference?: string; anthropic_adaptive_thinking?: { effort: string; modelMatchMode: string; models: string[] } | null; }, ctx: z.RefinementCtx ) { const maxTokens = data.anthropic_max_tokens_preference; const budget = data.anthropic_thinking_budget_preference; if (budget === "adaptive") { if (!data.anthropic_adaptive_thinking) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "adaptive thinking config is required when mode is adaptive", path: ["anthropic_adaptive_thinking"], }); } return; } if (maxTokens && maxTokens !== "inherit" && budget && budget !== "inherit") { const maxTokensNum = Number.parseInt(maxTokens, 10); const budgetNum = Number.parseInt(budget, 10); if (budgetNum >= maxTokensNum) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "thinking.budget_tokens must be less than max_tokens", path: ["anthropic_thinking_budget_preference"], }); } } }然后两处均使用
.superRefine(refineAnthropicThinkingBudget)。
| if (provider.anthropicThinkingBudgetPreference === "adaptive") { | ||
| const config = provider.anthropicAdaptiveThinking; | ||
| if (config) { | ||
| const modelId = typeof request.model === "string" ? request.model : null; | ||
| const isMatch = | ||
| config.modelMatchMode === "all" || | ||
| (modelId !== null && | ||
| config.models.some((m) => modelId === m || modelId.startsWith(`${m}-`))); | ||
| if (isMatch) { | ||
| ensureCloned(); | ||
| output.thinking = { type: "adaptive" }; | ||
| const existingOutputConfig = isPlainObject(output.output_config) | ||
| ? output.output_config | ||
| : {}; | ||
| output.output_config = { ...existingOutputConfig, effort: config.effort }; | ||
| } | ||
| } | ||
| return output; |
There was a problem hiding this comment.
Adaptive thinking leaves stale budget
When adaptive thinking matches, this branch sets output.thinking = { type: "adaptive" }, which drops any existing thinking fields (including budget_tokens). That’s OK, but it also returns early (line 80) without cleaning up an existing thinking.budget_tokens if some other code merges thinking objects rather than replacing them. To make the behavior robust and consistent with the test intent (“remove budget_tokens when applying adaptive”), explicitly delete budget_tokens from any existing thinking object before setting adaptive (or set output.thinking by spreading and omitting budget). Otherwise callers that pass a thinking object through another transformer could still end up sending budget_tokens alongside adaptive type.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/anthropic/provider-overrides.ts
Line: 63:80
Comment:
**Adaptive thinking leaves stale budget**
When adaptive thinking matches, this branch sets `output.thinking = { type: "adaptive" }`, which drops any existing `thinking` fields (including `budget_tokens`). That’s OK, but it *also* returns early (line 80) without cleaning up an existing `thinking.budget_tokens` if some other code merges thinking objects rather than replacing them. To make the behavior robust and consistent with the test intent (“remove budget_tokens when applying adaptive”), explicitly delete `budget_tokens` from any existing thinking object before setting adaptive (or set `output.thinking` by spreading and omitting budget). Otherwise callers that pass a `thinking` object through another transformer could still end up sending `budget_tokens` alongside adaptive type.
How can I resolve this? If you propose a fix, please make it concise.
drizzle/0066_hot_mauler.sql
Outdated
| @@ -0,0 +1 @@ | |||
| ALTER TABLE "providers" ADD COLUMN "anthropic_adaptive_thinking" jsonb DEFAULT 'null'::jsonb; No newline at end of file | |||
There was a problem hiding this comment.
Migration default is JSON null
This migration sets DEFAULT 'null'::jsonb rather than SQL DEFAULT NULL. That creates a JSONB value of null (distinct from SQL NULL) in existing rows, which can behave differently in queries/indexing and is easy to mis-handle in code expecting NULL. If the intent is “no config”, use DEFAULT NULL (or omit default) to store SQL NULL consistently with schema.ts’s .default(null) expectation.
Prompt To Fix With AI
This is a comment left during a code review.
Path: drizzle/0066_hot_mauler.sql
Line: 1:1
Comment:
**Migration default is JSON null**
This migration sets `DEFAULT 'null'::jsonb` rather than SQL `DEFAULT NULL`. That creates a JSONB value of `null` (distinct from SQL NULL) in existing rows, which can behave differently in queries/indexing and is easy to mis-handle in code expecting `NULL`. If the intent is “no config”, use `DEFAULT NULL` (or omit default) to store SQL NULL consistently with `schema.ts`’s `.default(null)` expectation.
How can I resolve this? If you propose a fix, please make it concise.| ), | ||
| ]); | ||
|
|
||
| const ANTHROPIC_ADAPTIVE_THINKING_CONFIG = z | ||
| .object({ | ||
| effort: z.enum(["low", "medium", "high", "max"]), | ||
| modelMatchMode: z.enum(["specific", "all"]), | ||
| models: z.array(z.string().min(1).max(100)).max(50), | ||
| }) | ||
| .nullable() | ||
| .optional(); | ||
|
|
||
| // Gemini (generateContent API) Google Search preference | ||
| // - 'inherit': follow client request (default) | ||
| // - 'enabled': force inject googleSearch tool |
There was a problem hiding this comment.
Adaptive models not required
ANTHROPIC_ADAPTIVE_THINKING_CONFIG.models allows an empty array even when modelMatchMode is "specific" (only .max(50) is enforced). With the current override logic, specific + empty models means adaptive mode will silently never apply. If specific is selected, models should be .min(1) (or enforced via superRefine) to prevent saving a non-functional config.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/validation/schemas.ts
Line: 56:70
Comment:
**Adaptive models not required**
`ANTHROPIC_ADAPTIVE_THINKING_CONFIG.models` allows an empty array even when `modelMatchMode` is `"specific"` (only `.max(50)` is enforced). With the current override logic, `specific` + empty `models` means adaptive mode will silently never apply. If `specific` is selected, `models` should be `.min(1)` (or enforced via `superRefine`) to prevent saving a non-functional config.
How can I resolve this? If you propose a fix, please make it concise.1. Add anthropic_adaptive_thinking, anthropic_max_tokens_preference, and anthropic_thinking_budget_preference to addProvider/editProvider type signatures for IDE autocomplete and compile-time safety. 2. Add .refine() validation ensuring models array is non-empty when modelMatchMode is "specific" in adaptive thinking config schema.
Change DEFAULT 'null'::jsonb to DEFAULT NULL in the migration for anthropic_adaptive_thinking column. JSONB null and SQL NULL have different semantics in queries and indexing.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/lib/validation/schemas.ts (1)
805-830:⚠️ Potential issue | 🟡 Minor部分更新场景下可能产生数据不一致。
在
UpdateProviderSchema中,如果用户仅发送anthropic_adaptive_thinking: null(清除配置)而不同时发送anthropic_thinking_budget_preference,校验会通过(因为budget为undefined,不会触发 adaptive 分支检查)。这可能导致数据库中budget = "adaptive"但anthropic_adaptive_thinking = null的不一致状态。建议在应用层(如 action/service 层)补充一致性保障,或在
superRefine中增加反向检查:当anthropic_adaptive_thinking被显式设为null时,如果需要也校验budget是否仍为"adaptive"。
🧪 测试结果
总体结果: ✅ 所有测试通过 |
| .object({ | ||
| effort: z.enum(["low", "medium", "high", "max"]), | ||
| modelMatchMode: z.enum(["specific", "all"]), | ||
| models: z.array(z.string().min(1).max(100)).max(50), |
There was a problem hiding this comment.
[HIGH] [TYPE-MISSING-VALIDATION] modelMatchMode: "specific" allows empty models (adaptive override becomes a silent no-op)
Why this is a problem: ANTHROPIC_ADAPTIVE_THINKING_CONFIG currently permits models: [] (src/lib/validation/schemas.ts:63), but the matcher later does config.models.some(...) (src/lib/anthropic/provider-overrides.ts:70). With an empty list and "specific" mode, no model can match, so adaptive thinking is never applied even though the provider is configured for it.
Suggested fix:
const ANTHROPIC_ADAPTIVE_THINKING_CONFIG = z
.discriminatedUnion("modelMatchMode", [
z.object({
effort: z.enum(["low", "medium", "high", "max"]),
modelMatchMode: z.literal("all"),
models: z.array(z.string().min(1).max(100)).max(50).default([]),
}),
z.object({
effort: z.enum(["low", "medium", "high", "max"]),
modelMatchMode: z.literal("specific"),
models: z.array(z.string().min(1).max(100)).min(1).max(50),
}),
])
.nullable()
.optional();| if (!data.anthropic_adaptive_thinking) { | ||
| ctx.addIssue({ | ||
| code: z.ZodIssueCode.custom, | ||
| message: "adaptive thinking config is required when mode is adaptive", |
There was a problem hiding this comment.
[MEDIUM] [TEST-MISSING-CRITICAL] New adaptive-thinking validation branches are untested
Why this is a problem: CreateProviderSchema and UpdateProviderSchema now have new superRefine branches for anthropic_thinking_budget_preference === "adaptive" (src/lib/validation/schemas.ts:595 and src/lib/validation/schemas.ts:805). Per repo standards, new feature logic should be covered (see CLAUDE.md:13: "All new features must have unit test coverage of at least 80%"), otherwise regressions can silently re-allow invalid provider configs.
Suggested fix (add to src/lib/validation/schemas.test.ts):
test("CreateProviderSchema: adaptive requires config", () => {
const base = { name: "p", url: "https://api.example.com", key: "sk-test" };
expect(
CreateProviderSchema.safeParse({
...base,
anthropic_thinking_budget_preference: "adaptive",
}).success
).toBe(false);
expect(
CreateProviderSchema.safeParse({
...base,
anthropic_thinking_budget_preference: "adaptive",
anthropic_adaptive_thinking: { effort: "high", modelMatchMode: "all", models: [] },
}).success
).toBe(true);
});
test("UpdateProviderSchema: setting adaptive requires config", () => {
expect(
UpdateProviderSchema.safeParse({
anthropic_thinking_budget_preference: "adaptive",
}).success
).toBe(false);
expect(
UpdateProviderSchema.safeParse({
anthropic_thinking_budget_preference: "adaptive",
anthropic_adaptive_thinking: { effort: "high", modelMatchMode: "all", models: [] },
}).success
).toBe(true);
});| const existingOutputConfig = isPlainObject(output.output_config) | ||
| ? output.output_config | ||
| : {}; | ||
| output.output_config = { ...existingOutputConfig, effort: config.effort }; |
There was a problem hiding this comment.
[MEDIUM] [COMMENT-INACCURATE] applyAnthropicProviderOverrides doc comment no longer matches behavior
Why this is a problem: The JSDoc says overrides only affect max_tokens and thinking.* (src/lib/anthropic/provider-overrides.ts:38), but adaptive mode now also writes output_config.effort via output.output_config = { ...existingOutputConfig, effort: config.effort } (src/lib/anthropic/provider-overrides.ts:77). This mismatch makes it easy to miss downstream impacts when adding or debugging overrides.
Suggested fix:
* - Overrides only affect:
* - max_tokens
* - thinking.type / thinking.budget_tokens
* - output_config.effort (adaptive thinking only)There was a problem hiding this comment.
Code Review Summary
No significant issues identified in this PR. The implementation is well-structured, follows existing codebase patterns, and includes comprehensive test coverage for the new adaptive thinking override feature.
PR Size: XL
- Lines changed: 3835 (3781 additions + 54 deletions)
- Files changed: 22
- Note: ~2983 lines are the auto-generated Drizzle snapshot (
drizzle/meta/0066_snapshot.json). Actual code changes are ~850 lines across 19 files, which is closer to a M/L PR in practice.
Review Coverage
- Logic and correctness - Clean. The adaptive branch in
applyAnthropicProviderOverridescorrectly handles all-models vs specific-models matching with prefix support, properly removesbudget_tokenswhen switching to adaptive, and the early return prevents falling through to the numeric budget path. - Security (OWASP Top 10) - Clean. No user-controlled input reaches SQL or shell. JSONB column is validated through Zod before persistence.
- Error handling - Clean. Null/undefined config is handled defensively (passthrough when
anthropicAdaptiveThinkingis null). The rectifier correctly skips adaptive thinking type. - Type safety - Clean. Types are properly threaded through the full stack (schema -> repository -> actions -> form state -> submission). The Drizzle
$typeusesstringfor JSONB fields which is a common pattern given Zod validates at the boundary. - Documentation accuracy - Clean. Comments match code behavior. The JSDoc on
applyAnthropicProviderOverridescould be updated to mentionoutput_config.effort, but this is minor. - Test coverage - Adequate. 10 new override tests cover: all-models mode, specific-models mode, non-matching passthrough, output_config preservation, effort levels, budget_tokens removal, null config defense, combined max_tokens+adaptive, prefix matching, and audit trail. 5 new rectifier tests cover trigger detection and adaptive skip.
- Code clarity - Good. The adaptive branch is cleanly separated from the numeric budget path. Form state management correctly preserves config when toggling modes while nullifying on submission when not in adaptive mode.
- i18n - All 5 locales (en, zh-CN, zh-TW, ja, ru) updated with matching key structures.
Automated review by Claude AI
There was a problem hiding this comment.
Code Review Summary
This is an XL PR that spans DB, validation, proxy logic, UI, i18n, and tests for Anthropic adaptive thinking overrides. The wiring is mostly consistent, but the current validation allows an invalid configuration state that results in adaptive thinking being silently skipped.
PR Size: XL
- Lines changed: 3835
- Files changed: 22
- Split suggestion: Consider splitting into (1) DB/schema + repository/types, (2) proxy override/rectifier + backend logic, (3) provider form UI + i18n, (4) tests.
Issues Found
| Category | Critical | High | Medium | Low |
|---|---|---|---|---|
| Logic/Bugs | 0 | 0 | 0 | 0 |
| Security | 0 | 0 | 0 | 0 |
| Error Handling | 0 | 0 | 0 | 0 |
| Types | 0 | 1 | 0 | 0 |
| Comments/Docs | 0 | 0 | 1 | 0 |
| Tests | 0 | 0 | 1 | 0 |
| Simplification | 0 | 0 | 0 | 0 |
Critical Issues (Must Fix)
None.
High Priority Issues (Should Fix)
- [High] [TYPE-MISSING-VALIDATION]
src/lib/validation/schemas.ts:63allowsmodelMatchMode: "specific"withmodels: [], which makes adaptive override a silent no-op. - [Medium] [TEST-MISSING-CRITICAL]
src/lib/validation/schemas.ts:595/src/lib/validation/schemas.ts:805add new adaptive validation branches without corresponding unit tests. - [Medium] [COMMENT-INACCURATE]
src/lib/anthropic/provider-overrides.ts:32JSDoc no longer reflects that adaptive mode writesoutput_config.effort.
Review Coverage
- Logic and correctness
- Security (OWASP Top 10)
- Error handling
- Type safety
- Documentation accuracy
- Test coverage
- Code clarity
Automated review by Codex AI
There was a problem hiding this comment.
- Identified PR
#758and applied labelsize/XL. - Left 3 inline review comments (diff-only):
src/lib/validation/schemas.ts:63—[TYPE-MISSING-VALIDATION]allowlist enables"specific"+ emptymodels, making adaptive thinking a silent no-op.src/lib/validation/schemas.ts:599—[TEST-MISSING-CRITICAL]new adaptivesuperRefinebranches lack unit tests; suggested concreteschemas.test.tscases.src/lib/anthropic/provider-overrides.ts:77—[COMMENT-INACCURATE]JSDoc no longer reflects that adaptive mode also writesoutput_config.effort.
- Submitted the required summary review via
gh pr review --comment(includes XL split suggestions and issue matrix).
| @@ -0,0 +1 @@ | |||
| ALTER TABLE "providers" ADD COLUMN "anthropic_adaptive_thinking" jsonb DEFAULT NULL; No newline at end of file | |||
There was a problem hiding this comment.
Missing trailing newline
The migration file is missing a trailing newline (\ No newline at end of file). Some tooling (formatters, patch applicators, certain SQL runners) can choke on this; it’s worth adding a newline so the file is well-formed across environments.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: drizzle/0066_hot_mauler.sql
Line: 1:1
Comment:
**Missing trailing newline**
The migration file is missing a trailing newline (`\ No newline at end of file`). Some tooling (formatters, patch applicators, certain SQL runners) can choke on this; it’s worth adding a newline so the file is well-formed across environments.
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.| if (provider.anthropicThinkingBudgetPreference === "adaptive") { | ||
| const config = provider.anthropicAdaptiveThinking; | ||
| if (config) { | ||
| const modelId = typeof request.model === "string" ? request.model : null; |
There was a problem hiding this comment.
Adaptive override flags hit
In applyAnthropicProviderOverridesWithAudit, hit is set to true whenever anthropicThinkingBudgetPreference === "adaptive" (even if anthropicAdaptiveThinking is null or doesn’t match the request model), which produces an audit record that looks like an override was applied when it wasn’t. This can confuse audit consumers because hit: true but all changes[].changed are false.
Also appears at src/lib/anthropic/provider-overrides.ts:124-128.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/anthropic/provider-overrides.ts
Line: 63:66
Comment:
**Adaptive override flags hit**
In `applyAnthropicProviderOverridesWithAudit`, `hit` is set to true whenever `anthropicThinkingBudgetPreference === "adaptive"` (even if `anthropicAdaptiveThinking` is null or doesn’t match the request model), which produces an audit record that looks like an override was applied when it wasn’t. This can confuse audit consumers because `hit: true` but all `changes[].changed` are false.
Also appears at `src/lib/anthropic/provider-overrides.ts:124-128`.
How can I resolve this? If you propose a fix, please make it concise.
Additional Comments (1)
Also appears in the select lists at Prompt To Fix With AIThis is a comment left during a code review.
Path: src/repository/provider.ts
Line: 167:220
Comment:
**Adaptive thinking never loads**
`createProvider`/`updateProvider` return `providers.anthropicAdaptiveThinking`, but `findProviderList`, `findAllProvidersFresh`, and `findProviderById` do not select `providers.anthropicAdaptiveThinking` at all. That means once saved, the adaptive config will come back as `undefined`/`null` after a fresh fetch and the UI/override layer can’t apply it.
Also appears in the select lists at `src/repository/provider.ts:171-231`, `src/repository/provider.ts:248-315`, and `src/repository/provider.ts:330-386`.
How can I resolve this? If you propose a fix, please make it concise. |
Summary
Add Anthropic adaptive thinking (
thinking: { type: "adaptive" }+output_config: { effort }) as a new provider-level override option, alongside the existing custom budget mode. Admins can configure effort level (low/medium/high/max) and target specific models via prefix matching.Problem
Anthropic's API supports an "adaptive" thinking mode where the model dynamically decides how much reasoning to apply, controlled by an
effortparameter inoutput_configrather than a fixedbudget_tokens. The existing provider override UI only supported "inherit" (passthrough) or a custom numeric budget, with no way to configure adaptive thinking at the provider level.Solution
Extend the thinking budget preference system with a third mode ("adaptive") that:
thinking.typeto"adaptive"and injectsoutput_config.efforton matching requestsclaude-opus-4-6matchesclaude-opus-4-6-20250514)anthropic_adaptive_thinking) on the providers tableoutput_config.effortchanges in the audit trail alongside existingmax_tokensandthinking.budget_tokenstrackingChanges
schema.ts,0066_hot_mauler.sqlanthropic_adaptive_thinkingprovider.tsAnthropicAdaptiveThinkingConfig, effort/model-match types, field added toProvider,ProviderDisplay,CreateProviderData,UpdateProviderDataschemas.ts"adaptive"literal in budget pref union, newANTHROPIC_ADAPTIVE_THINKING_CONFIGschema with superRefine cross-validationprovider.ts,transformers.tsproviders.tsProviderDisplaymappingprovider-overrides.tsoutput_config.effortthinking-budget-rectifier.tsthinking.type === "adaptive"Migration
0066: Addsanthropic_adaptive_thinking JSONB DEFAULT nulltoproviderstable. Non-destructive, no data transformation needed. Will auto-apply ifAUTO_MIGRATE=true.Breaking Changes
None. The new JSONB column defaults to
null, and the type union change ("adaptive"added toAnthropicThinkingBudgetPreference) is additive since"adaptive"was already a validstring.Testing
Automated Tests
anthropic-provider-overrides.test.tscovering: all-models mode, specific-models mode, non-matching passthrough, output_config preservation, effort levels, budget_tokens removal, null config defense, combined max_tokens+adaptive, prefix matching, audit trailthinking-budget-rectifier.test.tscovering: trigger detection, standard rectification, adaptive skipManual Testing
thinking.typeis set to"adaptive"andoutput_config.effortis injectedChecklist
bun run test- 56/56)bun run lint:fix)bun run typecheck)bun run build)Description enhanced by Claude AI
Greptile Overview
Greptile Summary
This PR adds an Anthropic provider-level “adaptive thinking” override alongside existing fixed thinking budgets. It persists a new
providers.anthropic_adaptive_thinkingJSONB config, extends validation/types/UI to edit it, applies the override at proxy-time by injectingthinking: { type: "adaptive" }andoutput_config.effort, and updates the thinking budget rectifier to skip adaptive requests. Unit tests cover adaptive override behavior and rectifier skip logic.Key integration points are: settings UI → server actions/repository persistence → proxy request override/audit → rectifier behavior.
Confidence Score: 3/5
Important Files Changed
Sequence Diagram