mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
🪨 feat: Bedrock Support for Claude-4 Reasoning (#7517)
* 🗑️ chore: Update .gitignore to reflect AI-related files * chore: linting in Bedrock options.js * 🪨 feat: Bedrock Claude-4 Reasoning
This commit is contained in:
parent
7e98702a87
commit
2265413387
4 changed files with 124 additions and 6 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -52,8 +52,9 @@ bower_components/
|
||||||
*.d.ts
|
*.d.ts
|
||||||
!vite-env.d.ts
|
!vite-env.d.ts
|
||||||
|
|
||||||
# Cline
|
# AI
|
||||||
.clineignore
|
.clineignore
|
||||||
|
.cursor
|
||||||
|
|
||||||
# Floobits
|
# Floobits
|
||||||
.floo
|
.floo
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,10 @@ const getOptions = async ({ req, overrideModel, endpointOption }) => {
|
||||||
let credentials = isUserProvided
|
let credentials = isUserProvided
|
||||||
? await getUserKey({ userId: req.user.id, name: EModelEndpoint.bedrock })
|
? await getUserKey({ userId: req.user.id, name: EModelEndpoint.bedrock })
|
||||||
: {
|
: {
|
||||||
accessKeyId: BEDROCK_AWS_ACCESS_KEY_ID,
|
accessKeyId: BEDROCK_AWS_ACCESS_KEY_ID,
|
||||||
secretAccessKey: BEDROCK_AWS_SECRET_ACCESS_KEY,
|
secretAccessKey: BEDROCK_AWS_SECRET_ACCESS_KEY,
|
||||||
...(BEDROCK_AWS_SESSION_TOKEN && { sessionToken: BEDROCK_AWS_SESSION_TOKEN }),
|
...(BEDROCK_AWS_SESSION_TOKEN && { sessionToken: BEDROCK_AWS_SESSION_TOKEN }),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!credentials) {
|
if (!credentials) {
|
||||||
throw new Error('Bedrock credentials not provided. Please provide them again.');
|
throw new Error('Bedrock credentials not provided. Please provide them again.');
|
||||||
|
|
|
||||||
114
packages/data-provider/specs/bedrock.spec.ts
Normal file
114
packages/data-provider/specs/bedrock.spec.ts
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
import { bedrockInputParser } from '../src/bedrock';
|
||||||
|
import type { BedrockConverseInput } from '../src/bedrock';
|
||||||
|
|
||||||
|
describe('bedrockInputParser', () => {
|
||||||
|
describe('Model Matching for Reasoning Configuration', () => {
|
||||||
|
test('should match anthropic.claude-3-7-sonnet model', () => {
|
||||||
|
const input = {
|
||||||
|
model: 'anthropic.claude-3-7-sonnet',
|
||||||
|
};
|
||||||
|
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']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should match anthropic.claude-sonnet-4 model', () => {
|
||||||
|
const input = {
|
||||||
|
model: 'anthropic.claude-sonnet-4',
|
||||||
|
};
|
||||||
|
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']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should match anthropic.claude-opus-5 model', () => {
|
||||||
|
const input = {
|
||||||
|
model: 'anthropic.claude-opus-5',
|
||||||
|
};
|
||||||
|
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']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should match anthropic.claude-haiku-6 model', () => {
|
||||||
|
const input = {
|
||||||
|
model: 'anthropic.claude-haiku-6',
|
||||||
|
};
|
||||||
|
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']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should match anthropic.claude-4-sonnet model', () => {
|
||||||
|
const input = {
|
||||||
|
model: 'anthropic.claude-4-sonnet',
|
||||||
|
};
|
||||||
|
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']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should match anthropic.claude-4.5-sonnet model', () => {
|
||||||
|
const input = {
|
||||||
|
model: 'anthropic.claude-4.5-sonnet',
|
||||||
|
};
|
||||||
|
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']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should match anthropic.claude-4-7-sonnet model', () => {
|
||||||
|
const input = {
|
||||||
|
model: 'anthropic.claude-4-7-sonnet',
|
||||||
|
};
|
||||||
|
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']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not match non-Claude models', () => {
|
||||||
|
const input = {
|
||||||
|
model: 'some-other-model',
|
||||||
|
};
|
||||||
|
const result = bedrockInputParser.parse(input) as BedrockConverseInput;
|
||||||
|
expect(result.additionalModelRequestFields).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should respect explicit thinking configuration', () => {
|
||||||
|
const input = {
|
||||||
|
model: 'anthropic.claude-sonnet-4',
|
||||||
|
thinking: false,
|
||||||
|
};
|
||||||
|
const result = bedrockInputParser.parse(input) as BedrockConverseInput;
|
||||||
|
const additionalFields = result.additionalModelRequestFields as Record<string, unknown>;
|
||||||
|
expect(additionalFields.thinking).toBeUndefined();
|
||||||
|
expect(additionalFields.thinkingBudget).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should respect custom thinking budget', () => {
|
||||||
|
const input = {
|
||||||
|
model: 'anthropic.claude-sonnet-4',
|
||||||
|
thinking: true,
|
||||||
|
thinkingBudget: 3000,
|
||||||
|
};
|
||||||
|
const result = bedrockInputParser.parse(input) as BedrockConverseInput;
|
||||||
|
const additionalFields = result.additionalModelRequestFields as Record<string, unknown>;
|
||||||
|
expect(additionalFields.thinking).toBe(true);
|
||||||
|
expect(additionalFields.thinkingBudget).toBe(3000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -119,7 +119,10 @@ export const bedrockInputParser = s.tConversationSchema
|
||||||
/** Default thinking and thinkingBudget for 'anthropic.claude-3-7-sonnet' models, if not defined */
|
/** Default thinking and thinkingBudget for 'anthropic.claude-3-7-sonnet' models, if not defined */
|
||||||
if (
|
if (
|
||||||
typeof typedData.model === 'string' &&
|
typeof typedData.model === 'string' &&
|
||||||
typedData.model.includes('anthropic.claude-3-7-sonnet')
|
(typedData.model.includes('anthropic.claude-3-7-sonnet') ||
|
||||||
|
/anthropic\.claude-(?:[4-9](?:\.\d+)?(?:-\d+)?-(?:sonnet|opus|haiku)|(?:sonnet|opus|haiku)-[4-9])/.test(
|
||||||
|
typedData.model,
|
||||||
|
))
|
||||||
) {
|
) {
|
||||||
if (additionalFields.thinking === undefined) {
|
if (additionalFields.thinking === undefined) {
|
||||||
additionalFields.thinking = true;
|
additionalFields.thinking = true;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue