Skip to content

Commit

Permalink
feat: add webpage documentation support (#650)
Browse files Browse the repository at this point in the history
* feat: documentation support while chatting

* feat: support managing web documentation entries
  • Loading branch information
carlrobertoh authored Aug 13, 2024
1 parent 4df8c14 commit b4ef573
Show file tree
Hide file tree
Showing 33 changed files with 1,132 additions and 610 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ codegpt-core/src/main/java/grammar/*
!codegpt-core/src/main/java/grammar/TypeScriptParserBase.java

.gradle
.kotlin
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jsoup = "1.17.2"
jtokkit = "1.0.0"
junit = "5.10.2"
kotlin = "2.0.0"
llm-client = "0.8.11"
llm-client = "0.8.12"
okio = "3.9.0"
tree-sitter = "0.22.6a"

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/ee/carlrobert/codegpt/CodeGPTKeys.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ee.carlrobert.codegpt;

import com.intellij.openapi.util.Key;
import ee.carlrobert.codegpt.ui.DocumentationDetails;
import ee.carlrobert.llm.client.codegpt.CodeGPTUserDetails;
import java.util.List;

Expand All @@ -14,4 +15,6 @@ public class CodeGPTKeys {
Key.create("codegpt.imageAttachmentFilePath");
public static final Key<CodeGPTUserDetails> CODEGPT_USER_DETAILS =
Key.create("codegpt.userDetails");
public static final Key<DocumentationDetails> ADDED_DOCUMENTATION =
Key.create("codegpt.addedDocumentation");
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import ee.carlrobert.llm.client.openai.completion.request.OpenAIImageUrl;
import ee.carlrobert.llm.client.openai.completion.request.OpenAIMessageImageURLContent;
import ee.carlrobert.llm.client.openai.completion.request.OpenAIMessageTextContent;
import ee.carlrobert.llm.client.openai.completion.request.RequestDocumentationDetails;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
Expand Down Expand Up @@ -227,6 +228,14 @@ public OpenAIChatCompletionRequest buildOpenAIChatCompletionRequest(
// tri-state boolean
requestBuilder.setWebSearchIncluded(true);
}
var documentationDetails =
callParameters.getMessage().getDocumentationDetails();
if (documentationDetails != null) {
var requestDocumentationDetails = new RequestDocumentationDetails();
requestDocumentationDetails.setName(documentationDetails.getName());
requestDocumentationDetails.setUrl(documentationDetails.getUrl());
requestBuilder.setDocumentationDetails(requestDocumentationDetails);
}
return requestBuilder.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import ee.carlrobert.codegpt.ui.DocumentationDetails;
import ee.carlrobert.llm.client.you.completion.YouSerpResult;
import java.util.List;
import java.util.Objects;
Expand All @@ -18,6 +19,7 @@ public class Message {
private List<String> referencedFilePaths;
private @Nullable String imageFilePath;
private boolean webSearchIncluded;
private DocumentationDetails documentationDetails;

public Message(String prompt, String response) {
this(prompt);
Expand Down Expand Up @@ -90,6 +92,14 @@ public void setWebSearchIncluded(boolean webSearchIncluded) {
this.webSearchIncluded = webSearchIncluded;
}

public DocumentationDetails getDocumentationDetails() {
return documentationDetails;
}

public void setDocumentationDetails(DocumentationDetails documentationDetails) {
this.documentationDetails = documentationDetails;
}

@Override
public boolean equals(Object obj) {
if (obj == this) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,12 @@ public boolean isImageActionSupported() {
.getState()
.getChatCompletionSettings()
.getModel();
yield List.of("gpt-4o", "gpt-4o-mini", "claude-3-opus").contains(codegptModel);
yield List.of(
"gpt-4o",
"gpt-4o-mini",
"claude-3-opus",
"claude-3.5-sonnet"
).contains(codegptModel);
case OPENAI:
var openaiModel = ApplicationManager.getApplication().getService(OpenAISettings.class)
.getState()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,12 @@ private Unit handleSubmit(String text, boolean webSearchIncluded) {
}
message.setUserMessage(text);
message.setWebSearchIncluded(webSearchIncluded);

var addedDocumentation = CodeGPTKeys.ADDED_DOCUMENTATION.get(project);
if (addedDocumentation != null) {
message.setDocumentationDetails(addedDocumentation);
}

sendMessage(message, ConversationType.DEFAULT);
return Unit.INSTANCE;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,22 @@
import com.intellij.ui.components.JBLabel;
import com.intellij.util.ui.JBFont;
import com.intellij.util.ui.JBUI;
import ee.carlrobert.codegpt.CodeGPTBundle;
import ee.carlrobert.codegpt.CodeGPTKeys;
import ee.carlrobert.codegpt.Icons;
import ee.carlrobert.codegpt.conversations.message.Message;
import ee.carlrobert.codegpt.events.Details;
import ee.carlrobert.codegpt.settings.GeneralSettings;
import ee.carlrobert.codegpt.toolwindow.ui.WebpageList;
import java.awt.BorderLayout;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.UUID;
import javax.swing.DefaultListModel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import org.jetbrains.annotations.Nullable;

public class UserMessagePanel extends JPanel {

Expand All @@ -31,9 +38,13 @@ public UserMessagePanel(Project project, Message message, Disposable parentDispo
setBackground(ColorUtil.brighter(getBackground(), 2));
add(headerPanel, BorderLayout.NORTH);

var additionalContextPanel = getAdditionalContextPanel(project, message);
if (additionalContextPanel != null) {
add(additionalContextPanel, BorderLayout.CENTER);
}

var referencedFilePaths = message.getReferencedFilePaths();
if (referencedFilePaths != null && !referencedFilePaths.isEmpty()) {
add(new SelectedFilesAccordion(project, referencedFilePaths), BorderLayout.CENTER);
add(createResponseBody(
project,
message.getUserMessage(),
Expand All @@ -43,6 +54,30 @@ public UserMessagePanel(Project project, Message message, Disposable parentDispo
}
}

public @Nullable JPanel getAdditionalContextPanel(Project project, Message message) {
var addedDocumentation = CodeGPTKeys.ADDED_DOCUMENTATION.get(project);
var referencedFilePaths = message.getReferencedFilePaths();
if (addedDocumentation == null
&& (referencedFilePaths == null
|| referencedFilePaths.isEmpty())) {
return null;
}

var panel = new JPanel(new BorderLayout());
panel.setOpaque(false);
if (addedDocumentation != null) {
var listModel = new DefaultListModel<Details>();
listModel.addElement(new Details(UUID.randomUUID().toString(), addedDocumentation.getName(),
addedDocumentation.getUrl(), addedDocumentation.getUrl()));
panel.add(createWebpageListPanel(new WebpageList(listModel)), BorderLayout.NORTH);
}

if (referencedFilePaths != null && !referencedFilePaths.isEmpty()) {
panel.add(new SelectedFilesAccordion(project, referencedFilePaths), BorderLayout.NORTH);
}
return panel;
}

public void displayImage(String imageFilePath) {
try {
var path = Paths.get(imageFilePath);
Expand Down Expand Up @@ -77,4 +112,21 @@ private JBLabel createDisplayNameLabel() {
.withFont(JBFont.label().asBold())
.withBorder(JBUI.Borders.emptyBottom(6));
}

private static JPanel createWebpageListPanel(WebpageList webpageList) {
var title = new JPanel(new BorderLayout());
title.setOpaque(false);
title.setBorder(JBUI.Borders.empty(8, 0));
title.add(new JBLabel(CodeGPTBundle.get("userMessagePanel.documentation.title"))
.withFont(JBUI.Fonts.miniFont()), BorderLayout.LINE_START);
var listPanel = new JPanel(new BorderLayout());
listPanel.setOpaque(false);
listPanel.add(webpageList, BorderLayout.LINE_START);

var panel = new JPanel(new BorderLayout());
panel.setOpaque(false);
panel.add(title, BorderLayout.NORTH);
panel.add(listPanel, BorderLayout.CENTER);
return panel;
}
}
4 changes: 2 additions & 2 deletions src/main/java/ee/carlrobert/codegpt/ui/OverlayUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public static int showTokenLimitExceededDialog() {
CodeGPTBundle.get("dialog.tokenLimitExceeded.title"),
CodeGPTBundle.get("dialog.tokenLimitExceeded.description"))
.yesText(CodeGPTBundle.get("dialog.continue"))
.noText(CodeGPTBundle.get("dialog.cancel"))
.noText(CodeGPTBundle.get("shared.cancel"))
.icon(Default)
.doNotAsk(new DoNotAskOption.Adapter() {
@Override
Expand Down Expand Up @@ -132,7 +132,7 @@ public static int showTokenSoftLimitWarningDialog(int tokenCount) {
CodeGPTBundle.get("dialog.tokenSoftLimitExceeded.title"),
format(CodeGPTBundle.get("dialog.tokenSoftLimitExceeded.description"), tokenCount))
.yesText(CodeGPTBundle.get("dialog.continue"))
.noText(CodeGPTBundle.get("dialog.cancel"))
.noText(CodeGPTBundle.get("shared.cancel"))
.icon(Default)
.doNotAsk(new DoNotAskOption.Adapter() {
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ee.carlrobert.codegpt.settings.documentation

import com.intellij.openapi.components.*

@Service
@State(
name = "CodeGPT_DocumentationSettings",
storages = [Storage("CodeGPT_DocumentationSettings.xml")]
)
class DocumentationSettings :
SimplePersistentStateComponent<DocumentationSettingsState>(DocumentationSettingsState())

class DocumentationSettingsState : BaseState() {
var documentations by list<DocumentationDetailsState>()
}

class DocumentationDetailsState : BaseState() {
var name by string("CodeGPT Docs")
var url by string("https://docs.codegpt.ee")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ee.carlrobert.codegpt.settings.documentation

import com.intellij.openapi.options.Configurable
import javax.swing.JComponent

class DocumentationsConfigurable : Configurable {

private lateinit var component: DocumentationsSettingsForm

override fun getDisplayName(): String {
return "CodeGPT: Documentations"
}

override fun createComponent(): JComponent {
component = DocumentationsSettingsForm()
return component.createPanel()
}

override fun isModified(): Boolean = component.isModified()

override fun apply() {
component.applyChanges()
}

override fun reset() {
component.resetChanges()
}
}
Loading

0 comments on commit b4ef573

Please sign in to comment.