claude refactor
This commit is contained in:
parent
1e6b33175a
commit
d1358cc6d3
@ -1,5 +1,5 @@
|
||||
#Llama Runner Configuration
|
||||
#Sun Mar 29 17:31:07 CEST 2026
|
||||
#Sun Mar 29 17:54:05 CEST 2026
|
||||
windowHeight=1189
|
||||
windowWidth=711
|
||||
windowX=1849
|
||||
|
||||
@ -10,114 +10,75 @@ import com.google.gson.JsonSyntaxException;
|
||||
*/
|
||||
public class ConfigValidation {
|
||||
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
/**
|
||||
* Validates numeric fields for valid ranges.
|
||||
* Validates a ModelConfig object.
|
||||
*
|
||||
* @return a list of error messages (empty if valid)
|
||||
* @param config the model config to validate
|
||||
* @return error message if invalid, null if valid
|
||||
*/
|
||||
public static java.util.List<String> validateNumericInputs(
|
||||
String host,
|
||||
int port,
|
||||
int parallel,
|
||||
int threads,
|
||||
double temperature,
|
||||
double topP,
|
||||
int topK,
|
||||
double minP,
|
||||
int ctxSize,
|
||||
int ngl) {
|
||||
|
||||
java.util.List<String> errors = new java.util.ArrayList<>();
|
||||
|
||||
public static String validateConfig(ModelConfig config) {
|
||||
// Validate port
|
||||
if (port < 1 || port > 65535) {
|
||||
errors.add("Port must be between 1 and 65535");
|
||||
if (config.getPort() < 1 || config.getPort() > 65535) {
|
||||
return "Port must be between 1 and 65535";
|
||||
}
|
||||
|
||||
// Validate parallel
|
||||
if (parallel < 1) {
|
||||
errors.add("Parallel must be at least 1");
|
||||
if (config.getParallel() < 1) {
|
||||
return "Parallel must be at least 1";
|
||||
}
|
||||
|
||||
// Validate threads
|
||||
if (threads < 1) {
|
||||
errors.add("Threads must be at least 1");
|
||||
if (config.getThreads() < 1) {
|
||||
return "Threads must be at least 1";
|
||||
}
|
||||
|
||||
// Validate temperature
|
||||
if (temperature < 0) {
|
||||
errors.add("Temperature must be non-negative");
|
||||
if (config.getTemperature() < 0) {
|
||||
return "Temperature must be non-negative";
|
||||
}
|
||||
|
||||
// Validate topP
|
||||
if (topP < 0 || topP > 1) {
|
||||
errors.add("Top P must be between 0 and 1");
|
||||
if (config.getTopP() < 0 || config.getTopP() > 1) {
|
||||
return "Top P must be between 0 and 1";
|
||||
}
|
||||
|
||||
// Validate topK
|
||||
if (topK < 0) {
|
||||
errors.add("Top K must be non-negative");
|
||||
if (config.getTopK() < 0) {
|
||||
return "Top K must be non-negative";
|
||||
}
|
||||
|
||||
// Validate minP
|
||||
if (minP < 0 || minP > 1) {
|
||||
errors.add("Min P must be between 0 and 1");
|
||||
if (config.getMinP() < 0 || config.getMinP() > 1) {
|
||||
return "Min P must be between 0 and 1";
|
||||
}
|
||||
|
||||
// Validate ctxSize
|
||||
if (ctxSize < 0) {
|
||||
errors.add("Context size must be non-negative");
|
||||
if (config.getCtxSize() < 0) {
|
||||
return "Context size must be non-negative";
|
||||
}
|
||||
|
||||
// Validate ngl
|
||||
if (ngl < 0) {
|
||||
errors.add("GPU layers must be non-negative");
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates JSON string for chatTemplateKwargs.
|
||||
*
|
||||
* @param json the JSON string to validate
|
||||
* @return error message if invalid, null if valid
|
||||
*/
|
||||
public static String validateJsonKwargs(String json) {
|
||||
if (json == null || json.trim().isEmpty()) {
|
||||
return null;
|
||||
// Validate model path
|
||||
if (config.getModelPath() == null || config.getModelPath().trim().isEmpty()) {
|
||||
return "Model path cannot be empty";
|
||||
}
|
||||
File modelFile = new File(config.getModelPath());
|
||||
if (!modelFile.exists()) {
|
||||
return "Model file does not exist: " + config.getModelPath();
|
||||
}
|
||||
if (!modelFile.isFile()) {
|
||||
return "Model path is not a file: " + config.getModelPath();
|
||||
}
|
||||
|
||||
// Validate JSON kwargs
|
||||
String json = config.getChatTemplateKwargs();
|
||||
if (json != null && !json.trim().isEmpty()) {
|
||||
try {
|
||||
GSON.fromJson(json, Object.class);
|
||||
return null;
|
||||
new Gson().fromJson(json, Object.class);
|
||||
} catch (JsonSyntaxException e) {
|
||||
return "Invalid JSON format in kwargs";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates model path exists.
|
||||
*
|
||||
* @param modelPath the model file path
|
||||
* @return error message if file doesn't exist, null if valid
|
||||
*/
|
||||
public static String validateModelPath(String modelPath) {
|
||||
if (modelPath == null || modelPath.trim().isEmpty()) {
|
||||
return "Model path cannot be empty";
|
||||
}
|
||||
|
||||
File modelFile = new File(modelPath);
|
||||
if (!modelFile.exists()) {
|
||||
return "Model file does not exist: " + modelPath;
|
||||
}
|
||||
|
||||
if (!modelFile.isFile()) {
|
||||
return "Model path is not a file: " + modelPath;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
95
src/main/java/cz/kamma/llamarunner/InputDialog.java
Normal file
95
src/main/java/cz/kamma/llamarunner/InputDialog.java
Normal file
@ -0,0 +1,95 @@
|
||||
package cz.kamma.llamarunner;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
|
||||
/**
|
||||
* Reusable dialog for simple text input.
|
||||
*/
|
||||
public class InputDialog {
|
||||
|
||||
private final JDialog dialog;
|
||||
private final String[] result = new String[1];
|
||||
|
||||
/**
|
||||
* Shows a dialog for text input.
|
||||
*
|
||||
* @param parent the parent frame
|
||||
* @param title the dialog title
|
||||
* @param label the label text
|
||||
* @param defaultValue the default value
|
||||
* @return the input text, or null if cancelled
|
||||
*/
|
||||
public static String showInputDialog(java.awt.Frame parent, String title, String label, String defaultValue) {
|
||||
InputDialog dialog = new InputDialog(parent, title, label, defaultValue);
|
||||
dialog.show();
|
||||
return dialog.getResult();
|
||||
}
|
||||
|
||||
private InputDialog(java.awt.Frame parent, String title, String label, String defaultValue) {
|
||||
dialog = new JDialog(parent, title, true);
|
||||
dialog.setResizable(false);
|
||||
|
||||
// Input panel
|
||||
JPanel inputPanel = new JPanel(new GridBagLayout());
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(4, 4, 4, 4);
|
||||
gbc.anchor = GridBagConstraints.WEST;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
inputPanel.add(new JLabel(label), gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
JTextField textField = new JTextField(20);
|
||||
textField.setText(defaultValue);
|
||||
textField.setCaretColor(Color.WHITE);
|
||||
inputPanel.add(textField, gbc);
|
||||
|
||||
dialog.add(inputPanel, BorderLayout.CENTER);
|
||||
|
||||
// Button panel
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 8, 0));
|
||||
JButton okButton = new JButton("OK");
|
||||
okButton.addActionListener(e -> {
|
||||
result[0] = textField.getText().trim();
|
||||
dialog.dispose();
|
||||
});
|
||||
buttonPanel.add(okButton);
|
||||
|
||||
JButton cancelButton = new JButton("Cancel");
|
||||
cancelButton.addActionListener(e -> {
|
||||
result[0] = null;
|
||||
dialog.dispose();
|
||||
});
|
||||
buttonPanel.add(cancelButton);
|
||||
|
||||
dialog.add(buttonPanel, BorderLayout.SOUTH);
|
||||
|
||||
// Center dialog
|
||||
dialog.setSize(350, 100);
|
||||
dialog.setLocationRelativeTo(parent);
|
||||
}
|
||||
|
||||
private void show() {
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
|
||||
private String getResult() {
|
||||
if (result[0] == null) {
|
||||
return null;
|
||||
}
|
||||
return result[0].isEmpty() ? null : result[0];
|
||||
}
|
||||
}
|
||||
@ -22,7 +22,6 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
@ -313,16 +312,27 @@ public class Main extends JFrame {
|
||||
}
|
||||
|
||||
try {
|
||||
String validationError = validateCurrentConfig();
|
||||
if (validationError != null) {
|
||||
JOptionPane.showMessageDialog(this, validationError, "Validation Error",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
saveProfileToName(currentProfile);
|
||||
JOptionPane.showMessageDialog(this, "Profile saved!");
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
JOptionPane.showMessageDialog(this, "Error saving: " + e.getMessage() + "\n" + e.toString(), "Error",
|
||||
JOptionPane.showMessageDialog(this, "Error saving: " + e.getMessage(), "Error",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private String validateCurrentConfig() {
|
||||
ModelConfig config = buildModelConfig();
|
||||
return ConfigValidation.validateConfig(config);
|
||||
}
|
||||
|
||||
private void saveProfileToName(String name) throws IOException {
|
||||
ModelConfig config = buildModelConfig();
|
||||
profileManager.saveProfile(name, config);
|
||||
@ -404,58 +414,9 @@ public class Main extends JFrame {
|
||||
String modelName = (String) modelComboBox.getSelectedItem();
|
||||
String defaultName = modelName != null ? modelName : "";
|
||||
|
||||
String[] result = new String[1];
|
||||
String newName = InputDialog.showInputDialog(this, "Save profile as...", "Profile name:", defaultName);
|
||||
|
||||
JDialog dialog = new JDialog(this, "Save profile as...", true);
|
||||
dialog.setLayout(new BorderLayout(8, 8));
|
||||
dialog.setResizable(false);
|
||||
|
||||
// Input panel
|
||||
JPanel inputPanel = new JPanel(new GridBagLayout());
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(4, 4, 4, 4);
|
||||
gbc.anchor = GridBagConstraints.WEST;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
inputPanel.add(new JLabel("Profile name:"), gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
JTextField nameField = new JTextField(20);
|
||||
nameField.setText(defaultName);
|
||||
nameField.setCaretColor(Color.WHITE);
|
||||
inputPanel.add(nameField, gbc);
|
||||
|
||||
dialog.add(inputPanel, BorderLayout.CENTER);
|
||||
|
||||
// Button panel
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 8, 0));
|
||||
JButton okButton = new JButton("OK");
|
||||
okButton.addActionListener(e -> {
|
||||
result[0] = nameField.getText().trim();
|
||||
dialog.dispose();
|
||||
});
|
||||
buttonPanel.add(okButton);
|
||||
|
||||
JButton cancelButton = new JButton("Cancel");
|
||||
cancelButton.addActionListener(e -> {
|
||||
result[0] = null;
|
||||
dialog.dispose();
|
||||
});
|
||||
buttonPanel.add(cancelButton);
|
||||
|
||||
dialog.add(buttonPanel, BorderLayout.SOUTH);
|
||||
|
||||
// Center dialog
|
||||
dialog.setSize(350, 100);
|
||||
dialog.setLocationRelativeTo(this);
|
||||
dialog.setVisible(true);
|
||||
|
||||
String newName = result[0];
|
||||
|
||||
if (newName == null || newName.isEmpty()) {
|
||||
if (newName == null) {
|
||||
return; // Cancelled by user
|
||||
}
|
||||
|
||||
@ -531,58 +492,9 @@ public class Main extends JFrame {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] result = new String[1];
|
||||
String newName = InputDialog.showInputDialog(this, "Rename profile", "New profile name:", oldName);
|
||||
|
||||
JDialog dialog = new JDialog(this, "Rename profile", true);
|
||||
dialog.setLayout(new BorderLayout(8, 8));
|
||||
dialog.setResizable(false);
|
||||
|
||||
// Input panel
|
||||
JPanel inputPanel = new JPanel(new GridBagLayout());
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(4, 4, 4, 4);
|
||||
gbc.anchor = GridBagConstraints.WEST;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
inputPanel.add(new JLabel("New profile name:"), gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
JTextField nameField = new JTextField(20);
|
||||
nameField.setText(oldName);
|
||||
nameField.setCaretColor(Color.WHITE);
|
||||
inputPanel.add(nameField, gbc);
|
||||
|
||||
dialog.add(inputPanel, BorderLayout.CENTER);
|
||||
|
||||
// Button panel
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 8, 0));
|
||||
JButton okButton = new JButton("OK");
|
||||
okButton.addActionListener(e -> {
|
||||
result[0] = nameField.getText().trim();
|
||||
dialog.dispose();
|
||||
});
|
||||
buttonPanel.add(okButton);
|
||||
|
||||
JButton cancelButton = new JButton("Cancel");
|
||||
cancelButton.addActionListener(e -> {
|
||||
result[0] = null;
|
||||
dialog.dispose();
|
||||
});
|
||||
buttonPanel.add(cancelButton);
|
||||
|
||||
dialog.add(buttonPanel, BorderLayout.SOUTH);
|
||||
|
||||
// Center dialog
|
||||
dialog.setSize(350, 100);
|
||||
dialog.setLocationRelativeTo(this);
|
||||
dialog.setVisible(true);
|
||||
|
||||
String newName = result[0];
|
||||
|
||||
if (newName == null || newName.isEmpty()) {
|
||||
if (newName == null) {
|
||||
return; // Cancelled by user
|
||||
}
|
||||
|
||||
@ -895,15 +807,7 @@ public class Main extends JFrame {
|
||||
ctxSizeField = new JTextField("180000", 10);
|
||||
ctxSizeField.setCaretColor(Color.WHITE);
|
||||
ctxSizeField.addActionListener(e -> updateCommandPreview());
|
||||
// Update preview on any change, not just Enter
|
||||
ctxSizeField.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(javax.swing.event.DocumentEvent e) { updateCommandPreview(); }
|
||||
@Override
|
||||
public void removeUpdate(javax.swing.event.DocumentEvent e) { updateCommandPreview(); }
|
||||
@Override
|
||||
public void changedUpdate(javax.swing.event.DocumentEvent e) { updateCommandPreview(); }
|
||||
});
|
||||
ctxSizeField.getDocument().addDocumentListener(new PreviewUpdateListener(this::updateCommandPreview));
|
||||
panel.add(ctxSizeField, gbc);
|
||||
|
||||
gbc.gridx = 0;
|
||||
@ -916,22 +820,7 @@ public class Main extends JFrame {
|
||||
gbc.weightx = 1.0;
|
||||
kwargsField = new JTextField("{\"enable_thinking\": true}");
|
||||
kwargsField.setCaretColor(Color.WHITE);
|
||||
kwargsField.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(javax.swing.event.DocumentEvent e) {
|
||||
updateCommandPreview();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(javax.swing.event.DocumentEvent e) {
|
||||
updateCommandPreview();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(javax.swing.event.DocumentEvent e) {
|
||||
updateCommandPreview();
|
||||
}
|
||||
});
|
||||
kwargsField.getDocument().addDocumentListener(new PreviewUpdateListener(this::updateCommandPreview));
|
||||
panel.add(kwargsField, gbc);
|
||||
|
||||
return panel;
|
||||
@ -987,22 +876,7 @@ public class Main extends JFrame {
|
||||
}
|
||||
|
||||
private javax.swing.event.DocumentListener createNglDocumentListener() {
|
||||
return new javax.swing.event.DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(javax.swing.event.DocumentEvent e) {
|
||||
updateCommandPreview();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(javax.swing.event.DocumentEvent e) {
|
||||
updateCommandPreview();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(javax.swing.event.DocumentEvent e) {
|
||||
updateCommandPreview();
|
||||
}
|
||||
};
|
||||
return new PreviewUpdateListener(this::updateCommandPreview);
|
||||
}
|
||||
|
||||
private void copyCommandToClipboard() {
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
package cz.kamma.llamarunner;
|
||||
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
/**
|
||||
* DocumentListener that updates command preview on text changes.
|
||||
*/
|
||||
public class PreviewUpdateListener implements DocumentListener {
|
||||
|
||||
private final Runnable updateCallback;
|
||||
|
||||
public PreviewUpdateListener(Runnable updateCallback) {
|
||||
this.updateCallback = updateCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
updateCallback.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
updateCallback.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
updateCallback.run();
|
||||
}
|
||||
}
|
||||
@ -34,18 +34,4 @@ public class ProfileValidator {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a profile name is unique among existing profiles.
|
||||
*
|
||||
* @param newName the new profile name
|
||||
* @param existingProfiles list of existing profile names
|
||||
* @return true if unique, false otherwise
|
||||
*/
|
||||
public static boolean isProfileNameUnique(String newName, java.util.List<String> existingProfiles) {
|
||||
if (existingProfiles == null) {
|
||||
return true;
|
||||
}
|
||||
return !existingProfiles.stream()
|
||||
.anyMatch(name -> name.equalsIgnoreCase(newName));
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user