👐 refactor: Agents Accessibility and Gemini Error Handling (#5972)

* style: Enhance ControlCombobox with Carat Display, ClassName, and Disabled State

* refactor(ModelPanel): replace SelectDropdown with ControlCombobox for improved accessibility

* style: Adjust padding and positioning in ModelPanel for improved layout

* style(ControlCombobox): add containerClassName and iconSide props for enhanced customization

* style(ControlCombobox): add iconClassName prop for customizable icon styling

* refactor(AgentPanel): enhance layout with new button for creating agents and adjust structure for better alignment

* refactor(AgentSelect): replace SelectDropDown with ControlCombobox for improved accessibility and layout

* feat(translation): add new translation key for improved UI clarity

* style(AgentSwitcher, AssistantSwitcher): add iconClassName prop for customizable icon styling

* refactor(AgentPanelSkeleton): improve layout of skeleton components to match new visual structure

* style(AgentPanel, AgentPanelSkeleton): add margin to flex container for improved layout consistency

* a11y(AgentSelect, ControlCombobox): add selectId prop for preventing focus going to start to page after agent selection

* fix(AgentSelect): update SELECT_ID constant for improved clarity in component identification

* fix(GoogleClient): update type annotation, add abort handling for content generation requests, catch "uncaught" abort errors and GoogleGenerativeAI errors from `@google/generative-ai`
This commit is contained in:
Danny Avila 2025-02-21 15:02:07 -05:00 committed by GitHub
parent 1e625f7557
commit fc733d2b9e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 227 additions and 150 deletions

View file

@ -1,14 +1,14 @@
import React, { useMemo, useEffect } from 'react';
import { ChevronLeft, RotateCcw } from 'lucide-react';
import { getSettingsKeys } from 'librechat-data-provider';
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 { getEndpointField, cn, cardStyle } from '~/utils';
import ControlCombobox from '~/components/ui/ControlCombobox';
import { useGetEndpointsQuery } from '~/data-provider';
import { SelectDropDown } from '~/components/ui';
import { getEndpointField, cn } from '~/utils';
import { useLocalize } from '~/hooks';
import { Panel } from '~/common';
@ -78,8 +78,8 @@ export default function Parameters({
return (
<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">
<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"
@ -109,37 +109,41 @@ export default function Parameters({
name="provider"
control={control}
rules={{ required: true, minLength: 1 }}
render={({ field, fieldState: { error } }) => (
<>
<SelectDropDown
id="provider"
aria-labelledby="provider-label"
aria-label={localize('com_ui_provider')}
aria-required="true"
emptyTitle={true}
value={field.value ?? ''}
title={localize('com_ui_provider')}
placeholder={localize('com_ui_select_provider')}
searchPlaceholder={localize('com_ui_select_search_provider')}
setValue={field.onChange}
availableValues={providers}
showAbove={false}
showLabel={false}
className={cn(
cardStyle,
'flex h-9 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',
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>
)}
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 */}
@ -158,39 +162,36 @@ export default function Parameters({
name="model"
control={control}
rules={{ required: true, minLength: 1 }}
render={({ field, fieldState: { error } }) => (
<>
<SelectDropDown
id="model"
aria-labelledby="model-label"
aria-label={localize('com_ui_model')}
aria-required="true"
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',
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>
)}
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>