Skip to content

Commit 7404373

Browse files
committed
LLM support in key.ui
# Conflicts: # settings.gradle
1 parent cd48783 commit 7404373

9 files changed

Lines changed: 185 additions & 414 deletions

File tree

key.ui/src/main/java/de/uka/ilkd/key/gui/settings/SettingsPanel.java

Lines changed: 46 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,25 @@
44
package de.uka.ilkd.key.gui.settings;
55

66

7-
import java.awt.*;
8-
import java.awt.event.ActionListener;
9-
import java.io.File;
10-
import java.util.Arrays;
11-
import java.util.Collections;
12-
import java.util.List;
13-
import java.util.function.Function;
14-
import javax.swing.*;
15-
167
import de.uka.ilkd.key.gui.KeYFileChooser;
178
import de.uka.ilkd.key.gui.fonticons.FontAwesomeSolid;
189
import de.uka.ilkd.key.gui.fonticons.IconFactory;
1910
import de.uka.ilkd.key.gui.fonticons.IconFontSwing;
20-
2111
import net.miginfocom.layout.AC;
2212
import net.miginfocom.layout.CC;
2313
import net.miginfocom.layout.LC;
2414
import net.miginfocom.swing.MigLayout;
2515
import org.jspecify.annotations.Nullable;
2616

