From 074c8fb763a9501b42480c478da4ded7a9dc1f4a Mon Sep 17 00:00:00 2001 From: Lucio Lelii Date: Tue, 8 Sep 2026 15:15:10 +0200 Subject: [PATCH] Give the model parameters the same treatment a node's own panel gives them The picker for a simulator or a judge and a node's parameter panel are the same dialog component, and the dialog has always known how to offer "Use default" on an optional field - showUseDefault, the "Using the default" hint, the reset. It reads one flag, defaultsWhenEmpty, which the node panels set on every optional field and this hand-written list never set at all. So a temperature typed here by mistake had no way back to unset: clearing the box by hand looks the same as never having decided. Set it on all five, and brought the rest of each field in line with what the schema-driven panel produces for the same object: an arrow step a decimal can actually move by, an integer step on the integers, and the tips ModelParameters itself declares, so the same explanation appears in both places. Temperature's max was 2 here and is 1.0 on the server, which has a comment explaining why - the dialog was offering a value the run would be rejected for. Still hand-written rather than derived from the published schema: this dialog picks a model for a run, not a node's configuration, and reaching for a block type's schema to render five known fields would buy a network call and a way to fail. Co-Authored-By: Claude Opus 5 (1M context) --- .../llm-descriptor-settings.spec.ts | 19 +++++++ .../llm-descriptor-settings.ts | 50 +++++++++++++++---- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/app/shared/llm-descriptor-settings/llm-descriptor-settings.spec.ts b/src/app/shared/llm-descriptor-settings/llm-descriptor-settings.spec.ts index 177e329..1c8674b 100644 --- a/src/app/shared/llm-descriptor-settings/llm-descriptor-settings.spec.ts +++ b/src/app/shared/llm-descriptor-settings/llm-descriptor-settings.spec.ts @@ -46,6 +46,25 @@ describe('openLLMDescriptorSettings', () => { expect(chosen).toEqual({ provider: 'InternalOllama', model: 'llama3', parameters: { seed: 7 } }); }); + it('offers the same treatment a node parameter panel gives these fields', async () => { + const { service, open } = dialog(null); + + await openLLMDescriptorSettings(service, retriever(), { title: 'Simulation Settings' }); + + const fields: Array> = open.mock.calls[0][0].fields; + const parameters = fields.filter((field) => field['group'] === 'Model parameters'); + expect(parameters.map((field) => field['key'])) + .toEqual(['temperature', 'topP', 'topK', 'maxTokens', 'seed']); + // Every one of them is optional, so every one of them can be put back to unset - which a + // filled box cannot express on its own, and which the node panels have always offered. + expect(parameters.every((field) => field['defaultsWhenEmpty'] === true)).toBe(true); + // The bound the server actually enforces on temperature, not the 2 this dialog used to allow. + expect(parameters.find((field) => field['key'] === 'temperature')).toMatchObject({ min: 0, max: 1 }); + // A decimal's arrows have somewhere sensible to go instead of jumping between the two ends. + expect(parameters.find((field) => field['key'] === 'topP')?.['stepIncrement']).toBe(0.1); + expect(parameters.find((field) => field['key'] === 'topK')?.['step']).toBe(1); + }); + it('shows an inherited parameter outside the collapsed section, where it can be seen', async () => { const { service, open } = dialog(null); diff --git a/src/app/shared/llm-descriptor-settings/llm-descriptor-settings.ts b/src/app/shared/llm-descriptor-settings/llm-descriptor-settings.ts index d7773a7..179a81c 100644 --- a/src/app/shared/llm-descriptor-settings/llm-descriptor-settings.ts +++ b/src/app/shared/llm-descriptor-settings/llm-descriptor-settings.ts @@ -14,18 +14,48 @@ 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', min: 0, max: 2, group: PARAMETER_GROUP, - placeholder: 'Leave empty for the default', tip: '0 makes the run as repeatable as the model allows' }, - { key: 'topP', label: 'Top P', type: 'number', min: 0, max: 1, group: PARAMETER_GROUP, - placeholder: 'Leave empty for the default' }, - { key: 'topK', label: 'Top K', type: 'number', min: 1, group: PARAMETER_GROUP, - placeholder: 'Leave empty for the default' }, - { key: 'maxTokens', label: 'Max tokens', type: 'number', min: 1, group: PARAMETER_GROUP, - placeholder: 'Leave empty for the default' }, - { key: 'seed', label: 'Seed', type: 'number', group: PARAMETER_GROUP, - placeholder: 'Leave empty for the default', tip: 'Fixes the randomness, so two runs can be compared' } + { + 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 = {