some ui fixes

This commit is contained in:
Radek Davidek 2026-04-14 16:53:28 +02:00
parent 1f297a41a7
commit 01e04008fd
5 changed files with 238 additions and 101 deletions

View File

@ -1,6 +1,6 @@
#Llama Runner Configuration #Llama Runner Configuration
#Thu Apr 02 10:20:00 CEST 2026 #Tue Apr 14 16:53:17 CEST 2026
windowHeight=1189 windowHeight=1189
windowWidth=711 windowWidth=684
windowX=1849 windowX=1876
windowY=211 windowY=210

View File

@ -1,45 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>cz.kamma</groupId> <groupId>cz.kamma</groupId>
<artifactId>llama-runner</artifactId> <artifactId>llama-runner</artifactId>
<name>Llama Runner</name> <name>Llama Runner</name>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<description>GUI application for running llama-server with customizable parameters</description> <description>GUI application for running llama-server with customizable parameters</description>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version> <version>3.11.0</version>
<configuration> <configuration>
<source>11</source> <source>11</source>
<target>11</target> <target>11</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.5.0</version> <version>3.5.0</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <phase>package</phase>
<goals> <goals>
<goal>shade</goal> <goal>shade</goal>
</goals> </goals>
<configuration> <configuration>
<transformers> <transformers>
<transformer> <transformer>
<mainClass>cz.kamma.llamarunner.Main</mainClass> <mainClass>cz.kamma.llamarunner.Main</mainClass>
</transformer> </transformer>
</transformers> </transformers>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<properties> <properties>
<maven.compiler.target>11</maven.compiler.target> <maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source> <maven.compiler.source>11</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
</project> </project>

View File

@ -29,8 +29,8 @@
"threads": 99, "threads": 99,
"flashAttention": true, "flashAttention": true,
"kvUnified": true, "kvUnified": true,
"cacheTypeK": "turbo3", "cacheTypeK": "q8_0",
"cacheTypeV": "turbo3", "cacheTypeV": "q8_0",
"temperature": 0.6, "temperature": 0.6,
"topP": 0.95, "topP": 0.95,
"topK": 20, "topK": 20,
@ -40,7 +40,7 @@
"modelPath": "/home/kamma/models/Qwen3.5-35B-A3B-Q6_K.gguf", "modelPath": "/home/kamma/models/Qwen3.5-35B-A3B-Q6_K.gguf",
"chatTemplateKwargs": "{\"enable_thinking\": false}", "chatTemplateKwargs": "{\"enable_thinking\": false}",
"ngl": 999, "ngl": 999,
"fit": false, "fit": true,
"reasoning": false "reasoning": false
}, },
"QwenCoderNext-160k": { "QwenCoderNext-160k": {
@ -249,8 +249,8 @@
"threads": 99, "threads": 99,
"flashAttention": true, "flashAttention": true,
"kvUnified": true, "kvUnified": true,
"cacheTypeK": "turbo4", "cacheTypeK": "q8_0",
"cacheTypeV": "turbo4", "cacheTypeV": "q8_0",
"temperature": 0.6, "temperature": 0.6,
"topP": 0.95, "topP": 0.95,
"topK": 20, "topK": 20,
@ -262,5 +262,93 @@
"ngl": 999, "ngl": 999,
"fit": true, "fit": true,
"reasoning": true "reasoning": true
},
"Qwen35-35B-A3B-Q6K-Q8-256k": {
"host": "0.0.0.0",
"port": 3080,
"parallel": 1,
"batchSize": 2048,
"threads": 99,
"flashAttention": true,
"kvUnified": true,
"cacheTypeK": "q8_0",
"cacheTypeV": "q8_0",
"temperature": 0.6,
"topP": 0.95,
"topK": 20,
"minP": 0.0,
"ctxSize": 256000,
"enableThinking": false,
"modelPath": "/home/kamma/models/Qwen3.5-35B-A3B-Q6_K.gguf",
"chatTemplateKwargs": "",
"ngl": 999,
"fit": true,
"reasoning": true
},
"gemma-4-31B-it-UD-Q6KXL-54k": {
"host": "0.0.0.0",
"port": 3080,
"parallel": 1,
"batchSize": 2048,
"threads": 99,
"flashAttention": true,
"kvUnified": true,
"cacheTypeK": "q8_0",
"cacheTypeV": "q8_0",
"temperature": 0.9,
"topP": 0.95,
"topK": 20,
"minP": 0.0,
"ctxSize": 54000,
"enableThinking": false,
"modelPath": "/home/kamma/models/gemma-4-31B-it-UD-Q6_K_XL.gguf",
"chatTemplateKwargs": "",
"ngl": 99,
"fit": false,
"reasoning": true
},
"gemma-4-26B-A4B-it-UD-Q8KXL-180k": {
"host": "0.0.0.0",
"port": 3080,
"parallel": 1,
"batchSize": 2048,
"threads": 99,
"flashAttention": true,
"kvUnified": true,
"cacheTypeK": "q8_0",
"cacheTypeV": "q8_0",
"temperature": 0.9,
"topP": 0.95,
"topK": 20,
"minP": 0.0,
"ctxSize": 180000,
"enableThinking": false,
"modelPath": "/home/kamma/models/gemma-4-26B-A4B-it-UD-Q8_K_XL.gguf",
"chatTemplateKwargs": "",
"ngl": 99,
"fit": false,
"reasoning": true
},
"gemma-4-31B-it-Q6K-112k": {
"host": "0.0.0.0",
"port": 3080,
"parallel": 1,
"batchSize": 2048,
"threads": 99,
"flashAttention": true,
"kvUnified": true,
"cacheTypeK": "q8_0",
"cacheTypeV": "q8_0",
"temperature": 0.8,
"topP": 0.95,
"topK": 20,
"minP": 0.0,
"ctxSize": 112000,
"enableThinking": false,
"modelPath": "/home/kamma/models/gemma-4-31B-it-Q6_K.gguf",
"chatTemplateKwargs": "{\"enable_thinking\": true}",
"ngl": 99,
"fit": false,
"reasoning": true
} }
} }

