);
}
diff --git a/examples/server/webui/src/components/MarkdownDisplay.tsx b/examples/server/webui/src/components/MarkdownDisplay.tsx
index 814920a74..33a64b2a6 100644
--- a/examples/server/webui/src/components/MarkdownDisplay.tsx
+++ b/examples/server/webui/src/components/MarkdownDisplay.tsx
@@ -9,8 +9,16 @@ import 'katex/dist/katex.min.css';
import { classNames, copyStr } from '../utils/misc';
import { ElementContent, Root } from 'hast';
import { visit } from 'unist-util-visit';
+import { useAppContext } from '../utils/app.context';
+import { CanvasType } from '../utils/types';
-export default function MarkdownDisplay({ content }: { content: string }) {
+export default function MarkdownDisplay({
+ content,
+ isGenerating,
+}: {
+ content: string;
+ isGenerating?: boolean;
+}) {
const preprocessedContent = useMemo(
() => preprocessLaTeX(content),
[content]
@@ -21,7 +29,11 @@ export default function MarkdownDisplay({ content }: { content: string }) {
rehypePlugins={[rehypeHightlight, rehypeKatex, rehypeCustomCopyButton]}
components={{
button: (props) => (
-
+
),
// note: do not use "pre", "p" or other basic html elements here, it will cause the node to re-render when the message is being generated (this should be a bug with react-markdown, not sure how to fix it)
}}
@@ -31,11 +43,12 @@ export default function MarkdownDisplay({ content }: { content: string }) {
);
}
-const CopyCodeButton: React.ElementType<
+const CodeBlockButtons: React.ElementType<
React.ClassAttributes &
React.HTMLAttributes &
- ExtraProps & { origContent: string }
-> = ({ node, origContent }) => {
+ ExtraProps & { origContent: string; isGenerating?: boolean }
+> = ({ node, origContent, isGenerating }) => {
+ const { config } = useAppContext();
const startOffset = node?.position?.start.offset ?? 0;
const endOffset = node?.position?.end.offset ?? 0;
@@ -48,6 +61,19 @@ const CopyCodeButton: React.ElementType<
[origContent, startOffset, endOffset]
);
+ const codeLanguage = useMemo(
+ () =>
+ origContent
+ .substring(startOffset, startOffset + 10)
+ .match(/^```([^\n]+)\n/)?.[1] ?? '',
+ [origContent, startOffset]
+ );
+
+ const canRunCode =
+ !isGenerating &&
+ config.pyIntepreterEnabled &&
+ codeLanguage.startsWith('py');
+
return (
+ {canRunCode && (
+
+ )}
);
};
@@ -82,6 +114,31 @@ export const CopyButton = ({
);
};
+export const RunPyCodeButton = ({
+ content,
+ className,
+}: {
+ content: string;
+ className?: string;
+}) => {
+ const { setCanvasData } = useAppContext();
+ return (
+ <>
+
+ setCanvasData({
+ type: CanvasType.PY_INTERPRETER,
+ content,
+ })
+ }
+ >
+ ▶️ Run
+
+ >
+ );
+};
+
/**
* This injects the "button" element before each "pre" element.
* The actual button will be replaced with a react component in the MarkdownDisplay.
diff --git a/examples/server/webui/src/components/SettingDialog.tsx b/examples/server/webui/src/components/SettingDialog.tsx
index d30925e6c..679c3a084 100644
--- a/examples/server/webui/src/components/SettingDialog.tsx
+++ b/examples/server/webui/src/components/SettingDialog.tsx
@@ -5,12 +5,14 @@ import { isDev } from '../Config';
import StorageUtils from '../utils/storage';
import { classNames, isBoolean, isNumeric, isString } from '../utils/misc';
import {
+ BeakerIcon,
ChatBubbleOvalLeftEllipsisIcon,
Cog6ToothIcon,
FunnelIcon,
HandRaisedIcon,
SquaresPlusIcon,
} from '@heroicons/react/24/outline';
+import { OpenInNewTab } from '../utils/common';
type SettKey = keyof typeof CONFIG_DEFAULT;
@@ -194,14 +196,9 @@ const SETTING_SECTIONS: SettingSection[] = [
label: (
<>
Custom JSON config (For more info, refer to{' '}
-
+
server documentation
-
+
)
>
),
@@ -209,6 +206,56 @@ const SETTING_SECTIONS: SettingSection[] = [
},
],
},
+ {
+ title: (
+ <>
+
+ Experimental
+ >
+ ),
+ fields: [
+ {
+ type: SettingInputType.CUSTOM,
+ key: 'custom', // dummy key, won't be used
+ component: () => (
+ <>
+
+ Experimental features are not guaranteed to work correctly.
+
+
+ If you encounter any problems, create a{' '}
+
+ Bug (misc.)
+ {' '}
+ report on Github. Please also specify webui/experimental on
+ the report title and include screenshots.
+
+
+ Some features may require packages downloaded from CDN, so they
+ need internet connection.
+
+ >
+ ),
+ },
+ {
+ type: SettingInputType.CHECKBOX,
+ label: (
+ <>
+ Enable Python interpreter
+
+
+ This feature uses{' '}
+ pyodide,
+ downloaded from CDN. To use this feature, ask the LLM to generate
+ python code inside a markdown code block. You will see a "Run"
+ button on the code block, near the "Copy" button.
+
+ >
+ ),
+ key: 'pyIntepreterEnabled',
+ },
+ ],
+ },
];
export default function SettingDialog({
@@ -278,7 +325,7 @@ export default function SettingDialog({
};
return (
-