import { firstValueFrom } from 'rxjs'; import { LLMDescriptor, ModelParameters } from '@models/flow'; import { FieldRetriever } from '@services/retriever/field-retriever'; import { NodeSettingField, NodeSettingsDialogService } from '@services/dialogs/node-settings-dialog'; import { readSimulatorParameters } from '@shared/task-execution-viewer/execution-viewer.utils'; /** The retrievers the editor already uses for a node's provider and model fields. */ const PROVIDER_RETRIEVER_URL = '/retriever/LLM/providers'; const MODEL_RETRIEVER_URL = '/retriever/LLM/models'; const PARAMETER_GROUP = 'Model parameters'; /** * The optional sampling knobs, behind a section that starts closed. Provider and model are what * anyone opening this dialog came for; these are for the runs where you already know you want * them, and shown flat they made the common case look like a five-field form. * *
These are the same five fields a node's own parameter panel shows, and they are declared to
* behave the same way: `defaultsWhenEmpty` so an empty box reads as "the provider decides" and
* there is a way back to it after typing, the bounds the server actually enforces, and an arrow
* step a decimal can move by. Hand-written rather than derived from the published schema the way
* `buildSchemaObjectDialog` does it: this dialog is picking a model for a run, not editing a node's
* configuration, and reaching for a block type's schema to render five known fields would buy a
* network call and a failure mode. The labels, tips and bounds are ModelParameters' own - keep them
* in step with it.
*/
const PARAMETER_FIELDS: NodeSettingField[] = [
{
key: 'temperature', label: 'Temperature', type: 'number', group: PARAMETER_GROUP,
// The server caps this at 1.0; offering 2 here only produced a value it would reject.
min: 0, max: 1, stepIncrement: 0.1,
defaultsWhenEmpty: true, placeholder: 'Leave empty for the default',
tip: 'Higher values make the output more varied. 0 makes it as repeatable as the model allows.'
},
{
key: 'topP', label: 'Top P', type: 'number', group: PARAMETER_GROUP,
min: 0, max: 1, stepIncrement: 0.1,
defaultsWhenEmpty: true, placeholder: 'Leave empty for the default',
tip: 'Nucleus sampling: consider only the most likely tokens adding up to this probability.'
},
{
key: 'topK', label: 'Top K', type: 'number', group: PARAMETER_GROUP,
min: 1, step: 1, stepIncrement: 1,
defaultsWhenEmpty: true, placeholder: 'Leave empty for the default',
tip: 'Consider only this many candidate tokens at each step.'
},
{
key: 'maxTokens', label: 'Max tokens', type: 'number', group: PARAMETER_GROUP,
min: 1, step: 1, stepIncrement: 1,
defaultsWhenEmpty: true, placeholder: 'Leave empty for the default',
tip: 'Upper bound on the length of the generated answer.'
},
{
key: 'seed', label: 'Seed', type: 'number', group: PARAMETER_GROUP,
step: 1, stepIncrement: 1,
defaultsWhenEmpty: true, placeholder: 'Leave empty for the default',
tip: 'Fixes the randomness, so the same inputs give the same answer. Needed to tell a real change from model noise.'
}
];
export type LLMDescriptorSettingsRequest = {
title: string;
/** Sampling defaults offered in the closed section, e.g. temperature 0 for a judge. */
defaultParameters?: Partial One dialog for every place that picks a model at the moment it is needed rather than in a
* node's configuration: the interaction simulator, and the LLM assessment of a bias comparison.
* They ask the same question, so they ask it with the same window - the provider list, the models
* that follow from the chosen provider, and the same optional parameters.
*
* Returns null when the dialog is dismissed. Throws when no provider is published at all: the
* caller has a place to say so, and answering null there made the button that opened it look broken.
*/
export async function openLLMDescriptorSettings(
settingsDialog: NodeSettingsDialogService,
fieldRetriever: FieldRetriever,
request: LLMDescriptorSettingsRequest
): Promise