This commit is contained in:
Timothy Jaeryang Baek
2026-07-15 00:14:01 -04:00
parent 254888cf15
commit 63ada24706
31 changed files with 2560 additions and 2754 deletions
@@ -1,5 +1,4 @@
<script lang="ts">
import { toast } from 'svelte-sonner';
import { onMount, getContext } from 'svelte';
import { getCodeExecutionConfig, setCodeExecutionConfig } from '$lib/apis/configs';
@@ -8,14 +7,25 @@
import Tooltip from '$lib/components/common/Tooltip.svelte';
import Textarea from '$lib/components/common/Textarea.svelte';
import Switch from '$lib/components/common/Switch.svelte';
import AdminSettingField from './AdminSettingField.svelte';
import AdminSettingRow from './AdminSettingRow.svelte';
import AdminSettingSection from './AdminSettingSection.svelte';
const i18n = getContext('i18n');
const i18n: any = getContext('i18n');
export let saveHandler: Function;
let config = null;
let config: any = null;
let engines = ['pyodide', 'jupyter'];
const inputClass =
'w-full h-7 rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 text-xs text-gray-700 outline-hidden transition-colors placeholder:text-gray-300 focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:placeholder:text-gray-700 dark:focus:border-blue-500';
const textareaClass =
'w-full rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 py-1.5 text-xs text-gray-700 outline-hidden transition-colors placeholder:text-gray-300 focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:placeholder:text-gray-700 dark:focus:border-blue-500';
const inlineInputClass =
'w-fit h-7 rounded-lg border border-transparent bg-transparent px-2 text-xs text-gray-600 outline-hidden transition-colors hover:bg-black/5 focus:border-blue-400 dark:text-gray-400 dark:hover:bg-white/5 dark:focus:border-blue-500';
const selectClass =
'cursor-pointer bg-transparent text-xs text-gray-600 outline-hidden dark:text-gray-400';
const submitHandler = async () => {
const res = await setCodeExecutionConfig(localStorage.token, config);
@@ -31,304 +41,217 @@
</script>
<form
class="flex flex-col h-full justify-between space-y-3 text-sm"
class="flex h-full flex-col justify-between text-sm"
on:submit|preventDefault={async () => {
await submitHandler();
saveHandler();
}}
>
<div class=" space-y-3 flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5 -mr-1.5">
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
{#if config}
<div>
<div class="mb-3.5">
<div class=" mt-0.5 mb-2.5 text-xs text-gray-400 dark:text-gray-600">
{$i18n.t('Code Execution')}
</div>
<AdminSettingSection first>
<AdminSettingRow label={$i18n.t('Enable Code Execution')}>
<Switch bind:state={config.ENABLE_CODE_EXECUTION} />
</AdminSettingRow>
<hr class=" border-gray-100/20 dark:border-white/[0.025] my-2" />
{#if config.ENABLE_CODE_EXECUTION}
<AdminSettingRow
label={$i18n.t('Code Execution Engine')}
description={config.CODE_EXECUTION_ENGINE === 'jupyter'
? $i18n.t(
'Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.'
)
: ''}
>
<select
class={selectClass}
bind:value={config.CODE_EXECUTION_ENGINE}
placeholder={$i18n.t('Select a engine')}
required
>
<option disabled selected value="">{$i18n.t('Select a engine')}</option>
{#each engines as engine}
<option value={engine}>{engine}{engine === 'jupyter' ? ' (Legacy)' : ''}</option>
{/each}
</select>
</AdminSettingRow>
<div class="mb-2.5">
<div class=" flex w-full justify-between">
<div class=" self-center text-xs font-normal">
{$i18n.t('Enable Code Execution')}
</div>
{#if config.CODE_EXECUTION_ENGINE === 'jupyter'}
<AdminSettingField label={$i18n.t('Jupyter URL')}>
<input
class={inputClass}
type="text"
placeholder={$i18n.t('Enter Jupyter URL')}
bind:value={config.CODE_EXECUTION_JUPYTER_URL}
autocomplete="off"
/>
</AdminSettingField>
<Switch bind:state={config.ENABLE_CODE_EXECUTION} />
</div>
</div>
<AdminSettingRow label={$i18n.t('Jupyter Auth')}>
<select
class={selectClass}
bind:value={config.CODE_EXECUTION_JUPYTER_AUTH}
placeholder={$i18n.t('Select an auth method')}
>
<option selected value="">{$i18n.t('None')}</option>
<option value="token">{$i18n.t('Token')}</option>
<option value="password">{$i18n.t('Password')}</option>
</select>
</AdminSettingRow>
{#if config.ENABLE_CODE_EXECUTION}
<div class="mb-2.5">
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-normal">
{$i18n.t('Code Execution Engine')}
</div>
<div class="flex items-center relative">
<select
class="w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
bind:value={config.CODE_EXECUTION_ENGINE}
placeholder={$i18n.t('Select a engine')}
required
>
<option disabled selected value="">{$i18n.t('Select a engine')}</option>
{#each engines as engine}
<option value={engine}
>{engine}{engine === 'jupyter' ? ' (Legacy)' : ''}</option
>
{/each}
</select>
</div>
</div>
{#if config.CODE_EXECUTION_ENGINE === 'jupyter'}
<div class="text-gray-500 text-xs">
{$i18n.t(
'Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.'
)}
</div>
{/if}
</div>
{#if config.CODE_EXECUTION_ENGINE === 'jupyter'}
<div class="mb-2.5 flex flex-col gap-1.5 w-full">
<div class="text-xs font-normal">
{$i18n.t('Jupyter URL')}
</div>
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full text-sm py-0.5 placeholder:text-gray-300 dark:placeholder:text-gray-700 bg-transparent outline-hidden"
type="text"
placeholder={$i18n.t('Enter Jupyter URL')}
bind:value={config.CODE_EXECUTION_JUPYTER_URL}
autocomplete="off"
/>
</div>
</div>
</div>
<div class="mb-2.5 flex flex-col gap-1.5 w-full">
<div class=" flex gap-2 w-full items-center justify-between">
<div class="text-xs font-normal">
{$i18n.t('Jupyter Auth')}
</div>
<div>
<select
class="w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-left"
bind:value={config.CODE_EXECUTION_JUPYTER_AUTH}
placeholder={$i18n.t('Select an auth method')}
>
<option selected value="">{$i18n.t('None')}</option>
<option value="token">{$i18n.t('Token')}</option>
<option value="password">{$i18n.t('Password')}</option>
</select>
</div>
</div>
{#if config.CODE_EXECUTION_JUPYTER_AUTH}
<div class="flex w-full gap-2">
<div class="flex-1">
{#if config.CODE_EXECUTION_JUPYTER_AUTH === 'password'}
<SensitiveInput
type="text"
placeholder={$i18n.t('Enter Jupyter Password')}
bind:value={config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD}
autocomplete="off"
/>
{:else}
<SensitiveInput
type="text"
placeholder={$i18n.t('Enter Jupyter Token')}
bind:value={config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN}
autocomplete="off"
/>
{/if}
</div>
</div>
{/if}
</div>
<div class="flex gap-2 w-full items-center justify-between">
<div class="text-xs font-normal">
{$i18n.t('Code Execution Timeout')}
</div>
<div class="">
<Tooltip content={$i18n.t('Enter timeout in seconds')}>
<input
class="w-fit rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
type="number"
bind:value={config.CODE_EXECUTION_JUPYTER_TIMEOUT}
placeholder={$i18n.t('e.g. 60')}
autocomplete="off"
/>
</Tooltip>
</div>
</div>
{/if}
{/if}
</div>
<div class="mb-3.5">
<div class=" mt-0.5 mb-2.5 text-xs text-gray-400 dark:text-gray-600">
{$i18n.t('Code Interpreter')}
</div>
<hr class=" border-gray-100/20 dark:border-white/[0.025] my-2" />
<div class="mb-2.5">
<div class=" flex w-full justify-between">
<div class=" self-center text-xs font-normal">
{$i18n.t('Enable Code Interpreter')}
</div>
<Switch bind:state={config.ENABLE_CODE_INTERPRETER} />
</div>
</div>
{#if config.ENABLE_CODE_INTERPRETER}
<div class="mb-2.5">
<div class=" flex w-full justify-between">
<div class=" self-center text-xs font-normal">
{$i18n.t('Code Interpreter Engine')}
</div>
<div class="flex items-center relative">
<select
class="w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
bind:value={config.CODE_INTERPRETER_ENGINE}
placeholder={$i18n.t('Select a engine')}
required
>
<option disabled selected value="">{$i18n.t('Select a engine')}</option>
{#each engines as engine}
<option value={engine}
>{engine}{engine === 'jupyter' ? ' (Legacy)' : ''}</option
>
{/each}
</select>
</div>
</div>
{#if config.CODE_INTERPRETER_ENGINE === 'jupyter'}
<div class="text-gray-500 text-xs">
{$i18n.t(
'Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.'
)}
</div>
{/if}
</div>
{#if config.CODE_INTERPRETER_ENGINE === 'jupyter'}
<div class="mb-2.5 flex flex-col gap-1.5 w-full">
<div class="text-xs font-normal">
{$i18n.t('Jupyter URL')}
</div>
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full text-sm py-0.5 placeholder:text-gray-300 dark:placeholder:text-gray-700 bg-transparent outline-hidden"
type="text"
placeholder={$i18n.t('Enter Jupyter URL')}
bind:value={config.CODE_INTERPRETER_JUPYTER_URL}
autocomplete="off"
/>
</div>
</div>
</div>
<div class="mb-2.5 flex flex-col gap-1.5 w-full">
<div class="flex gap-2 w-full items-center justify-between">
<div class="text-xs font-normal">
{$i18n.t('Jupyter Auth')}
</div>
<div>
<select
class="w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-left"
bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH}
placeholder={$i18n.t('Select an auth method')}
>
<option selected value="">{$i18n.t('None')}</option>
<option value="token">{$i18n.t('Token')}</option>
<option value="password">{$i18n.t('Password')}</option>
</select>
</div>
</div>
{#if config.CODE_INTERPRETER_JUPYTER_AUTH}
<div class="flex w-full gap-2">
<div class="flex-1">
{#if config.CODE_INTERPRETER_JUPYTER_AUTH === 'password'}
<SensitiveInput
type="text"
placeholder={$i18n.t('Enter Jupyter Password')}
bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD}
autocomplete="off"
/>
{:else}
<SensitiveInput
type="text"
placeholder={$i18n.t('Enter Jupyter Token')}
bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN}
autocomplete="off"
/>
{/if}
</div>
</div>
{/if}
</div>
<div class="flex gap-2 w-full items-center justify-between">
<div class="text-xs font-normal">
{$i18n.t('Code Execution Timeout')}
</div>
<div class="">
<Tooltip content={$i18n.t('Enter timeout in seconds')}>
<input
class="w-fit rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
type="number"
bind:value={config.CODE_INTERPRETER_JUPYTER_TIMEOUT}
placeholder={$i18n.t('e.g. 60')}
autocomplete="off"
/>
</Tooltip>
</div>
</div>
{/if}
<hr class="border-gray-100/20 dark:border-white/[0.025] my-2" />
<div>
<div class="py-0.5 w-full">
<div class=" mb-2.5 text-xs font-normal">
{$i18n.t('Code Interpreter Prompt Template')}
</div>
<Tooltip
content={$i18n.t(
'Leave empty to use the default prompt, or enter a custom prompt'
)}
placement="top-start"
>
<Textarea
bind:value={config.CODE_INTERPRETER_PROMPT_TEMPLATE}
placeholder={$i18n.t(
'Leave empty to use the default prompt, or enter a custom prompt'
)}
{#if config.CODE_EXECUTION_JUPYTER_AUTH}
<AdminSettingField
label={config.CODE_EXECUTION_JUPYTER_AUTH === 'password'
? $i18n.t('Jupyter Password')
: $i18n.t('Jupyter Token')}
>
{#if config.CODE_EXECUTION_JUPYTER_AUTH === 'password'}
<SensitiveInput
type="text"
placeholder={$i18n.t('Enter Jupyter Password')}
bind:value={config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD}
autocomplete="off"
outerClassName="flex flex-1"
inputClassName={inputClass}
/>
</Tooltip>
</div>
</div>
{:else}
<SensitiveInput
type="text"
placeholder={$i18n.t('Enter Jupyter Token')}
bind:value={config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN}
autocomplete="off"
outerClassName="flex flex-1"
inputClassName={inputClass}
/>
{/if}
</AdminSettingField>
{/if}
<AdminSettingRow label={$i18n.t('Code Execution Timeout')}>
<Tooltip content={$i18n.t('Enter timeout in seconds')}>
<input
class={inlineInputClass}
type="number"
bind:value={config.CODE_EXECUTION_JUPYTER_TIMEOUT}
placeholder={$i18n.t('e.g. 60')}
autocomplete="off"
/>
</Tooltip>
</AdminSettingRow>
{/if}
</div>
</div>
{/if}
</AdminSettingSection>
<AdminSettingSection title={$i18n.t('Code Interpreter')}>
<AdminSettingRow label={$i18n.t('Enable Code Interpreter')}>
<Switch bind:state={config.ENABLE_CODE_INTERPRETER} />
</AdminSettingRow>
{#if config.ENABLE_CODE_INTERPRETER}
<AdminSettingRow
label={$i18n.t('Code Interpreter Engine')}
description={config.CODE_INTERPRETER_ENGINE === 'jupyter'
? $i18n.t(
'Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.'
)
: ''}
>
<select
class={selectClass}
bind:value={config.CODE_INTERPRETER_ENGINE}
placeholder={$i18n.t('Select a engine')}
required
>
<option disabled selected value="">{$i18n.t('Select a engine')}</option>
{#each engines as engine}
<option value={engine}>{engine}{engine === 'jupyter' ? ' (Legacy)' : ''}</option>
{/each}
</select>
</AdminSettingRow>
{#if config.CODE_INTERPRETER_ENGINE === 'jupyter'}
<AdminSettingField label={$i18n.t('Jupyter URL')}>
<input
class={inputClass}
type="text"
placeholder={$i18n.t('Enter Jupyter URL')}
bind:value={config.CODE_INTERPRETER_JUPYTER_URL}
autocomplete="off"
/>
</AdminSettingField>
<AdminSettingRow label={$i18n.t('Jupyter Auth')}>
<select
class={selectClass}
bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH}
placeholder={$i18n.t('Select an auth method')}
>
<option selected value="">{$i18n.t('None')}</option>
<option value="token">{$i18n.t('Token')}</option>
<option value="password">{$i18n.t('Password')}</option>
</select>
</AdminSettingRow>
{#if config.CODE_INTERPRETER_JUPYTER_AUTH}
<AdminSettingField
label={config.CODE_INTERPRETER_JUPYTER_AUTH === 'password'
? $i18n.t('Jupyter Password')
: $i18n.t('Jupyter Token')}
>
{#if config.CODE_INTERPRETER_JUPYTER_AUTH === 'password'}
<SensitiveInput
type="text"
placeholder={$i18n.t('Enter Jupyter Password')}
bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD}
autocomplete="off"
outerClassName="flex flex-1"
inputClassName={inputClass}
/>
{:else}
<SensitiveInput
type="text"
placeholder={$i18n.t('Enter Jupyter Token')}
bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN}
autocomplete="off"
outerClassName="flex flex-1"
inputClassName={inputClass}
/>
{/if}
</AdminSettingField>
{/if}
<AdminSettingRow label={$i18n.t('Code Execution Timeout')}>
<Tooltip content={$i18n.t('Enter timeout in seconds')}>
<input
class={inlineInputClass}
type="number"
bind:value={config.CODE_INTERPRETER_JUPYTER_TIMEOUT}
placeholder={$i18n.t('e.g. 60')}
autocomplete="off"
/>
</Tooltip>
</AdminSettingRow>
{/if}
<AdminSettingField label={$i18n.t('Code Interpreter Prompt Template')}>
<Tooltip
content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
placement="top-start"
>
<Textarea
className={textareaClass}
bind:value={config.CODE_INTERPRETER_PROMPT_TEMPLATE}
placeholder={$i18n.t(
'Leave empty to use the default prompt, or enter a custom prompt'
)}
/>
</Tooltip>
</AdminSettingField>
{/if}
</AdminSettingSection>
{/if}
</div>
<div class="flex justify-end pt-3 text-sm font-normal">
<div class="flex justify-end pt-6 text-sm font-normal">
<button
class="px-3.5 py-1.5 text-sm font-normal bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
type="submit"