feat: Anthropic Agents Prompt Caching & UI Accessibility Enhancements (#6045)

* chore: remove auto-focus for now

* refactor: move react-hook-form Controller Logic to AgentSelect from AgentPanel

* fix: a11y focus issue with AgentSelect by never replacing it in its component tree

* fix: maintain ComboBox focus and force re-render on agent ID change in AgentPanel

* chore: `gemini-2.0-flash-lite-preview-02-05` (deprecated)

* refactor: extract cache control logic and headers configuration to helper functions in AnthropicClient

* feat: anthropic agents prompt caching

* chore: bump @librechat/agents and related dependencies

* fix: typo
This commit is contained in:
Danny Avila 2025-02-25 22:14:58 -05:00 committed by GitHub
parent d3d7d11ea8
commit e14df5956a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 4460 additions and 1477 deletions

View file

@ -17,6 +17,11 @@ const {
parseParamFromPrompt,
createContextHandlers,
} = require('./prompts');
const {
getClaudeHeaders,
configureReasoning,
checkPromptCacheSupport,
} = require('~/server/services/Endpoints/anthropic/helpers');
const { getModelMaxTokens, getModelMaxOutputTokens, matchModelName } = require('~/utils');
const { spendTokens, spendStructuredTokens } = require('~/models/spendTokens');
const Tokenizer = require('~/server/services/Tokenizer');
@ -101,8 +106,7 @@ class AnthropicClient extends BaseClient {
const modelMatch = matchModelName(this.modelOptions.model, EModelEndpoint.anthropic);
this.isClaude3 = modelMatch.includes('claude-3');
this.isLegacyOutput = !modelMatch.includes('claude-3-5-sonnet');
this.supportsCacheControl =
this.options.promptCache && this.checkPromptCacheSupport(modelMatch);
this.supportsCacheControl = this.options.promptCache && checkPromptCacheSupport(modelMatch);
if (
this.isLegacyOutput &&
@ -174,26 +178,9 @@ class AnthropicClient extends BaseClient {
options.baseURL = this.options.reverseProxyUrl;
}
if (
this.supportsCacheControl &&
requestOptions?.model &&
requestOptions.model.includes('claude-3-5-sonnet')
) {
options.defaultHeaders = {
'anthropic-beta': 'max-tokens-3-5-sonnet-2024-07-15,prompt-caching-2024-07-31',
};
} else if (
this.supportsCacheControl &&
requestOptions?.model &&
requestOptions.model.includes('claude-3-7')
) {
options.defaultHeaders = {
'anthropic-beta': 'output-128k-2025-02-19,prompt-caching-2024-07-31',
};
} else if (this.supportsCacheControl) {
options.defaultHeaders = {
'anthropic-beta': 'prompt-caching-2024-07-31',
};
const headers = getClaudeHeaders(requestOptions?.model, this.supportsCacheControl);
if (headers) {
options.defaultHeaders = headers;
}
return new Anthropic(options);
@ -684,27 +671,6 @@ class AnthropicClient extends BaseClient {
: await client.completions.create(options);
}
/**
* @param {string} modelName
* @returns {boolean}
*/
checkPromptCacheSupport(modelName) {
const modelMatch = matchModelName(modelName, EModelEndpoint.anthropic);
if (modelMatch.includes('claude-3-5-sonnet-latest')) {
return false;
}
if (
modelMatch === 'claude-3-7-sonnet' ||
modelMatch === 'claude-3-5-sonnet' ||
modelMatch === 'claude-3-5-haiku' ||
modelMatch === 'claude-3-haiku' ||
modelMatch === 'claude-3-opus'
) {
return true;
}
return false;
}
getMessageMapMethod() {
/**
* @param {TMessage} msg
@ -761,7 +727,7 @@ class AnthropicClient extends BaseClient {
topK: top_k,
} = this.modelOptions;
const requestOptions = {
let requestOptions = {
model,
stream: stream || true,
stop_sequences,
@ -780,40 +746,10 @@ class AnthropicClient extends BaseClient {
requestOptions.max_tokens_to_sample = maxOutputTokens || legacy.maxOutputTokens.default;
}
if (
this.options.thinking &&
requestOptions?.model &&
requestOptions.model.includes('claude-3-7')
) {
requestOptions.thinking = {
type: 'enabled',
};
}
if (requestOptions.thinking != null && this.options.thinkingBudget != null) {
requestOptions.thinking = {
...requestOptions.thinking,
budget_tokens: this.options.thinkingBudget,
};
}
if (
requestOptions.thinking != null &&
(requestOptions.max_tokens == null ||
requestOptions.thinking.budget_tokens > requestOptions.max_tokens)
) {
const maxTokens = anthropicSettings.maxOutputTokens.reset(requestOptions.model);
requestOptions.max_tokens = requestOptions.max_tokens ?? maxTokens;
logger.warn(
requestOptions.max_tokens === maxTokens
? '[AnthropicClient] max_tokens is not defined while thinking is enabled. Setting max_tokens to model default.'
: `[AnthropicClient] thinking budget_tokens (${requestOptions.thinking.budget_tokens}) exceeds max_tokens (${requestOptions.max_tokens}). Adjusting budget_tokens.`,
);
requestOptions.thinking.budget_tokens = Math.min(
requestOptions.thinking.budget_tokens,
Math.floor(requestOptions.max_tokens * 0.9),
);
}
requestOptions = configureReasoning(requestOptions, {
thinking: this.options.thinking,
thinkingBudget: this.options.thinkingBudget,
});
if (this.systemMessage && this.supportsCacheControl === true) {
requestOptions.system = [

View file

@ -1,7 +1,7 @@
/**
* Anthropic API: Adds cache control to the appropriate user messages in the payload.
* @param {Array<AnthropicMessage>} messages - The array of message objects.
* @returns {Array<AnthropicMessage>} - The updated array of message objects with cache control added.
* @param {Array<AnthropicMessage | BaseMessage>} messages - The array of message objects.
* @returns {Array<AnthropicMessage | BaseMessage>} - The updated array of message objects with cache control added.
*/
function addCacheControl(messages) {
if (!Array.isArray(messages) || messages.length < 2) {
@ -13,7 +13,9 @@ function addCacheControl(messages) {
for (let i = updatedMessages.length - 1; i >= 0 && userMessagesModified < 2; i--) {
const message = updatedMessages[i];
if (message.role !== 'user') {
if (message.getType != null && message.getType() !== 'human') {
continue;
} else if (message.getType == null && message.role !== 'user') {
continue;
}

View file

@ -41,11 +41,11 @@
"@keyv/mongo": "^2.1.8",
"@keyv/redis": "^2.8.1",
"@langchain/community": "^0.3.14",
"@langchain/core": "^0.3.37",
"@langchain/google-genai": "^0.1.7",
"@langchain/google-vertexai": "^0.1.8",
"@langchain/core": "^0.3.40",
"@langchain/google-genai": "^0.1.9",
"@langchain/google-vertexai": "^0.2.0",
"@langchain/textsplitters": "^0.1.0",
"@librechat/agents": "^2.1.2",
"@librechat/agents": "^2.1.3",
"@waylaidwanderer/fetch-event-source": "^3.0.1",
"axios": "1.7.8",
"bcryptjs": "^2.4.3",

View file

@ -22,6 +22,7 @@ const {
} = require('librechat-data-provider');
const {
formatMessage,
addCacheControl,
formatAgentMessages,
formatContentStrings,
createContextHandlers,
@ -589,7 +590,7 @@ class AgentClient extends BaseClient {
* @param {number} [i]
* @param {TMessageContentParts[]} [contentData]
*/
const runAgent = async (agent, messages, i = 0, contentData = []) => {
const runAgent = async (agent, _messages, i = 0, contentData = []) => {
config.configurable.model = agent.model_parameters.model;
if (i > 0) {
this.model = agent.model_parameters.model;
@ -622,12 +623,21 @@ class AgentClient extends BaseClient {
}
if (noSystemMessages === true && systemContent?.length) {
let latestMessage = messages.pop().content;
let latestMessage = _messages.pop().content;
if (typeof latestMessage !== 'string') {
latestMessage = latestMessage[0].text;
}
latestMessage = [systemContent, latestMessage].join('\n');
messages.push(new HumanMessage(latestMessage));
_messages.push(new HumanMessage(latestMessage));
}
let messages = _messages;
if (
agent.model_parameters?.clientOptions?.defaultHeaders?.['anthropic-beta']?.includes(
'prompt-caching',
)
) {
messages = addCacheControl(messages);
}
run = await createRun({

View file

@ -0,0 +1,110 @@
const { EModelEndpoint, anthropicSettings } = require('librechat-data-provider');
const { matchModelName } = require('~/utils');
const { logger } = require('~/config');
/**
* @param {string} modelName
* @returns {boolean}
*/
function checkPromptCacheSupport(modelName) {
const modelMatch = matchModelName(modelName, EModelEndpoint.anthropic);
if (
modelMatch.includes('claude-3-5-sonnet-latest') ||
modelMatch.includes('claude-3.5-sonnet-latest')
) {
return false;
}
if (
modelMatch === 'claude-3-7-sonnet' ||
modelMatch === 'claude-3-5-sonnet' ||
modelMatch === 'claude-3-5-haiku' ||
modelMatch === 'claude-3-haiku' ||
modelMatch === 'claude-3-opus' ||
modelMatch === 'claude-3.7-sonnet' ||
modelMatch === 'claude-3.5-sonnet' ||
modelMatch === 'claude-3.5-haiku'
) {
return true;
}
return false;
}
/**
* Gets the appropriate headers for Claude models with cache control
* @param {string} model The model name
* @param {boolean} supportsCacheControl Whether the model supports cache control
* @returns {AnthropicClientOptions['extendedOptions']['defaultHeaders']|undefined} The headers object or undefined if not applicable
*/
function getClaudeHeaders(model, supportsCacheControl) {
if (!supportsCacheControl) {
return undefined;
}
if (/claude-3[-.]5-sonnet/.test(model)) {
return {
'anthropic-beta': 'max-tokens-3-5-sonnet-2024-07-15,prompt-caching-2024-07-31',
};
} else if (/claude-3[-.]7/.test(model)) {
return {
'anthropic-beta': 'output-128k-2025-02-19,prompt-caching-2024-07-31',
};
} else {
return {
'anthropic-beta': 'prompt-caching-2024-07-31',
};
}
}
/**
* Configures reasoning-related options for Claude models
* @param {AnthropicClientOptions & { max_tokens?: number }} anthropicInput The request options object
* @param {Object} extendedOptions Additional client configuration options
* @param {boolean} extendedOptions.thinking Whether thinking is enabled in client config
* @param {number|null} extendedOptions.thinkingBudget The token budget for thinking
* @returns {Object} Updated request options
*/
function configureReasoning(anthropicInput, extendedOptions = {}) {
const updatedOptions = { ...anthropicInput };
const currentMaxTokens = updatedOptions.max_tokens ?? updatedOptions.maxTokens;
if (
extendedOptions.thinking &&
updatedOptions?.model &&
/claude-3[-.]7/.test(updatedOptions.model)
) {
updatedOptions.thinking = {
type: 'enabled',
};
}
if (updatedOptions.thinking != null && extendedOptions.thinkingBudget != null) {
updatedOptions.thinking = {
...updatedOptions.thinking,
budget_tokens: extendedOptions.thinkingBudget,
};
}
if (
updatedOptions.thinking != null &&
(currentMaxTokens == null || updatedOptions.thinking.budget_tokens > currentMaxTokens)
) {
const maxTokens = anthropicSettings.maxOutputTokens.reset(updatedOptions.model);
updatedOptions.max_tokens = currentMaxTokens ?? maxTokens;
logger.warn(
updatedOptions.max_tokens === maxTokens
? '[AnthropicClient] max_tokens is not defined while thinking is enabled. Setting max_tokens to model default.'
: `[AnthropicClient] thinking budget_tokens (${updatedOptions.thinking.budget_tokens}) exceeds max_tokens (${updatedOptions.max_tokens}). Adjusting budget_tokens.`,
);
updatedOptions.thinking.budget_tokens = Math.min(
updatedOptions.thinking.budget_tokens,
Math.floor(updatedOptions.max_tokens * 0.9),
);
}
return updatedOptions;
}
module.exports = { checkPromptCacheSupport, getClaudeHeaders, configureReasoning };

View file

@ -1,5 +1,6 @@
const { HttpsProxyAgent } = require('https-proxy-agent');
const { anthropicSettings, removeNullishValues } = require('librechat-data-provider');
const { checkPromptCacheSupport, getClaudeHeaders } = require('./helpers');
/**
* Generates configuration options for creating an Anthropic language model (LLM) instance.
@ -20,6 +21,14 @@ const { anthropicSettings, removeNullishValues } = require('librechat-data-provi
* @returns {Object} Configuration options for creating an Anthropic LLM instance, with null and undefined values removed.
*/
function getLLMConfig(apiKey, options = {}) {
const systemOptions = {
thinking: options.modelOptions.thinking ?? anthropicSettings.thinking.default,
promptCache: options.modelOptions.promptCache ?? anthropicSettings.promptCache.default,
thinkingBudget: options.modelOptions.thinkingBudget ?? anthropicSettings.thinkingBudget.default,
};
for (let key in systemOptions) {
delete options.modelOptions[key];
}
const defaultOptions = {
model: anthropicSettings.model.default,
maxOutputTokens: anthropicSettings.maxOutputTokens.default,
@ -29,7 +38,7 @@ function getLLMConfig(apiKey, options = {}) {
const mergedOptions = Object.assign(defaultOptions, options.modelOptions);
/** @type {AnthropicClientOptions} */
const requestOptions = {
let requestOptions = {
apiKey,
model: mergedOptions.model,
stream: mergedOptions.stream,
@ -42,6 +51,13 @@ function getLLMConfig(apiKey, options = {}) {
clientOptions: {},
};
const supportsCacheControl =
systemOptions.promptCache === true && checkPromptCacheSupport(requestOptions.model);
const headers = getClaudeHeaders(requestOptions.model, supportsCacheControl);
if (headers) {
requestOptions.clientOptions.defaultHeaders = headers;
}
if (options.proxy) {
requestOptions.clientOptions.httpAgent = new HttpsProxyAgent(options.proxy);
}

View file

@ -1,7 +1,7 @@
import { Plus } from 'lucide-react';
import React, { useMemo, useCallback } from 'react';
import { useWatch, useForm, FormProvider } from 'react-hook-form';
import { useGetModelsQuery } from 'librechat-data-provider/react-query';
import { Controller, useWatch, useForm, FormProvider } from 'react-hook-form';
import {
Tools,
SystemRoles,
@ -201,10 +201,6 @@ export default function AgentPanel({
user?.role,
]);
if (agentQuery.isInitialLoading) {
return <AgentPanelSkeleton />;
}
return (
<FormProvider {...methods}>
<form
@ -214,19 +210,13 @@ export default function AgentPanel({
>
<div className="mx-1 mt-2 flex w-full flex-wrap gap-2">
<div className="w-full">
<Controller
name="agent"
control={control}
render={({ field }) => (
<AgentSelect
reset={reset}
value={field.value}
agentQuery={agentQuery}
setCurrentAgentId={setCurrentAgentId}
selectedAgentId={current_agent_id ?? null}
createMutation={create}
/>
)}
<AgentSelect
createMutation={create}
agentQuery={agentQuery}
setCurrentAgentId={setCurrentAgentId}
// The following is required to force re-render the component when the form's agent ID changes
// Also maintains ComboBox Focus for Accessibility
selectedAgentId={agentQuery.isInitialLoading ? null : (current_agent_id ?? null)}
/>
</div>
{/* Create + Select Button */}
@ -240,6 +230,7 @@ export default function AgentPanel({
reset(defaultAgentFormValues);
setCurrentAgentId(undefined);
}}
disabled={agentQuery.isInitialLoading}
>
<Plus className="mr-1 h-4 w-4" />
{localize('com_ui_create') +
@ -250,7 +241,7 @@ export default function AgentPanel({
</Button>
<Button
variant="submit"
disabled={!agent_id}
disabled={!agent_id || agentQuery.isInitialLoading}
onClick={(e) => {
e.preventDefault();
handleSelectAgent();
@ -262,7 +253,8 @@ export default function AgentPanel({
</div>
)}
</div>
{!canEditAgent && (
{agentQuery.isInitialLoading && <AgentPanelSkeleton />}
{!canEditAgent && !agentQuery.isInitialLoading && (
<div className="flex h-[30vh] w-full items-center justify-center">
<div className="text-center">
<h2 className="text-token-text-primary m-2 text-xl font-semibold">
@ -272,7 +264,7 @@ export default function AgentPanel({
</div>
</div>
)}
{canEditAgent && activePanel === Panel.model && (
{canEditAgent && !agentQuery.isInitialLoading && activePanel === Panel.model && (
<ModelPanel
setActivePanel={setActivePanel}
agent_id={agent_id}
@ -280,7 +272,7 @@ export default function AgentPanel({
models={models}
/>
)}
{canEditAgent && activePanel === Panel.builder && (
{canEditAgent && !agentQuery.isInitialLoading && activePanel === Panel.builder && (
<AgentConfig
actions={actions}
setAction={setAction}

View file

@ -3,81 +3,67 @@ import { Skeleton } from '~/components/ui';
export default function AgentPanelSkeleton() {
return (
<div className="scrollbar-gutter-stable h-auto w-full flex-shrink-0 overflow-x-hidden">
<div className="mx-1 mt-2 flex w-full flex-wrap gap-2">
{/* Agent Select Dropdown */}
<div className="w-full">
<Skeleton className="h-[40px] w-full rounded-md" />
<div className="h-auto bg-white px-4 pb-8 pt-3 dark:bg-transparent">
{/* Avatar */}
<div className="mb-4">
<div className="flex w-full items-center justify-center gap-4">
<Skeleton className="relative h-20 w-20 rounded-full" />
</div>
{/* Create and Select Buttons */}
<div className="flex w-full gap-2">
<Skeleton className="h-[40px] w-3/4 rounded-md" /> {/* Create Button */}
<Skeleton className="h-[40px] w-1/4 rounded-md" /> {/* Select Button */}
{/* Name */}
<Skeleton className="mb-2 h-5 w-1/5 rounded-lg" />
<Skeleton className="mb-1 h-[40px] w-full rounded-lg" />
<Skeleton className="h-3 w-1/4 rounded-lg" />
</div>
{/* Description */}
<div className="mb-4">
<Skeleton className="mb-2 h-5 w-1/4 rounded-lg" />
<Skeleton className="h-[40px] w-full rounded-lg" />
</div>
{/* Instructions */}
<div className="mb-6">
<Skeleton className="mb-2 h-5 w-1/4 rounded-lg" />
<Skeleton className="h-[100px] w-full rounded-lg" />
</div>
{/* Model and Provider */}
<div className="mb-6">
<Skeleton className="mb-2 h-5 w-1/4 rounded-lg" />
<Skeleton className="h-[40px] w-full rounded-lg" />
</div>
{/* Capabilities */}
<div className="mb-6">
<Skeleton className="mb-2 h-5 w-1/4 rounded-lg" />
<Skeleton className="mb-2 h-5 w-36 rounded-lg" />
<Skeleton className="mb-4 h-[35px] w-full rounded-lg" />
<Skeleton className="mb-2 h-5 w-24 rounded-lg" />
<Skeleton className="h-[35px] w-full rounded-lg" />
</div>
{/* Tools & Actions */}
<div className="mb-6">
<Skeleton className="mb-2 h-5 w-1/4 rounded-lg" />
<Skeleton className="mb-2 h-[35px] w-full rounded-lg" />
<Skeleton className="mb-2 h-[35px] w-full rounded-lg" />
<div className="flex space-x-2">
<Skeleton className="h-8 w-1/2 rounded-lg" />
<Skeleton className="h-8 w-1/2 rounded-lg" />
</div>
</div>
<div className="h-auto bg-white px-4 pb-8 pt-3 dark:bg-transparent">
{/* Avatar */}
<div className="mb-4">
<div className="flex w-full items-center justify-center gap-4">
<Skeleton className="relative h-20 w-20 rounded-full" />
</div>
{/* Name */}
<Skeleton className="mb-2 h-5 w-1/5 rounded-lg" />
<Skeleton className="mb-1 h-[40px] w-full rounded-lg" />
<Skeleton className="h-3 w-1/4 rounded-lg" />
</div>
{/* Admin Settings */}
<div className="mb-6">
<Skeleton className="h-[35px] w-full rounded-lg" />
</div>
{/* Description */}
<div className="mb-4">
<Skeleton className="mb-2 h-5 w-1/4 rounded-lg" />
<Skeleton className="h-[40px] w-full rounded-lg" />
</div>
{/* Instructions */}
<div className="mb-6">
<Skeleton className="mb-2 h-5 w-1/4 rounded-lg" />
<Skeleton className="h-[100px] w-full rounded-lg" />
</div>
{/* Model and Provider */}
<div className="mb-6">
<Skeleton className="mb-2 h-5 w-1/4 rounded-lg" />
<Skeleton className="h-[40px] w-full rounded-lg" />
</div>
{/* Capabilities */}
<div className="mb-6">
<Skeleton className="mb-2 h-5 w-1/4 rounded-lg" />
<Skeleton className="mb-2 h-5 w-36 rounded-lg" />
<Skeleton className="mb-4 h-[35px] w-full rounded-lg" />
<Skeleton className="mb-2 h-5 w-24 rounded-lg" />
<Skeleton className="h-[35px] w-full rounded-lg" />
</div>
{/* Tools & Actions */}
<div className="mb-6">
<Skeleton className="mb-2 h-5 w-1/4 rounded-lg" />
<Skeleton className="mb-2 h-[35px] w-full rounded-lg" />
<Skeleton className="mb-2 h-[35px] w-full rounded-lg" />
<div className="flex space-x-2">
<Skeleton className="h-8 w-1/2 rounded-lg" />
<Skeleton className="h-8 w-1/2 rounded-lg" />
</div>
</div>
{/* Admin Settings */}
<div className="mb-6">
<Skeleton className="h-[35px] w-full rounded-lg" />
</div>
{/* Bottom Buttons */}
<div className="flex items-center justify-end gap-2">
<Skeleton className="h-[35px] w-16 rounded-lg" />
<Skeleton className="h-[35px] w-16 rounded-lg" />
<Skeleton className="h-[35px] w-16 rounded-lg" />
<Skeleton className="h-[35px] w-full rounded-lg" />
</div>
{/* Bottom Buttons */}
<div className="flex items-center justify-end gap-2">
<Skeleton className="h-[35px] w-16 rounded-lg" />
<Skeleton className="h-[35px] w-16 rounded-lg" />
<Skeleton className="h-[35px] w-16 rounded-lg" />
<Skeleton className="h-[35px] w-full rounded-lg" />
</div>
</div>
);

View file

@ -1,28 +1,23 @@
import { EarthIcon } from 'lucide-react';
import { useCallback, useEffect, useRef } from 'react';
import { useFormContext, Controller } from 'react-hook-form';
import { AgentCapabilities, defaultAgentFormValues } from 'librechat-data-provider';
import type { UseMutationResult, QueryObserverResult } from '@tanstack/react-query';
import type { Agent, AgentCreateParams } from 'librechat-data-provider';
import type { UseFormReset } from 'react-hook-form';
import type { TAgentCapabilities, AgentForm, TAgentOption } from '~/common';
import type { TAgentCapabilities, AgentForm } from '~/common';
import { useListAgentsQuery, useGetStartupConfig } from '~/data-provider';
import { cn, createProviderOption, processAgentOption } from '~/utils';
import ControlCombobox from '~/components/ui/ControlCombobox';
import { useLocalize } from '~/hooks';
const keys = new Set(Object.keys(defaultAgentFormValues));
const SELECT_ID = 'agent-builder-combobox';
export default function AgentSelect({
reset,
agentQuery,
value: currentAgentValue,
selectedAgentId = null,
setCurrentAgentId,
createMutation,
}: {
reset: UseFormReset<AgentForm>;
value?: TAgentOption;
selectedAgentId: string | null;
agentQuery: QueryObserverResult<Agent>;
setCurrentAgentId: React.Dispatch<React.SetStateAction<string | undefined>>;
@ -30,6 +25,7 @@ export default function AgentSelect({
}) {
const localize = useLocalize();
const lastSelectedAgent = useRef<string | null>(null);
const { control, reset } = useFormContext();
const { data: startupConfig } = useGetStartupConfig();
const { data: agents = null } = useListAgentsQuery(undefined, {
@ -121,9 +117,6 @@ export default function AgentSelect({
}
resetAgentForm(agent);
setTimeout(() => {
document.getElementById(SELECT_ID)?.focus();
}, 5);
},
[agents, createMutation, setCurrentAgentId, agentQuery.data, resetAgentForm, reset],
);
@ -158,34 +151,39 @@ export default function AgentSelect({
const createAgent = localize('com_ui_create') + ' ' + localize('com_ui_agent');
return (
<ControlCombobox
selectId={SELECT_ID}
containerClassName="px-0"
selectedValue={(currentAgentValue?.value ?? '') + ''}
displayValue={currentAgentValue?.label ?? ''}
selectPlaceholder={createAgent}
iconSide="right"
searchPlaceholder={localize('com_agents_search_name')}
SelectIcon={currentAgentValue?.icon}
setValue={onSelect}
items={
agents?.map((agent) => ({
label: agent.name ?? '',
value: agent.id ?? '',
icon: agent.icon,
})) ?? [
{
label: 'Loading...',
value: '',
},
]
}
className={cn(
'z-50 flex h-[40px] w-full flex-none items-center justify-center truncate rounded-md bg-transparent font-bold',
<Controller
name="agent"
control={control}
render={({ field }) => (
<ControlCombobox
containerClassName="px-0"
selectedValue={(field?.value?.value ?? '') + ''}
displayValue={field?.value?.label ?? ''}
selectPlaceholder={createAgent}
iconSide="right"
searchPlaceholder={localize('com_agents_search_name')}
SelectIcon={field?.value?.icon}
setValue={onSelect}
items={
agents?.map((agent) => ({
label: agent.name ?? '',
value: agent.id ?? '',
icon: agent.icon,
})) ?? [
{
label: 'Loading...',
value: '',
},
]
}
className={cn(
'z-50 flex h-[40px] w-full flex-none items-center justify-center truncate rounded-md bg-transparent font-bold',
)}
ariaLabel={localize('com_ui_agent')}
isCollapsed={false}
showCarat={true}
/>
)}
ariaLabel={localize('com_ui_agent')}
isCollapsed={false}
showCarat={true}
/>
);
}

View file

@ -143,7 +143,7 @@
"com_endpoint_thinking": "Thinking",
"com_endpoint_thinking_budget": "Thinking Budget",
"com_endpoint_anthropic_thinking": "Enables internal reasoning for supported Claude models (3.7 Sonnet). Note: requires \"Thinking Budget\" to be set and lower than \"Max Output Tokens\"",
"com_endpoint_anthropic_thinking_budget": "Determines the max number of tokens Claude is allowed use for its internal reasoning process. Larger budgets can improve response quality by enabling more thorough analysis for complex problems, although Claude may not use the entire budget allocated, especially at ranges above 32K. This setting must be lower than \"Max Output Tokens.\"",
"com_endpoint_anthropic_thinking_budget": "Determines the max number of tokens Claude is allowed to use for its internal reasoning process. Larger budgets can improve response quality by enabling more thorough analysis for complex problems, although Claude may not use the entire budget allocated, especially at ranges above 32K. This setting must be lower than \"Max Output Tokens.\"",
"com_endpoint_anthropic_temp": "Ranges from 0 to 1. Use temp closer to 0 for analytical / multiple choice, and closer to 1 for creative and generative tasks. We recommend altering this or Top P but not both.",
"com_endpoint_anthropic_topk": "Top-k changes how the model selects tokens for output. A top-k of 1 means the selected token is the most probable among all tokens in the model's vocabulary (also called greedy decoding), while a top-k of 3 means that the next token is selected from among the 3 most probable tokens (using temperature).",
"com_endpoint_anthropic_topp": "Top-p changes how the model selects tokens for output. Tokens are selected from most K (see topK parameter) probable to least until the sum of their probabilities equals the top-p value.",

5447
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -709,7 +709,7 @@ export const defaultModels = {
// Gemini 2.0 Models
'gemini-2.0-flash-001',
'gemini-2.0-flash-exp',
'gemini-2.0-flash-lite-preview-02-05',
'gemini-2.0-flash-lite',
'gemini-2.0-pro-exp-02-05',
// Gemini 1.5 Models
'gemini-1.5-flash-001',