diff --git a/apps.json b/apps.json index 2ca1a4c..7e32437 100644 --- a/apps.json +++ b/apps.json @@ -1 +1 @@ -{"apps":[{"path":"file:///home/sb/lm/koboldcpp-rocm/koboldcpp.py","interpreter":"file:///usr/bin/python","arguments":[],"appArguments":[{"argument":"--usecublas","optional":true,"default_":true},{"argument":"mmq","optional":true,"default_":false},{"argument":"lowvram","optional":true,"default_":false},{"argument":"0","optional":true,"default_":true},{"argument":"--gpulayers","optional":true,"default_":true},{"argument":"GPU_LAYERS","optional":true,"default_":true},{"argument":"--smartcontext","optional":true,"default_":false},{"argument":"--noshift","optional":true,"default_":false},{"argument":"--contextsize","optional":false,"default_":true},{"argument":"CONTEXT_SIZE","optional":false,"default_":true},{"argument":"--launch","optional":false,"default_":true},{"argument":"--host","optional":true,"default_":false},{"argument":"0.0.0.0","optional":true,"default_":false},{"argument":"--threads","optional":false,"default_":true},{"argument":"THREADS","optional":false,"default_":true},{"argument":"--model","optional":false,"default_":true},{"argument":"MODEL","optional":false,"default_":true}]},{"path":"file:///home/sb/lm/llama.cpp/llama-server","interpreter":null,"arguments":[],"appArguments":[{"argument":"-nkvo","optional":true,"default_":false},{"argument":"--parallel","optional":true,"default_":false},{"argument":"2","optional":true,"default_":false},{"argument":"4","optional":true,"default_":false},{"argument":"--host","optional":true,"default_":true},{"argument":"0.0.0.0","optional":true,"default_":true},{"argument":"--port","optional":false,"default_":true},{"argument":"5001","optional":false,"default_":true},{"argument":"-t","optional":false,"default_":true},{"argument":"THREADS","optional":false,"default_":true},{"argument":"-ngl","optional":false,"default_":true},{"argument":"GPU_LAYERS","optional":false,"default_":true},{"argument":"--ctx-size","optional":false,"default_":true},{"argument":"CONTEXT_SIZE","optional":false,"default_":true},{"argument":"-m","optional":false,"default_":true},{"argument":"MODEL","optional":false,"default_":true}]},{"path":"file:///home/sb/lm/SillyTavern/start.sh","interpreter":null,"arguments":[],"appArguments":[]},{"path":"file:///home/sb/lm/ollama-oai-bridge/start.sh","interpreter":null,"arguments":[],"appArguments":[]}]} \ No newline at end of file +{"apps":[{"path":"file:///home/sb/lm/koboldcpp-rocm/koboldcpp.py","interpreter":"file:///usr/bin/python","arguments":[],"appArguments":[{"argument":"--usecublas","optional":true,"default_":true},{"argument":"mmq","optional":true,"default_":false},{"argument":"lowvram","optional":true,"default_":false},{"argument":"0","optional":true,"default_":true},{"argument":"--gpulayers","optional":true,"default_":true},{"argument":"GPU_LAYERS","optional":true,"default_":true},{"argument":"--smartcontext","optional":true,"default_":false},{"argument":"--noshift","optional":true,"default_":false},{"argument":"--contextsize","optional":false,"default_":true},{"argument":"CONTEXT_SIZE","optional":false,"default_":true},{"argument":"--launch","optional":false,"default_":true},{"argument":"--host","optional":true,"default_":false},{"argument":"0.0.0.0","optional":true,"default_":false},{"argument":"--threads","optional":false,"default_":true},{"argument":"THREADS","optional":false,"default_":true},{"argument":"--model","optional":false,"default_":true},{"argument":"MODEL","optional":false,"default_":true}],"modelSettings":[]},{"path":"file:///home/sb/lm/llama.cpp/llama-server","interpreter":null,"arguments":[],"appArguments":[{"argument":"-nkvo","optional":true,"default_":false},{"argument":"--parallel","optional":true,"default_":false},{"argument":"2","optional":true,"default_":false},{"argument":"4","optional":true,"default_":false},{"argument":"--host","optional":true,"default_":true},{"argument":"0.0.0.0","optional":true,"default_":true},{"argument":"--port","optional":false,"default_":true},{"argument":"5001","optional":false,"default_":true},{"argument":"-t","optional":false,"default_":true},{"argument":"THREADS","optional":false,"default_":true},{"argument":"-ngl","optional":false,"default_":true},{"argument":"GPU_LAYERS","optional":false,"default_":true},{"argument":"--ctx-size","optional":false,"default_":true},{"argument":"CONTEXT_SIZE","optional":false,"default_":true},{"argument":"-m","optional":false,"default_":true},{"argument":"MODEL","optional":false,"default_":true}],"modelSettings":[{"modelPath":"/mnt/data/data/lm/models/gguf/m/Qwen3.6-35B-A3B-UD-Q5_K_XL-mtp.gguf","appArguments":[{"argument":"-nkvo","optional":true,"default_":false},{"argument":"--parallel","optional":true,"default_":true},{"argument":"2","optional":true,"default_":true},{"argument":"4","optional":true,"default_":false},{"argument":"--host","optional":true,"default_":true},{"argument":"0.0.0.0","optional":true,"default_":true},{"argument":"--port","optional":false,"default_":true},{"argument":"5001","optional":false,"default_":true},{"argument":"-t","optional":false,"default_":true},{"argument":"THREADS","optional":false,"default_":true},{"argument":"-ngl","optional":false,"default_":true},{"argument":"GPU_LAYERS","optional":false,"default_":true},{"argument":"--ctx-size","optional":false,"default_":true},{"argument":"CONTEXT_SIZE","optional":false,"default_":true},{"argument":"-m","optional":false,"default_":true},{"argument":"MODEL","optional":false,"default_":true}]}]},{"path":"file:///home/sb/lm/SillyTavern/start.sh","interpreter":null,"arguments":[],"appArguments":[],"modelSettings":[]},{"path":"file:///home/sb/lm/ollama-oai-bridge/start.sh","interpreter":null,"arguments":[],"appArguments":[],"modelSettings":[]}]} \ No newline at end of file diff --git a/lm-launcher.jar b/lm-launcher.jar index e30a2ab..f020a69 100644 Binary files a/lm-launcher.jar and b/lm-launcher.jar differ diff --git a/models.json b/models.json index 1554b04..a821160 100644 --- a/models.json +++ b/models.json @@ -1 +1 @@ -{"models":[{"path":"/home/sb/lm/models/Meta-Llama-3.1-8B-Instruct-Q8_0.gguf","contextSize":32768,"maxContextSize":32768,"gpuLayers":99,"threads":6,"promptTemplate":"llama-3"}]} +{"models":[{"path":"/mnt/data/data/lm/models/gguf/m/Qwen3.6-35B-A3B-UD-Q5_K_XL-mtp.gguf","contextSize":16000,"maxContextSize":128000,"gpuLayers":99,"threads":8,"temperature":0.8,"topP":0.95,"topK":40,"minP":0.05,"promptTemplate":"","ctime":1779609453785},{"path":"/home/sb/lm/models/Meta-Llama-3.1-8B-Instruct-Q8_0.gguf","contextSize":32768,"maxContextSize":32768,"gpuLayers":99,"threads":6,"temperature":0.8,"topP":0.95,"topK":40,"minP":0.05,"promptTemplate":"llama-3","ctime":0}]} \ No newline at end of file diff --git a/src/de/extio/lm_launcher/App.java b/src/de/extio/lm_launcher/App.java index 928d2c3..5f0584f 100644 --- a/src/de/extio/lm_launcher/App.java +++ b/src/de/extio/lm_launcher/App.java @@ -2,6 +2,8 @@ package de.extio.lm_launcher; import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -9,7 +11,13 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; -record App(Path path, Path interpreter, @Deprecated List arguments, List appArguments) { +record App(Path path, Path interpreter, @Deprecated List arguments, List appArguments, List modelSettings) { + + public App { + arguments = arguments == null ? new ArrayList<>() : arguments; + appArguments = appArguments == null ? new ArrayList<>() : appArguments; + modelSettings = modelSettings == null ? new ArrayList<>() : modelSettings; + } static Map> PLACEHOLDERS = Map.of( "MODEL", m -> m.path(), @@ -32,6 +40,31 @@ record App(Path path, Path interpreter, @Deprecated List arguments, List showAll); } + List argumentsForModel(final String modelPath) { + if (modelPath == null || modelPath.isBlank()) { + return copyArguments(this.appArguments); + } + final AppModelSettings savedSettings = this.findModelSettings(modelPath); + if (savedSettings == null) { + return copyArguments(this.appArguments); + } + return this.mergeArguments(savedSettings.appArguments()); + } + + void saveArgumentsForModel(final String modelPath, final List selectedArguments) { + if (modelPath == null || modelPath.isBlank()) { + return; + } + final AppModelSettings updatedSettings = new AppModelSettings(modelPath, copyArguments(selectedArguments)); + for (int i = 0; i < this.modelSettings.size(); i++) { + if (this.modelSettings.get(i).modelPath().equals(modelPath)) { + this.modelSettings.set(i, updatedSettings); + return; + } + } + this.modelSettings.add(updatedSettings); + } + private String argumentsToStringByArgs(final Stream args, boolean showAll) { return args .filter(arg -> showAll || (arg.optional() ? arg.default_() : true)) @@ -39,6 +72,50 @@ record App(Path path, Path interpreter, @Deprecated List arguments, List .collect(Collectors.joining(" ")); } + private List mergeArguments(final List savedArguments) { + if (savedArguments == null || savedArguments.isEmpty()) { + return copyArguments(this.appArguments); + } + final Map occurrences = new HashMap<>(); + final List mergedArguments = new ArrayList<>(this.appArguments.size()); + for (final AppArgument baseArgument : this.appArguments) { + final int occurrenceIndex = occurrences.getOrDefault(baseArgument.argument(), 0); + occurrences.put(baseArgument.argument(), occurrenceIndex + 1); + final AppArgument savedArgument = nthArgument(savedArguments, baseArgument.argument(), occurrenceIndex); + mergedArguments.add(savedArgument == null + ? new AppArgument(baseArgument.argument(), baseArgument.optional(), baseArgument.default_()) + : new AppArgument(baseArgument.argument(), baseArgument.optional(), baseArgument.optional() ? savedArgument.default_() : baseArgument.default_())); + } + return mergedArguments; + } + + private AppModelSettings findModelSettings(final String modelPath) { + return this.modelSettings.stream() + .filter(settings -> settings.modelPath().equals(modelPath)) + .findFirst() + .orElse(null); + } + + private static AppArgument nthArgument(final List arguments, final String argument, final int occurrenceIndex) { + int matchIndex = 0; + for (final AppArgument candidate : arguments) { + if (!candidate.argument().equals(argument)) { + continue; + } + if (matchIndex == occurrenceIndex) { + return candidate; + } + matchIndex++; + } + return null; + } + + private static List copyArguments(final List arguments) { + return arguments.stream() + .map(argument -> new AppArgument(argument.argument(), argument.optional(), argument.default_())) + .collect(Collectors.toCollection(ArrayList::new)); + } + void run(final Model model, final List argumentOverrides) throws IOException, IllegalArgumentException { String interpreterStr = ""; if (this.interpreter() != null) { @@ -62,4 +139,11 @@ record App(Path path, Path interpreter, @Deprecated List arguments, List null, this.path().toAbsolutePath().getParent().toFile()); } + + static record AppModelSettings(String modelPath, List appArguments) { + + public AppModelSettings { + appArguments = appArguments == null ? new ArrayList<>() : appArguments; + } + } } diff --git a/src/de/extio/lm_launcher/Data.java b/src/de/extio/lm_launcher/Data.java index 9b064d4..516e96a 100644 --- a/src/de/extio/lm_launcher/Data.java +++ b/src/de/extio/lm_launcher/Data.java @@ -65,10 +65,13 @@ public class Data { // Migration for (int i = 0; i < appData.apps.size(); i++) { final App app = appData.apps.get(i); - if (app.appArguments() == null) { + if (app.appArguments().isEmpty() && !app.arguments().isEmpty()) { final List arguments = new ArrayList<>(app.arguments().size()); app.arguments().stream().map(argument -> new AppArgument(argument, false, true)).forEach(arguments::add); - appData.apps.set(i, new App(app.path(), app.interpreter(), new ArrayList<>(), arguments)); + appData.apps.set(i, new App(app.path(), app.interpreter(), new ArrayList<>(), arguments, new ArrayList<>(app.modelSettings()))); + } + else if (!app.arguments().isEmpty()) { + appData.apps.set(i, new App(app.path(), app.interpreter(), new ArrayList<>(), new ArrayList<>(app.appArguments()), new ArrayList<>(app.modelSettings()))); } } } @@ -147,6 +150,16 @@ public class Data { .orElse(null); } + static void upsertModel(final Model model) { + for (int i = 0; i < modelData.models().size(); i++) { + if (modelData.models().get(i).path().equals(model.path())) { + modelData.models().set(i, model); + return; + } + } + modelData.models().add(model); + } + static Model defaultModel(final String path) { long ctime = 0; try { diff --git a/src/de/extio/lm_launcher/LaunchFrame.java b/src/de/extio/lm_launcher/LaunchFrame.java index 7601e47..c17321f 100644 --- a/src/de/extio/lm_launcher/LaunchFrame.java +++ b/src/de/extio/lm_launcher/LaunchFrame.java @@ -225,15 +225,7 @@ public class LaunchFrame extends JFrame { this.list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); this.list.getSelectionModel().addListSelectionListener(e -> { if (!e.getValueIsAdjusting()) { - if (this.table.getCellEditor() != null) { - this.table.getCellEditor().stopCellEditing(); - } - this.appArgumentsTableModel.setRowCount(0); - final DefaultListSelectionModel selectionModel = (DefaultListSelectionModel) e.getSource(); - if (selectionModel.getMinSelectionIndex() > -1) { - final App app = Data.appData.apps().get(selectionModel.getMinSelectionIndex()); - app.appArguments().forEach(arg -> this.appArgumentsTableModel.addRow(new Object[] { arg })); - } + LaunchFrame.this.refreshSelectedAppArguments(); } }); this.scrollPane.setViewportView(this.list); @@ -279,6 +271,7 @@ public class LaunchFrame extends JFrame { final Model model = saved != null ? saved : Data.defaultModel(p.toString()); this.refreshModelSliders(model); } + LaunchFrame.this.refreshSelectedAppArguments(); }); this.scrollPane_1.setViewportView(this.modelTree); @@ -469,7 +462,7 @@ public class LaunchFrame extends JFrame { interpreter = fileChooser.getSelectedFile().toPath(); } - final App app = new App(executable, interpreter, new ArrayList<>(), new ArrayList<>()); + final App app = new App(executable, interpreter, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); Data.appData.apps().add(app); final AppPropertiesDialog appPropertiesDialog = new AppPropertiesDialog(LaunchFrame.this, app, () -> { @@ -537,7 +530,7 @@ public class LaunchFrame extends JFrame { } final App app = Data.appData.apps().get(index); - Data.appData.apps().add(index, new App(app.path(), app.interpreter(), new ArrayList<>(app.arguments()), new ArrayList<>(app.appArguments()))); + Data.appData.apps().add(index, new App(app.path(), app.interpreter(), new ArrayList<>(app.arguments()), new ArrayList<>(app.appArguments()), new ArrayList<>(app.modelSettings()))); Data.saveApps(); LaunchFrame.this.refresh(); @@ -581,19 +574,19 @@ public class LaunchFrame extends JFrame { template.ctime()); } - final List argumentOverrides = new ArrayList<>(); - if (LaunchFrame.this.table.getCellEditor() != null) { - LaunchFrame.this.table.getCellEditor().stopCellEditing(); - } - for (int i = 0; i < LaunchFrame.this.appArgumentsTableModel.getRowCount(); i++) { - final AppArgument appArgument = (AppArgument) LaunchFrame.this.appArgumentsTableModel.getValueAt(i, 0); - if (appArgument.optional()) { - argumentOverrides.add(appArgument); - } - } + final List selectedArguments = LaunchFrame.this.currentTableArguments(); + final List argumentOverrides = selectedArguments.stream().filter(AppArgument::optional).toList(); try { app.run(model, argumentOverrides); + if (model != null) { + Data.upsertModel(model); + Data.saveModels(); + app.saveArgumentsForModel(selectedPath, selectedArguments); + Data.saveApps(); + LaunchFrame.this.refreshModelSliders(model); + LaunchFrame.this.refreshSelectedAppArguments(); + } } catch (final IllegalArgumentException iae) { JOptionPane.showMessageDialog(LaunchFrame.this, iae.getMessage(), "Error Message", JOptionPane.WARNING_MESSAGE); @@ -631,6 +624,38 @@ public class LaunchFrame extends JFrame { return null; } + private App getSelectedApp() { + final int selectedIndex = this.list.getSelectedIndex(); + if (selectedIndex < 0) { + return null; + } + return Data.appData.apps().get(selectedIndex); + } + + private void refreshSelectedAppArguments() { + if (this.table.getCellEditor() != null) { + this.table.getCellEditor().stopCellEditing(); + } + this.appArgumentsTableModel.setRowCount(0); + final App selectedApp = this.getSelectedApp(); + if (selectedApp == null) { + return; + } + selectedApp.argumentsForModel(this.getSelectedModelPath()) + .forEach(argument -> this.appArgumentsTableModel.addRow(new Object[] { argument })); + } + + private List currentTableArguments() { + if (this.table.getCellEditor() != null) { + this.table.getCellEditor().stopCellEditing(); + } + final List selectedArguments = new ArrayList<>(this.appArgumentsTableModel.getRowCount()); + for (int i = 0; i < this.appArgumentsTableModel.getRowCount(); i++) { + selectedArguments.add((AppArgument) this.appArgumentsTableModel.getValueAt(i, 0)); + } + return selectedArguments; + } + private void refreshModelSliders(final Model model) { this.slider_context.setMaximum(model.maxContextSize()); this.slider_context.setMajorTickSpacing(Math.max(1, model.maxContextSize() / 4)); @@ -712,6 +737,7 @@ public class LaunchFrame extends JFrame { this.modelTree.setModel(new DefaultTreeModel(root)); } + this.refreshSelectedAppArguments(); this.repaint(); }