View File

@ -157,30 +157,46 @@ public class Main extends JFrame {
} }
private JPanel createModelSelectionPanel() { private JPanel createModelSelectionPanel() {
JPanel panel = new JPanel(new BorderLayout(5, 5)); JPanel panel = new JPanel(new GridBagLayout());
panel.setBorder(BorderFactory.createTitledBorder( panel.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createLineBorder(Color.WHITE), "Model")); BorderFactory.createLineBorder(Color.WHITE), "Model"));
// Model selection row GridBagConstraints gbc = new GridBagConstraints();
JPanel modelRow = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0)); gbc.insets = new Insets(5, 5, 5, 5);
modelRow.add(new JLabel("Model:")); gbc.anchor = GridBagConstraints.WEST;
// Label
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 0;
gbc.fill = GridBagConstraints.NONE;
panel.add(new JLabel("Model:"), gbc);
// ComboBox
gbc.gridx = 1;
gbc.weightx = 1.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
modelComboBox = new JComboBox<>(); modelComboBox = new JComboBox<>();
modelComboBox.setEditable(true); modelComboBox.setEditable(true);
modelComboBox.setPreferredSize(new Dimension(300, 25)); modelComboBox.setMinimumSize(new Dimension(200, 25));
modelComboBox.setPreferredSize(new Dimension(500, 25));
modelComboBox.addActionListener(e -> updateCommandPreview()); modelComboBox.addActionListener(e -> updateCommandPreview());
modelRow.add(modelComboBox); panel.add(modelComboBox, gbc);
// Buttons panel
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0));
browseModelsButton = new JButton("Browse"); browseModelsButton = new JButton("Browse");
browseModelsButton.addActionListener(e -> browseModelsDir()); browseModelsButton.addActionListener(e -> browseModelsDir());
modelRow.add(browseModelsButton); buttonPanel.add(browseModelsButton);
refreshModelsButton = new JButton("Refresh"); refreshModelsButton = new JButton("Refresh");
refreshModelsButton.addActionListener(e -> loadModels()); refreshModelsButton.addActionListener(e -> loadModels());
modelRow.add(refreshModelsButton); buttonPanel.add(refreshModelsButton);
JScrollPane scrollPane = new JScrollPane(modelRow); gbc.gridx = 2;
scrollPane.setBorder(null); gbc.weightx = 0;
panel.add(scrollPane, BorderLayout.CENTER); gbc.fill = GridBagConstraints.NONE;
panel.add(buttonPanel, gbc);
return panel; return panel;
} }
@ -204,35 +220,35 @@ public class Main extends JFrame {
gbc.fill = GridBagConstraints.HORIZONTAL; gbc.fill = GridBagConstraints.HORIZONTAL;
profileComboBox = new JComboBox<>(); profileComboBox = new JComboBox<>();
profileComboBox.setEditable(true); profileComboBox.setEditable(true);
profileComboBox.setMaximumSize(new Dimension(200, 25)); profileComboBox.setMinimumSize(new Dimension(200, 25));
profileComboBox.setPreferredSize(new Dimension(150, 25)); profileComboBox.setPreferredSize(new Dimension(500, 25));
profileChangeListener = e -> profileComboBoxChanged(); profileChangeListener = e -> profileComboBoxChanged();
profileComboBox.addActionListener(profileChangeListener); profileComboBox.addActionListener(profileChangeListener);
panel.add(profileComboBox, gbc); panel.add(profileComboBox, gbc);
// Buttons container
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0));
saveProfileButton = new JButton("Save");
saveProfileButton.addActionListener(e -> showSaveProfileDialog());
buttonPanel.add(saveProfileButton);
JButton saveAsButton = new JButton("Save As...");
saveAsButton.addActionListener(e -> showSaveAsProfileDialog());
buttonPanel.add(saveAsButton);
deleteProfileButton = new JButton("Delete");
deleteProfileButton.addActionListener(e -> showDeleteProfileDialog());
buttonPanel.add(deleteProfileButton);
JButton renameButton = new JButton("Rename");
renameButton.addActionListener(e -> showRenameProfileDialog());
buttonPanel.add(renameButton);
gbc.gridx = 2; gbc.gridx = 2;
gbc.weightx = 0; gbc.weightx = 0;
gbc.fill = GridBagConstraints.NONE; gbc.fill = GridBagConstraints.NONE;
panel.add(buttonPanel, gbc);
gbc.gridx = 3;
saveProfileButton = new JButton("Save");
saveProfileButton.addActionListener(e -> showSaveProfileDialog());
panel.add(saveProfileButton, gbc);
gbc.gridx = 4;
JButton saveAsButton = new JButton("Save As...");
saveAsButton.addActionListener(e -> showSaveAsProfileDialog());
panel.add(saveAsButton, gbc);
gbc.gridx = 5;
deleteProfileButton = new JButton("Delete");
deleteProfileButton.addActionListener(e -> showDeleteProfileDialog());
panel.add(deleteProfileButton, gbc);
gbc.gridx = 6;
JButton renameButton = new JButton("Rename");
renameButton.addActionListener(e -> showRenameProfileDialog());
panel.add(renameButton, gbc);
return panel; return panel;
} }
@ -247,16 +263,26 @@ public class Main extends JFrame {
File[] modelFiles = modelsDir.listFiles((dir, name) -> name.endsWith(".gguf")); File[] modelFiles = modelsDir.listFiles((dir, name) -> name.endsWith(".gguf"));
if (modelFiles != null) { if (modelFiles != null) {
List<File> sortedFiles = new ArrayList<>(Arrays.asList(modelFiles)); List<File> sortedFiles = new ArrayList<>(Arrays.asList(modelFiles));
sortedFiles.sort((a, b) -> a.getName().compareTo(b.getName())); sortedFiles.sort((a, b) -> a.getName().compareToIgnoreCase(b.getName()));
for (File file : sortedFiles) { for (File file : sortedFiles) {
modelComboBox.addItem(file.getName()); double sizeInGb = (double) file.length() / (1024 * 1024 * 1024);
String displayName = String.format("%s (%.2f GB)", file.getName(), sizeInGb);
modelComboBox.addItem(displayName);
} }
} }
} }
if (selectedItem != null && modelComboBox.getItemCount() > 0 if (selectedItem != null && modelComboBox.getItemCount() > 0) {
&& !modelComboBox.getItemAt(0).equals(selectedItem)) { boolean found = false;
modelComboBox.addItem(selectedItem); for (int i = 0; i < modelComboBox.getItemCount(); i++) {
if (modelComboBox.getItemAt(i).equals(selectedItem)) {
found = true;
break;
}
}
if (!found) {
modelComboBox.addItem(selectedItem);
}
} }
} }
@ -356,8 +382,16 @@ public class Main extends JFrame {
config.setTopK(Integer.parseInt(topKField.getText())); config.setTopK(Integer.parseInt(topKField.getText()));
config.setMinP(Double.parseDouble(minPField.getText())); config.setMinP(Double.parseDouble(minPField.getText()));
config.setCtxSize(Integer.parseInt(ctxSizeField.getText())); config.setCtxSize(Integer.parseInt(ctxSizeField.getText()));
String modelName = (String) modelComboBox.getSelectedItem(); String displayName = (String) modelComboBox.getSelectedItem();
config.setModelPath(modelName != null ? new File(modelsDirPath, modelName).getAbsolutePath() : ""); String fileName = "";
if (displayName != null) {
if (displayName.contains(" (") && displayName.endsWith(" GB)")) {
fileName = displayName.substring(0, displayName.lastIndexOf(" ("));
} else {
fileName = displayName;
}
}
config.setModelPath(fileName != null && !fileName.isEmpty() ? new File(modelsDirPath, fileName).getAbsolutePath() : "");
config.setChatTemplateKwargs(kwargsField.getText()); config.setChatTemplateKwargs(kwargsField.getText());
config.setNgl(Integer.parseInt(nglField.getText())); config.setNgl(Integer.parseInt(nglField.getText()));
return config; return config;
@ -391,6 +425,13 @@ public class Main extends JFrame {
String modelName = config.getModelPath() != null String modelName = config.getModelPath() != null
? new File(config.getModelPath()).getName() ? new File(config.getModelPath()).getName()
: ""; : "";
if (!modelName.isEmpty()) {
File modelFile = new File(modelsDirPath, modelName);
if (modelFile.exists()) {
double sizeInGb = (double) modelFile.length() / (1024 * 1024 * 1024);
modelName = String.format("%s (%.2f GB)", modelName, sizeInGb);
}
}
modelComboBox.setSelectedItem(modelName); modelComboBox.setSelectedItem(modelName);
kwargsField.setText(config.getChatTemplateKwargs()); kwargsField.setText(config.getChatTemplateKwargs());
@ -411,8 +452,21 @@ public class Main extends JFrame {
} }
private void showSaveAsProfileDialog() { private void showSaveAsProfileDialog() {
String modelName = (String) modelComboBox.getSelectedItem(); String currentProfile = (String) profileComboBox.getSelectedItem();
String defaultName = modelName != null ? modelName : ""; String defaultName = (currentProfile != null && !currentProfile.isEmpty()) ? currentProfile : "";
if (defaultName.isEmpty()) {
String displayName = (String) modelComboBox.getSelectedItem();
String modelName = "";
if (displayName != null) {
if (displayName.contains(" (") && displayName.endsWith(" GB)")) {
modelName = displayName.substring(0, displayName.lastIndexOf(" ("));
} else {
modelName = displayName;
}
}
defaultName = modelName != null ? modelName : "";
}
String newName = InputDialog.showInputDialog(this, "Save profile as...", "Profile name:", defaultName); String newName = InputDialog.showInputDialog(this, "Save profile as...", "Profile name:", defaultName);

View File

@ -8,7 +8,6 @@ import java.util.regex.Pattern;
public class ProfileValidator { public class ProfileValidator {
private static final int MAX_NAME_LENGTH = 100; private static final int MAX_NAME_LENGTH = 100;
private static final Pattern VALID_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_-]+$");
/** /**
* Validates a profile name. * Validates a profile name.
@ -27,10 +26,6 @@ public class ProfileValidator {
return "Profile name must be at most " + MAX_NAME_LENGTH + " characters"; return "Profile name must be at most " + MAX_NAME_LENGTH + " characters";
} }
if (!VALID_NAME_PATTERN.matcher(trimmedName).matches()) {
return "Profile name can only contain letters, numbers, underscores, and hyphens";
}
return null; return null;
} }