55
66import java .awt .*;
77import java .awt .event .*;
8- import java .util .Collection ;
8+ import java .io .IOException ;
9+ import java .net .URI ;
10+ import java .util .*;
911import java .util .List ;
10- import java .util .Map ;
1112import java .util .concurrent .ForkJoinPool ;
13+ import java .util .function .Supplier ;
1214import javax .swing .*;
15+ import javax .swing .table .DefaultTableModel ;
1316
17+ import de .uka .ilkd .key .core .KeYMediator ;
18+ import de .uka .ilkd .key .core .KeYSelectionEvent ;
19+ import de .uka .ilkd .key .core .KeYSelectionListener ;
1420import de .uka .ilkd .key .gui .MainWindow ;
1521import de .uka .ilkd .key .gui .actions .KeyAction ;
1622import de .uka .ilkd .key .gui .colors .ColorSettings ;
23+ import de .uka .ilkd .key .gui .docking .DynamicCMenu ;
1724import de .uka .ilkd .key .gui .extension .api .TabPanel ;
18-
25+ import de .uka .ilkd .key .gui .fonticons .IconFactory ;
26+ import de .uka .ilkd .key .gui .help .HelpFacade ;
27+ import de .uka .ilkd .key .proof .Proof ;
28+
29+ import bibliothek .gui .dock .common .action .CAction ;
30+ import bibliothek .gui .dock .common .action .CMenu ;
31+ import bibliothek .gui .dock .common .action .CRadioButton ;
32+ import bibliothek .gui .dock .common .action .CRadioGroup ;
1933import com .google .gson .GsonBuilder ;
2034import net .miginfocom .layout .CC ;
2135import net .miginfocom .layout .LC ;
@@ -39,7 +53,7 @@ public class LlmPrompt extends JPanel implements TabPanel {
3953 ColorSettings .define ("llm.output.bg.error" , "Background color in chat of LLM answers" ,
4054 new Color (255 , 180 , 180 , 255 ));
4155
42- private static final ColorSettings .ColorProperty COLOR_BG_ANSWER = ColorSettings .define (
56+ public static final ColorSettings .ColorProperty COLOR_BG_ANSWER = ColorSettings .define (
4357 "llm.output.bg.answer" , "Background color in chat of LLM answers" , Color .LIGHT_GRAY );
4458
4559 private final JSplitPane splitPane = new JSplitPane (JSplitPane .VERTICAL_SPLIT );
@@ -51,21 +65,43 @@ public class LlmPrompt extends JPanel implements TabPanel {
5165
5266 private final KeyAction actionSwitchOrientation = new SwitchOrientationAction ();
5367 private final SendPromptAction actionSendPrompt = new SendPromptAction ();
68+ private final JPanel tblFiles = new JPanel (new MigLayout (new LC ().fillX ().wrapAfter (1 )));
69+ private final DefaultTableModel modelFiles = new DefaultTableModel ();
70+
71+ private final DefaultListModel <LlmPromptModel <?>> model = new DefaultListModel <>();
72+ private final MainWindow mainWindow ;
73+ private final KeYMediator mediator ;
74+
75+ public LlmPrompt (MainWindow mainWindow , @ NonNull KeYMediator mediator ) {
76+ this .mainWindow = mainWindow ;
77+ this .mediator = mediator ;
5478
55- public LlmPrompt () {
5679 setLayout (new BorderLayout ());
5780 add (splitPane , BorderLayout .CENTER );
5881 final var comp = new JScrollPane (pOutput );
5982 comp .getVerticalScrollBar ().setUnitIncrement (16 );
6083 splitPane .add (comp );
61- splitPane .add (new JScrollPane (txtInput ));
84+ var scrpInput = new JScrollPane (txtInput );
85+ var scrpFiles = new JScrollPane (tblFiles );
86+ var tabInputPanes = new JTabbedPane ();
87+ tabInputPanes .addTab ("Prompt" , scrpInput );
88+ tabInputPanes .addTab ("Files" , scrpFiles );
89+ splitPane .add (tabInputPanes );
90+
91+ SwingUtilities .invokeLater (this ::populateFiles );
92+ mediator .addKeYSelectionListener (new KeYSelectionListener () {
93+ @ Override
94+ public void selectedProofChanged (KeYSelectionEvent <Proof > e ) {
95+ populateFiles ();
96+ }
97+ });
98+
6299
63100 handle (new Exception ("Test Exception" ));
64101 handle (new GsonBuilder ().create ().fromJson (
65102 "{\" id\" :\" chatcmpl-CdLvyHhYLoKFk3F28rE6JgNJVGZHU\" ,\" created\" :1763495142,\" model\" :\" gpt-4.1-mini-2025-04-14\" ,\" object\" :\" chat.completion\" ,\" system_fingerprint\" :\" fp_3dcd5944f5\" ,\" choices\" :[{\" finish_reason\" :\" stop\" ,\" index\" :0,\" message\" :{\" content\" :\" Die Rayleigh-Streuung beschreibt die Streuung von Licht an kleinen Teilchen, deren Größe viel kleiner ist als die Lichtwellenlänge. Dabei wird kurzwelliges Licht (blaues und violettes) stärker gestreut als langwelliges (rotes), was z.B. den blauen Himmel erklärt. Die Intensität der Streuung ist proportional zur vierten Potenz der Frequenz des Lichts.\" ,\" role\" :\" assistant\" ,\" annotations\" :[]},\" provider_specific_fields\" :{\" content_filter_results\" :{\" hate\" :{\" filtered\" :false,\" severity\" :\" safe\" },\" protected_material_text\" :{\" filtered\" :false,\" detected\" :false},\" self_harm\" :{\" filtered\" :false,\" severity\" :\" safe\" },\" sexual\" :{\" filtered\" :false,\" severity\" :\" safe\" },\" violence\" :{\" filtered\" :false,\" severity\" :\" safe\" }}}}],\" usage\" :{\" completion_tokens\" :91,\" prompt_tokens\" :36,\" total_tokens\" :127,\" completion_tokens_details\" :{\" accepted_prediction_tokens\" :0,\" audio_tokens\" :0,\" reasoning_tokens\" :0,\" rejected_prediction_tokens\" :0},\" prompt_tokens_details\" :{\" audio_tokens\" :0,\" cached_tokens\" :0}},\" prompt_filter_results\" :[{\" prompt_index\" :0,\" content_filter_results\" :{\" hate\" :{\" filtered\" :false,\" severity\" :\" safe\" },\" jailbreak\" :{\" filtered\" :false,\" detected\" :false},\" self_harm\" :{\" filtered\" :false,\" severity\" :\" safe\" },\" sexual\" :{\" filtered\" :false,\" severity\" :\" safe\" },\" violence\" :{\" filtered\" :false,\" severity\" :\" safe\" }}}]}" ,
66103 Map .class ));
67- addInput (">>> Input data" );
68-
104+ addInput ("Input data" );
69105
70106 txtInput .addKeyListener (new KeyAdapter () {
71107 @ Override
@@ -78,75 +114,71 @@ public void keyTyped(KeyEvent e) {
78114 });
79115 }
80116
81- public static class OutputBox <T > extends JPanel {
82- protected final T userData ;
83- protected final JEditorPane output = new JEditorPane ();
84- protected final JPanel buttons = new JPanel ();
85- protected final JPopupMenu menu = new JPopupMenu ();
117+ static class AddFileAction extends KeyAction {
118+ private final Set <URI > selectedFiles ;
119+ private final URI file ;
86120
87- public OutputBox (T userData ) {
88- this (userData , userData .toString ());
89- output .add (menu );
121+ public AddFileAction (URI file , Set <URI > selectedFiles ) {
122+ this .file = file ;
123+ this .selectedFiles = selectedFiles ;
124+ setName (file .toString ());
90125 }
91126
92- public OutputBox (T userData , String text ) {
93- this .userData = userData ;
94- setLayout (new BorderLayout ());
95- output .setEditable (false );
96- add (output , 0 );
97- buttons .setVisible (false );
98- output .addMouseListener (new MouseAdapter () {
99- @ Override
100- public void mouseEntered (MouseEvent e ) {
101- buttons .setVisible (true );
102- }
103-
104- @ Override
105- public void mouseExited (MouseEvent e ) {
106- buttons .setVisible (false );
107- }
108- });
109- output .setText (text );
110-
111- setBorder (BorderFactory .createEmptyBorder (10 , 10 , 10 , 10 ));
127+ @ Override
128+ public void actionPerformed (ActionEvent e ) {
129+ var chk = (JCheckBox ) e .getSource ();
130+ if (chk .isSelected ()) {
131+ selectedFiles .add (file );
132+ } else {
133+ selectedFiles .remove (file );
134+ }
112135 }
136+ }
113137
114- @ Override
115- public void setBackground (Color bg ) {
116- super .setBackground (bg );
117- if (output != null )
118- output .setBackground (bg );
138+ private void populateFiles () {
139+ try {
140+ tblFiles .removeAll ();
141+ LlmSession session = LlmUtils .getSession (mediator .getSelectedProof ());
142+ List <URI > possibleFiles =
143+ new ArrayList <>(LlmUtils .getPossibleFiles (mediator .getSelectedProof ()));
144+ Set <URI > selectedFiles = session .getSelectedFiles ();
145+ possibleFiles .sort (Comparator .comparing (URI ::toString ));
146+ for (URI file : possibleFiles ) {
147+ tblFiles .add (new JCheckBox (new AddFileAction (file , selectedFiles )));
148+ }
149+ tblFiles .invalidate ();
150+ } catch (IOException e ) {
151+ throw new RuntimeException (e );
119152 }
120153 }
121154
122155 private OutputBox <String > addInput (String text ) {
123- var o = addBox (text , new RepromptAction (text ));
156+ var o = addBox (new LlmPromptModel (LlmPromptModel .Kind .INPUT , text , text ),
157+ new RepromptAction (text ));
124158 o .setBackground (COLOR_BG_INPUT .get ());
125159 return o ;
126160 }
127161
128- private <T > OutputBox <T > addBox (T data , Action ... actions ) {
162+ private <T > OutputBox <T > addBox (LlmPromptModel < T > data , Action ... actions ) {
129163 OutputBox <T > box = new OutputBox <>(data );
130164 for (Action it : actions ) {
131165 box .menu .add (it );
132166 }
133167 pOutput .add (box , new CC ().growX ());
168+ box .setBackground (data .kind ().background ().get ());
134169 return box ;
135170 }
136171
137172 private void handle (Map <String , Object > jsonResponse ) {
138173 LOGGER .info ("LLM prompt {}" , jsonResponse );
139- var o = new OutputBox <>( jsonResponse ,
174+ final var text =
140175 ((Map <String , Object >) ((Map <String , Object >) ((List <?>) jsonResponse .get ("choices" ))
141- .get (0 )).get ("message" )).get ("content" ).toString ());
142- pOutput .add (o , new CC ().growX ());
143- o .setBackground (COLOR_BG_ANSWER .get ());
176+ .get (0 )).get ("message" )).get ("content" ).toString ();
177+ addBox (new LlmPromptModel <>(LlmPromptModel .Kind .OUTPUT , text , jsonResponse ));
144178 }
145179
146180 private void handle (Throwable e ) {
147- LOGGER .error ("Error during LLM prompt" , e );
148- var box = addBox (e );
149- box .setBackground (COLOR_BG_ERROR .get ());
181+ addBox (new LlmPromptModel <>(LlmPromptModel .Kind .ERROR , e .toString (), e ));
150182 }
151183
152184 @ Override
@@ -160,8 +192,36 @@ private void handle(Throwable e) {
160192 }
161193
162194 @ Override
163- public @ NonNull Collection <Action > getTitleActions () {
164- return List .of (actionSwitchOrientation );
195+ public @ NonNull Collection <CAction > getTitleCActions () {
196+ Supplier <CMenu > supplier = () -> {
197+ CMenu menu = new CMenu ();
198+ menu .add (actionSwitchOrientation .toCAction ());
199+
200+ CMenu menuModels = new CMenu ("Models" , null );
201+ menu .add (menuModels );
202+ var groupModels = new CRadioGroup ();
203+ var llmSession =
204+ LlmUtils .getSession (MainWindow .getInstance ().getMediator ().getSelectedProof ());
205+
206+ for (var m : LlmSettings .INSTANCE .getAvailableModels ()) {
207+ var selected = m .equals (llmSession .getModel ());
208+ final var action = new CRadioButton (m , null ) {
209+ @ Override
210+ protected void changed () {
211+ llmSession .setModel (m );
212+ }
213+ };
214+ action .setSelected (selected );
215+ groupModels .add (action );
216+ menuModels .add (action );
217+ }
218+ return menu ;
219+ };
220+
221+ var a = new DynamicCMenu ("Settings" , IconFactory .properties (MainWindow .TOOLBAR_ICON_SIZE ),
222+ supplier );
223+ var help = HelpFacade .createHelpButton ("user/LLM/" );
224+ return List .of (help , a );
165225 }
166226
167227 class SwitchOrientationAction extends KeyAction {
@@ -179,7 +239,7 @@ public void actionPerformed(ActionEvent e) {
179239 }
180240 }
181241
182- class SelectContextAction extends KeyAction {
242+ static class SelectContextAction extends KeyAction {
183243 @ Override
184244 public void actionPerformed (ActionEvent e ) {
185245
@@ -240,3 +300,42 @@ public void actionPerformed(ActionEvent e) {
240300 }
241301 }
242302}
303+
304+
305+ class OutputBox <T > extends JPanel {
306+ protected final LlmPromptModel <T > model ;
307+ protected final JTextArea output = new JTextArea ();
308+ protected final JPanel buttons = new JPanel ();
309+ protected final JPopupMenu menu = new JPopupMenu ();
310+
311+ public OutputBox (LlmPromptModel <T > userData ) {
312+ this .model = userData ;
313+ setLayout (new BorderLayout ());
314+ output .setEditable (false );
315+ add (output , 0 );
316+ buttons .setVisible (false );
317+ output .addMouseListener (new MouseAdapter () {
318+ @ Override
319+ public void mouseEntered (MouseEvent e ) {
320+ buttons .setVisible (true );
321+ }
322+
323+ @ Override
324+ public void mouseExited (MouseEvent e ) {
325+ buttons .setVisible (false );
326+ }
327+ });
328+ output .setText (userData .text ());
329+ output .setLineWrap (true ); // Makes the text wrap to the next line
330+ output .setWrapStyleWord (true ); // Makes the text wrap full words, not just letters
331+ output .setComponentPopupMenu (menu );
332+ setBorder (BorderFactory .createEmptyBorder (10 , 10 , 10 , 10 ));
333+ }
334+
335+ @ Override
336+ public void setBackground (Color bg ) {
337+ super .setBackground (bg );
338+ if (output != null )
339+ output .setBackground (bg );
340+ }
341+ }
0 commit comments