Enhance model management: add temperature, top-p, top-k, and min-p settings with UI sliders

This commit is contained in:
extio-de
2026-05-29 21:36:43 +02:00
parent 0267aa34cd
commit f05611ebb4
8 changed files with 440 additions and 40 deletions

View File

@@ -15,7 +15,11 @@ record App(Path path, Path interpreter, @Deprecated List<String> arguments, List
"MODEL", m -> m.path(),
"CONTEXT_SIZE", m -> String.valueOf(m.contextSize()),
"THREADS", m -> String.valueOf(m.threads()),
"GPU_LAYERS", m -> String.valueOf(m.gpuLayers()));
"GPU_LAYERS", m -> String.valueOf(m.gpuLayers()),
"TEMP", Model::temperatureDisplay,
"TOP_P", Model::topPDisplay,
"TOP_K", Model::topKDisplay,
"MIN_P", Model::minPDisplay);
String argumentsToString(boolean showAll) {
return this.argumentsToStringByArgs(this.appArguments.stream(), showAll);

View File

@@ -10,7 +10,6 @@ import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -46,8 +45,10 @@ public class Data {
// Migration
for (int i = 0; i < modelData.models().size(); i++) {
final Model model = modelData.models.get(i);
if (model.maxContextSize() < model.contextSize() || model.promptTemplate() == null) {
modelData.models.set(i, new Model(model.path(), model.contextSize(), model.contextSize(), model.gpuLayers(), model.threads(), Objects.requireNonNullElse(model.promptTemplate(), ""), model.ctime()));
if (model.maxContextSize() < model.contextSize() || model.promptTemplate() == null || model.temperature() == null || model.topP() == null || model.topK() == null
|| model.minP() == null) {
modelData.models.set(i, new Model(model.path(), model.contextSize(), model.maxContextSize(), model.gpuLayers(), model.threads(), model.temperature(), model.topP(), model.topK(),
model.minP(), model.promptTemplate(), model.ctime()));
}
}
sortModels();
@@ -154,7 +155,7 @@ public class Data {
catch (final IOException e) {
// Use 0 as fallback
}
return new Model(path, 16000, 128000, 99, 6, "", ctime);
return new Model(path, 16000, 128000, 99, 6, Model.DEFAULT_TEMPERATURE, Model.DEFAULT_TOP_P, Model.DEFAULT_TOP_K, Model.DEFAULT_MIN_P, "", ctime);
}
static record ModelData(List<Model> models) {

View File

@@ -11,6 +11,7 @@ import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
@@ -32,6 +33,7 @@ import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
import javax.swing.border.EmptyBorder;
@@ -48,6 +50,8 @@ public class LaunchFrame extends JFrame {
private static final long serialVersionUID = 1L;
private static final int DECIMAL_SLIDER_SCALE = 100;
private final JPanel contentPane;
JPanel panel;
@@ -125,6 +129,30 @@ public class LaunchFrame extends JFrame {
JLabel lblPromptTemplate_1;
JLabel lblTemperature;
JSlider slider_temperature;
JLabel lblTemperatureValue;
JLabel lblTopP;
JSlider slider_topP;
JLabel lblTopPValue;
JLabel lblTopK;
JSlider slider_topK;
JLabel lblTopKValue;
JLabel lblMinP;
JSlider slider_minP;
JLabel lblMinPValue;
/**
* Create the frame.
*/
@@ -255,8 +283,12 @@ public class LaunchFrame extends JFrame {
this.scrollPane_1.setViewportView(this.modelTree);
{
final JScrollPane modelSettingsScrollPane = new JScrollPane();
modelSettingsScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
modelSettingsScrollPane.getVerticalScrollBar().setUnitIncrement(UIScaler.scale(16));
this.splitPane_2.setRightComponent(modelSettingsScrollPane);
this.panel_3 = new JPanel();
this.splitPane_2.setRightComponent(this.panel_3);
modelSettingsScrollPane.setViewportView(this.panel_3);
this.panel_3.setLayout(new GridLayout(0, 1, 0, 0));
{
this.lblContextLength = new JLabel("Context Length");
@@ -306,6 +338,7 @@ public class LaunchFrame extends JFrame {
}
{
this.lblPromptTemplate = new JLabel("Prompt Template");
this.lblPromptTemplate.setHorizontalAlignment(SwingConstants.CENTER);
this.panel_3.add(this.lblPromptTemplate);
}
{
@@ -313,6 +346,98 @@ public class LaunchFrame extends JFrame {
this.lblPromptTemplate_1.setHorizontalAlignment(SwingConstants.CENTER);
this.panel_3.add(this.lblPromptTemplate_1);
}
{
this.lblTemperature = new JLabel("Temperature");
this.lblTemperature.setHorizontalAlignment(SwingConstants.CENTER);
this.panel_3.add(this.lblTemperature);
}
{
this.slider_temperature = new JSlider();
this.slider_temperature.setMinimum((int) (Model.MIN_TEMPERATURE * DECIMAL_SLIDER_SCALE));
this.slider_temperature.setMaximum((int) (Model.MAX_TEMPERATURE * DECIMAL_SLIDER_SCALE));
this.slider_temperature.setValue(LaunchFrame.doubleToSlider(Model.DEFAULT_TEMPERATURE));
this.slider_temperature.setMajorTickSpacing(50);
this.slider_temperature.setMinorTickSpacing(5);
this.slider_temperature.setLabelTable(LaunchFrame.createDecimalLabelTable(new int[] { 0, 50, 100, 150, 200 }));
this.slider_temperature.setPaintLabels(true);
this.slider_temperature.setPaintTicks(true);
this.slider_temperature.addChangeListener(e -> LaunchFrame.this.updateSamplingValueLabels());
this.panel_3.add(this.slider_temperature);
}
{
this.lblTemperatureValue = new JLabel("-");
this.lblTemperatureValue.setHorizontalAlignment(SwingConstants.CENTER);
this.panel_3.add(this.lblTemperatureValue);
}
{
this.lblTopP = new JLabel("Top P");
this.lblTopP.setHorizontalAlignment(SwingConstants.CENTER);
this.panel_3.add(this.lblTopP);
}
{
this.slider_topP = new JSlider();
this.slider_topP.setMinimum((int) (Model.MIN_TOP_P * DECIMAL_SLIDER_SCALE));
this.slider_topP.setMaximum((int) (Model.MAX_TOP_P * DECIMAL_SLIDER_SCALE));
this.slider_topP.setValue(LaunchFrame.doubleToSlider(Model.DEFAULT_TOP_P));
this.slider_topP.setMajorTickSpacing(25);
this.slider_topP.setMinorTickSpacing(5);
this.slider_topP.setLabelTable(LaunchFrame.createDecimalLabelTable(new int[] { 0, 25, 50, 75, 100 }));
this.slider_topP.setPaintLabels(true);
this.slider_topP.setPaintTicks(true);
this.slider_topP.addChangeListener(e -> LaunchFrame.this.updateSamplingValueLabels());
this.panel_3.add(this.slider_topP);
}
{
this.lblTopPValue = new JLabel("-");
this.lblTopPValue.setHorizontalAlignment(SwingConstants.CENTER);
this.panel_3.add(this.lblTopPValue);
}
{
this.lblTopK = new JLabel("Top K");
this.lblTopK.setHorizontalAlignment(SwingConstants.CENTER);
this.panel_3.add(this.lblTopK);
}
{
this.slider_topK = new JSlider();
this.slider_topK.setMinimum(Model.MIN_TOP_K);
this.slider_topK.setMaximum(Model.MAX_TOP_K);
this.slider_topK.setValue(Model.DEFAULT_TOP_K);
this.slider_topK.setMajorTickSpacing(100);
this.slider_topK.setMinorTickSpacing(25);
this.slider_topK.setSnapToTicks(true);
this.slider_topK.setPaintLabels(true);
this.slider_topK.setPaintTicks(true);
this.slider_topK.addChangeListener(e -> LaunchFrame.this.updateSamplingValueLabels());
this.panel_3.add(this.slider_topK);
}
{
this.lblTopKValue = new JLabel("-");
this.lblTopKValue.setHorizontalAlignment(SwingConstants.CENTER);
this.panel_3.add(this.lblTopKValue);
}
{
this.lblMinP = new JLabel("Min P");
this.lblMinP.setHorizontalAlignment(SwingConstants.CENTER);
this.panel_3.add(this.lblMinP);
}
{
this.slider_minP = new JSlider();
this.slider_minP.setMinimum((int) (Model.MIN_MIN_P * DECIMAL_SLIDER_SCALE));
this.slider_minP.setMaximum((int) (Model.MAX_MIN_P * DECIMAL_SLIDER_SCALE));
this.slider_minP.setValue(LaunchFrame.doubleToSlider(Model.DEFAULT_MIN_P));
this.slider_minP.setMajorTickSpacing(25);
this.slider_minP.setMinorTickSpacing(5);
this.slider_minP.setLabelTable(LaunchFrame.createDecimalLabelTable(new int[] { 0, 25, 50, 75, 100 }));
this.slider_minP.setPaintLabels(true);
this.slider_minP.setPaintTicks(true);
this.slider_minP.addChangeListener(e -> LaunchFrame.this.updateSamplingValueLabels());
this.panel_3.add(this.slider_minP);
}
{
this.lblMinPValue = new JLabel("-");
this.lblMinPValue.setHorizontalAlignment(SwingConstants.CENTER);
this.panel_3.add(this.lblMinPValue);
}
{
this.verticalStrut = Box.createVerticalStrut(20);
this.panel_3.add(this.verticalStrut);
@@ -448,6 +573,10 @@ public class LaunchFrame extends JFrame {
template.maxContextSize(),
LaunchFrame.this.slider_gpu.getValue(),
LaunchFrame.this.slider_threads.getValue(),
LaunchFrame.this.sliderValue(LaunchFrame.this.slider_temperature),
LaunchFrame.this.sliderValue(LaunchFrame.this.slider_topP),
LaunchFrame.this.slider_topK.getValue(),
LaunchFrame.this.sliderValue(LaunchFrame.this.slider_minP),
template.promptTemplate(),
template.ctime());
}
@@ -484,6 +613,7 @@ public class LaunchFrame extends JFrame {
UIScaler.scaleComponentFonts(this);
UIScaler.centerOnPrimaryDisplay(this);
this.updateSamplingValueLabels();
this.repaint();
this.splitPane.setDividerLocation(0.33);
this.splitPane_1.setDividerLocation(0.66);
@@ -512,7 +642,35 @@ public class LaunchFrame extends JFrame {
this.slider_threads.setValue(model.threads());
this.slider_gpu.setValue(model.gpuLayers());
this.slider_temperature.setValue(LaunchFrame.doubleToSlider(model.temperatureValue()));
this.slider_topP.setValue(LaunchFrame.doubleToSlider(model.topPValue()));
this.slider_topK.setValue(model.topKValue());
this.slider_minP.setValue(LaunchFrame.doubleToSlider(model.minPValue()));
this.lblPromptTemplate_1.setText(model.promptTemplate() != null && !model.promptTemplate().isEmpty() ? model.promptTemplate() : "-");
this.updateSamplingValueLabels();
}
private void updateSamplingValueLabels() {
this.lblTemperatureValue.setText(Model.formatDecimal(this.sliderValue(this.slider_temperature)));
this.lblTopPValue.setText(Model.formatDecimal(this.sliderValue(this.slider_topP)));
this.lblTopKValue.setText(String.valueOf(this.slider_topK.getValue()));
this.lblMinPValue.setText(Model.formatDecimal(this.sliderValue(this.slider_minP)));
}
private double sliderValue(final JSlider slider) {
return slider.getValue() / (double) DECIMAL_SLIDER_SCALE;
}
private static int doubleToSlider(final double value) {
return (int) Math.round(value * DECIMAL_SLIDER_SCALE);
}
private static Hashtable<Integer, JLabel> createDecimalLabelTable(final int[] values) {
final Hashtable<Integer, JLabel> labels = new Hashtable<>();
for (final int value : values) {
labels.put(value, new JLabel(Model.formatDecimal(value / (double) DECIMAL_SLIDER_SCALE)));
}
return labels;
}
private void refresh() {

View File

@@ -1,4 +1,97 @@
package de.extio.lm_launcher;
record Model(String path, int contextSize, int maxContextSize, int gpuLayers, int threads, String promptTemplate, long ctime) {
import java.math.BigDecimal;
record Model(String path, int contextSize, int maxContextSize, int gpuLayers, int threads, Double temperature, Double topP,
Integer topK, Double minP, String promptTemplate, long ctime) {
static final double DEFAULT_TEMPERATURE = 0.8d;
static final double DEFAULT_TOP_P = 0.95d;
static final int DEFAULT_TOP_K = 40;
static final double DEFAULT_MIN_P = 0.05d;
static final double MIN_TEMPERATURE = 0d;
static final double MAX_TEMPERATURE = 2d;
static final double MIN_TOP_P = 0d;
static final double MAX_TOP_P = 1d;
static final int MIN_TOP_K = 0;
static final int MAX_TOP_K = 500;
static final double MIN_MIN_P = 0d;
static final double MAX_MIN_P = 1d;
public Model {
maxContextSize = Math.max(Math.max(1, maxContextSize), Math.max(1, contextSize));
contextSize = clampInt(contextSize, 1, maxContextSize);
gpuLayers = Math.max(0, gpuLayers);
threads = Math.max(0, threads);
temperature = sanitizeDouble(temperature, DEFAULT_TEMPERATURE, MIN_TEMPERATURE, MAX_TEMPERATURE);
topP = sanitizeDouble(topP, DEFAULT_TOP_P, MIN_TOP_P, MAX_TOP_P);
topK = sanitizeInt(topK, DEFAULT_TOP_K, MIN_TOP_K, MAX_TOP_K);
minP = sanitizeDouble(minP, DEFAULT_MIN_P, MIN_MIN_P, MAX_MIN_P);
promptTemplate = promptTemplate == null ? "" : promptTemplate;
}
double temperatureValue() {
return this.temperature;
}
double topPValue() {
return this.topP;
}
int topKValue() {
return this.topK;
}
double minPValue() {
return this.minP;
}
String temperatureDisplay() {
return formatDecimal(this.temperatureValue());
}
String topPDisplay() {
return formatDecimal(this.topPValue());
}
String topKDisplay() {
return String.valueOf(this.topKValue());
}
String minPDisplay() {
return formatDecimal(this.minPValue());
}
private static Double sanitizeDouble(final Double value, final double defaultValue, final double minValue, final double maxValue) {
if (value == null || value.isNaN() || value.isInfinite()) {
return defaultValue;
}
return Math.max(minValue, Math.min(maxValue, value));
}
private static Integer sanitizeInt(final Integer value, final int defaultValue, final int minValue, final int maxValue) {
if (value == null) {
return defaultValue;
}
return clampInt(value, minValue, maxValue);
}
private static int clampInt(final int value, final int minValue, final int maxValue) {
return Math.max(minValue, Math.min(maxValue, value));
}
static String formatDecimal(final double value) {
return BigDecimal.valueOf(value).stripTrailingZeros().toPlainString();
}
}

View File

@@ -12,6 +12,7 @@ import java.util.function.Consumer;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
@@ -42,18 +43,27 @@ public class ModelPropertiesDialog extends JDialog {
JTextField textField_3;
JLabel lblPromptTemplate;
JTextField txtPrompttemplate;
JLabel lblTemperature;
JTextField txtTemperature;
JLabel lblTopP;
JTextField txtTopP;
JLabel lblTopK;
JTextField txtTopK;
JLabel lblMinP;
JTextField txtMinP;
/**
* Create the dialog.
*/
public ModelPropertiesDialog(final Frame parent, final String modelPath, final Model editModel, final Consumer<Model> modelConsumer) {
super(parent);
final Model modelToEdit = editModel != null ? editModel : Data.defaultModel(modelPath);
this.setTitle("Model properties");
this.setModalityType(ModalityType.APPLICATION_MODAL);
this.setModal(true);
this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
this.setBounds(UIScaler.scale(100), UIScaler.scale(100), UIScaler.scale(678), UIScaler.scale(191));
this.setBounds(UIScaler.scale(100), UIScaler.scale(100), UIScaler.scale(678), UIScaler.scale(320));
this.getContentPane().setLayout(new BorderLayout());
this.contentPanel.setBorder(new EmptyBorder(UIScaler.scale(5), UIScaler.scale(5), UIScaler.scale(5), UIScaler.scale(5)));
this.getContentPane().add(this.contentPanel, BorderLayout.CENTER);
@@ -106,6 +116,46 @@ public class ModelPropertiesDialog extends JDialog {
this.contentPanel.add(this.textField_1);
this.textField_1.setColumns(10);
}
{
this.lblTemperature = new JLabel("Temperature");
this.contentPanel.add(this.lblTemperature);
}
{
this.txtTemperature = new JTextField();
this.txtTemperature.setText(Model.formatDecimal(Model.DEFAULT_TEMPERATURE));
this.contentPanel.add(this.txtTemperature);
this.txtTemperature.setColumns(10);
}
{
this.lblTopP = new JLabel("Top P");
this.contentPanel.add(this.lblTopP);
}
{
this.txtTopP = new JTextField();
this.txtTopP.setText(Model.formatDecimal(Model.DEFAULT_TOP_P));
this.contentPanel.add(this.txtTopP);
this.txtTopP.setColumns(10);
}
{
this.lblTopK = new JLabel("Top K");
this.contentPanel.add(this.lblTopK);
}
{
this.txtTopK = new JTextField();
this.txtTopK.setText(String.valueOf(Model.DEFAULT_TOP_K));
this.contentPanel.add(this.txtTopK);
this.txtTopK.setColumns(10);
}
{
this.lblMinP = new JLabel("Min P");
this.contentPanel.add(this.lblMinP);
}
{
this.txtMinP = new JTextField();
this.txtMinP.setText(Model.formatDecimal(Model.DEFAULT_MIN_P));
this.contentPanel.add(this.txtMinP);
this.txtMinP.setColumns(10);
}
{
final JPanel buttonPane = new JPanel();
buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
@@ -116,17 +166,30 @@ public class ModelPropertiesDialog extends JDialog {
@Override
public void actionPerformed(final ActionEvent e) {
ModelPropertiesDialog.this.setVisible(false);
final Model modelToSave = editModel != null ? editModel : Data.defaultModel(modelPath);
modelConsumer.accept(
new Model(modelPath,
Integer.parseInt(ModelPropertiesDialog.this.textField_3.getText()),
Integer.parseInt(ModelPropertiesDialog.this.textField.getText()),
Integer.parseInt(ModelPropertiesDialog.this.textField_1.getText()),
Integer.parseInt(ModelPropertiesDialog.this.textField_2.getText()),
ModelPropertiesDialog.this.txtPrompttemplate.getText(),
modelToSave.ctime()));
ModelPropertiesDialog.this.dispose();
try {
final int contextSize = ModelPropertiesDialog.clampInt(ModelPropertiesDialog.parseIntField(ModelPropertiesDialog.this.textField_3, "Context Length"), 1,
Integer.MAX_VALUE);
final int maxContextSize = ModelPropertiesDialog.clampInt(ModelPropertiesDialog.parseIntField(ModelPropertiesDialog.this.textField, "Max Context Length"), 1,
Integer.MAX_VALUE);
final int gpuLayers = Math.max(0, ModelPropertiesDialog.parseIntField(ModelPropertiesDialog.this.textField_1, "GPU Layers"));
final int threads = Math.max(0, ModelPropertiesDialog.parseIntField(ModelPropertiesDialog.this.textField_2, "Threads"));
final double temperature = ModelPropertiesDialog.clampDouble(ModelPropertiesDialog.parseDoubleField(ModelPropertiesDialog.this.txtTemperature, "Temperature"),
Model.MIN_TEMPERATURE, Model.MAX_TEMPERATURE);
final double topP = ModelPropertiesDialog.clampDouble(ModelPropertiesDialog.parseDoubleField(ModelPropertiesDialog.this.txtTopP, "Top P"), Model.MIN_TOP_P,
Model.MAX_TOP_P);
final int topK = ModelPropertiesDialog.clampInt(ModelPropertiesDialog.parseIntField(ModelPropertiesDialog.this.txtTopK, "Top K"), Model.MIN_TOP_K,
Model.MAX_TOP_K);
final double minP = ModelPropertiesDialog.clampDouble(ModelPropertiesDialog.parseDoubleField(ModelPropertiesDialog.this.txtMinP, "Min P"), Model.MIN_MIN_P,
Model.MAX_MIN_P);
ModelPropertiesDialog.this.setVisible(false);
modelConsumer.accept(
new Model(modelPath, contextSize, maxContextSize, gpuLayers, threads, temperature, topP, topK, minP, ModelPropertiesDialog.this.txtPrompttemplate.getText(),
modelToEdit.ctime()));
ModelPropertiesDialog.this.dispose();
}
catch (final NumberFormatException nfe) {
JOptionPane.showMessageDialog(ModelPropertiesDialog.this, nfe.getMessage(), "Invalid model property", JOptionPane.WARNING_MESSAGE);
}
}
});
okButton.setActionCommand("OK");
@@ -145,16 +208,44 @@ public class ModelPropertiesDialog extends JDialog {
contentPanel.add(this.txtPrompttemplate);
this.txtPrompttemplate.setColumns(10);
}
if (editModel != null) {
this.textField.setText(String.valueOf(editModel.maxContextSize()));
this.textField_1.setText(String.valueOf(editModel.gpuLayers()));
this.textField_2.setText(String.valueOf(editModel.threads()));
this.textField_3.setText(String.valueOf(editModel.contextSize()));
this.txtPrompttemplate.setText(editModel.promptTemplate());
}
this.textField.setText(String.valueOf(modelToEdit.maxContextSize()));
this.textField_1.setText(String.valueOf(modelToEdit.gpuLayers()));
this.textField_2.setText(String.valueOf(modelToEdit.threads()));
this.textField_3.setText(String.valueOf(modelToEdit.contextSize()));
this.txtTemperature.setText(modelToEdit.temperatureDisplay());
this.txtTopP.setText(modelToEdit.topPDisplay());
this.txtTopK.setText(modelToEdit.topKDisplay());
this.txtMinP.setText(modelToEdit.minPDisplay());
this.txtPrompttemplate.setText(modelToEdit.promptTemplate());
// Apply font scaling to all components
UIScaler.scaleComponentFonts(this);
}
private static int parseIntField(final JTextField field, final String fieldName) {
try {
return Integer.parseInt(field.getText().trim());
}
catch (final NumberFormatException nfe) {
throw new NumberFormatException(fieldName + " must be a whole number.");
}
}
private static double parseDoubleField(final JTextField field, final String fieldName) {
try {
return Double.parseDouble(field.getText().trim());
}
catch (final NumberFormatException nfe) {
throw new NumberFormatException(fieldName + " must be a decimal number.");
}
}
private static int clampInt(final int value, final int minValue, final int maxValue) {
return Math.max(minValue, Math.min(maxValue, value));
}
private static double clampDouble(final double value, final double minValue, final double maxValue) {
return Math.max(minValue, Math.min(maxValue, value));
}
}