17+
import javax.swing.*;
18+
import java.awt.*;
19+
import java.awt.event.ActionListener;
20+
import java.io.File;
21+
import java.util.Arrays;
22+
import java.util.Collections;
23+
import java.util.List;
24+
import java.util.function.Function;
25+
2726
/**
2827
* Extension of {@link SimpleSettingsPanel} which uses {@link MigLayout} to create a nice
2928
* three-column view.
@@ -40,19 +39,19 @@ public abstract class SettingsPanel extends SimpleSettingsPanel {
4039

4140
protected SettingsPanel() {
4241
pCenter.setLayout(new MigLayout(
43-
// set up rows:
44-
new LC().fillX()
45-
// remove the padding after the help icon
46-
.insets(null, null, null, "0").wrapAfter(3),
47-
// set up columns:
48-
new AC().count(3).fill(1)
49-
// label column does not grow
50-
.grow(0f, 0)
51-
// input area does grow
52-
.grow(1000f, 1)
53-
// help icon always has the same size
54-
.size("16px", 2)
55-
.align("right", 0)));
42+
// set up rows:
43+
new LC().fillX()
44+
// remove the padding after the help icon
45+
.insets(null, null, null, "0").wrapAfter(3),
46+
// set up columns:
47+
new AC().count(3).fill(1)
48+
// label column does not grow
49+
.grow(0f, 0)
50+
// input area does grow
51+
.grow(1000f, 1)
52+
// help icon always has the same size
53+
.size("16px", 2)
54+
.align("right", 0)));
5655
}
5756

5857
/**
@@ -123,7 +122,7 @@ protected <T> JComboBox<T> createSelection(T[] elements, Validator<T> validator)
123122
* @return
124123
*/
125124
protected JCheckBox addCheckBox(String title, String info, boolean value,
126-
final Validator<Boolean> validator) {
125+
final Validator<Boolean> validator) {
127126
JCheckBox checkBox = createCheckBox(title, value, validator);
128127
addRowWithHelp(info, new JLabel(), checkBox);
129128
return checkBox;
@@ -139,7 +138,7 @@ protected JCheckBox addCheckBox(String title, String info, boolean value,
139138
* @return
140139
*/
141140
protected JTextField addFileChooserPanel(String title, String file, String info, boolean isSave,
142-
final Validator<String> validator) {
141+
final Validator<String> validator) {
143142
JTextField textField = new JTextField(file);
144143
textField.addActionListener(e -> {
145144
try {
@@ -168,7 +167,7 @@ protected JTextField addFileChooserPanel(String title, String file, String info,
168167
fileChooser = KeYFileChooser.getFileChooser("Save file");
169168
fileChooser.setFileFilter(fileChooser.getAcceptAllFileFilter());
170169
result = fileChooser.showSaveDialog((Component) e.getSource(),
171-
new File(textField.getText()));
170+
new File(textField.getText()));
172171
} else {
173172
fileChooser = KeYFileChooser.getFileChooser("Open file");
174173
fileChooser.setFileFilter(fileChooser.getAcceptAllFileFilter());
@@ -188,16 +187,16 @@ protected JTextField addFileChooserPanel(String title, String file, String info,
188187
/**
189188
* Adds a new combobox to the panel.
190189
*
191-
* @param title label of the combo box
192-
* @param info help text
190+
* @param title label of the combo box
191+
* @param info help text
193192
* @param selectionIndex which item to initially select
194-
* @param validator validator
195-
* @param items the items
196-
* @param <T> the type of the items
193+
* @param validator validator
194+
* @param items the items
195+
* @param <T> the type of the items
197196
* @return the combo box
198197
*/
199198
protected <T> JComboBox<T> addComboBox(String title, String info, int selectionIndex,
200-
@Nullable Validator<T> validator, T... items) {
199+
@Nullable Validator<T> validator, T... items) {
201200
JComboBox<T> comboBox = new JComboBox<>(items);
202201
comboBox.setSelectedIndex(selectionIndex);
203202
comboBox.addActionListener(e -> {
@@ -237,8 +236,8 @@ protected void addTitledComponent(String title, JComponent component, String hel
237236
}
238237

239238
protected <T> JList<T> addListBox(String title, String info,
240-
final Validator<List<T>> validator,
241-
List<T> seq, Function<String, T> converter) {
239+
final Validator<List<T>> validator,
240+
List<T> seq, Function<String, T> converter) {
242241
var model = new DefaultListModel<T>();
243242
model.addAll(seq);
244243

@@ -287,7 +286,7 @@ protected <T> JList<T> addListBox(String title, String info,
287286
btnAdd.addActionListener(addItem);
288287

289288
ActionListener removeItem = e -> {
290-
if (list.getSelectedIndex() != -1) {
289+
if(list.getSelectedIndex() != -1) {
291290
model.removeElementAt(list.getSelectedIndex());
292291
}
293292
};
@@ -297,14 +296,14 @@ protected <T> JList<T> addListBox(String title, String info,
297296
}
298297

299298
protected JTextArea addTextArea(String title, String text, String info,
300-
final Validator<String> validator) {
299+
final Validator<String> validator) {
301300
JScrollPane field = createTextArea(text, validator);
302301
addTitledComponent(title, field, info);
303302
return (JTextArea) field.getViewport().getView();
304303
}
305304

306305
protected JTextArea addTextAreaWithoutScroll(String title, String text, String info,
307-
final Validator<String> validator) {
306+
final Validator<String> validator) {
308307
JTextArea field = createTextAreaWithoutScroll(text, validator);
309308
addTitledComponent(title, field, info);
310309
return field;
@@ -319,15 +318,15 @@ protected JTextArea addTextAreaWithoutScroll(String title, String text, String i
319318
* @return
320319
*/
321320
protected JTextField addTextField(String title, String text, String info,
322-
final Validator<String> validator) {
321+
final Validator<String> validator) {
323322
JTextField field = createTextField(text, validator);
324323
addTitledComponent(title, field, info);
325324
return field;
326325
}
327326

328327

329328
protected JTextField addTextField(String title, String text, String info,
330-
final Validator<String> validator, JComponent additionalActions) {
329+
final Validator<String> validator, JComponent additionalActions) {
331330
JTextField field = createTextField(text, validator);
332331
JLabel label = new JLabel(title);
333332
label.setLabelFor(field);
@@ -350,18 +349,18 @@ protected JTextField addTextField(String title, String text, String info,
350349
* text
351350
* field), otherwise the {@link Validator} will fail.
352351
*
353-
* @param title the title of the text field
354-
* @param min the minimum value that can be entered
355-
* @param max the maximum value that can be entered
356-
* @param step the step size used when changing the entered value using the JSpinner's arrow
357-
* buttons
358-
* @param info arbitrary information about the text field
352+
* @param title the title of the text field
353+
* @param min the minimum value that can be entered
354+
* @param max the maximum value that can be entered
355+
* @param step the step size used when changing the entered value using the JSpinner's arrow
356+
* buttons
357+
* @param info arbitrary information about the text field
359358
* @param validator a validator for checking the entered values
360-
* @param <T> the class of the minimum value
359+
* @param <T> the class of the minimum value
361360
* @return the created JSpinner
362361
*/
363362
protected <T extends Number & Comparable<T>> JSpinner addNumberField(String title, T min,
364-
Comparable<T> max, Number step, String info, final Validator<Number> validator) {
363+
Comparable<T> max, Number step, String info, final Validator<Number> validator) {
365364
JSpinner field = createNumberTextField(min, max, step, validator);
366365
addTitledComponent(title, field, info);
367366
return field;

keyext.llm/src/main/java/org/key_project/key/llm/LlmClient.java

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
/* This file is part of KeY - https://key-project.org
2-
* KeY is licensed under the GNU General Public License Version 2
3-
* SPDX-License-Identifier: GPL-2.0-only */
41
package org.key_project.key.llm;
52

6-
import java.io.IOException;
7-
import java.util.ArrayList;
8-
import java.util.Map;
9-
import java.util.concurrent.Callable;
10-
113
import com.google.gson.GsonBuilder;
4+
import org.apache.hc.client5.http.classic.methods.HttpGet;
125
import org.apache.hc.client5.http.classic.methods.HttpPost;
136
import org.apache.hc.client5.http.impl.classic.AbstractHttpClientResponseHandler;
147
import org.apache.hc.client5.http.impl.classic.HttpClients;
158
import org.apache.hc.core5.http.HttpEntity;
169
import org.apache.hc.core5.http.ParseException;
1710
import org.apache.hc.core5.http.io.entity.EntityUtils;
1811
import org.apache.hc.core5.http.io.entity.StringEntity;
12+
import org.key_project.util.java.IOUtil;
13+
import org.slf4j.LoggerFactory;
14+
15+
import java.io.IOException;
16+
import java.util.List;
17+
import java.util.Map;
18+
import java.util.concurrent.Callable;
1919

2020
/**
2121
*
@@ -24,13 +24,9 @@
2424
*/
2525
public class LlmClient implements Callable<Map<String, Object>> {
2626
private final LlmSession llmSession;
27-
private final LlmContext context;
28-
private final LlmContext.LlmMessage prompt;
2927

30-
public LlmClient(LlmSession llmSession, LlmContext context, String message) {
28+
public LlmClient(LlmSession llmSession) {
3129
this.llmSession = llmSession;
32-
this.context = context;
33-
this.prompt = new LlmContext.LlmMessage("user", message);
3430
}
3531

3632
@Override
@@ -42,13 +38,13 @@ public Map<String, Object> call() throws Exception {
4238
request.addHeader("Content-Type", "application/json");
4339
request.addHeader("Accept", "application/json");
4440

45-
var msg = new ArrayList<>(context.getMessages());
46-
msg.add(prompt);
47-
4841
var data = Map.of(
49-
"model", llmSession.getModel(),
50-
"messages", msg);
51-
42+
"model", "azure.gpt-4.1-mini",
43+
"messages", List.of(
44+
createMessage("system", "Du bist ein hilfreicher Assistent am KIT."),
45+
createMessage("user", "Erkläre das Prinzip der Rayleigh-Streuung indrei Sätzen.")
46+
)
47+
);
5248
var gson = new GsonBuilder().create();
5349
var stringBody = gson.toJson(data);
5450
request.setEntity(new StringEntity(stringBody));
@@ -58,9 +54,12 @@ public Map<String, Object> call() throws Exception {
5854
}
5955
}
6056

57+
private Map<String, Object> createMessage(String role, String content) {
58+
return Map.of("role", role, "content", content);
59+
}
60+
6161

62-
private static class GsonHttpClientResponseHandler
63-
extends AbstractHttpClientResponseHandler<Map<String, Object>> {
62+
private static class GsonHttpClientResponseHandler extends AbstractHttpClientResponseHandler<Map<String, Object>> {
6463
@Override
6564
public Map<String, Object> handleEntity(HttpEntity entity) throws IOException {
6665
String content = null;
@@ -69,8 +68,7 @@ public Map<String, Object> handleEntity(HttpEntity entity) throws IOException {
6968
} catch (ParseException e) {
7069
throw new RuntimeException(e);
7170
}
72-
// LoggerFactory.getLogger(LlmClient.class).error("Could not parse json response
73-
// {}",content);
71+
//LoggerFactory.getLogger(LlmClient.class).error("Could not parse json response {}",content);
7472
try {
7573
return new GsonBuilder().create().fromJson(content, Map.class);
7674
} catch (Exception e) {

keyext.llm/src/main/java/org/key_project/key/llm/LlmExtension.java

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
1-
/* This file is part of KeY - https://key-project.org
2-
* KeY is licensed under the GNU General Public License Version 2
3-
* SPDX-License-Identifier: GPL-2.0-only */
41
package org.key_project.key.llm;
52

6-
import java.awt.event.ActionEvent;
7-
import java.util.Collection;
8-
import java.util.List;
9-
import javax.swing.*;
10-
113
import de.uka.ilkd.key.core.KeYMediator;
124
import de.uka.ilkd.key.gui.MainWindow;
135
import de.uka.ilkd.key.gui.actions.KeyAction;
@@ -19,26 +11,28 @@
1911
import de.uka.ilkd.key.gui.settings.InvalidSettingsInputException;
2012
import de.uka.ilkd.key.gui.settings.SettingsProvider;
2113
import de.uka.ilkd.key.settings.ProofIndependentSettings;
22-
2314
import org.jspecify.annotations.NonNull;
2415
import org.jspecify.annotations.Nullable;
2516

17+
import javax.swing.*;
18+
import java.awt.event.ActionEvent;
19+
import java.util.Collection;
20+
import java.util.List;
21+
2622
/**
2723
*
2824
* @author Alexander Weigl
2925
* @version 1 (11/18/25)
3026
*/
3127
@KeYGuiExtension.Info(experimental = false, description = "LLM support for KeY")
3228
public class LlmExtension implements KeYGuiExtension, KeYGuiExtension.ContextMenu,
33-
KeYGuiExtension.Settings, KeYGuiExtension.Startup, KeYGuiExtension.LeftPanel,
34-
KeYGuiExtension.MainMenu {
29+
KeYGuiExtension.Settings, KeYGuiExtension.Startup, KeYGuiExtension.LeftPanel, KeYGuiExtension.MainMenu {
3530
private KeyAction actionStartLlmPromptForCurrentProof;
36-
private TabPanel uiPrompt;
31+
private TabPanel uiPrompt = new LlmPrompt();
3732

3833
@Override
3934
public @NonNull List<Action> getContextActions(
40-
@NonNull KeYMediator mediator, @NonNull ContextMenuKind kind,
41-
@NonNull Object underlyingObject) {
35+
@NonNull KeYMediator mediator, @NonNull ContextMenuKind kind, @NonNull Object underlyingObject) {
4236
return List.of();
4337
}
4438

@@ -59,9 +53,7 @@ public void preInit(MainWindow window, KeYMediator mediator) {
5953
}
6054

6155
@Override
62-
public @NonNull Collection<TabPanel> getPanels(@NonNull MainWindow window,
63-
@NonNull KeYMediator mediator) {
64-
uiPrompt = new LlmPrompt(window, mediator);
56+
public @NonNull Collection<TabPanel> getPanels(@NonNull MainWindow window, @NonNull KeYMediator mediator) {
6557
return List.of(uiPrompt);
6658
}
6759

0 commit comments

Comments
 (0)