File size: 3,785 Bytes
9705b6c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
import { useRecoilValue } from 'recoil';
import { Disclosure } from '@headlessui/react';
import { useCallback, memo, ReactNode } from 'react';
import type { TResPlugin, TInput } from 'librechat-data-provider';
import { ChevronDownIcon, LucideProps } from 'lucide-react';
import { cn, formatJSON } from '~/utils';
import { Spinner } from '~/components';
import CodeBlock from './CodeBlock';
import store from '~/store';
type PluginsMap = {
[pluginKey: string]: string;
};
type PluginIconProps = LucideProps & {
className?: string;
};
function formatInputs(inputs: TInput[]) {
let output = '';
for (let i = 0; i < inputs.length; i++) {
const input = formatJSON(`${inputs[i]?.inputStr ?? inputs[i]}`);
output += input;
if (inputs.length > 1 && i !== inputs.length - 1) {
output += ',\n';
}
}
return output;
}
type PluginProps = {
plugin: TResPlugin;
};
const Plugin: React.FC<PluginProps> = ({ plugin }) => {
const plugins: PluginsMap = useRecoilValue(store.plugins);
const getPluginName = useCallback(
(pluginKey: string) => {
if (!pluginKey) {
return null;
}
if (pluginKey === 'n/a' || pluginKey === 'self reflection') {
return pluginKey;
}
return plugins[pluginKey] ?? 'self reflection';
},
[plugins],
);
if (!plugin || !plugin.latest) {
return null;
}
const latestPlugin = getPluginName(plugin.latest);
if (!latestPlugin || (latestPlugin && latestPlugin === 'n/a')) {
return null;
}
const generateStatus = (): ReactNode => {
if (!plugin.loading && latestPlugin === 'self reflection') {
return 'Finished';
} else if (latestPlugin === 'self reflection') {
return 'I\'m thinking...';
} else {
return (
<>
{plugin.loading ? 'Using' : 'Used'} <b>{latestPlugin}</b>
{plugin.loading ? '...' : ''}
</>
);
}
};
return (
<div className="flex flex-col items-start">
<Disclosure>
{({ open }) => {
const iconProps: PluginIconProps = {
className: cn(open ? 'rotate-180 transform' : '', 'h-4 w-4'),
};
return (
<>
<div
className={cn(
plugin.loading ? 'bg-green-100' : 'bg-gray-20',
'flex items-center rounded p-3 text-xs text-gray-900',
)}
>
<div>
<div className="flex items-center gap-3">
<div>{generateStatus()}</div>
</div>
</div>
{plugin.loading && <Spinner className="ml-1" />}
<Disclosure.Button className="ml-12 flex items-center gap-2">
<ChevronDownIcon {...iconProps} />
</Disclosure.Button>
</div>
<Disclosure.Panel className="my-3 flex max-w-full flex-col gap-3">
<CodeBlock
lang={latestPlugin ? `REQUEST TO ${latestPlugin?.toUpperCase()}` : 'REQUEST'}
codeChildren={formatInputs(plugin.inputs ?? [])}
plugin={true}
classProp="max-h-[450px]"
/>
{plugin.outputs && plugin.outputs.length > 0 && (
<CodeBlock
lang={
latestPlugin ? `RESPONSE FROM ${latestPlugin?.toUpperCase()}` : 'RESPONSE'
}
codeChildren={formatJSON(plugin.outputs ?? '')}
plugin={true}
classProp="max-h-[450px]"
/>
)}
</Disclosure.Panel>
</>
);
}}
</Disclosure>
</div>
);
};
export default memo(Plugin);
|