Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed
- Fixed Claude Opus 4.7 compatibility by using adaptive thinking mode instead of budget-based thinking. Also added `ANTHROPIC_EFFORT` environment variable to control thinking depth, and extended thinking support to `google-vertex-anthropic` and `amazon-bedrock` providers. [#1125](https://github.com/sourcebot-dev/sourcebot/pull/1125)

## [4.16.9] - 2026-04-15

### Added
Expand Down
7 changes: 7 additions & 0 deletions packages/shared/src/env.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,13 @@ const options = {
ANTHROPIC_API_KEY: z.string().optional(),
ANTHROPIC_AUTH_TOKEN: z.string().optional(),
ANTHROPIC_THINKING_BUDGET_TOKENS: numberSchema.default(12000),
/**
* The effort level for Anthropic models using adaptive thinking.
* Controls how much thinking Claude uses when responding.
* Valid values: 'low', 'medium', 'high' (default), 'max'
* @see https://docs.anthropic.com/en/docs/build-with-claude/effort
*/
ANTHROPIC_EFFORT: z.enum(['low', 'medium', 'high', 'max']).default('high'),

AZURE_API_KEY: z.string().optional(),
AZURE_RESOURCE_NAME: z.string().optional(),
Expand Down
78 changes: 72 additions & 6 deletions packages/web/src/features/chat/utils.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,66 @@ import fs from 'fs';
import path from 'path';
import { LanguageModelInfo, SBChatMessage } from './types';

type AnthropicThinkingMode = 'adaptive' | 'budget' | 'disabled';

/**
* Determines the appropriate thinking configuration for an Anthropic model.
*
* - Claude Opus 4.7+: Only supports adaptive thinking (budget mode is rejected)
* - Claude Opus 4.6/Sonnet 4.6+: Supports both but adaptive is recommended
* - Older models (Sonnet 4.5, Opus 4.5, etc.): Only support budget-based thinking
*/
const getAnthropicThinkingMode = (modelId: string): AnthropicThinkingMode => {
const lowerModelId = modelId.toLowerCase();

// Claude Opus 4.7 and later versions ONLY support adaptive thinking
// Model IDs: claude-opus-4-7, claude-opus-4-7-*, claude-opus-4-8, etc.
if (lowerModelId.includes('opus-4-7') ||
lowerModelId.includes('opus-4-8') ||
lowerModelId.includes('opus-4-9') ||
lowerModelId.includes('opus-5') ||
lowerModelId.includes('mythos')) {
return 'adaptive';
}

// Claude Opus 4.6 and Sonnet 4.6+ support both, but adaptive is recommended
// Model IDs: claude-opus-4-6, claude-sonnet-4-6, etc.
if (lowerModelId.includes('opus-4-6') || lowerModelId.includes('sonnet-4-6')) {
return 'adaptive';
}

// Older models (Sonnet 4.5, Opus 4.5, Sonnet 3.7, etc.) only support budget-based thinking
return 'budget';
};

/**
* Builds the Anthropic thinking provider options based on the model and environment configuration.
*/
const getAnthropicThinkingOptions = (modelId: string): AnthropicProviderOptions => {
const thinkingMode = getAnthropicThinkingMode(modelId);

if (thinkingMode === 'disabled') {
return {};
}

if (thinkingMode === 'adaptive') {
return {
thinking: {
type: 'adaptive',
},
effort: env.ANTHROPIC_EFFORT,
};
}

// Budget-based thinking for older models
return {
thinking: {
type: 'enabled',
budgetTokens: env.ANTHROPIC_THINKING_BUDGET_TOKENS,
},
};
};

/**
* Checks if the current user (authenticated or anonymous) is the owner of a chat.
*/
Expand Down Expand Up @@ -192,8 +252,16 @@ export const getAISDKLanguageModelAndOptions = async (config: LanguageModel): Pr
: undefined,
});

// Add thinking options for Anthropic models on Bedrock
const isAnthropicModel = modelId.toLowerCase().includes('anthropic') ||
modelId.toLowerCase().includes('claude');
const providerOptions = isAnthropicModel
? { anthropic: getAnthropicThinkingOptions(modelId) }
: undefined;

return {
model: aws(modelId),
providerOptions,
};
}
case 'anthropic': {
Expand All @@ -213,12 +281,7 @@ export const getAISDKLanguageModelAndOptions = async (config: LanguageModel): Pr
return {
model: anthropic(modelId),
providerOptions: {
anthropic: {
thinking: {
type: "enabled",
budgetTokens: env.ANTHROPIC_THINKING_BUDGET_TOKENS,
}
} satisfies AnthropicProviderOptions,
anthropic: getAnthropicThinkingOptions(modelId),
},
};
}
Expand Down Expand Up @@ -326,6 +389,9 @@ export const getAISDKLanguageModelAndOptions = async (config: LanguageModel): Pr

return {
model: vertexAnthropic(modelId),
providerOptions: {
anthropic: getAnthropicThinkingOptions(modelId),
},
};
}
case 'mistral': {
Expand Down
Loading