mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-31 15:48:51 +01:00
🛠️ Fix: Update Agent Cache and Improve Actions UI (#5020)
* style: improve a11y, localization, and styling consistency of actions input form * refactor: move agent mutations to dedicated module * fix: update agent cache on agent deletion + delete and update actions
This commit is contained in:
parent
6ef05dd2e6
commit
0a97ad3915
8 changed files with 337 additions and 320 deletions
|
|
@ -16,10 +16,10 @@ import type { ActionAuthForm } from '~/common';
|
|||
import type { Spec } from './ActionsTable';
|
||||
import { ActionsTable, columns } from './ActionsTable';
|
||||
import { useUpdateAgentAction } from '~/data-provider';
|
||||
import { cn, removeFocusOutlines } from '~/utils';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import { Spinner } from '~/components/svg';
|
||||
import { logger } from '~/utils';
|
||||
|
||||
const debouncedValidation = debounce(
|
||||
(input: string, callback: (result: ValidationResult) => void) => {
|
||||
|
|
@ -56,12 +56,13 @@ export default function ActionsInput({
|
|||
const [functions, setFunctions] = useState<FunctionTool[] | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!action?.metadata?.raw_spec) {
|
||||
const rawSpec = action?.metadata.raw_spec ?? '';
|
||||
if (!rawSpec) {
|
||||
return;
|
||||
}
|
||||
setInputValue(action.metadata.raw_spec);
|
||||
debouncedValidation(action.metadata.raw_spec, handleResult);
|
||||
}, [action?.metadata?.raw_spec]);
|
||||
setInputValue(rawSpec);
|
||||
debouncedValidation(rawSpec, handleResult);
|
||||
}, [action?.metadata.raw_spec]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!validationResult || !validationResult.status || !validationResult.spec) {
|
||||
|
|
@ -94,15 +95,16 @@ export default function ActionsInput({
|
|||
},
|
||||
onError(error) {
|
||||
showToast({
|
||||
message: (error as Error)?.message ?? localize('com_assistants_update_actions_error'),
|
||||
message: (error as Error).message || localize('com_assistants_update_actions_error'),
|
||||
status: 'error',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const saveAction = handleSubmit((authFormData) => {
|
||||
console.log('authFormData', authFormData);
|
||||
if (!agent_id) {
|
||||
logger.log('actions', 'saving action', authFormData);
|
||||
const currentAgentId = agent_id ?? '';
|
||||
if (!currentAgentId) {
|
||||
// alert user?
|
||||
return;
|
||||
}
|
||||
|
|
@ -171,7 +173,7 @@ export default function ActionsInput({
|
|||
action_id,
|
||||
metadata,
|
||||
functions,
|
||||
agent_id,
|
||||
agent_id: currentAgentId,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -186,17 +188,34 @@ export default function ActionsInput({
|
|||
debouncedValidation(newValue, handleResult);
|
||||
};
|
||||
|
||||
const getButtonContent = () => {
|
||||
if (updateAgentAction.isLoading) {
|
||||
return <Spinner className="icon-md" />;
|
||||
}
|
||||
|
||||
if (action?.action_id != null && action.action_id) {
|
||||
return localize('com_ui_update');
|
||||
}
|
||||
|
||||
return localize('com_ui_create');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="">
|
||||
<div className="mb-1 flex flex-wrap items-center justify-between gap-4">
|
||||
<label className="text-token-text-primary whitespace-nowrap font-medium">Schema</label>
|
||||
<label
|
||||
htmlFor="schemaInput"
|
||||
className="text-token-text-primary whitespace-nowrap font-medium"
|
||||
>
|
||||
Schema
|
||||
</label>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* <button className="btn btn-neutral border-token-border-light relative h-8 min-w-[100px] rounded-lg font-medium">
|
||||
<div className="flex w-full items-center justify-center text-xs">Import from URL</div>
|
||||
</button> */}
|
||||
<select
|
||||
onChange={(e) => console.log(e.target.value)}
|
||||
onChange={(e) => logger.log('actions', 'selecting example action', e.target.value)}
|
||||
className="border-token-border-medium h-8 min-w-[100px] rounded-lg border bg-transparent px-2 py-0 text-sm"
|
||||
>
|
||||
<option value="label">{localize('com_ui_examples')}</option>
|
||||
|
|
@ -207,17 +226,15 @@ export default function ActionsInput({
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-token-border-light mb-4 overflow-hidden rounded-lg border">
|
||||
<div className="border-token-border-medium bg-token-surface-primary hover:border-token-border-hover mb-4 w-full overflow-hidden rounded-lg border ring-0">
|
||||
<div className="relative">
|
||||
<textarea
|
||||
id="schemaInput"
|
||||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
spellCheck="false"
|
||||
placeholder="Enter your OpenAPI schema here"
|
||||
className={cn(
|
||||
'text-token-text-primary block h-96 w-full border-none bg-transparent p-2 font-mono text-xs',
|
||||
removeFocusOutlines,
|
||||
)}
|
||||
placeholder={localize('com_ui_enter_openapi_schema')}
|
||||
className="text-token-text-primary block h-96 w-full bg-transparent p-2 font-mono text-xs outline-none focus:ring-1 focus:ring-border-light"
|
||||
/>
|
||||
{/* TODO: format input button */}
|
||||
</div>
|
||||
|
|
@ -240,28 +257,18 @@ export default function ActionsInput({
|
|||
<ActionsTable columns={columns} data={data} />
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-4">
|
||||
<div className="relative my-1">
|
||||
<div className="mb-1.5 flex items-center">
|
||||
<span className="" data-state="closed">
|
||||
<label className="text-token-text-primary block font-medium">
|
||||
{localize('com_ui_privacy_policy')}
|
||||
</label>
|
||||
</span>
|
||||
<label className="text-token-text-primary block font-medium">
|
||||
{localize('com_ui_privacy_policy_url')}
|
||||
</label>
|
||||
</div>
|
||||
<div className="rounded-md border border-gray-300 px-3 py-2 shadow-none focus-within:border-gray-800 focus-within:ring-1 focus-within:ring-gray-800 dark:border-gray-700 dark:bg-gray-700 dark:focus-within:border-gray-500 dark:focus-within:ring-gray-500">
|
||||
<label
|
||||
htmlFor="privacyPolicyUrl"
|
||||
className="block text-xs font-medium text-gray-900 dark:text-gray-100"
|
||||
<div className="border-token-border-medium bg-token-surface-primary hover:border-token-border-hover flex h-9 w-full rounded-lg border">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="https://api.example-weather-app.com/privacy"
|
||||
className="flex-1 rounded-lg bg-transparent px-3 py-1.5 text-sm outline-none focus:ring-1 focus:ring-border-light"
|
||||
/>
|
||||
<div className="relative">
|
||||
<input
|
||||
name="privacyPolicyUrl"
|
||||
id="privacyPolicyUrl"
|
||||
className="block w-full border-0 p-0 text-gray-900 placeholder-gray-500 shadow-none outline-none focus-within:shadow-none focus-within:outline-none focus-within:ring-0 focus:border-none focus:ring-0 dark:bg-gray-700 dark:text-gray-100 sm:text-sm"
|
||||
placeholder="https://api.example-weather-app.com/privacy"
|
||||
// value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-end">
|
||||
|
|
@ -271,13 +278,7 @@ export default function ActionsInput({
|
|||
className="focus:shadow-outline mt-1 flex min-w-[100px] items-center justify-center rounded bg-green-500 px-4 py-2 font-semibold text-white hover:bg-green-400 focus:border-green-500 focus:outline-none focus:ring-0 disabled:bg-green-400"
|
||||
type="button"
|
||||
>
|
||||
{updateAgentAction.isLoading ? (
|
||||
<Spinner className="icon-md" />
|
||||
) : action?.action_id ? (
|
||||
localize('com_ui_update')
|
||||
) : (
|
||||
localize('com_ui_create')
|
||||
)}
|
||||
{getButtonContent()}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export default function ActionsPanel({
|
|||
},
|
||||
onError(error) {
|
||||
showToast({
|
||||
message: (error as Error)?.message ?? localize('com_assistants_delete_actions_error'),
|
||||
message: (error as Error).message ?? localize('com_assistants_delete_actions_error'),
|
||||
status: 'error',
|
||||
});
|
||||
},
|
||||
|
|
@ -68,7 +68,7 @@ export default function ActionsPanel({
|
|||
const type = watch('type');
|
||||
|
||||
useEffect(() => {
|
||||
if (action?.metadata?.auth) {
|
||||
if (action?.metadata.auth) {
|
||||
reset({
|
||||
type: action.metadata.auth.type || AuthTypeEnum.None,
|
||||
saved_auth_fields: false,
|
||||
|
|
@ -149,16 +149,16 @@ export default function ActionsPanel({
|
|||
)}
|
||||
|
||||
<div className="text-xl font-medium">{(action ? 'Edit' : 'Add') + ' ' + 'actions'}</div>
|
||||
<div className="text-token-text-tertiary text-sm">
|
||||
<div className="text-xs text-text-secondary">
|
||||
{localize('com_assistants_actions_info')}
|
||||
</div>
|
||||
{/* <div className="text-sm text-token-text-tertiary">
|
||||
{/* <div className="text-sm text-text-secondary">
|
||||
<a href="https://help.openai.com/en/articles/8554397-creating-a-gpt" target="_blank" rel="noreferrer" className="font-medium">Learn more.</a>
|
||||
</div> */}
|
||||
</div>
|
||||
<Dialog open={openAuthDialog} onOpenChange={setOpenAuthDialog}>
|
||||
<DialogTrigger asChild>
|
||||
<div className="relative mb-6">
|
||||
<div className="relative mb-4">
|
||||
<div className="mb-1.5 flex items-center">
|
||||
<label className="text-token-text-primary block font-medium">
|
||||
{localize('com_ui_authentication')}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import type { Spec } from './ActionsTable';
|
|||
import { useAssistantsMapContext, useToastContext } from '~/Providers';
|
||||
import { ActionsTable, columns } from './ActionsTable';
|
||||
import { useUpdateAction } from '~/data-provider';
|
||||
import { cn, removeFocusOutlines } from '~/utils';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import { Spinner } from '~/components/svg';
|
||||
|
||||
|
|
@ -219,7 +218,7 @@ export default function ActionsInput({
|
|||
htmlFor="example-schema"
|
||||
className="text-token-text-primary whitespace-nowrap font-medium"
|
||||
>
|
||||
Schema
|
||||
{localize('com_ui_schema')}
|
||||
</label>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* <button className="btn btn-neutral border-token-border-light relative h-8 min-w-[100px] rounded-lg font-medium">
|
||||
|
|
@ -238,17 +237,15 @@ export default function ActionsInput({
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-token-border-light mb-4 overflow-hidden rounded-lg border">
|
||||
<div className="border-token-border-medium bg-token-surface-primary hover:border-token-border-hover mb-4 w-full overflow-hidden rounded-lg border ring-0">
|
||||
<div className="relative">
|
||||
<textarea
|
||||
id="schemaInput"
|
||||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
spellCheck="false"
|
||||
placeholder="Enter your OpenAPI schema here"
|
||||
className={cn(
|
||||
'text-token-text-primary block h-96 w-full border-none bg-transparent p-2 font-mono text-xs',
|
||||
removeFocusOutlines,
|
||||
)}
|
||||
placeholder={localize('com_ui_enter_openapi_schema')}
|
||||
className="text-token-text-primary block h-96 w-full bg-transparent p-2 font-mono text-xs outline-none focus:ring-1 focus:ring-border-light"
|
||||
/>
|
||||
{/* TODO: format input button */}
|
||||
</div>
|
||||
|
|
@ -271,20 +268,18 @@ export default function ActionsInput({
|
|||
<ActionsTable columns={columns} data={data} />
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-4">
|
||||
<div className="rounded-md border border-gray-300 px-3 py-2 shadow-none focus-within:border-gray-800 focus-within:ring-1 focus-within:ring-gray-800 dark:border-gray-700 dark:bg-gray-700 dark:focus-within:border-gray-500 dark:focus-within:ring-gray-500">
|
||||
<label htmlFor="privacyPolicyUrl" className="block text-xs text-text-secondary">
|
||||
Privacy Policy URL
|
||||
<div className="relative my-1">
|
||||
<div className="mb-1.5 flex items-center">
|
||||
<label className="text-token-text-primary block font-medium">
|
||||
{localize('com_ui_privacy_policy_url')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
name="privacyPolicyUrl"
|
||||
id="privacyPolicyUrl"
|
||||
className="block w-full border-0 bg-transparent p-0 placeholder-text-secondary shadow-none outline-none focus-within:shadow-none focus-within:outline-none focus-within:ring-0 focus:border-none focus:ring-0 sm:text-sm"
|
||||
placeholder="https://api.example-weather-app.com/privacy"
|
||||
// value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-token-border-medium bg-token-surface-primary hover:border-token-border-hover flex h-9 w-full rounded-lg border">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="https://api.example-weather-app.com/privacy"
|
||||
className="flex-1 rounded-lg bg-transparent px-3 py-1.5 text-sm outline-none focus:ring-1 focus:ring-border-light"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-end">
|
||||
|
|
|
|||
|
|
@ -155,16 +155,16 @@ export default function ActionsPanel({
|
|||
)}
|
||||
|
||||
<div className="text-xl font-medium">{(action ? 'Edit' : 'Add') + ' ' + 'actions'}</div>
|
||||
<div className="text-token-text-tertiary text-sm">
|
||||
<div className="text-xs text-text-secondary">
|
||||
{localize('com_assistants_actions_info')}
|
||||
</div>
|
||||
{/* <div className="text-sm text-token-text-tertiary">
|
||||
{/* <div className="text-sm text-text-secondary">
|
||||
<a href="https://help.openai.com/en/articles/8554397-creating-a-gpt" target="_blank" rel="noreferrer" className="font-medium">Learn more.</a>
|
||||
</div> */}
|
||||
</div>
|
||||
<Dialog open={openAuthDialog} onOpenChange={setOpenAuthDialog}>
|
||||
<DialogTrigger asChild>
|
||||
<div className="relative mb-6">
|
||||
<div className="relative mb-4">
|
||||
<div className="mb-1.5 flex items-center">
|
||||
<label className="text-token-text-primary block font-medium">
|
||||
{localize('com_ui_authentication')}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue