mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-06-25 17:06:24 +00:00
* wip: first pass, dropdown for selecting sequential agents * refactor: Improve agent selection logic and enhance performance in SequentialAgents component * wip: seq. agents working ideas * wip: sequential agents style change * refactor: move agent form options/submission outside of AgentConfig * refactor: prevent repeating code * refactor: simplify current agent display in SequentialAgents component * feat: persist form value handling in AgentSelect component for agent_ids * feat: first pass, sequential agnets agent update * feat: enhance message display with agent updates and empty text handling * chore: update Icon component to use EModelEndpoint for agent endpoints * feat: update content type checks in BaseClient to use constants for better readability * feat: adjust max context tokens calculation to use 90% of the model's max tokens * feat: first pass, agent run message pruning * chore: increase max listeners for abort controller to prevent memory leaks * feat: enhance runAgent function to include current index count map for improved token tracking * chore: update @librechat/agents dependency to version 2.2.5 * feat: update icons and style of SequentialAgents component for improved UI consistency * feat: add AdvancedButton and AdvancedPanel components for enhanced agent settings navigation, update styling for agent form * chore: adjust minimum height of AdvancedPanel component for better layout consistency * chore: update @librechat/agents dependency to version 2.2.6 * feat: enhance message formatting by incorporating tool set into agent message processing, in order to allow better mix/matching of agents (as tool calls for tools not found in set will be stringified) * refactor: reorder components in AgentConfig for improved readability and maintainability * refactor: enhance layout of AgentUpdate component for improved visual structure * feat: add DeepSeek provider to Bedrock settings and schemas * feat: enhance link styling in mobile.css for better visibility and accessibility * fix: update banner model import in update banner script; export Banner model * refactor: `duplicateAgentHandler` to include tool_resources only for OCR context files * feat: add 'qwen-vl' to visionModels for enhanced model support * fix: change image format from JPEG to PNG in DALLE3 response * feat: reorganize Advanced components and add localizations * refactor: simplify JSX structure in AgentChain component to defer container styling to parent * feat: add FormInput component for reusable input handling * feat: make agent recursion limit configurable from builder * feat: add support for agent capabilities chain in AdvancedPanel and update data-provider version * feat: add maxRecursionLimit configuration for agents and update related documentation * fix: update CONFIG_VERSION to 1.2.3 in data provider configuration * feat: replace recursion limit input with MaxAgentSteps component and enhance input handling * feat: enhance AgentChain component with hover card for additional information and update related labels * fix: pass request and response objects to `createActionTool` when using assistant actions to prevent auth error * feat: update AgentChain component layout to include agent count display * feat: increase default max listeners and implement capability check function for agent chain * fix: update link styles in mobile.css for better visibility in dark mode * chore: temp. remove agents package while bumping shared packages * chore: update @langchain/google-genai package to version 0.1.11 * chore: update @langchain/google-vertexai package to version 0.2.2 * chore: add @librechat/agents package at version 2.2.8 * feat: add deepseek.r1 model with token rate and context values for bedrock
240 lines
8.9 KiB
TypeScript
240 lines
8.9 KiB
TypeScript
import React, { useMemo, useEffect } from 'react';
|
|
import { ChevronLeft, RotateCcw } from 'lucide-react';
|
|
import { useFormContext, useWatch, Controller } from 'react-hook-form';
|
|
import { getSettingsKeys, alternateName } from 'librechat-data-provider';
|
|
import type * as t from 'librechat-data-provider';
|
|
import type { AgentForm, AgentModelPanelProps, StringOption } from '~/common';
|
|
import { componentMapping } from '~/components/SidePanel/Parameters/components';
|
|
import { agentSettings } from '~/components/SidePanel/Parameters/settings';
|
|
import ControlCombobox from '~/components/ui/ControlCombobox';
|
|
import { useGetEndpointsQuery } from '~/data-provider';
|
|
import { getEndpointField, cn } from '~/utils';
|
|
import { useLocalize } from '~/hooks';
|
|
import { Panel } from '~/common';
|
|
|
|
export default function Parameters({
|
|
setActivePanel,
|
|
providers,
|
|
models: modelsData,
|
|
}: AgentModelPanelProps) {
|
|
const localize = useLocalize();
|
|
|
|
const { control, setValue } = useFormContext<AgentForm>();
|
|
|
|
const model = useWatch({ control, name: 'model' });
|
|
const providerOption = useWatch({ control, name: 'provider' });
|
|
const modelParameters = useWatch({ control, name: 'model_parameters' });
|
|
|
|
const provider = useMemo(() => {
|
|
const value =
|
|
typeof providerOption === 'string'
|
|
? providerOption
|
|
: (providerOption as StringOption | undefined)?.value;
|
|
return value ?? '';
|
|
}, [providerOption]);
|
|
const models = useMemo(
|
|
() => (provider ? (modelsData[provider] ?? []) : []),
|
|
[modelsData, provider],
|
|
);
|
|
|
|
useEffect(() => {
|
|
const _model = model ?? '';
|
|
if (provider && _model) {
|
|
const modelExists = models.includes(_model);
|
|
if (!modelExists) {
|
|
const newModels = modelsData[provider] ?? [];
|
|
setValue('model', newModels[0] ?? '');
|
|
}
|
|
}
|
|
|
|
if (provider && !_model) {
|
|
setValue('model', models[0] ?? '');
|
|
}
|
|
}, [provider, models, modelsData, setValue, model]);
|
|
|
|
const { data: endpointsConfig } = useGetEndpointsQuery();
|
|
|
|
const bedrockRegions = useMemo(() => {
|
|
return endpointsConfig?.[provider]?.availableRegions ?? [];
|
|
}, [endpointsConfig, provider]);
|
|
|
|
const endpointType = useMemo(
|
|
() => getEndpointField(endpointsConfig, provider, 'type'),
|
|
[provider, endpointsConfig],
|
|
);
|
|
|
|
const parameters = useMemo(() => {
|
|
const [combinedKey, endpointKey] = getSettingsKeys(endpointType ?? provider, model ?? '');
|
|
return agentSettings[combinedKey] ?? agentSettings[endpointKey];
|
|
}, [endpointType, model, provider]);
|
|
|
|
const setOption = (optionKey: keyof t.AgentModelParameters) => (value: t.AgentParameterValue) => {
|
|
setValue(`model_parameters.${optionKey}`, value);
|
|
};
|
|
|
|
const handleResetParameters = () => {
|
|
setValue('model_parameters', {} as t.AgentModelParameters);
|
|
};
|
|
|
|
return (
|
|
<div className="mx-1 mb-1 flex h-full min-h-[50vh] w-full flex-col gap-2 text-sm">
|
|
<div className="model-panel relative flex flex-col items-center px-16 py-4 text-center">
|
|
<div className="absolute left-0 top-4">
|
|
<button
|
|
type="button"
|
|
className="btn btn-neutral relative"
|
|
onClick={() => {
|
|
setActivePanel(Panel.builder);
|
|
}}
|
|
>
|
|
<div className="model-panel-content flex w-full items-center justify-center gap-2">
|
|
<ChevronLeft />
|
|
</div>
|
|
</button>
|
|
</div>
|
|
|
|
<div className="mb-2 mt-2 text-xl font-medium">{localize('com_ui_model_parameters')}</div>
|
|
</div>
|
|
<div className="p-2">
|
|
{/* Endpoint aka Provider for Agents */}
|
|
<div className="mb-4">
|
|
<label
|
|
id="provider-label"
|
|
className="text-token-text-primary model-panel-label mb-2 block font-medium"
|
|
htmlFor="provider"
|
|
>
|
|
{localize('com_ui_provider')} <span className="text-red-500">*</span>
|
|
</label>
|
|
<Controller
|
|
name="provider"
|
|
control={control}
|
|
rules={{ required: true, minLength: 1 }}
|
|
render={({ field, fieldState: { error } }) => {
|
|
const value =
|
|
typeof field.value === 'string'
|
|
? field.value
|
|
: ((field.value as StringOption)?.value ?? '');
|
|
const display =
|
|
typeof field.value === 'string'
|
|
? field.value
|
|
: ((field.value as StringOption)?.label ?? '');
|
|
|
|
return (
|
|
<>
|
|
<ControlCombobox
|
|
selectedValue={value}
|
|
displayValue={alternateName[display] ?? display}
|
|
selectPlaceholder={localize('com_ui_select_provider')}
|
|
searchPlaceholder={localize('com_ui_select_search_provider')}
|
|
setValue={field.onChange}
|
|
items={providers.map((provider) => ({
|
|
label: typeof provider === 'string' ? provider : provider.label,
|
|
value: typeof provider === 'string' ? provider : provider.value,
|
|
}))}
|
|
className={cn(error ? 'border-2 border-red-500' : '')}
|
|
ariaLabel={localize('com_ui_provider')}
|
|
isCollapsed={false}
|
|
showCarat={true}
|
|
/>
|
|
{error && (
|
|
<span className="model-panel-error text-sm text-red-500 transition duration-300 ease-in-out">
|
|
{localize('com_ui_field_required')}
|
|
</span>
|
|
)}
|
|
</>
|
|
);
|
|
}}
|
|
/>
|
|
</div>
|
|
{/* Model */}
|
|
<div className="model-panel-section mb-4">
|
|
<label
|
|
id="model-label"
|
|
className={cn(
|
|
'text-token-text-primary model-panel-label mb-2 block font-medium',
|
|
!provider && 'text-gray-500 dark:text-gray-400',
|
|
)}
|
|
htmlFor="model"
|
|
>
|
|
{localize('com_ui_model')} <span className="text-red-500">*</span>
|
|
</label>
|
|
<Controller
|
|
name="model"
|
|
control={control}
|
|
rules={{ required: true, minLength: 1 }}
|
|
render={({ field, fieldState: { error } }) => {
|
|
return (
|
|
<>
|
|
<ControlCombobox
|
|
selectedValue={field.value || ''}
|
|
selectPlaceholder={
|
|
provider
|
|
? localize('com_ui_select_model')
|
|
: localize('com_ui_select_provider_first')
|
|
}
|
|
searchPlaceholder={localize('com_ui_select_model')}
|
|
setValue={field.onChange}
|
|
items={models.map((model) => ({
|
|
label: model,
|
|
value: model,
|
|
}))}
|
|
disabled={!provider}
|
|
className={cn('disabled:opacity-50', error ? 'border-2 border-red-500' : '')}
|
|
ariaLabel={localize('com_ui_model')}
|
|
isCollapsed={false}
|
|
showCarat={true}
|
|
/>
|
|
{provider && error && (
|
|
<span className="text-sm text-red-500 transition duration-300 ease-in-out">
|
|
{localize('com_ui_field_required')}
|
|
</span>
|
|
)}
|
|
</>
|
|
);
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
{/* Model Parameters */}
|
|
{parameters && (
|
|
<div className="h-auto max-w-full overflow-x-hidden p-2">
|
|
<div className="grid grid-cols-4 gap-6">
|
|
{/* This is the parent element containing all settings */}
|
|
{/* Below is an example of an applied dynamic setting, each be contained by a div with the column span specified */}
|
|
{parameters.map((setting) => {
|
|
const Component = componentMapping[setting.component];
|
|
if (!Component) {
|
|
return null;
|
|
}
|
|
const { key, default: defaultValue, ...rest } = setting;
|
|
|
|
if (key === 'region' && bedrockRegions.length) {
|
|
rest.options = bedrockRegions;
|
|
}
|
|
|
|
return (
|
|
<Component
|
|
key={key}
|
|
settingKey={key}
|
|
defaultValue={defaultValue}
|
|
{...rest}
|
|
setOption={setOption as t.TSetOption}
|
|
conversation={modelParameters as Partial<t.TConversation>}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
)}
|
|
{/* Reset Parameters Button */}
|
|
<button
|
|
type="button"
|
|
onClick={handleResetParameters}
|
|
className="btn btn-neutral my-1 flex w-full items-center justify-center gap-2 px-4 py-2 text-sm"
|
|
>
|
|
<RotateCcw className="h-4 w-4" aria-hidden="true" />
|
|
{localize('com_ui_reset_var', { 0: localize('com_ui_model_parameters') })}
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|