Skip to content

Commit

Permalink
Merge pull request #409 from samkerr4coding/fix/issue-243
Browse files Browse the repository at this point in the history
fix(issue-243): Fix UI chatresponsemodel
  • Loading branch information
stephanj authored Dec 19, 2024
2 parents 3cf8c16 + e03389b commit 2ecdb4a
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 163 deletions.
42 changes: 13 additions & 29 deletions src/main/java/com/devoxx/genie/ui/panel/ChatResponsePanel.java
Original file line number Diff line number Diff line change
@@ -1,46 +1,30 @@
package com.devoxx.genie.ui.panel;

import com.devoxx.genie.model.request.ChatMessageContext;
import com.devoxx.genie.service.FileListManager;
import com.devoxx.genie.ui.panel.chatresponse.*;
import com.devoxx.genie.ui.settings.DevoxxGenieStateService;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.awt.*;

public class ChatResponsePanel extends BackgroundPanel {

private final transient ChatMessageContext chatMessageContext;

public ChatResponsePanel(@NotNull ChatMessageContext chatMessageContext) {
super(chatMessageContext.getId());
this.chatMessageContext = chatMessageContext;
setLayout(new GridBagLayout());
buildResponsePanel();
}

private void buildResponsePanel() {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1; // full width components
gbc.fill = GridBagConstraints.HORIZONTAL; // Fill horizontally
gbc.anchor = GridBagConstraints.WEST; // Anchor to the west (left)

add(new ResponseHeaderPanel(chatMessageContext), gbc);
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

gbc.gridy++;
add(new ResponseContentPanel(chatMessageContext), gbc);
add(new ResponseHeaderPanel(chatMessageContext));
add(new ResponseDocumentPanel(chatMessageContext));

gbc.gridy++;
add(new FileListPanel(chatMessageContext, FileListManager.getInstance().getFiles()), gbc);
if (chatMessageContext.hasFiles()) {
add(new FileListPanel(chatMessageContext));
}

gbc.gridy++;
add(new SemanticSearchReferencesPanel(chatMessageContext, chatMessageContext.getSemanticReferences()), gbc);
if (chatMessageContext.getSemanticReferences() != null && !chatMessageContext.getSemanticReferences().isEmpty()) {
add(new SemanticSearchReferencesPanel(chatMessageContext));
}

gbc.gridy++;
JPanel metricPanelWrapper = new JPanel(new FlowLayout(FlowLayout.LEFT));
metricPanelWrapper.add(new MetricExecutionInfoPanel(chatMessageContext));
add(metricPanelWrapper, gbc);
if (Boolean.TRUE.equals(DevoxxGenieStateService.getInstance().getShowExecutionTime())) {
add(new MetricExecutionInfoPanel(chatMessageContext));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
package com.devoxx.genie.ui.panel.chatresponse;

import com.devoxx.genie.model.request.ChatMessageContext;
import com.devoxx.genie.service.FileListManager;
import com.devoxx.genie.ui.component.ExpandablePanel;
import com.intellij.openapi.vfs.VirtualFile;

import javax.swing.*;
import java.util.List;
public class FileListPanel extends ExpandablePanel {

// FileListPanel.java
public class FileListPanel extends JPanel {
public FileListPanel(ChatMessageContext chatMessageContext, List<VirtualFile> files) {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setOpaque(false);
if (chatMessageContext.hasFiles()) {
add(new ExpandablePanel(chatMessageContext, files));
}
public FileListPanel(ChatMessageContext chatMessageContext) {
super(chatMessageContext, FileListManager.getInstance().getFiles());
}
}
Original file line number Diff line number Diff line change
@@ -1,65 +1,58 @@
package com.devoxx.genie.ui.panel.chatresponse;

import com.devoxx.genie.model.enumarations.ModelProvider;
import com.devoxx.genie.model.request.ChatMessageContext;
import com.devoxx.genie.ui.settings.DevoxxGenieStateService;
import com.devoxx.genie.service.ProjectContentService;
import com.devoxx.genie.util.DefaultLLMSettingsUtil;
import com.intellij.ui.JBColor;
import com.knuddels.jtokkit.api.Encoding;
import dev.langchain4j.model.output.TokenUsage;

import javax.swing.*;
import java.awt.*;
import java.text.NumberFormat;
import java.util.Locale;

// MetricExecutionInfoPanel.java
public class MetricExecutionInfoPanel extends JPanel {

private static final float METRIC_FONT_SIZE = 12f;
private static final double MS_TO_SECONDS = 1000.0;

public MetricExecutionInfoPanel(ChatMessageContext chatMessageContext) {
setLayout(new FlowLayout(FlowLayout.LEFT));
setOpaque(false);

if (shouldShowExecutionTime(chatMessageContext)) {
add(createMetricLabel(chatMessageContext));
}
}
String metricInfoLabel = String.format("ϟ %.2fs", chatMessageContext.getExecutionTimeMs() / 1000.0);
TokenUsage tokenUsage = chatMessageContext.getTokenUsage();

private boolean shouldShowExecutionTime(ChatMessageContext chatMessageContext) {
return Boolean.TRUE.equals(DevoxxGenieStateService.getInstance().getShowExecutionTime());
}
if (tokenUsage != null) {
String cost = "";
if (DefaultLLMSettingsUtil.isApiKeyBasedProvider(chatMessageContext.getLanguageModel().getProvider())) {
cost = String.format("- %.5f $", chatMessageContext.getCost());
}

private JLabel createMetricLabel(ChatMessageContext chatMessageContext) {
String metricInfo = buildMetricInfo(chatMessageContext);
JLabel label = new JLabel(metricInfo);
label.setForeground(JBColor.GRAY);
label.setFont(label.getFont().deriveFont(METRIC_FONT_SIZE));
return label;
}
tokenUsage = calcOllamaInputTokenCount(chatMessageContext, tokenUsage);

private String buildMetricInfo(ChatMessageContext chatMessageContext) {
String metricInfoLabel = String.format("ϟ %.2fs", chatMessageContext.getExecutionTimeMs() / MS_TO_SECONDS);
TokenUsage tokenUsage = chatMessageContext.getTokenUsage();
if (tokenUsage != null) {
metricInfoLabel = buildTokenUsageLabel(tokenUsage, metricInfoLabel, chatMessageContext);
}
return metricInfoLabel;
}
NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.getDefault());
String formattedInputTokens = numberFormat.format(tokenUsage.inputTokenCount());
String formattedOutputTokens = numberFormat.format(tokenUsage.outputTokenCount());

private String buildTokenUsageLabel(TokenUsage tokenUsage, String metricInfoLabel, ChatMessageContext chatMessageContext) {
String cost = "";
if (DefaultLLMSettingsUtil.isApiKeyBasedProvider(chatMessageContext.getLanguageModel().getProvider())) {
cost = String.format("- %.5f $", chatMessageContext.getCost());
metricInfoLabel += String.format(" - Tokens ↑ %s ↓️ %s %s", formattedInputTokens, formattedOutputTokens, cost);
}

// ... (Implementation for calculating token usage, you can move the relevant code here)

NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.getDefault());
String formattedInputTokens = numberFormat.format(tokenUsage.inputTokenCount());
String formattedOutputTokens = numberFormat.format(tokenUsage.outputTokenCount());
JLabel tokenLabel = new JLabel(metricInfoLabel);
tokenLabel.setForeground(JBColor.GRAY);
tokenLabel.setFont(tokenLabel.getFont().deriveFont(12f));
add(tokenLabel);
}

metricInfoLabel += String.format(" - Tokens ↑ %s ↓️ %s %s", formattedInputTokens, formattedOutputTokens, cost);
return metricInfoLabel;
private static TokenUsage calcOllamaInputTokenCount(ChatMessageContext chatMessageContext, TokenUsage tokenUsage) {
if (chatMessageContext.getLanguageModel().getProvider().equals(ModelProvider.Ollama)) {
int inputContextTokens = 0;
if (chatMessageContext.getContext() != null) {
Encoding encodingForProvider = ProjectContentService.getEncodingForProvider(chatMessageContext.getLanguageModel().getProvider());
inputContextTokens = encodingForProvider.encode(chatMessageContext.getContext()).size();
}
tokenUsage = new TokenUsage(tokenUsage.inputTokenCount() + inputContextTokens, tokenUsage.outputTokenCount());
}
return tokenUsage;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package com.devoxx.genie.ui.panel.chatresponse;

import com.devoxx.genie.model.request.ChatMessageContext;
import com.devoxx.genie.model.request.EditorInfo;
import com.devoxx.genie.service.gitdiff.GitMergeService;
import com.devoxx.genie.ui.processor.NodeProcessorFactory;
import com.devoxx.genie.ui.settings.DevoxxGenieStateService;
import com.intellij.openapi.vfs.VirtualFile;
import org.commonmark.node.Block;
import org.commonmark.node.FencedCodeBlock;
import org.commonmark.node.IndentedCodeBlock;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;

public class ResponseDocumentPanel extends JPanel {


private final transient ChatMessageContext chatMessageContext;

public ResponseDocumentPanel(@NotNull ChatMessageContext chatMessageContext) {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

this.chatMessageContext = chatMessageContext;

String markDownResponse = chatMessageContext.getAiMessage().text();

System.out.println(markDownResponse);

Node document = Parser.builder().build().parse(markDownResponse);

DevoxxGenieStateService stateService = DevoxxGenieStateService.getInstance();

if (Boolean.TRUE.equals(stateService.getGitDiffActivated())) {
processGitDiff(chatMessageContext, document);
}

addDocumentNodesToPanel(document);
}

private void processGitDiff(@NotNull ChatMessageContext chatMessageContext, @NotNull Node document) {
// Get original file info
EditorInfo editorInfo = chatMessageContext.getEditorInfo();
if (editorInfo == null) {
return;
}

if (editorInfo.getSelectedFiles() != null && !editorInfo.getSelectedFiles().isEmpty()) {
List<VirtualFile> files = editorInfo.getSelectedFiles();
List<String> modifiedContents = new ArrayList<>();

// Collect modified contents from code blocks
Node node = document.getFirstChild();
while (node != null) {
if (node instanceof FencedCodeBlock codeBlock) {
modifiedContents.add(codeBlock.getLiteral());
}
node = node.getNext();
}

GitMergeService.getInstance().showDiffView(
chatMessageContext.getProject(),
files.get(0),
modifiedContents.isEmpty() ? "" : modifiedContents.get(0));
}
}


/**
* Add document nodes to the panel.
*
* @param document the document
*/
private void addDocumentNodesToPanel(@NotNull Node document) {
JPanel jPanel = createPanel();

Node node = document.getFirstChild();

while (node != null) {
JPanel panel;
if (node instanceof FencedCodeBlock fencedCodeBlock) {
panel = processBlock(fencedCodeBlock);
} else if (node instanceof IndentedCodeBlock indentedCodeBlock) {
panel = processBlock(indentedCodeBlock);
} else {
panel = processBlock((Block) node);
}

setFullWidth(panel);
jPanel.add(panel);
node = node.getNext();
}

add(jPanel);
}

private void setFullWidth(@NotNull JPanel panel) {
Dimension maximumSize = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
panel.setMaximumSize(maximumSize);
panel.setMinimumSize(new Dimension(panel.getPreferredSize().width, panel.getPreferredSize().height));
}

private @NotNull JPanel createPanel() {
JPanel jPanel = new JPanel();
jPanel.setLayout(new BoxLayout(jPanel, BoxLayout.Y_AXIS));
jPanel.setOpaque(false);
return jPanel;
}

/**
* Process a block and return a panel.
*
* @param theBlock the block
* @return the panel
*/
private JPanel processBlock(Block theBlock) {
return NodeProcessorFactory.createProcessor(chatMessageContext, theBlock).processNode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class ResponseHeaderPanel extends JBPanel<ResponseHeaderPanel> {
*/
public ResponseHeaderPanel(@NotNull ChatMessageContext chatMessageContext) {
super(new BorderLayout());

setBackground(Color.BLUE);
andTransparent()
.withMaximumHeight(30)
.withPreferredHeight(30);
Expand Down
Loading

0 comments on commit 2ecdb4a

Please sign in to comment.