mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-16 23:45:33 +01:00
🪨 feat: Anthropic Beta Support for Bedrock (#11371)
* 🪨 feat: Anthropic Beta Support for Bedrock
- Updated the Bedrock input parser to dynamically generate `anthropic_beta` headers based on the model identifier.
- Added a new utility function `getBedrockAnthropicBetaHeaders` to determine applicable headers for various Anthropic models.
- Modified existing tests to reflect changes in expected `anthropic_beta` values, including new test cases for full model IDs.
* test: Update Bedrock Input Parser Tests for Beta Headers
- Modified the test case for explicit thinking configuration to reflect the addition of `anthropic_beta` headers.
- Ensured that the test now verifies the presence of specific beta header values in the additional model request fields.
This commit is contained in:
parent
476882455e
commit
81f4af55b5
2 changed files with 75 additions and 12 deletions
|
|
@ -14,7 +14,7 @@ describe('bedrockInputParser', () => {
|
|||
expect(additionalFields.anthropic_beta).toEqual(['output-128k-2025-02-19']);
|
||||
});
|
||||
|
||||
test('should match anthropic.claude-sonnet-4 model', () => {
|
||||
test('should match anthropic.claude-sonnet-4 model with 1M context header', () => {
|
||||
const input = {
|
||||
model: 'anthropic.claude-sonnet-4',
|
||||
};
|
||||
|
|
@ -22,10 +22,13 @@ describe('bedrockInputParser', () => {
|
|||
const additionalFields = result.additionalModelRequestFields as Record<string, unknown>;
|
||||
expect(additionalFields.thinking).toBe(true);
|
||||
expect(additionalFields.thinkingBudget).toBe(2000);
|
||||
expect(additionalFields.anthropic_beta).toEqual(['output-128k-2025-02-19']);
|
||||
expect(additionalFields.anthropic_beta).toEqual([
|
||||
'output-128k-2025-02-19',
|
||||
'context-1m-2025-08-07',
|
||||
]);
|
||||
});
|
||||
|
||||
test('should match anthropic.claude-opus-5 model', () => {
|
||||
test('should match anthropic.claude-opus-5 model without 1M context header', () => {
|
||||
const input = {
|
||||
model: 'anthropic.claude-opus-5',
|
||||
};
|
||||
|
|
@ -36,7 +39,7 @@ describe('bedrockInputParser', () => {
|
|||
expect(additionalFields.anthropic_beta).toEqual(['output-128k-2025-02-19']);
|
||||
});
|
||||
|
||||
test('should match anthropic.claude-haiku-6 model', () => {
|
||||
test('should match anthropic.claude-haiku-6 model without 1M context header', () => {
|
||||
const input = {
|
||||
model: 'anthropic.claude-haiku-6',
|
||||
};
|
||||
|
|
@ -47,7 +50,7 @@ describe('bedrockInputParser', () => {
|
|||
expect(additionalFields.anthropic_beta).toEqual(['output-128k-2025-02-19']);
|
||||
});
|
||||
|
||||
test('should match anthropic.claude-4-sonnet model', () => {
|
||||
test('should match anthropic.claude-4-sonnet model with 1M context header', () => {
|
||||
const input = {
|
||||
model: 'anthropic.claude-4-sonnet',
|
||||
};
|
||||
|
|
@ -55,10 +58,13 @@ describe('bedrockInputParser', () => {
|
|||
const additionalFields = result.additionalModelRequestFields as Record<string, unknown>;
|
||||
expect(additionalFields.thinking).toBe(true);
|
||||
expect(additionalFields.thinkingBudget).toBe(2000);
|
||||
expect(additionalFields.anthropic_beta).toEqual(['output-128k-2025-02-19']);
|
||||
expect(additionalFields.anthropic_beta).toEqual([
|
||||
'output-128k-2025-02-19',
|
||||
'context-1m-2025-08-07',
|
||||
]);
|
||||
});
|
||||
|
||||
test('should match anthropic.claude-4.5-sonnet model', () => {
|
||||
test('should match anthropic.claude-4.5-sonnet model with 1M context header', () => {
|
||||
const input = {
|
||||
model: 'anthropic.claude-4.5-sonnet',
|
||||
};
|
||||
|
|
@ -66,10 +72,13 @@ describe('bedrockInputParser', () => {
|
|||
const additionalFields = result.additionalModelRequestFields as Record<string, unknown>;
|
||||
expect(additionalFields.thinking).toBe(true);
|
||||
expect(additionalFields.thinkingBudget).toBe(2000);
|
||||
expect(additionalFields.anthropic_beta).toEqual(['output-128k-2025-02-19']);
|
||||
expect(additionalFields.anthropic_beta).toEqual([
|
||||
'output-128k-2025-02-19',
|
||||
'context-1m-2025-08-07',
|
||||
]);
|
||||
});
|
||||
|
||||
test('should match anthropic.claude-4-7-sonnet model', () => {
|
||||
test('should match anthropic.claude-4-7-sonnet model with 1M context header', () => {
|
||||
const input = {
|
||||
model: 'anthropic.claude-4-7-sonnet',
|
||||
};
|
||||
|
|
@ -77,7 +86,24 @@ describe('bedrockInputParser', () => {
|
|||
const additionalFields = result.additionalModelRequestFields as Record<string, unknown>;
|
||||
expect(additionalFields.thinking).toBe(true);
|
||||
expect(additionalFields.thinkingBudget).toBe(2000);
|
||||
expect(additionalFields.anthropic_beta).toEqual(['output-128k-2025-02-19']);
|
||||
expect(additionalFields.anthropic_beta).toEqual([
|
||||
'output-128k-2025-02-19',
|
||||
'context-1m-2025-08-07',
|
||||
]);
|
||||
});
|
||||
|
||||
test('should match anthropic.claude-sonnet-4-20250514-v1:0 with full model ID', () => {
|
||||
const input = {
|
||||
model: 'anthropic.claude-sonnet-4-20250514-v1:0',
|
||||
};
|
||||
const result = bedrockInputParser.parse(input) as BedrockConverseInput;
|
||||
const additionalFields = result.additionalModelRequestFields as Record<string, unknown>;
|
||||
expect(additionalFields.thinking).toBe(true);
|
||||
expect(additionalFields.thinkingBudget).toBe(2000);
|
||||
expect(additionalFields.anthropic_beta).toEqual([
|
||||
'output-128k-2025-02-19',
|
||||
'context-1m-2025-08-07',
|
||||
]);
|
||||
});
|
||||
|
||||
test('should not match non-Claude models', () => {
|
||||
|
|
@ -110,7 +136,7 @@ describe('bedrockInputParser', () => {
|
|||
expect(additionalFields?.anthropic_beta).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should respect explicit thinking configuration', () => {
|
||||
test('should respect explicit thinking configuration but still add beta headers', () => {
|
||||
const input = {
|
||||
model: 'anthropic.claude-sonnet-4',
|
||||
thinking: false,
|
||||
|
|
@ -119,6 +145,10 @@ describe('bedrockInputParser', () => {
|
|||
const additionalFields = result.additionalModelRequestFields as Record<string, unknown>;
|
||||
expect(additionalFields.thinking).toBeUndefined();
|
||||
expect(additionalFields.thinkingBudget).toBeUndefined();
|
||||
expect(additionalFields.anthropic_beta).toEqual([
|
||||
'output-128k-2025-02-19',
|
||||
'context-1m-2025-08-07',
|
||||
]);
|
||||
});
|
||||
|
||||
test('should respect custom thinking budget', () => {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,36 @@ type AnthropicInput = BedrockConverseInput & {
|
|||
AnthropicReasoning;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the appropriate anthropic_beta headers for Bedrock Anthropic models.
|
||||
* Bedrock uses `anthropic_beta` (with underscore) in additionalModelRequestFields.
|
||||
*
|
||||
* @param model - The Bedrock model identifier (e.g., "anthropic.claude-sonnet-4-20250514-v1:0")
|
||||
* @returns Array of beta header strings, or empty array if not applicable
|
||||
*/
|
||||
function getBedrockAnthropicBetaHeaders(model: string): string[] {
|
||||
const betaHeaders: string[] = [];
|
||||
|
||||
const isClaudeThinkingModel =
|
||||
model.includes('anthropic.claude-3-7-sonnet') ||
|
||||
/anthropic\.claude-(?:[4-9](?:\.\d+)?(?:-\d+)?-(?:sonnet|opus|haiku)|(?:sonnet|opus|haiku)-[4-9])/.test(
|
||||
model,
|
||||
);
|
||||
|
||||
const isSonnet4PlusModel =
|
||||
/anthropic\.claude-(?:sonnet-[4-9]|[4-9](?:\.\d+)?(?:-\d+)?-sonnet)/.test(model);
|
||||
|
||||
if (isClaudeThinkingModel) {
|
||||
betaHeaders.push('output-128k-2025-02-19');
|
||||
}
|
||||
|
||||
if (isSonnet4PlusModel) {
|
||||
betaHeaders.push('context-1m-2025-08-07');
|
||||
}
|
||||
|
||||
return betaHeaders;
|
||||
}
|
||||
|
||||
export const bedrockInputSchema = s.tConversationSchema
|
||||
.pick({
|
||||
/* LibreChat params; optionType: 'conversation' */
|
||||
|
|
@ -138,7 +168,10 @@ export const bedrockInputParser = s.tConversationSchema
|
|||
additionalFields.thinkingBudget = 2000;
|
||||
}
|
||||
if (typedData.model.includes('anthropic.')) {
|
||||
additionalFields.anthropic_beta = ['output-128k-2025-02-19'];
|
||||
const betaHeaders = getBedrockAnthropicBetaHeaders(typedData.model);
|
||||
if (betaHeaders.length > 0) {
|
||||
additionalFields.anthropic_beta = betaHeaders;
|
||||
}
|
||||
}
|
||||
} else if (additionalFields.thinking != null || additionalFields.thinkingBudget != null) {
|
||||
delete additionalFields.thinking;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue