mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-18 09:20:15 +01:00
🚧 chore: merge latest dev build (#4288)
* fix: agent initialization, add `collectedUsage` handling * style: improve side panel styling * refactor(loadAgent): Optimize order agent project ID retrieval * feat: code execution * fix: typing issues * feat: ExecuteCode content part * refactor: use local state for default collapsed state of analysis content parts * fix: code parsing in ExecuteCode component * chore: bump agents package, export loadAuthValues * refactor: Update handleTools.js to use EnvVar for code execution tool authentication * WIP * feat: download code outputs * fix(useEventHandlers): type issues * feat: backend handling for code outputs * Refactor: Remove console.log statement in Part.tsx * refactor: add attachments to TMessage/messageSchema * WIP: prelim handling for code outputs * feat: attachments rendering * refactor: improve attachments rendering * fix: attachments, nullish edge case, handle attachments from event stream, bump agents package * fix filename download * fix: tool assignment for 'run code' on agent creation * fix: image handling by adding attachments * refactor: prevent agent creation without provider/model * refactor: remove unnecessary space in agent creation success message * refactor: select first model if selecting provider from empty on form * fix: Agent avatar bug * fix: `defaultAgentFormValues` causing boolean typing issue and typeerror * fix: capabilities counting as tools, causing duplication of them * fix: formatted messages edge case where consecutive content text type parts with the latter having tool_call_ids would cause consecutive AI messages to be created. furthermore, content could not be an array for tool_use messages (anthropic limitation) * chore: bump @librechat/agents dependency to version 1.6.9 * feat: bedrock agents * feat: new Agents icon * feat: agent titling * feat: agent landing * refactor: allow sharing agent globally only if user is admin or author * feat: initial AgentPanelSkeleton * feat: AgentPanelSkeleton * feat: collaborative agents * chore: add potential authorName as part of schema * chore: Remove unnecessary console.log statement * WIP: agent model parameters * chore: ToolsDialog typing and tool related localization chnages * refactor: update tool instance type (latest langchain class), and rename google tool to 'google' proper * chore: add back tools * feat: Agent knowledge files upload * refactor: better verbiage for disabled knowledge * chore: debug logs for file deletions * chore: debug logs for file deletions * feat: upload/delete agent knowledge/file-search files * feat: file search UI for agents * feat: first pass, file search tool * chore: update default agent capabilities and info
This commit is contained in:
parent
f33e75e2ee
commit
ad74350036
123 changed files with 3611 additions and 1541 deletions
|
|
@ -1,13 +1,18 @@
|
|||
import { useEffect, useMemo } from 'react';
|
||||
import React, { useMemo, useEffect } from 'react';
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import type { AgentForm, AgentModelPanelProps } from '~/common';
|
||||
import { SelectDropDown, ModelParameters } from '~/components/ui';
|
||||
import { cn, cardStyle } from '~/utils';
|
||||
import { getSettingsKeys } from 'librechat-data-provider';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
|
||||
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 { getEndpointField, cn, cardStyle } from '~/utils';
|
||||
import { SelectDropDown } from '~/components/ui';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { Panel } from '~/common';
|
||||
|
||||
export default function ModelPanel({
|
||||
export default function Parameters({
|
||||
setActivePanel,
|
||||
providers,
|
||||
models: modelsData,
|
||||
|
|
@ -15,30 +20,56 @@ export default function ModelPanel({
|
|||
const localize = useLocalize();
|
||||
|
||||
const { control, setValue, watch } = useFormContext<AgentForm>();
|
||||
const model = watch('model');
|
||||
const modelParameters = watch('model_parameters');
|
||||
const providerOption = watch('provider');
|
||||
const model = watch('model');
|
||||
|
||||
const provider = useMemo(() => {
|
||||
if (!providerOption) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return typeof providerOption === 'string' ? providerOption : providerOption.value;
|
||||
const value =
|
||||
typeof providerOption === 'string'
|
||||
? providerOption
|
||||
: (providerOption as StringOption | undefined)?.value;
|
||||
return value ?? '';
|
||||
}, [providerOption]);
|
||||
const models = useMemo(() => (provider ? modelsData[provider] : []), [modelsData, provider]);
|
||||
|
||||
useEffect(() => {
|
||||
if (provider && model) {
|
||||
const modelExists = models.includes(model);
|
||||
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);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full overflow-auto px-2 pb-12 text-sm">
|
||||
<div className="scrollbar-gutter-stable h-full min-h-[50vh] overflow-auto pb-12 text-sm">
|
||||
<div className="model-panel relative flex flex-col items-center px-16 py-6 text-center">
|
||||
<div className="absolute left-0 top-6">
|
||||
<button
|
||||
|
|
@ -56,228 +87,125 @@ export default function ModelPanel({
|
|||
|
||||
<div className="mb-2 mt-2 text-xl font-medium">{localize('com_ui_model_parameters')}</div>
|
||||
</div>
|
||||
{/* Endpoint aka Provider for Agents */}
|
||||
<div className="mb-4">
|
||||
<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 } }) => (
|
||||
<>
|
||||
<SelectDropDown
|
||||
emptyTitle={true}
|
||||
value={field.value ?? ''}
|
||||
placeholder={localize('com_ui_select_provider')}
|
||||
setValue={field.onChange}
|
||||
availableValues={providers}
|
||||
showAbove={false}
|
||||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'flex h-[40px] w-full flex-none items-center justify-center border-none px-4 hover:cursor-pointer',
|
||||
!field.value && 'border-2 border-yellow-400',
|
||||
<div className="p-2">
|
||||
{/* Endpoint aka Provider for Agents */}
|
||||
<div className="mb-4">
|
||||
<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 } }) => (
|
||||
<>
|
||||
<SelectDropDown
|
||||
emptyTitle={true}
|
||||
value={field.value ?? ''}
|
||||
placeholder={localize('com_ui_select_provider')}
|
||||
setValue={field.onChange}
|
||||
availableValues={providers}
|
||||
showAbove={false}
|
||||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'flex h-[40px] w-full flex-none items-center justify-center border-none px-4 hover:cursor-pointer',
|
||||
(field.value === undefined || field.value === '') &&
|
||||
'border-2 border-yellow-400',
|
||||
)}
|
||||
containerClassName={cn('rounded-md', error ? 'border-red-500 border-2' : '')}
|
||||
/>
|
||||
{error && (
|
||||
<span className="model-panel-error text-sm text-red-500 transition duration-300 ease-in-out">
|
||||
{localize('com_ui_field_required')}
|
||||
</span>
|
||||
)}
|
||||
containerClassName={cn('rounded-md', error ? 'border-red-500 border-2' : '')}
|
||||
/>
|
||||
{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-6">
|
||||
<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 } }) => (
|
||||
<>
|
||||
<SelectDropDown
|
||||
emptyTitle={true}
|
||||
placeholder={
|
||||
provider
|
||||
? localize('com_ui_select_model')
|
||||
: localize('com_ui_select_provider_first')
|
||||
}
|
||||
value={field.value}
|
||||
setValue={field.onChange}
|
||||
availableValues={models}
|
||||
showAbove={false}
|
||||
showLabel={false}
|
||||
disabled={!provider}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'flex h-[40px] w-full flex-none items-center justify-center border-none px-4',
|
||||
!provider ? 'cursor-not-allowed bg-gray-200' : 'hover:cursor-pointer',
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{/* Model */}
|
||||
<div className="model-panel-section mb-4">
|
||||
<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 } }) => (
|
||||
<>
|
||||
<SelectDropDown
|
||||
emptyTitle={true}
|
||||
placeholder={
|
||||
provider
|
||||
? localize('com_ui_select_model')
|
||||
: localize('com_ui_select_provider_first')
|
||||
}
|
||||
value={field.value}
|
||||
setValue={field.onChange}
|
||||
availableValues={models}
|
||||
showAbove={false}
|
||||
showLabel={false}
|
||||
disabled={!provider}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'flex h-[40px] w-full flex-none items-center justify-center border-none px-4',
|
||||
!provider ? 'cursor-not-allowed bg-gray-200' : 'hover:cursor-pointer',
|
||||
)}
|
||||
containerClassName={cn('rounded-md', error ? 'border-red-500 border-2' : '')}
|
||||
/>
|
||||
{provider && error && (
|
||||
<span className="text-sm text-red-500 transition duration-300 ease-in-out">
|
||||
{localize('com_ui_field_required')}
|
||||
</span>
|
||||
)}
|
||||
containerClassName={cn('rounded-md', error ? 'border-red-500 border-2' : '')}
|
||||
/>
|
||||
{provider && error && (
|
||||
<span className="text-sm text-red-500 transition duration-300 ease-in-out">
|
||||
{localize('com_ui_field_required')}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<Controller
|
||||
name="model_parameters.temperature"
|
||||
control={control}
|
||||
rules={{ required: false }}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<ModelParameters
|
||||
label="com_endpoint_temperature"
|
||||
ariaLabel="Temperature"
|
||||
min={-2}
|
||||
max={2}
|
||||
step={0.01}
|
||||
stepClick={0.01}
|
||||
initialValue={field.value ?? 1}
|
||||
onChange={field.onChange}
|
||||
showButtons={true}
|
||||
disabled={!provider}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<Controller
|
||||
name="model_parameters.max_context_tokens"
|
||||
control={control}
|
||||
rules={{ required: false }}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<ModelParameters
|
||||
label="com_endpoint_max_output_tokens"
|
||||
ariaLabel="Max Context Tokens"
|
||||
min={0}
|
||||
max={4096}
|
||||
step={1}
|
||||
stepClick={1}
|
||||
initialValue={field.value ?? 0}
|
||||
onChange={field.onChange}
|
||||
showButtons={true}
|
||||
disabled={!provider}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<Controller
|
||||
name="model_parameters.max_output_tokens"
|
||||
control={control}
|
||||
rules={{ required: false }}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<ModelParameters
|
||||
label="com_endpoint_context_tokens"
|
||||
ariaLabel="Max Context Tokens"
|
||||
min={0}
|
||||
max={4096}
|
||||
step={1}
|
||||
stepClick={1}
|
||||
initialValue={field.value ?? 0}
|
||||
onChange={field.onChange}
|
||||
showButtons={true}
|
||||
disabled={!provider}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<Controller
|
||||
name="model_parameters.top_p"
|
||||
control={control}
|
||||
rules={{ required: false }}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<ModelParameters
|
||||
label="com_endpoint_top_p"
|
||||
ariaLabel="Top P"
|
||||
min={-2}
|
||||
max={2}
|
||||
step={0.01}
|
||||
stepClick={0.01}
|
||||
initialValue={field.value ?? 1}
|
||||
onChange={field.onChange}
|
||||
showButtons={true}
|
||||
disabled={!provider}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<Controller
|
||||
name="model_parameters.frequency_penalty"
|
||||
control={control}
|
||||
rules={{ required: false }}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<ModelParameters
|
||||
label="com_endpoint_frequency_penalty"
|
||||
ariaLabel="Frequency Penalty"
|
||||
min={-2}
|
||||
max={2}
|
||||
step={0.01}
|
||||
stepClick={0.01}
|
||||
initialValue={field.value ?? 0}
|
||||
onChange={field.onChange}
|
||||
showButtons={true}
|
||||
disabled={!provider}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<Controller
|
||||
name="model_parameters.presence_penalty"
|
||||
control={control}
|
||||
rules={{ required: false }}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<ModelParameters
|
||||
label="com_endpoint_presence_penalty"
|
||||
ariaLabel="Presence Penalty"
|
||||
min={-2}
|
||||
max={2}
|
||||
step={0.01}
|
||||
stepClick={0.01}
|
||||
initialValue={field.value ?? 0}
|
||||
onChange={field.onChange}
|
||||
showButtons={true}
|
||||
disabled={!provider}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</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>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue