mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-24 04:10:15 +01:00
🧠 feat: Bedrock Anthropic Reasoning & Update Endpoint Handling (#6163)
* feat: Add thinking and thinkingBudget parameters for Bedrock Anthropic models * chore: Update @librechat/agents to version 2.1.8 * refactor: change region order in params * refactor: Add maxTokens parameter to conversation preset schema * refactor: Update agent client to use bedrockInputSchema and improve error handling for model parameters * refactor: streamline/optimize llmConfig initialization and saving for bedrock * fix: ensure config titleModel is used for all endpoints * refactor: enhance OpenAIClient and agent initialization to support endpoint checks for OpenRouter * chore: bump @google/generative-ai
This commit is contained in:
parent
3accf91094
commit
ceb0da874b
17 changed files with 2224 additions and 667 deletions
|
|
@ -1,6 +1,20 @@
|
|||
import { z } from 'zod';
|
||||
import * as s from './schemas';
|
||||
|
||||
type ThinkingConfig = {
|
||||
type: 'enabled';
|
||||
budget_tokens: number;
|
||||
};
|
||||
type AnthropicReasoning = {
|
||||
thinking?: ThinkingConfig | boolean;
|
||||
thinkingBudget?: number;
|
||||
};
|
||||
|
||||
type AnthropicInput = BedrockConverseInput & {
|
||||
additionalModelRequestFields: BedrockConverseInput['additionalModelRequestFields'] &
|
||||
AnthropicReasoning;
|
||||
};
|
||||
|
||||
export const bedrockInputSchema = s.tConversationSchema
|
||||
.pick({
|
||||
/* LibreChat params; optionType: 'conversation' */
|
||||
|
|
@ -21,11 +35,24 @@ export const bedrockInputSchema = s.tConversationSchema
|
|||
temperature: true,
|
||||
topP: true,
|
||||
stop: true,
|
||||
thinking: true,
|
||||
thinkingBudget: true,
|
||||
/* Catch-all fields */
|
||||
topK: true,
|
||||
additionalModelRequestFields: true,
|
||||
})
|
||||
.transform((obj) => s.removeNullishValues(obj))
|
||||
.transform((obj) => {
|
||||
if ((obj as AnthropicInput).additionalModelRequestFields?.thinking != null) {
|
||||
const _obj = obj as AnthropicInput;
|
||||
obj.thinking = !!_obj.additionalModelRequestFields.thinking;
|
||||
obj.thinkingBudget =
|
||||
typeof _obj.additionalModelRequestFields.thinking === 'object'
|
||||
? (_obj.additionalModelRequestFields.thinking as ThinkingConfig)?.budget_tokens
|
||||
: undefined;
|
||||
delete obj.additionalModelRequestFields;
|
||||
}
|
||||
return s.removeNullishValues(obj);
|
||||
})
|
||||
.catch(() => ({}));
|
||||
|
||||
export type BedrockConverseInput = z.infer<typeof bedrockInputSchema>;
|
||||
|
|
@ -49,6 +76,8 @@ export const bedrockInputParser = s.tConversationSchema
|
|||
temperature: true,
|
||||
topP: true,
|
||||
stop: true,
|
||||
thinking: true,
|
||||
thinkingBudget: true,
|
||||
/* Catch-all fields */
|
||||
topK: true,
|
||||
additionalModelRequestFields: true,
|
||||
|
|
@ -87,6 +116,27 @@ export const bedrockInputParser = s.tConversationSchema
|
|||
}
|
||||
});
|
||||
|
||||
/** Default thinking and thinkingBudget for 'anthropic.claude-3-7-sonnet' models, if not defined */
|
||||
if (
|
||||
typeof typedData.model === 'string' &&
|
||||
typedData.model.includes('anthropic.claude-3-7-sonnet')
|
||||
) {
|
||||
if (additionalFields.thinking === undefined) {
|
||||
additionalFields.thinking = true;
|
||||
} else if (additionalFields.thinking === false) {
|
||||
delete additionalFields.thinking;
|
||||
delete additionalFields.thinkingBudget;
|
||||
}
|
||||
|
||||
if (additionalFields.thinking === true && additionalFields.thinkingBudget === undefined) {
|
||||
additionalFields.thinkingBudget = 2000;
|
||||
}
|
||||
additionalFields.anthropic_beta = ['output-128k-2025-02-19'];
|
||||
} else if (additionalFields.thinking != null || additionalFields.thinkingBudget != null) {
|
||||
delete additionalFields.thinking;
|
||||
delete additionalFields.thinkingBudget;
|
||||
}
|
||||
|
||||
if (Object.keys(additionalFields).length > 0) {
|
||||
typedData.additionalModelRequestFields = {
|
||||
...((typedData.additionalModelRequestFields as Record<string, unknown> | undefined) || {}),
|
||||
|
|
@ -104,9 +154,34 @@ export const bedrockInputParser = s.tConversationSchema
|
|||
})
|
||||
.catch(() => ({}));
|
||||
|
||||
/**
|
||||
* Configures the "thinking" parameter based on given input and thinking options.
|
||||
*
|
||||
* @param data - The parsed Bedrock request options object
|
||||
* @returns The object with thinking configured appropriately
|
||||
*/
|
||||
function configureThinking(data: AnthropicInput): AnthropicInput {
|
||||
const updatedData = { ...data };
|
||||
if (updatedData.additionalModelRequestFields?.thinking === true) {
|
||||
updatedData.maxTokens = updatedData.maxTokens ?? updatedData.maxOutputTokens ?? 8192;
|
||||
delete updatedData.maxOutputTokens;
|
||||
const thinkingConfig: AnthropicReasoning['thinking'] = {
|
||||
type: 'enabled',
|
||||
budget_tokens: updatedData.additionalModelRequestFields.thinkingBudget ?? 2000,
|
||||
};
|
||||
|
||||
if (thinkingConfig.budget_tokens > updatedData.maxTokens) {
|
||||
thinkingConfig.budget_tokens = Math.floor(updatedData.maxTokens * 0.9);
|
||||
}
|
||||
updatedData.additionalModelRequestFields.thinking = thinkingConfig;
|
||||
delete updatedData.additionalModelRequestFields.thinkingBudget;
|
||||
}
|
||||
return updatedData;
|
||||
}
|
||||
|
||||
export const bedrockOutputParser = (data: Record<string, unknown>) => {
|
||||
const knownKeys = [...Object.keys(s.tConversationSchema.shape), 'topK', 'top_k'];
|
||||
const result: Record<string, unknown> = {};
|
||||
let result: Record<string, unknown> = {};
|
||||
|
||||
// Extract known fields from the root level
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
|
|
@ -125,6 +200,8 @@ export const bedrockOutputParser = (data: Record<string, unknown>) => {
|
|||
if (knownKeys.includes(key)) {
|
||||
if (key === 'top_k') {
|
||||
result['topK'] = value;
|
||||
} else if (key === 'thinking' || key === 'thinkingBudget') {
|
||||
return;
|
||||
} else {
|
||||
result[key] = value;
|
||||
}
|
||||
|
|
@ -140,8 +217,11 @@ export const bedrockOutputParser = (data: Record<string, unknown>) => {
|
|||
result.maxTokens = result.maxOutputTokens;
|
||||
}
|
||||
|
||||
// Remove additionalModelRequestFields from the result
|
||||
delete result.additionalModelRequestFields;
|
||||
result = configureThinking(result as AnthropicInput);
|
||||
// Remove additionalModelRequestFields from the result if it doesn't thinking config
|
||||
if ((result as AnthropicInput).additionalModelRequestFields?.thinking == null) {
|
||||
delete result.additionalModelRequestFields;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue