UNPKG

@datalayer/core

Version:
169 lines (168 loc) 9.82 kB
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; /* * Copyright (c) 2023-2025 Datalayer, Inc. * Distributed under the terms of the Modified BSD License. */ import { useCallback, useEffect, useState } from 'react'; import { ActionList, FormControl, ToggleSwitch, Tooltip, IconButton } from '@primer/react'; import { Box } from "@datalayer/primer-addons"; import { AlertIcon } from '@primer/octicons-react'; import { JSONExt } from '@lumino/coreutils'; import { KernelExecutor } from '@datalayer/jupyter-react'; import { DatalayerThemeProvider } from '../../theme'; import { RuntimeSnippetsFacade } from '../../api'; import { ExternalTokenSilentLogin } from '../../components/iam'; import { useCoreStore, useIAMStore } from '../../state'; import { RuntimeReservationControl, MAXIMAL_RUNTIME_TIME_RESERVATION_MINUTES } from './RuntimeReservationControl'; import { RuntimeVariables } from './RuntimeVariables'; import { RuntimePickerBase } from './RuntimePickerBase'; /** * Runtime Picker components for a Notebook. */ export function RuntimePickerNotebook(props) { const { multiServiceManager, sessionContext, setValue, translator } = props; const { configuration } = useCoreStore(); const { credits, refreshCredits, token } = useIAMStore(); const [selectedRuntimeDesc, setSelectedRuntimeDesc] = useState(); const [timeLimit, setTimeLimit] = useState(Math.min(credits?.available ?? 0, 10)); const [userStorage, setUserStorage] = useState(false); const [canTransferFrom, setTransferFrom] = useState(false); const [canTransferTo, setTransferTo] = useState(false); const [transferVariables, setTransferVariables] = useState(false); const [hasLoadedVariables, setHasLoadedVariables] = useState(false); const [kernelVariables, setRuntimeVariables] = useState(); const [toTransfer, setToTransfer] = useState([]); useEffect(() => { refreshCredits(); }, []); useEffect(() => { const specs = sessionContext.specsManager.specs?.kernelspecs; if (sessionContext.session?.kernel?.name && specs) { const spec = Object.values(specs).find(spec => spec?.name === sessionContext.session.kernel.name); if (spec) { setSelectedRuntimeDesc({ name: spec.name, kernelId: sessionContext.session.kernel.id, location: sessionContext.location, language: spec.language, displayName: sessionContext.kernelDisplayName }); setTransferFrom(RuntimeSnippetsFacade.supports(spec.language)); } } }, [sessionContext]); const listRuntimeVariables = useCallback(async () => { if (hasLoadedVariables) { return Promise.resolve(); } setHasLoadedVariables(true); const connection = sessionContext.session.kernel; const spec = sessionContext.specsManager.specs.kernelspecs[connection.model.name]; const snippets = new RuntimeSnippetsFacade(spec.language); const outputs = await new KernelExecutor({ connection }).execute(snippets.listVariables()); const content = outputs.get(0).data['text/plain']; if (content) { setRuntimeVariables(JSON.parse( // We need to remove the quotes prior to parsing. content.slice(1, content.length - 1))); if (toTransfer.length) { const candidates = Object.keys(kernelVariables ?? {}); setToTransfer(toTransfer.filter(n => candidates.includes(n))); } else { // By default select all variables. setToTransfer(Object.keys(kernelVariables ?? {})); } } else { setRuntimeVariables({}); } }, [hasLoadedVariables, kernelVariables, toTransfer]); const setSelectedVariables = useCallback((l) => { if (!JSONExt.deepEqual(toTransfer, l)) { setToTransfer(l); } }, [toTransfer]); const setTransferVariable = useCallback((value) => { if (transferVariables !== value) { if (value) { listRuntimeVariables().catch(error => { console.error('Failed to list the runtime variable', error); setHasLoadedVariables(false); }); } setTransferVariables(value); } }, [transferVariables]); const setRuntimeDesc = useCallback((runtimeDesc) => { if (!runtimeDesc) { if (selectedRuntimeDesc) { setSelectedRuntimeDesc(undefined); setTransferTo(false); } return; } if (selectedRuntimeDesc?.displayName !== runtimeDesc.displayName || selectedRuntimeDesc?.kernelId !== runtimeDesc.kernelId) { setSelectedRuntimeDesc({ ...runtimeDesc }); setTransferTo(RuntimeSnippetsFacade.supports(runtimeDesc.language)); } }, [selectedRuntimeDesc]); const handleUserStorageChange = useCallback((e) => { e.preventDefault(); setUserStorage(!userStorage); }, [userStorage]); useEffect(() => { const creditsLimit = selectedRuntimeDesc?.location === 'remote' && selectedRuntimeDesc.burningRate ? Math.min(timeLimit, MAXIMAL_RUNTIME_TIME_RESERVATION_MINUTES) * selectedRuntimeDesc.burningRate * 60 : undefined; setValue(creditsLimit !== 0 ? { kernel: selectedRuntimeDesc ? { environmentName: ['browser', 'remote'].includes(selectedRuntimeDesc.location) ? `${selectedRuntimeDesc.location}-${selectedRuntimeDesc.name}` : selectedRuntimeDesc.name, id: selectedRuntimeDesc.kernelId, creditsLimit, capabilities: userStorage ? ['user_storage'] : undefined } : null, selectedVariables: toTransfer, } : new Error('Credits limit must be strictly positive.')); }, [selectedRuntimeDesc, userStorage, toTransfer, timeLimit]); const { kernelPreference: { canStart } } = sessionContext; const max = Math.floor((credits?.available ?? 0) / (selectedRuntimeDesc?.burningRate ?? -1) / 60.0); const outOfCredits = !credits?.available || max < Number.EPSILON; return (_jsx(DatalayerThemeProvider, { children: _jsxs(Box, { as: "form", className: "dla-Runtimes-picker", children: [_jsx(Box, { sx: { padding: 'var(--stack-padding-condensed) 0' }, children: _jsx(RuntimePickerBase, { display: "radio", disabled: canStart === false, preference: { id: sessionContext.session?.id, kernelDisplayName: sessionContext.kernelPreference.shouldStart ? sessionContext.kernelDisplayName : undefined, }, sessionContext: sessionContext, multiServiceManager: multiServiceManager, translator: translator, runtimeDesc: selectedRuntimeDesc, setRuntimeDesc: setRuntimeDesc, postActions: token || !props.logIn ? /* <Button variant="default" onClick={e => { e.preventDefault(); commands.execute(CommandIDs.launchRemoteRuntime); close(); }} > Launch a New Runtime </Button> */ _jsx(_Fragment, {}) : _jsx(ActionList.Item, { onSelect: props.logIn, title: 'Connect to Runtime provider.', children: _jsx(ExternalTokenSilentLogin, { message: "Connect to the Runtime provider" }) }) }) }), !selectedRuntimeDesc?.kernelId && selectedRuntimeDesc?.location === 'remote' && _jsxs(_Fragment, { children: [_jsx(RuntimeReservationControl, { disabled: outOfCredits || selectedRuntimeDesc?.location !== 'remote', label: 'Time reservation', max: max < 0 ? 1 : max, time: timeLimit, onTimeChange: setTimeLimit, error: outOfCredits && max >= 0 ? 'You must add credits to your account.' : timeLimit === 0 ? 'You must set a time limit.' : undefined, burningRate: selectedRuntimeDesc.burningRate }), !configuration.whiteLabel && _jsxs(FormControl, { disabled: !!selectedRuntimeDesc?.kernelId || selectedRuntimeDesc?.location !== 'remote', layout: "horizontal", children: [_jsxs(FormControl.Label, { children: ["User storage", _jsx(Tooltip, { text: 'The runtime will be slower to start.', direction: "e", style: { marginLeft: 3 }, children: _jsx(IconButton, { icon: AlertIcon, "aria-label": "", variant: "invisible" }) })] }), _jsx(ToggleSwitch, { disabled: !!selectedRuntimeDesc?.kernelId || selectedRuntimeDesc?.location !== 'remote', checked: userStorage, size: "small", onClick: handleUserStorageChange })] })] }), canTransferFrom && canTransferTo && _jsx(RuntimeVariables, { selectedVariables: toTransfer, setSelectVariable: setSelectedVariables, transferVariables: transferVariables, setTransferVariable: setTransferVariable, kernelVariables: kernelVariables, translator: translator })] }) })); } export default RuntimePickerNotebook;