mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 08:50:15 +01:00
110 lines
3.2 KiB
TypeScript
110 lines
3.2 KiB
TypeScript
|
|
import debounce from 'lodash/debounce';
|
||
|
|
import { Tools, AuthType } from 'librechat-data-provider';
|
||
|
|
import { TerminalSquareIcon, Loader } from 'lucide-react';
|
||
|
|
import React, { useMemo, useCallback, useEffect } from 'react';
|
||
|
|
import type { CodeBarProps } from '~/common';
|
||
|
|
import { useVerifyAgentToolAuth, useToolCallMutation } from '~/data-provider';
|
||
|
|
import ApiKeyDialog from '~/components/SidePanel/Agents/Code/ApiKeyDialog';
|
||
|
|
import { useLocalize, useCodeApiKeyForm } from '~/hooks';
|
||
|
|
import { useMessageContext } from '~/Providers';
|
||
|
|
import { cn, normalizeLanguage } from '~/utils';
|
||
|
|
import { useToastContext } from '~/Providers';
|
||
|
|
|
||
|
|
const RunCode: React.FC<CodeBarProps> = React.memo(({ lang, codeRef, blockIndex }) => {
|
||
|
|
const localize = useLocalize();
|
||
|
|
const { showToast } = useToastContext();
|
||
|
|
const execute = useToolCallMutation(Tools.execute_code, {
|
||
|
|
onError: () => {
|
||
|
|
showToast({ message: localize('com_ui_run_code_error'), status: 'error' });
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
const { messageId, conversationId, partIndex } = useMessageContext();
|
||
|
|
const normalizedLang = useMemo(() => normalizeLanguage(lang), [lang]);
|
||
|
|
const { data } = useVerifyAgentToolAuth({ toolId: Tools.execute_code });
|
||
|
|
const authType = useMemo(() => data?.message ?? false, [data?.message]);
|
||
|
|
const isAuthenticated = useMemo(() => data?.authenticated ?? false, [data?.authenticated]);
|
||
|
|
const { methods, onSubmit, isDialogOpen, setIsDialogOpen, handleRevokeApiKey } =
|
||
|
|
useCodeApiKeyForm({});
|
||
|
|
|
||
|
|
const handleExecute = useCallback(async () => {
|
||
|
|
if (!isAuthenticated) {
|
||
|
|
setIsDialogOpen(true);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const codeString: string = codeRef.current?.textContent ?? '';
|
||
|
|
if (
|
||
|
|
typeof codeString !== 'string' ||
|
||
|
|
codeString.length === 0 ||
|
||
|
|
typeof normalizedLang !== 'string' ||
|
||
|
|
normalizedLang.length === 0
|
||
|
|
) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
execute.mutate({
|
||
|
|
partIndex,
|
||
|
|
messageId,
|
||
|
|
blockIndex,
|
||
|
|
conversationId: conversationId ?? '',
|
||
|
|
lang: normalizedLang,
|
||
|
|
code: codeString,
|
||
|
|
});
|
||
|
|
}, [
|
||
|
|
codeRef,
|
||
|
|
execute,
|
||
|
|
partIndex,
|
||
|
|
messageId,
|
||
|
|
blockIndex,
|
||
|
|
conversationId,
|
||
|
|
normalizedLang,
|
||
|
|
setIsDialogOpen,
|
||
|
|
isAuthenticated,
|
||
|
|
]);
|
||
|
|
|
||
|
|
const debouncedExecute = useMemo(
|
||
|
|
() => debounce(handleExecute, 1000, { leading: true }),
|
||
|
|
[handleExecute],
|
||
|
|
);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
return () => {
|
||
|
|
debouncedExecute.cancel();
|
||
|
|
};
|
||
|
|
}, [debouncedExecute]);
|
||
|
|
|
||
|
|
if (typeof normalizedLang !== 'string' || normalizedLang.length === 0) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<>
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
className={cn('ml-auto flex gap-2')}
|
||
|
|
onClick={debouncedExecute}
|
||
|
|
disabled={execute.isLoading}
|
||
|
|
>
|
||
|
|
{execute.isLoading ? (
|
||
|
|
<Loader className="animate-spin" size={18} />
|
||
|
|
) : (
|
||
|
|
<TerminalSquareIcon size={18} />
|
||
|
|
)}
|
||
|
|
{localize('com_ui_run_code')}
|
||
|
|
</button>
|
||
|
|
<ApiKeyDialog
|
||
|
|
onSubmit={onSubmit}
|
||
|
|
isOpen={isDialogOpen}
|
||
|
|
register={methods.register}
|
||
|
|
onRevoke={handleRevokeApiKey}
|
||
|
|
onOpenChange={setIsDialogOpen}
|
||
|
|
handleSubmit={methods.handleSubmit}
|
||
|
|
isToolAuthenticated={isAuthenticated}
|
||
|
|
isUserProvided={authType === AuthType.USER_PROVIDED}
|
||
|
|
/>
|
||
|
|
</>
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
export default RunCode;
|