@datalayer/core
Version:
**Datalayer Core**
169 lines (168 loc) • 9.82 kB
JavaScript
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;