11package org .key_project .key .llm ;
22
3+ import bibliothek .gui .dock .common .action .CAction ;
4+ import bibliothek .gui .dock .common .action .CMenu ;
5+ import bibliothek .gui .dock .common .action .CRadioButton ;
6+ import bibliothek .gui .dock .common .action .CRadioGroup ;
37import com .google .gson .GsonBuilder ;
8+ import de .uka .ilkd .key .core .KeYMediator ;
9+ import de .uka .ilkd .key .core .KeYSelectionEvent ;
10+ import de .uka .ilkd .key .core .KeYSelectionListener ;
411import de .uka .ilkd .key .gui .MainWindow ;
512import de .uka .ilkd .key .gui .actions .KeyAction ;
613import de .uka .ilkd .key .gui .colors .ColorSettings ;
14+ import de .uka .ilkd .key .gui .docking .DynamicCMenu ;
715import de .uka .ilkd .key .gui .extension .api .TabPanel ;
16+ import de .uka .ilkd .key .gui .fonticons .IconFactory ;
17+ import de .uka .ilkd .key .gui .help .HelpFacade ;
18+ import de .uka .ilkd .key .proof .Proof ;
819import net .miginfocom .layout .CC ;
920import net .miginfocom .layout .LC ;
1021import net .miginfocom .swing .MigLayout ;
1324import org .slf4j .LoggerFactory ;
1425
1526import javax .swing .*;
27+ import javax .swing .table .DefaultTableModel ;
1628import java .awt .*;
1729import java .awt .event .*;
18- import java .util .Arrays ;
19- import java .util .Collection ;
30+ import java .io .IOException ;
31+ import java .net .URI ;
32+ import java .util .*;
2033import java .util .List ;
21- import java .util .Map ;
2234import java .util .concurrent .ForkJoinPool ;
35+ import java .util .function .Supplier ;
2336
2437/**
2538 *
@@ -35,7 +48,7 @@ public class LlmPrompt extends JPanel implements TabPanel {
3548 public static final ColorSettings .ColorProperty COLOR_BG_ERROR
3649 = ColorSettings .define ("llm.output.bg.error" , "Background color in chat of LLM answers" , new Color (255 , 180 , 180 , 255 ));
3750
38- private static final ColorSettings .ColorProperty COLOR_BG_ANSWER
51+ public static final ColorSettings .ColorProperty COLOR_BG_ANSWER
3952 = ColorSettings .define ("llm.output.bg.answer" , "Background color in chat of LLM answers" , Color .LIGHT_GRAY );
4053
4154 private final JSplitPane splitPane = new JSplitPane (JSplitPane .VERTICAL_SPLIT );
@@ -46,20 +59,42 @@ public class LlmPrompt extends JPanel implements TabPanel {
4659
4760 private final KeyAction actionSwitchOrientation = new SwitchOrientationAction ();
4861 private final SendPromptAction actionSendPrompt = new SendPromptAction ();
62+ private final JPanel tblFiles = new JPanel (new MigLayout (new LC ().fillX ().wrapAfter (1 )));
63+ private final DefaultTableModel modelFiles = new DefaultTableModel ();
64+
65+ private final DefaultListModel <LlmPromptModel <?>> model = new DefaultListModel <>();
66+ private final MainWindow mainWindow ;
67+ private final KeYMediator mediator ;
68+
69+ public LlmPrompt (MainWindow mainWindow , @ NonNull KeYMediator mediator ) {
70+ this .mainWindow = mainWindow ;
71+ this .mediator = mediator ;
4972
50- public LlmPrompt () {
5173 setLayout (new BorderLayout ());
5274 add (splitPane , BorderLayout .CENTER );
5375 final var comp = new JScrollPane (pOutput );
5476 comp .getVerticalScrollBar ().setUnitIncrement (16 );
5577 splitPane .add (comp );
56- splitPane .add (new JScrollPane (txtInput ));
78+ var scrpInput = new JScrollPane (txtInput );
79+ var scrpFiles = new JScrollPane (tblFiles );
80+ var tabInputPanes = new JTabbedPane ();
81+ tabInputPanes .addTab ("Prompt" , scrpInput );
82+ tabInputPanes .addTab ("Files" , scrpFiles );
83+ splitPane .add (tabInputPanes );
84+
85+ SwingUtilities .invokeLater (this ::populateFiles );
86+ mediator .addKeYSelectionListener (new KeYSelectionListener () {
87+ @ Override
88+ public void selectedProofChanged (KeYSelectionEvent <Proof > e ) {
89+ populateFiles ();
90+ }
91+ });
92+
5793
5894 handle (new Exception ("Test Exception" ));
5995 handle (new GsonBuilder ().create ().fromJson ("{\" 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\" }}}]}" ,
6096 Map .class ));
61- addInput (">>> Input data" );
62-
97+ addInput ("Input data" );
6398
6499 txtInput .addKeyListener (new KeyAdapter () {
65100 @ Override
@@ -71,73 +106,68 @@ public void keyTyped(KeyEvent e) {
71106 });
72107 }
73108
74- public static class OutputBox <T > extends JPanel {
75- protected final T userData ;
76- protected final JEditorPane output = new JEditorPane ();
77- protected final JPanel buttons = new JPanel ();
78- protected final JPopupMenu menu = new JPopupMenu ();
109+ static class AddFileAction extends KeyAction {
110+ private final Set <URI > selectedFiles ;
111+ private final URI file ;
79112
80- public OutputBox (T userData ) {
81- this (userData , userData .toString ());
82- output .add (menu );
113+ public AddFileAction (URI file , Set <URI > selectedFiles ) {
114+ this .file = file ;
115+ this .selectedFiles = selectedFiles ;
116+ setName (file .toString ());
83117 }
84118
85- public OutputBox (T userData , String text ) {
86- this .userData = userData ;
87- setLayout (new BorderLayout ());
88- output .setEditable (false );
89- add (output , 0 );
90- buttons .setVisible (false );
91- output .addMouseListener (new MouseAdapter () {
92- @ Override
93- public void mouseEntered (MouseEvent e ) {
94- buttons .setVisible (true );
95- }
96-
97- @ Override
98- public void mouseExited (MouseEvent e ) {
99- buttons .setVisible (false );
100- }
101- });
102- output .setText (text );
103-
104- setBorder (BorderFactory .createEmptyBorder (10 , 10 , 10 , 10 ));
119+ @ Override
120+ public void actionPerformed (ActionEvent e ) {
121+ var chk = (JCheckBox ) e .getSource ();
122+ if (chk .isSelected ()) {
123+ selectedFiles .add (file );
124+ } else {
125+ selectedFiles .remove (file );
126+ }
105127 }
128+ }
106129
107- @ Override
108- public void setBackground (Color bg ) {
109- super .setBackground (bg );
110- if (output != null ) output .setBackground (bg );
130+ private void populateFiles () {
131+ try {
132+ tblFiles .removeAll ();
133+ LlmSession session = LlmUtils .getSession (mediator .getSelectedProof ());
134+ List <URI > possibleFiles = new ArrayList <>(LlmUtils .getPossibleFiles (mediator .getSelectedProof ()));
135+ Set <URI > selectedFiles = session .getSelectedFiles ();
136+ possibleFiles .sort (Comparator .comparing (URI ::toString ));
137+ for (URI file : possibleFiles ) {
138+ tblFiles .add (new JCheckBox (new AddFileAction (file , selectedFiles )));
139+ }
140+ tblFiles .invalidate ();
141+ } catch (IOException e ) {
142+ throw new RuntimeException (e );
111143 }
112144 }
113145
114146 private OutputBox <String > addInput (String text ) {
115- var o = addBox (text , new RepromptAction (text ));
147+ var o = addBox (new LlmPromptModel ( LlmPromptModel . Kind . INPUT , text , text ) , new RepromptAction (text ));
116148 o .setBackground (COLOR_BG_INPUT .get ());
117149 return o ;
118150 }
119151
120- private <T > OutputBox <T > addBox (T data , Action ... actions ) {
152+ private <T > OutputBox <T > addBox (LlmPromptModel < T > data , Action ... actions ) {
121153 OutputBox <T > box = new OutputBox <>(data );
122154 for (Action it : actions ) {
123155 box .menu .add (it );
124156 }
125157 pOutput .add (box , new CC ().growX ());
158+ box .setBackground (data .kind ().background ().get ());
126159 return box ;
127160 }
128161
129162 private void handle (Map <String , Object > jsonResponse ) {
130163 LOGGER .info ("LLM prompt {}" , jsonResponse );
131- var o = new OutputBox <>(jsonResponse ,
132- ((Map <String , Object >) ((Map <String , Object >) ((List <?>) jsonResponse .get ("choices" )).get (0 )).get ("message" )).get ("content" ).toString ());
133- pOutput .add (o , new CC ().growX ());
134- o .setBackground (COLOR_BG_ANSWER .get ());
164+ final var text = ((Map <String , Object >) ((Map <String , Object >) ((List <?>)
165+ jsonResponse .get ("choices" )).get (0 )).get ("message" )).get ("content" ).toString ();
166+ addBox (new LlmPromptModel <>(LlmPromptModel .Kind .OUTPUT , text , jsonResponse ));
135167 }
136168
137169 private void handle (Throwable e ) {
138- LOGGER .error ("Error during LLM prompt" , e );
139- var box = addBox (e );
140- box .setBackground (COLOR_BG_ERROR .get ());
170+ addBox (new LlmPromptModel <>(LlmPromptModel .Kind .ERROR , e .toString (), e ));
141171 }
142172
143173 @ Override
@@ -151,8 +181,34 @@ private void handle(Throwable e) {
151181 }
152182
153183 @ Override
154- public @ NonNull Collection <Action > getTitleActions () {
155- return List .of (actionSwitchOrientation );
184+ public @ NonNull Collection <CAction > getTitleCActions () {
185+ Supplier <CMenu > supplier = () -> {
186+ CMenu menu = new CMenu ();
187+ menu .add (actionSwitchOrientation .toCAction ());
188+
189+ CMenu menuModels = new CMenu ("Models" , null );
190+ menu .add (menuModels );
191+ var groupModels = new CRadioGroup ();
192+ var llmSession = LlmUtils .getSession (MainWindow .getInstance ().getMediator ().getSelectedProof ());
193+
194+ for (var m : LlmSettings .INSTANCE .getAvailableModels ()) {
195+ var selected = m .equals (llmSession .getModel ());
196+ final var action = new CRadioButton (m , null ) {
197+ @ Override
198+ protected void changed () {
199+ llmSession .setModel (m );
200+ }
201+ };
202+ action .setSelected (selected );
203+ groupModels .add (action );
204+ menuModels .add (action );
205+ }
206+ return menu ;
207+ };
208+
209+ var a = new DynamicCMenu ("Settings" , IconFactory .properties (MainWindow .TOOLBAR_ICON_SIZE ), supplier );
210+ var help = HelpFacade .createHelpButton ("user/LLM/" );
211+ return List .of (help , a );
156212 }
157213
158214 class SwitchOrientationAction extends KeyAction {
@@ -170,7 +226,7 @@ public void actionPerformed(ActionEvent e) {
170226 }
171227 }
172228
173- class SelectContextAction extends KeyAction {
229+ static class SelectContextAction extends KeyAction {
174230 @ Override
175231 public void actionPerformed (ActionEvent e ) {
176232
@@ -231,3 +287,40 @@ public void actionPerformed(ActionEvent e) {
231287 }
232288 }
233289}
290+
291+ class OutputBox <T > extends JPanel {
292+ protected final LlmPromptModel <T > model ;
293+ protected final JTextArea output = new JTextArea ();
294+ protected final JPanel buttons = new JPanel ();
295+ protected final JPopupMenu menu = new JPopupMenu ();
296+
297+ public OutputBox (LlmPromptModel <T > userData ) {
298+ this .model = userData ;
299+ setLayout (new BorderLayout ());
300+ output .setEditable (false );
301+ add (output , 0 );
302+ buttons .setVisible (false );
303+ output .addMouseListener (new MouseAdapter () {
304+ @ Override
305+ public void mouseEntered (MouseEvent e ) {
306+ buttons .setVisible (true );
307+ }
308+
309+ @ Override
310+ public void mouseExited (MouseEvent e ) {
311+ buttons .setVisible (false );
312+ }
313+ });
314+ output .setText (userData .text ());
315+ output .setLineWrap (true ); //Makes the text wrap to the next line
316+ output .setWrapStyleWord (true ); //Makes the text wrap full words, not just letters
317+ output .setComponentPopupMenu (menu );
318+ setBorder (BorderFactory .createEmptyBorder (10 , 10 , 10 , 10 ));
319+ }
320+
321+ @ Override
322+ public void setBackground (Color bg ) {
323+ super .setBackground (bg );
324+ if (output != null ) output .setBackground (bg );
325+ }
326+ }
0 commit comments