feat: support image outputs during code execution (#674)

Co-authored-by: Bailey Simrell <baileysimrell@gmail.com>
This commit is contained in:
Jeremy 2025-01-08 00:00:21 +05:30 committed by GitHub
parent 50fbc0dab2
commit 50bd4604b6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 313 additions and 117 deletions

View file

@ -1,18 +1,12 @@
import { cn, generateUUID } from '@/lib/utils';
import { ClockRewind, CopyIcon, PlayIcon, RedoIcon, UndoIcon } from './icons';
import { cn } from '@/lib/utils';
import { ClockRewind, CopyIcon, RedoIcon, UndoIcon } from './icons';
import { Button } from './ui/button';
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip';
import { useCopyToClipboard } from 'usehooks-ts';
import { toast } from 'sonner';
import { ConsoleOutput, UIBlock } from './block';
import {
Dispatch,
memo,
SetStateAction,
startTransition,
useCallback,
useState,
} from 'react';
import { Dispatch, memo, SetStateAction } from 'react';
import { RunCodeButton } from './run-code-button';
interface BlockActionsProps {
block: UIBlock;
@ -23,98 +17,6 @@ interface BlockActionsProps {
setConsoleOutputs: Dispatch<SetStateAction<Array<ConsoleOutput>>>;
}
export function RunCodeButton({
block,
setConsoleOutputs,
}: {
block: UIBlock;
setConsoleOutputs: Dispatch<SetStateAction<Array<ConsoleOutput>>>;
}) {
const [pyodide, setPyodide] = useState<any>(null);
const isPython = true;
const codeContent = block.content;
const updateConsoleOutput = useCallback(
(runId: string, content: string | null, status: 'completed' | 'failed') => {
setConsoleOutputs((consoleOutputs) => {
const index = consoleOutputs.findIndex((output) => output.id === runId);
if (index === -1) return consoleOutputs;
const updatedOutputs = [...consoleOutputs];
updatedOutputs[index] = {
id: runId,
content,
status,
};
return updatedOutputs;
});
},
[setConsoleOutputs],
);
const loadAndRunPython = useCallback(async () => {
const runId = generateUUID();
setConsoleOutputs((consoleOutputs) => [
...consoleOutputs,
{
id: runId,
content: null,
status: 'in_progress',
},
]);
let currentPyodideInstance = pyodide;
if (isPython) {
if (!currentPyodideInstance) {
// @ts-expect-error - pyodide is not defined
const newPyodideInstance = await loadPyodide({
indexURL: 'https://cdn.jsdelivr.net/pyodide/v0.23.4/full/',
});
setPyodide(newPyodideInstance);
currentPyodideInstance = newPyodideInstance;
}
try {
await currentPyodideInstance.runPythonAsync(`
import sys
import io
sys.stdout = io.StringIO()
`);
await currentPyodideInstance.runPythonAsync(codeContent);
const output: string = await currentPyodideInstance.runPythonAsync(
`sys.stdout.getvalue()`,
);
updateConsoleOutput(runId, output, 'completed');
} catch (error: any) {
updateConsoleOutput(runId, error.message, 'failed');
}
}
}, [pyodide, codeContent, isPython, setConsoleOutputs, updateConsoleOutput]);
return (
<Button
variant="outline"
className="py-1.5 px-2 h-fit dark:hover:bg-zinc-700"
onClick={() => {
startTransition(() => {
loadAndRunPython();
});
}}
disabled={block.status === 'streaming'}
>
<PlayIcon size={18} /> Run
</Button>
);
}
function PureBlockActions({
block,
handleVersionChange,