mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-07-09 17:28:59 +00:00
You API integration (#203)
* Ability to configure custom service * Add example preset templates, rename module * Custom service client impl * Add YOU API integration * Remove/ignore generated antlr classes * Remove text completion models(deprecated) * Remove unused code, fix settings state sync * Display model name/icon in the tool window * Update chat history UI * Fix model/service sync * Clear plugin state * Fix minor bugs, add settings sync tests * UI changes * Separate model configuration * Add support for overriding the completion path * Update Find Bugs prompt
This commit is contained in:
parent
a860054360
commit
37af74ebdf
125 changed files with 1673 additions and 1537 deletions
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
|
|
@ -16,6 +16,6 @@ updates:
|
|||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "gradle"
|
||||
directory: "/embeddings"
|
||||
directory: "/codegpt-core"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
|
|
|||
12
.gitignore
vendored
12
.gitignore
vendored
|
|
@ -1,9 +1,9 @@
|
|||
embeddings/src/main/java/grammar/*
|
||||
!embeddings/src/main/java/grammar/PythonLexerBase.java
|
||||
!embeddings/src/main/java/grammar/PythonParserBase.java
|
||||
!embeddings/src/main/java/grammar/PythonVersion.java
|
||||
!embeddings/src/main/java/grammar/TypeScriptLexerBase.java
|
||||
!embeddings/src/main/java/grammar/TypeScriptParserBase.java
|
||||
codegpt-core/src/main/java/grammar/*
|
||||
!codegpt-core/src/main/java/grammar/PythonLexerBase.java
|
||||
!codegpt-core/src/main/java/grammar/PythonParserBase.java
|
||||
!codegpt-core/src/main/java/grammar/PythonVersion.java
|
||||
!codegpt-core/src/main/java/grammar/TypeScriptLexerBase.java
|
||||
!codegpt-core/src/main/java/grammar/TypeScriptParserBase.java
|
||||
|
||||
.gradle
|
||||
build/
|
||||
|
|
|
|||
24
CHANGELOG.md
24
CHANGELOG.md
|
|
@ -1,4 +1,5 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
|
@ -8,21 +9,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
## [2.0.6] - 2023-08-29
|
||||
|
||||
### Removed
|
||||
|
||||
- Functionality to fetch/use OpenAI account name
|
||||
|
||||
### Fixed
|
||||
|
||||
- Rendering user input's newlines
|
||||
|
||||
### Added
|
||||
|
||||
- Support for closing other tabs (#172)
|
||||
- Support for configuring custom hosts for OpenAI and Azure services
|
||||
|
||||
## [2.0.5] - 2023-06-12
|
||||
|
||||
### Fixed
|
||||
|
||||
- Tool window not opening on editor actions (#157)
|
||||
|
||||
### Added
|
||||
|
||||
- Support for changing the editor action behaviour (#157)
|
||||
- Support for overriding completions request parameters (#152)
|
||||
- User text area autofocus on creating a new chat (#155)
|
||||
|
|
@ -31,10 +37,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
## [2.0.4] - 2023-05-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- TypeScript and C# code highlighting
|
||||
|
||||
- ToolWindow usability when virtual space option turned ON (#125)
|
||||
|
||||
### Added
|
||||
|
||||
- ToolWindow code editor copy/replace header actions
|
||||
- Custom prompt main editor action (#144)
|
||||
- Support for 2023.2 EAP builds (#149)
|
||||
|
|
@ -42,56 +51,71 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
## [2.0.3] - 2023-05-18
|
||||
|
||||
### Fixed
|
||||
|
||||
- Empty editor context menu item text (#137)
|
||||
- Temp file path resolving (#130)
|
||||
|
||||
### Improved
|
||||
|
||||
- Response streaming
|
||||
|
||||
### Added
|
||||
|
||||
- Reset chat window toolbar action (#138)
|
||||
|
||||
## [2.0.2] - 2023-05-16
|
||||
|
||||
### Fixed
|
||||
|
||||
- Settings deserialization error
|
||||
|
||||
### Improved
|
||||
|
||||
- Memory consumption by disposing unused editors which are no longer needed
|
||||
|
||||
### Removed
|
||||
|
||||
- Main editor focus stealing on response streaming
|
||||
|
||||
## [2.0.1] - 2023-05-14
|
||||
|
||||
### Added
|
||||
|
||||
- New GPT-3.5-16k model
|
||||
|
||||
### Fixed
|
||||
|
||||
- NPE when `displayName` couldn't be fetched
|
||||
|
||||
### Improved
|
||||
|
||||
- Proxy support by disabling the default trustmanager
|
||||
|
||||
### Removed
|
||||
|
||||
- Off-screen rendering setting option
|
||||
- Automatic textarea focus on stream completion (#126)
|
||||
|
||||
## [2.0.0] - 2023-05-03
|
||||
|
||||
### Added
|
||||
|
||||
- Automatic retry logic on stream timeouts
|
||||
|
||||
### Improved
|
||||
|
||||
- Input prompt text field UI/UX (height grows with the content)
|
||||
|
||||
### Removed
|
||||
|
||||
- Custom prompt editor action (users can now provide custom actions within the chat window itself)
|
||||
|
||||
### Replaced
|
||||
|
||||
- ToolWindow HTML content with native Swing components
|
||||
|
||||
### Secured
|
||||
|
||||
- `OPENAI_API_KEY` persistence, key is saved in the OS password safe from now on
|
||||
|
||||
[Unreleased]: https://github.com/carlrobertoh/CodeGPT/compare/v2.0.6...HEAD
|
||||
|
|
|
|||
|
|
@ -31,8 +31,7 @@ changelog {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project("embeddings", "instrumentedJar"))
|
||||
implementation(project(mapOf("path" to ":embeddings")))
|
||||
implementation(project(":codegpt-core"))
|
||||
|
||||
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.15.2")
|
||||
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2")
|
||||
|
|
@ -47,6 +46,7 @@ dependencies {
|
|||
|
||||
testImplementation("org.assertj:assertj-core:3.24.2")
|
||||
testImplementation("org.awaitility:awaitility:4.2.0")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.10.0")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.0")
|
||||
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.10.0")
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ intellij {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation("ee.carlrobert:openai-client:1.2.1")
|
||||
implementation("ee.carlrobert:llm-client:0.0.3")
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
|
|
|||
19
codegpt-core/build.gradle.kts
Normal file
19
codegpt-core/build.gradle.kts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
plugins {
|
||||
id("codegpt.java-conventions")
|
||||
id("antlr")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
antlr("org.antlr:antlr4:4.13.1")
|
||||
api("com.github.jelmerk:hnswlib-core:1.0.1")
|
||||
api("com.github.jelmerk:hnswlib-utils:1.0.1")
|
||||
implementation("org.antlr:antlr4-runtime:4.13.1")
|
||||
implementation("org.json:json:20230618")
|
||||
}
|
||||
|
||||
tasks {
|
||||
generateGrammarSource {
|
||||
outputDirectory = file("src/main/java/grammar")
|
||||
arguments = arguments + listOf("-package", "grammar")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.embeddings;
|
||||
package ee.carlrobert.embedding;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
|
@ -1,26 +1,24 @@
|
|||
package ee.carlrobert.codegpt.embeddings;
|
||||
package ee.carlrobert.embedding;
|
||||
|
||||
import static com.github.jelmerk.knn.util.VectorUtils.normalize;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.github.jelmerk.knn.Item;
|
||||
import com.github.jelmerk.knn.SearchResult;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import ee.carlrobert.codegpt.embeddings.splitter.SplitterFactory;
|
||||
import ee.carlrobert.openai.client.completion.CompletionClient;
|
||||
import ee.carlrobert.openai.client.completion.chat.ChatCompletionModel;
|
||||
import ee.carlrobert.openai.client.completion.chat.request.ChatCompletionMessage;
|
||||
import ee.carlrobert.openai.client.completion.chat.request.ChatCompletionRequest;
|
||||
import ee.carlrobert.openai.client.completion.chat.response.ChatCompletionResponse;
|
||||
import ee.carlrobert.openai.client.embeddings.EmbeddingsClient;
|
||||
import ee.carlrobert.llm.client.openai.OpenAIClient;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.OpenAIChatCompletionModel;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.request.OpenAIChatCompletionMessage;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.request.OpenAIChatCompletionRequest;
|
||||
import ee.carlrobert.splitter.SplitterFactory;
|
||||
import ee.carlrobert.vector.VectorStore;
|
||||
import ee.carlrobert.vector.Word;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
|
@ -32,22 +30,20 @@ public class EmbeddingsService {
|
|||
private static final Logger LOG = Logger.getInstance(EmbeddingsService.class);
|
||||
|
||||
private final VectorStore vectorStore;
|
||||
private final EmbeddingsClient embeddingsClient;
|
||||
private final CompletionClient completionClient;
|
||||
private final OpenAIClient openAIClient;
|
||||
|
||||
public EmbeddingsService(EmbeddingsClient embeddingsClient, CompletionClient completionClient, Path pluginBasePath) {
|
||||
this.embeddingsClient = embeddingsClient;
|
||||
this.completionClient = completionClient;
|
||||
public EmbeddingsService(OpenAIClient openAIClient, Path pluginBasePath) {
|
||||
this.openAIClient = openAIClient;
|
||||
this.vectorStore = VectorStore.getInstance(pluginBasePath);
|
||||
}
|
||||
|
||||
public List<double[]> getEmbeddings(List<String> chunks) {
|
||||
return embeddingsClient.getEmbeddings(chunks);
|
||||
return openAIClient.getEmbeddings(chunks);
|
||||
}
|
||||
|
||||
public GeneratedContextDetails buildRelevantContext(String prompt) {
|
||||
|
||||
public String buildPromptWithContext(String prompt) {
|
||||
try {
|
||||
var inputEmbedding = embeddingsClient.getEmbedding(getSearchQuery(prompt));
|
||||
var inputEmbedding = openAIClient.getEmbedding(getSearchQuery(prompt));
|
||||
var sortedResult = vectorStore.loadIndex()
|
||||
.findNearest(normalize(inputEmbedding), 10)
|
||||
.stream()
|
||||
|
|
@ -58,10 +54,12 @@ public class EmbeddingsService {
|
|||
var context = sortedResult.stream().map(Word::id).collect(Collectors.joining());
|
||||
var fileNames = sortedResult.stream().map(Word::getMeta).collect(Collectors.toSet());
|
||||
|
||||
return new GeneratedContextDetails(context, fileNames);
|
||||
return getResourceContent("/prompts/prompt-with-context.txt")
|
||||
.replace("{prompt}", prompt)
|
||||
.replace("{context}", new GeneratedContextDetails(context, fileNames).getContext());
|
||||
} catch (IOException e) {
|
||||
LOG.error("Unable to load vector index", e);
|
||||
return new GeneratedContextDetails(prompt, Collections.emptySet());
|
||||
return prompt;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -83,16 +81,15 @@ public class EmbeddingsService {
|
|||
}
|
||||
|
||||
private String getSearchQuery(String userPrompt) throws JsonProcessingException {
|
||||
var message = new ChatCompletionMessage("user", getResourceContent("/prompts/text-generator.txt").replace("{prompt}", userPrompt));
|
||||
var request = new ChatCompletionRequest.Builder(List.of(message))
|
||||
.setModel(ChatCompletionModel.GPT_4)
|
||||
var message = new OpenAIChatCompletionMessage("user", getResourceContent("/prompts/text-generator.txt").replace("{prompt}", userPrompt));
|
||||
var request = new OpenAIChatCompletionRequest.Builder(List.of(message))
|
||||
.setModel(OpenAIChatCompletionModel.GPT_4)
|
||||
.setMaxTokens(400)
|
||||
.setTemperature(0.1)
|
||||
.setStream(false)
|
||||
.build();
|
||||
|
||||
return new ObjectMapper()
|
||||
.readValue(completionClient.call(request), ChatCompletionResponse.class)
|
||||
return openAIClient.getChatCompletion(request)
|
||||
.getChoices()
|
||||
.get(0)
|
||||
.getMessage()
|
||||
|
|
@ -104,7 +101,7 @@ public class EmbeddingsService {
|
|||
var codeSplitter = SplitterFactory.getCodeSplitter(fileExtension);
|
||||
if (codeSplitter != null) {
|
||||
var chunks = codeSplitter.split(checkedFile.getFileName(), checkedFile.getFileContent());
|
||||
var embeddings = embeddingsClient.getEmbeddings(chunks);
|
||||
var embeddings = openAIClient.getEmbeddings(chunks);
|
||||
for (int i = 0; i < chunks.size(); i++) {
|
||||
prevEmbeddings.add(new Word(chunks.get(i), checkedFile.getFileName(), normalize(embeddings.get(i))));
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.embeddings;
|
||||
package ee.carlrobert.embedding;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.embeddings.splitter;
|
||||
package ee.carlrobert.splitter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.embeddings.splitter;
|
||||
package ee.carlrobert.splitter;
|
||||
|
||||
import grammar.JavaLexer;
|
||||
import grammar.JavaParser;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.embeddings.splitter;
|
||||
package ee.carlrobert.splitter;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.embeddings.splitter;
|
||||
package ee.carlrobert.splitter;
|
||||
|
||||
import grammar.PythonLexer;
|
||||
import grammar.PythonParser;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.embeddings.splitter;
|
||||
package ee.carlrobert.splitter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.embeddings.splitter;
|
||||
package ee.carlrobert.splitter;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.embeddings.splitter;
|
||||
package ee.carlrobert.splitter;
|
||||
|
||||
import grammar.TypeScriptLexer;
|
||||
import grammar.TypeScriptParser;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.embeddings;
|
||||
package ee.carlrobert.vector;
|
||||
|
||||
import com.github.jelmerk.knn.DistanceFunctions;
|
||||
import com.github.jelmerk.knn.Item;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.embeddings;
|
||||
package ee.carlrobert.vector;
|
||||
|
||||
import com.github.jelmerk.knn.Item;
|
||||
import java.util.Arrays;
|
||||
BIN
codegpt-core/src/test/resources/indexes/hnsw.index
Normal file
BIN
codegpt-core/src/test/resources/indexes/hnsw.index
Normal file
Binary file not shown.
|
|
@ -1,19 +0,0 @@
|
|||
plugins {
|
||||
id("codegpt.java-conventions")
|
||||
id("antlr")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
antlr("org.antlr:antlr4:4.13.1")
|
||||
implementation("org.antlr:antlr4-runtime:4.13.1")
|
||||
implementation("org.json:json:20230618")
|
||||
implementation("com.github.jelmerk:hnswlib-core:1.1.0")
|
||||
implementation("com.github.jelmerk:hnswlib-utils:1.1.0")
|
||||
}
|
||||
|
||||
tasks {
|
||||
generateGrammarSource {
|
||||
outputDirectory = file("src/main/java/grammar")
|
||||
arguments = arguments + listOf("-package", "grammar")
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ pluginGroup = ee.carlrobert
|
|||
pluginName = CodeGPT
|
||||
pluginRepositoryUrl = https://github.com/carlrobertoh/CodeGPT
|
||||
# SemVer format -> https://semver.org
|
||||
pluginVersion = 2.0.6
|
||||
pluginVersion = 2.1.0
|
||||
|
||||
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
|
||||
pluginSinceBuild = 213
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
rootProject.name = "CodeGPT"
|
||||
include(":embeddings")
|
||||
project(":embeddings").projectDir = File("embeddings")
|
||||
include("codegpt-core")
|
||||
project(":codegpt-core").projectDir = File("codegpt-core")
|
||||
|
|
@ -32,8 +32,4 @@ public final class CodeGPTPlugin {
|
|||
public static @NotNull String getProjectIndexStorePath(@NotNull Project project) {
|
||||
return getIndexStorePath() + File.separator + project.getName();
|
||||
}
|
||||
|
||||
public static @NotNull String getProjectIndexPath(@NotNull Project project) {
|
||||
return getProjectIndexStorePath(project) + File.separator + "hnsw.index";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,28 +5,23 @@ import com.intellij.openapi.components.Service;
|
|||
import com.knuddels.jtokkit.Encodings;
|
||||
import com.knuddels.jtokkit.api.Encoding;
|
||||
import com.knuddels.jtokkit.api.EncodingRegistry;
|
||||
import ee.carlrobert.codegpt.settings.state.ModelSettingsState;
|
||||
import ee.carlrobert.openai.client.completion.chat.request.ChatCompletionMessage;
|
||||
import com.knuddels.jtokkit.api.EncodingType;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.request.OpenAIChatCompletionMessage;
|
||||
|
||||
@Service
|
||||
public final class EncodingManager {
|
||||
|
||||
private final EncodingRegistry registry = Encodings.newDefaultEncodingRegistry();
|
||||
private Encoding encoding;
|
||||
private final Encoding encoding = registry.getEncoding(EncodingType.CL100K_BASE);
|
||||
|
||||
private EncodingManager() {
|
||||
setEncoding(ModelSettingsState.getInstance().getCompletionModel());
|
||||
}
|
||||
|
||||
public static EncodingManager getInstance() {
|
||||
return ApplicationManager.getApplication().getService(EncodingManager.class);
|
||||
}
|
||||
|
||||
public void setEncoding(String modelName) {
|
||||
this.encoding = registry.getEncodingForModel(modelName).orElseThrow();
|
||||
}
|
||||
|
||||
public int countMessageTokens(ChatCompletionMessage message) {
|
||||
public int countMessageTokens(OpenAIChatCompletionMessage message) {
|
||||
var tokensPerMessage = 4; // every message follows <|start|>{role/name}\n{content}<|end|>\n
|
||||
return encoding.countTokens(message.getRole() + message.getContent()) + tokensPerMessage;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ public final class Icons {
|
|||
public static final Icon DefaultIcon = IconLoader.getIcon("/icons/codegpt.svg", Icons.class);
|
||||
public static final Icon DefaultSmallIcon = IconLoader.getIcon("/icons/codegpt-small.svg", Icons.class);
|
||||
public static final Icon SendIcon = IconLoader.getIcon("/icons/send.svg", Icons.class);
|
||||
public static final Icon OpenAIIcon = IconLoader.getIcon("/icons/openai.svg", Icons.class);
|
||||
public static final Icon AzureIcon = IconLoader.getIcon("/icons/azure.svg", Icons.class);
|
||||
public static final Icon YouIcon = IconLoader.getIcon("/icons/you.svg", Icons.class);
|
||||
public static final ImageIcon DefaultImageIcon = getImageIcon("/icons/chatgpt.png");
|
||||
|
||||
private static ImageIcon getImageIcon(String path) {
|
||||
|
|
|
|||
|
|
@ -10,9 +10,11 @@ import ee.carlrobert.codegpt.actions.editor.EditorActionsUtil;
|
|||
import ee.carlrobert.codegpt.credentials.UserCredentialsManager;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.user.UserManager;
|
||||
import ee.carlrobert.codegpt.user.auth.AuthenticationError;
|
||||
import ee.carlrobert.codegpt.user.auth.AuthenticationHandler;
|
||||
import ee.carlrobert.codegpt.user.auth.AuthenticationService;
|
||||
import ee.carlrobert.codegpt.user.auth.SessionVerificationJob;
|
||||
import ee.carlrobert.codegpt.user.auth.response.AuthenticationResponse;
|
||||
import ee.carlrobert.codegpt.util.OverlayUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.quartz.JobBuilder;
|
||||
|
|
@ -31,9 +33,8 @@ public class PluginStartupActivity implements StartupActivity {
|
|||
public void runActivity(@NotNull Project project) {
|
||||
EditorActionsUtil.refreshActions();
|
||||
|
||||
var userManager = UserManager.getInstance();
|
||||
var session = userManager.getSession();
|
||||
if (session == null || session.isExpired()) {
|
||||
var authenticationResponse = UserManager.getInstance().getAuthenticationResponse();
|
||||
if (authenticationResponse == null) {
|
||||
handleAuthentication();
|
||||
} else {
|
||||
startSessionVerificationJob();
|
||||
|
|
@ -78,7 +79,7 @@ public class PluginStartupActivity implements StartupActivity {
|
|||
|
||||
private void handleAuthentication() {
|
||||
var settings = SettingsState.getInstance();
|
||||
if (settings == null || !settings.isPreviouslySignedIn()) {
|
||||
if (!settings.isPreviouslySignedIn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -87,20 +88,20 @@ public class PluginStartupActivity implements StartupActivity {
|
|||
AuthenticationService.getInstance()
|
||||
.signInAsync(settings.getEmail(), password, new AuthenticationHandler() {
|
||||
@Override
|
||||
public void handleAuthenticated() {
|
||||
public void handleAuthenticated(AuthenticationResponse authenticationResponse) {
|
||||
OverlayUtils.showNotification("Authentication successful.", NotificationType.INFORMATION);
|
||||
startSessionVerificationJob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleInvalidCredentials() {
|
||||
OverlayUtils.showNotification("Authentication unsuccessful. Invalid credentials provided.", NotificationType.ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleGenericError() {
|
||||
OverlayUtils.showNotification("Something went wrong while trying to authenticate.", NotificationType.ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(AuthenticationError authenticationError) {
|
||||
OverlayUtils.showNotification(authenticationError.getErrorMessage(), NotificationType.ERROR);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import org.apache.commons.text.CaseUtils;
|
|||
public class EditorActionsUtil {
|
||||
|
||||
public static Map<String, String> DEFAULT_ACTIONS = new LinkedHashMap<>(Map.of(
|
||||
"Find Bugs", "Find bugs in the selected code {{selectedCode}}",
|
||||
"Find Bugs", "Find bugs and output code with bugs fixed in the following code: {{selectedCode}}",
|
||||
"Write Tests", "Write Tests for the selected code {{selectedCode}}",
|
||||
"Explain", "Explain the selected code {{selectedCode}}",
|
||||
"Refactor", "Refactor the selected code {{selectedCode}}",
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public class DeleteAllConversationsAction extends AnAction {
|
|||
var project = event.getProject();
|
||||
if (project != null) {
|
||||
ConversationService.getInstance().clearAll();
|
||||
StandardChatToolWindowContentManager.getInstance(project).resetTabbedPane();
|
||||
StandardChatToolWindowContentManager.getInstance(project).resetActiveTab();
|
||||
}
|
||||
this.onRefresh.run();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ import org.jetbrains.annotations.NotNull;
|
|||
|
||||
public class DeleteConversationAction extends AnAction {
|
||||
|
||||
private final Runnable onRefresh;
|
||||
private final Runnable onDelete;
|
||||
|
||||
public DeleteConversationAction(Runnable onRefresh) {
|
||||
public DeleteConversationAction(Runnable onDelete) {
|
||||
super("Delete Conversation", "Delete single conversation", AllIcons.General.Remove);
|
||||
this.onRefresh = onRefresh;
|
||||
this.onDelete = onDelete;
|
||||
EditorActionsUtil.registerOrReplaceAction(this);
|
||||
}
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ public class DeleteConversationAction extends AnAction {
|
|||
var project = event.getProject();
|
||||
if (project != null) {
|
||||
ConversationService.getInstance().deleteSelectedConversation();
|
||||
onRefresh.run();
|
||||
onDelete.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,6 @@ public class MoveUpAction extends MoveAction {
|
|||
|
||||
@Override
|
||||
protected Optional<Conversation> getConversation(@NotNull Project project) {
|
||||
return project.getService(ConversationService.class).getPreviousConversation();
|
||||
return ConversationService.getInstance().getPreviousConversation();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,42 +2,31 @@ package ee.carlrobert.codegpt.completions;
|
|||
|
||||
import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
|
||||
import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
|
||||
import ee.carlrobert.codegpt.settings.advanced.AdvancedSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.settings.advanced.AdvancedSettingsState;
|
||||
import ee.carlrobert.openai.client.AzureClient;
|
||||
import ee.carlrobert.openai.client.Client;
|
||||
import ee.carlrobert.openai.client.OpenAIClient;
|
||||
import ee.carlrobert.openai.client.ProxyAuthenticator;
|
||||
import ee.carlrobert.openai.client.azure.AzureClientRequestParams;
|
||||
import ee.carlrobert.openai.client.completion.CompletionClient;
|
||||
import ee.carlrobert.openai.client.embeddings.EmbeddingsClient;
|
||||
import ee.carlrobert.llm.client.Client;
|
||||
import ee.carlrobert.llm.client.ProxyAuthenticator;
|
||||
import ee.carlrobert.llm.client.azure.AzureClient;
|
||||
import ee.carlrobert.llm.client.azure.AzureCompletionRequestParams;
|
||||
import ee.carlrobert.llm.client.openai.OpenAIClient;
|
||||
import ee.carlrobert.llm.client.you.YouClient;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CompletionClientProvider {
|
||||
|
||||
public static EmbeddingsClient getEmbeddingsClient() {
|
||||
if (SettingsState.getInstance().isUseOpenAIService()) {
|
||||
return getOpenAIClientBuilder().buildEmbeddingsClient();
|
||||
}
|
||||
// TODO
|
||||
return null;
|
||||
public static OpenAIClient getOpenAIClient() {
|
||||
return getOpenAIClientBuilder().build();
|
||||
}
|
||||
|
||||
public static CompletionClient getChatCompletionClient(SettingsState settings) {
|
||||
return getClientBuilder(settings).buildChatCompletionClient();
|
||||
public static AzureClient getAzureClient() {
|
||||
return getAzureClientBuilder().build();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static CompletionClient getTextCompletionClient(SettingsState settings) {
|
||||
return getClientBuilder(settings).buildTextCompletionClient();
|
||||
}
|
||||
|
||||
public static Client.Builder getClientBuilder(SettingsState settings) {
|
||||
return settings.isUseAzureService() ? getAzureClientBuilder() : getOpenAIClientBuilder();
|
||||
public static YouClient getYouClient(String sessionId, String accessToken) {
|
||||
return new YouClient.Builder(sessionId, accessToken).build();
|
||||
}
|
||||
|
||||
private static OpenAIClient.Builder getOpenAIClientBuilder() {
|
||||
|
|
@ -50,12 +39,20 @@ public class CompletionClientProvider {
|
|||
|
||||
private static AzureClient.Builder getAzureClientBuilder() {
|
||||
var settings = AzureSettingsState.getInstance();
|
||||
var params = new AzureClientRequestParams(settings.getResourceName(), settings.getDeploymentId(), settings.getApiVersion());
|
||||
var params = new AzureCompletionRequestParams(settings.getResourceName(), settings.getDeploymentId(), settings.getApiVersion());
|
||||
var builder = new AzureClient.Builder(AzureCredentialsManager.getInstance().getSecret(), params)
|
||||
.setActiveDirectoryAuthentication(settings.isUseAzureActiveDirectoryAuthentication());
|
||||
return (AzureClient.Builder) addDefaultClientParams(builder).setHost(settings.getBaseHost());
|
||||
}
|
||||
|
||||
private static YouClient.Builder getYouClientBuilder() {
|
||||
var settings = OpenAISettingsState.getInstance();
|
||||
var builder = new OpenAIClient
|
||||
.Builder(OpenAICredentialsManager.getInstance().getApiKey())
|
||||
.setOrganization(settings.getOrganization());
|
||||
return (YouClient.Builder) addDefaultClientParams(builder).setHost(settings.getBaseHost());
|
||||
}
|
||||
|
||||
private static Client.Builder addDefaultClientParams(Client.Builder builder) {
|
||||
var advancedSettings = AdvancedSettingsState.getInstance();
|
||||
var proxyHost = advancedSettings.getProxyHost();
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@ package ee.carlrobert.codegpt.completions;
|
|||
|
||||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import ee.carlrobert.codegpt.conversations.message.Message;
|
||||
import ee.carlrobert.codegpt.settings.state.ModelSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.openai.client.completion.CompletionEventListener;
|
||||
import ee.carlrobert.openai.client.completion.ErrorDetails;
|
||||
import ee.carlrobert.llm.client.openai.completion.ErrorDetails;
|
||||
import ee.carlrobert.llm.client.you.completion.YouCompletionEventListener;
|
||||
import ee.carlrobert.llm.client.you.completion.YouSerpResult;
|
||||
import ee.carlrobert.llm.completion.CompletionEventListener;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
|
@ -22,6 +25,7 @@ public class CompletionRequestHandler {
|
|||
private @Nullable Consumer<String> messageListener;
|
||||
private @Nullable BiConsumer<ErrorDetails, Throwable> errorListener;
|
||||
private @Nullable Consumer<String> completedListener;
|
||||
private @Nullable Consumer<List<YouSerpResult>> serpResultsListener;
|
||||
private @Nullable Runnable tokensExceededListener;
|
||||
private boolean useContextualSearch;
|
||||
|
||||
|
|
@ -46,48 +50,12 @@ public class CompletionRequestHandler {
|
|||
this.tokensExceededListener = tokensExceededListener;
|
||||
}
|
||||
|
||||
public void addSerpResultsListener(Consumer<List<YouSerpResult>> serpResultsListener) {
|
||||
this.serpResultsListener = serpResultsListener;
|
||||
}
|
||||
|
||||
public void call(Conversation conversation, Message message, boolean isRetry) {
|
||||
swingWorker = new SwingWorker<>() {
|
||||
protected Void doInBackground() {
|
||||
try {
|
||||
eventSource = startCall(conversation, message, isRetry, new CompletionEventListener() {
|
||||
@Override
|
||||
public void onMessage(String message) {
|
||||
publish(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(StringBuilder messageBuilder) {
|
||||
if (completedListener != null) {
|
||||
completedListener.accept(messageBuilder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorDetails error, Throwable ex) {
|
||||
if (errorListener != null) {
|
||||
errorListener.accept(error, ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (TotalUsageExceededException e) {
|
||||
if (tokensExceededListener != null) {
|
||||
tokensExceededListener.run();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void process(List<String> chunks) {
|
||||
message.setResponse(messageBuilder.toString());
|
||||
for (String text : chunks) {
|
||||
messageBuilder.append(text);
|
||||
if (messageListener != null) {
|
||||
messageListener.accept(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
swingWorker = new CompletionRequestWorker(conversation, message, isRetry);
|
||||
swingWorker.execute();
|
||||
}
|
||||
|
||||
|
|
@ -104,14 +72,132 @@ public class CompletionRequestHandler {
|
|||
boolean isRetry,
|
||||
CompletionEventListener eventListener) {
|
||||
var settings = SettingsState.getInstance();
|
||||
var modelSettings = ModelSettingsState.getInstance();
|
||||
var requestProvider = new CompletionRequestProvider(conversation);
|
||||
|
||||
if (modelSettings.isUseChatCompletion()) {
|
||||
return CompletionClientProvider.getChatCompletionClient(settings).stream(
|
||||
requestProvider.buildChatCompletionRequest(modelSettings.getChatCompletionModel(), message, isRetry, useContextualSearch), eventListener);
|
||||
try {
|
||||
if (settings.isUseYouService()) {
|
||||
return CompletionClientProvider.getYouClient("", "")
|
||||
.getChatCompletion(requestProvider.buildYouCompletionRequest(message), eventListener);
|
||||
}
|
||||
|
||||
if (settings.isUseAzureService()) {
|
||||
var azureSettings = AzureSettingsState.getInstance();
|
||||
return CompletionClientProvider.getAzureClient().getChatCompletion(
|
||||
requestProvider.buildOpenAIChatCompletionRequest(
|
||||
azureSettings.getModel(),
|
||||
message,
|
||||
isRetry,
|
||||
useContextualSearch,
|
||||
azureSettings.isUsingCustomPath() ? azureSettings.getPath() : null),
|
||||
eventListener);
|
||||
}
|
||||
|
||||
var openAISettings = OpenAISettingsState.getInstance();
|
||||
return CompletionClientProvider.getOpenAIClient().getChatCompletion(
|
||||
requestProvider.buildOpenAIChatCompletionRequest(
|
||||
openAISettings.getModel(),
|
||||
message,
|
||||
isRetry,
|
||||
useContextualSearch,
|
||||
openAISettings.isUsingCustomPath() ? openAISettings.getPath() : null),
|
||||
eventListener);
|
||||
} catch (Throwable t) {
|
||||
if (errorListener != null) {
|
||||
errorListener.accept(new ErrorDetails("Something went wrong"), t);
|
||||
}
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
private class CompletionRequestWorker extends SwingWorker<Void, String> {
|
||||
|
||||
private final Conversation conversation;
|
||||
private final Message message;
|
||||
private final boolean isRetry;
|
||||
|
||||
public CompletionRequestWorker(Conversation conversation, Message message, boolean isRetry) {
|
||||
this.conversation = conversation;
|
||||
this.message = message;
|
||||
this.isRetry = isRetry;
|
||||
}
|
||||
|
||||
protected Void doInBackground() {
|
||||
try {
|
||||
eventSource = startCall(
|
||||
conversation,
|
||||
message,
|
||||
isRetry,
|
||||
SettingsState.getInstance().isUseYouService() ? getYouCompletionEventListener() : getCompletionEventListener());
|
||||
} catch (TotalUsageExceededException e) {
|
||||
if (tokensExceededListener != null) {
|
||||
tokensExceededListener.run();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void process(List<String> chunks) {
|
||||
message.setResponse(messageBuilder.toString());
|
||||
for (String text : chunks) {
|
||||
messageBuilder.append(text);
|
||||
if (messageListener != null) {
|
||||
messageListener.accept(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CompletionEventListener getCompletionEventListener() {
|
||||
return new CompletionEventListener() {
|
||||
@Override
|
||||
public void onMessage(String message) {
|
||||
publish(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(StringBuilder messageBuilder) {
|
||||
if (completedListener != null) {
|
||||
completedListener.accept(messageBuilder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorDetails error, Throwable ex) {
|
||||
if (errorListener != null) {
|
||||
errorListener.accept(error, ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Refactor
|
||||
private YouCompletionEventListener getYouCompletionEventListener() {
|
||||
return new YouCompletionEventListener() {
|
||||
@Override
|
||||
public void onMessage(String message) {
|
||||
publish(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(StringBuilder messageBuilder) {
|
||||
if (completedListener != null) {
|
||||
completedListener.accept(messageBuilder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorDetails error, Throwable ex) {
|
||||
if (errorListener != null) {
|
||||
errorListener.accept(error, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSerpResults(List<YouSerpResult> results) {
|
||||
if (serpResultsListener != null) {
|
||||
serpResultsListener.accept(results);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return CompletionClientProvider.getTextCompletionClient(settings).stream(
|
||||
requestProvider.buildTextCompletionRequest(modelSettings.getTextCompletionModel(), message, isRetry), eventListener);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,18 +8,19 @@ import ee.carlrobert.codegpt.EncodingManager;
|
|||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import ee.carlrobert.codegpt.conversations.ConversationsState;
|
||||
import ee.carlrobert.codegpt.conversations.message.Message;
|
||||
import ee.carlrobert.codegpt.embeddings.EmbeddingsService;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.settings.configuration.ConfigurationState;
|
||||
import ee.carlrobert.codegpt.util.FileUtils;
|
||||
import ee.carlrobert.openai.client.completion.chat.ChatCompletionModel;
|
||||
import ee.carlrobert.openai.client.completion.chat.request.ChatCompletionMessage;
|
||||
import ee.carlrobert.openai.client.completion.chat.request.ChatCompletionRequest;
|
||||
import ee.carlrobert.openai.client.completion.text.TextCompletionModel;
|
||||
import ee.carlrobert.openai.client.completion.text.request.TextCompletionRequest;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.embedding.EmbeddingsService;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.OpenAIChatCompletionModel;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.request.OpenAIChatCompletionMessage;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.request.OpenAIChatCompletionRequest;
|
||||
import ee.carlrobert.llm.client.you.completion.YouCompletionRequest;
|
||||
import ee.carlrobert.llm.client.you.completion.YouCompletionRequestMessage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class CompletionRequestProvider {
|
||||
|
||||
|
|
@ -48,72 +49,85 @@ public class CompletionRequestProvider {
|
|||
private final Conversation conversation;
|
||||
|
||||
public CompletionRequestProvider(Conversation conversation) {
|
||||
this.embeddingsService = new EmbeddingsService(
|
||||
CompletionClientProvider.getEmbeddingsClient(),
|
||||
CompletionClientProvider.getChatCompletionClient(SettingsState.getInstance()),
|
||||
CodeGPTPlugin.getPluginBasePath());
|
||||
this.embeddingsService = new EmbeddingsService(CompletionClientProvider.getOpenAIClient(), CodeGPTPlugin.getPluginBasePath());
|
||||
this.conversation = conversation;
|
||||
}
|
||||
|
||||
public ChatCompletionRequest buildChatCompletionRequest(String model, Message message, boolean isRetry) {
|
||||
return buildChatCompletionRequest(model, message, isRetry, false);
|
||||
}
|
||||
|
||||
public ChatCompletionRequest buildChatCompletionRequest(String model, Message message, boolean isRetry, boolean useContextualSearch) {
|
||||
return (ChatCompletionRequest) new ChatCompletionRequest.Builder(buildMessages(model, message, isRetry, useContextualSearch))
|
||||
.setModel(model)
|
||||
.setMaxTokens(ConfigurationState.getInstance().getMaxTokens())
|
||||
.setTemperature(ConfigurationState.getInstance().getTemperature())
|
||||
public YouCompletionRequest buildYouCompletionRequest(Message message) {
|
||||
return new YouCompletionRequest.Builder(message.getPrompt())
|
||||
.setChatHistory(conversation.getMessages().stream()
|
||||
.map(prevMessage -> new YouCompletionRequestMessage(prevMessage.getPrompt(), prevMessage.getResponse()))
|
||||
.collect(toList()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public TextCompletionRequest buildTextCompletionRequest(String model, Message message, boolean isRetry) {
|
||||
return (TextCompletionRequest) new TextCompletionRequest.Builder(buildPrompt(model, message, isRetry))
|
||||
.setStop(List.of(" Human:", " AI:"))
|
||||
.setModel(model)
|
||||
.setMaxTokens(ConfigurationState.getInstance().getMaxTokens())
|
||||
.setTemperature(ConfigurationState.getInstance().getTemperature())
|
||||
.build();
|
||||
public OpenAIChatCompletionRequest buildOpenAIChatCompletionRequest(String model, Message message, boolean isRetry) {
|
||||
return buildOpenAIChatCompletionRequest(model, message, isRetry, false, null);
|
||||
}
|
||||
|
||||
private List<ChatCompletionMessage> buildMessages(String model, Message message, boolean isRetry, boolean useContextualSearch) {
|
||||
var messages = new ArrayList<ChatCompletionMessage>();
|
||||
public OpenAIChatCompletionRequest buildOpenAIChatCompletionRequest(
|
||||
String model,
|
||||
Message message,
|
||||
boolean isRetry,
|
||||
boolean useContextualSearch,
|
||||
@Nullable String overriddenPath) {
|
||||
var builder = new OpenAIChatCompletionRequest.Builder(buildMessages(model, message, isRetry, useContextualSearch))
|
||||
.setModel(model)
|
||||
.setMaxTokens(ConfigurationState.getInstance().getMaxTokens())
|
||||
.setTemperature(ConfigurationState.getInstance().getTemperature());
|
||||
|
||||
if (overriddenPath != null) {
|
||||
builder.setOverriddenPath(overriddenPath);
|
||||
}
|
||||
|
||||
return (OpenAIChatCompletionRequest) builder.build();
|
||||
}
|
||||
|
||||
private List<OpenAIChatCompletionMessage> buildMessages(String model, Message message, boolean isRetry, boolean useContextualSearch) {
|
||||
var messages = new ArrayList<OpenAIChatCompletionMessage>();
|
||||
if (useContextualSearch) {
|
||||
var context = embeddingsService.buildRelevantContext(message.getPrompt());
|
||||
var prompt = FileUtils.getResourceContent("/prompts/retrieval-prompt.txt")
|
||||
.replace("{prompt}", message.getPrompt())
|
||||
.replace("{context}", context.getContext());
|
||||
|
||||
var prompt = embeddingsService.buildPromptWithContext(message.getPrompt());
|
||||
LOG.info("Retrieved context:\n" + prompt);
|
||||
messages.add(new ChatCompletionMessage("user", prompt));
|
||||
messages.add(new OpenAIChatCompletionMessage("user", prompt));
|
||||
} else {
|
||||
var systemPrompt = ConfigurationState.getInstance().getSystemPrompt();
|
||||
messages.add(new ChatCompletionMessage("system",
|
||||
messages.add(new OpenAIChatCompletionMessage("system",
|
||||
systemPrompt.isEmpty() ? COMPLETION_SYSTEM_PROMPT : systemPrompt));
|
||||
|
||||
for (var prevMessage : conversation.getMessages()) {
|
||||
if (isRetry && prevMessage.getId().equals(message.getId())) {
|
||||
break;
|
||||
}
|
||||
messages.add(new ChatCompletionMessage("user", prevMessage.getPrompt()));
|
||||
messages.add(new ChatCompletionMessage("assistant", prevMessage.getResponse()));
|
||||
messages.add(new OpenAIChatCompletionMessage("user", prevMessage.getPrompt()));
|
||||
messages.add(new OpenAIChatCompletionMessage("assistant", prevMessage.getResponse()));
|
||||
}
|
||||
messages.add(new ChatCompletionMessage("user", message.getPrompt()));
|
||||
messages.add(new OpenAIChatCompletionMessage("user", message.getPrompt()));
|
||||
}
|
||||
|
||||
if (SettingsState.getInstance().isUseYouService()) {
|
||||
return messages;
|
||||
}
|
||||
|
||||
int totalUsage = messages.parallelStream()
|
||||
.mapToInt(encodingManager::countMessageTokens)
|
||||
.sum() + ConfigurationState.getInstance().getMaxTokens();
|
||||
int modelMaxTokens = ChatCompletionModel.findByCode(model).getMaxTokens();
|
||||
int modelMaxTokens;
|
||||
try {
|
||||
modelMaxTokens = OpenAIChatCompletionModel.findByCode(model).getMaxTokens();
|
||||
|
||||
if (totalUsage <= modelMaxTokens) {
|
||||
if (totalUsage <= modelMaxTokens) {
|
||||
return messages;
|
||||
}
|
||||
} catch (NoSuchElementException ex) {
|
||||
return messages;
|
||||
}
|
||||
|
||||
return tryReducingMessagesOrThrow(messages, totalUsage, modelMaxTokens);
|
||||
}
|
||||
|
||||
private List<ChatCompletionMessage> tryReducingMessagesOrThrow(List<ChatCompletionMessage> messages, int totalUsage, int modelMaxTokens) {
|
||||
private List<OpenAIChatCompletionMessage> tryReducingMessagesOrThrow(
|
||||
List<OpenAIChatCompletionMessage> messages,
|
||||
int totalUsage,
|
||||
int modelMaxTokens) {
|
||||
if (!ConversationsState.getInstance().discardAllTokenLimits) {
|
||||
if (!conversation.isDiscardTokenLimit()) {
|
||||
throw new TotalUsageExceededException();
|
||||
|
|
@ -132,33 +146,4 @@ public class CompletionRequestProvider {
|
|||
|
||||
return messages.stream().filter(Objects::nonNull).collect(toList());
|
||||
}
|
||||
|
||||
private StringBuilder getBasePrompt(String model) {
|
||||
var isDavinciModel = TextCompletionModel.DAVINCI.getCode().equals(model);
|
||||
if (isDavinciModel) {
|
||||
return new StringBuilder(
|
||||
"You are ChatGPT, a large language model trained by OpenAI.\n" +
|
||||
"Answer in a markdown language, code blocks should contain language whenever possible.\n");
|
||||
}
|
||||
return new StringBuilder(
|
||||
"The following is a conversation with an AI assistant. The assistant is helpful, creative, clever, and very friendly.\n\n");
|
||||
}
|
||||
|
||||
private String buildPrompt(String model, Message message, boolean isRetry) {
|
||||
var systemPrompt = ConfigurationState.getInstance().getSystemPrompt();
|
||||
var basePrompt = systemPrompt.isEmpty() ? getBasePrompt(model) : new StringBuilder(systemPrompt + "\n");
|
||||
conversation.getMessages().forEach(prevMessage ->
|
||||
basePrompt.append("Human: ")
|
||||
.append(prevMessage.getPrompt())
|
||||
.append("\n")
|
||||
.append("AI: ")
|
||||
.append(prevMessage.getResponse())
|
||||
.append("\n"));
|
||||
basePrompt.append("Human: ")
|
||||
.append(message.getPrompt())
|
||||
.append("\n")
|
||||
.append("AI: ")
|
||||
.append("\n");
|
||||
return basePrompt.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
package ee.carlrobert.codegpt.completions;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class SerpResult {
|
||||
|
||||
private final String url;
|
||||
private final String name;
|
||||
private final String snippet;
|
||||
private final String snippetSource;
|
||||
|
||||
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
|
||||
public SerpResult(
|
||||
@JsonProperty("url") String url,
|
||||
@JsonProperty("name") String name,
|
||||
@JsonProperty("snippet") String snippet,
|
||||
@JsonProperty("snippetSource") String snippetSource) {
|
||||
this.url = url;
|
||||
this.name = name;
|
||||
this.snippet = snippet;
|
||||
this.snippetSource = snippetSource;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getSnippet() {
|
||||
return snippet;
|
||||
}
|
||||
|
||||
public String getSnippetSource() {
|
||||
return snippetSource;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ package ee.carlrobert.codegpt.conversations;
|
|||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import ee.carlrobert.codegpt.conversations.message.Message;
|
||||
import ee.carlrobert.openai.client.ClientCode;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
|
@ -13,7 +12,7 @@ public class Conversation {
|
|||
|
||||
private UUID id;
|
||||
private List<Message> messages = new ArrayList<>();
|
||||
private ClientCode clientCode;
|
||||
private String clientCode;
|
||||
private String model;
|
||||
private LocalDateTime createdOn;
|
||||
private LocalDateTime updatedOn;
|
||||
|
|
@ -35,11 +34,11 @@ public class Conversation {
|
|||
this.messages = messages;
|
||||
}
|
||||
|
||||
public ClientCode getClientCode() {
|
||||
public String getClientCode() {
|
||||
return clientCode;
|
||||
}
|
||||
|
||||
public void setClientCode(ClientCode clientCode) {
|
||||
public void setClientCode(String clientCode) {
|
||||
this.clientCode = clientCode;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@ package ee.carlrobert.codegpt.conversations;
|
|||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.Service;
|
||||
import ee.carlrobert.codegpt.conversations.message.Message;
|
||||
import ee.carlrobert.codegpt.settings.state.ModelSettingsState;
|
||||
import ee.carlrobert.openai.client.ClientCode;
|
||||
import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
|
|
@ -13,19 +16,16 @@ import java.util.Optional;
|
|||
import java.util.UUID;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ConversationService {
|
||||
@Service
|
||||
public final class ConversationService {
|
||||
|
||||
private static ConversationService instance;
|
||||
private final ConversationsState conversationState = ConversationsState.getInstance();
|
||||
|
||||
private ConversationService() {
|
||||
}
|
||||
|
||||
public static ConversationService getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new ConversationService();
|
||||
}
|
||||
return instance;
|
||||
return ApplicationManager.getApplication().getService(ConversationService.class);
|
||||
}
|
||||
|
||||
public List<Conversation> getSortedConversations() {
|
||||
|
|
@ -37,11 +37,18 @@ public class ConversationService {
|
|||
.collect(toList());
|
||||
}
|
||||
|
||||
public Conversation createConversation(ClientCode clientCode) {
|
||||
public Conversation createConversation(String clientCode) {
|
||||
var settings = SettingsState.getInstance();
|
||||
var conversation = new Conversation();
|
||||
conversation.setId(UUID.randomUUID());
|
||||
conversation.setClientCode(clientCode);
|
||||
conversation.setModel(ModelSettingsState.getInstance().getCompletionModel());
|
||||
if (settings.isUseYouService()) {
|
||||
conversation.setModel("YouCode");
|
||||
} else if (settings.isUseAzureService()) {
|
||||
conversation.setModel(AzureSettingsState.getInstance().getModel());
|
||||
} else {
|
||||
conversation.setModel(OpenAISettingsState.getInstance().getModel());
|
||||
}
|
||||
conversation.setCreatedOn(LocalDateTime.now());
|
||||
conversation.setUpdatedOn(LocalDateTime.now());
|
||||
return conversation;
|
||||
|
|
@ -107,9 +114,19 @@ public class ConversationService {
|
|||
conversationState.setCurrentConversation(conversation);
|
||||
}
|
||||
|
||||
private String getClientCode() {
|
||||
var settings = SettingsState.getInstance();
|
||||
if (settings.isUseOpenAIService()) {
|
||||
return "chat.completion";
|
||||
}
|
||||
if (settings.isUseAzureService()) {
|
||||
return "azure.chat.completion";
|
||||
}
|
||||
return "you.chat.completion";
|
||||
}
|
||||
|
||||
public Conversation startConversation() {
|
||||
var currentClientCode = ModelSettingsState.getInstance().isUseChatCompletion() ? ClientCode.CHAT_COMPLETION : ClientCode.TEXT_COMPLETION;
|
||||
var conversation = createConversation(currentClientCode);
|
||||
var conversation = createConversation(getClientCode());
|
||||
conversationState.setCurrentConversation(conversation);
|
||||
addConversation(conversation);
|
||||
return conversation;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,18 @@
|
|||
package ee.carlrobert.codegpt.conversations;
|
||||
|
||||
import ee.carlrobert.openai.client.ClientCode;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ConversationsContainer {
|
||||
|
||||
private Map<ClientCode, List<Conversation>> conversationsMapping = new HashMap<>();
|
||||
private Map<String, List<Conversation>> conversationsMapping = new HashMap<>();
|
||||
|
||||
public Map<ClientCode, List<Conversation>> getConversationsMapping() {
|
||||
public Map<String, List<Conversation>> getConversationsMapping() {
|
||||
return conversationsMapping;
|
||||
}
|
||||
|
||||
public void setConversationsMapping(Map<ClientCode, List<Conversation>> conversationsMapping) {
|
||||
public void setConversationsMapping(Map<String, List<Conversation>> conversationsMapping) {
|
||||
this.conversationsMapping = conversationsMapping;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,16 +8,12 @@ import com.intellij.util.xmlb.XmlSerializerUtil;
|
|||
import com.intellij.util.xmlb.annotations.OptionTag;
|
||||
import ee.carlrobert.codegpt.conversations.converter.ConversationConverter;
|
||||
import ee.carlrobert.codegpt.conversations.converter.ConversationsConverter;
|
||||
import ee.carlrobert.openai.client.ClientCode;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@State(
|
||||
name = "ee.carlrobert.codegpt.state.conversations.ConversationsState",
|
||||
storages = @Storage("ChatGPTConversations_170.xml")
|
||||
)
|
||||
@State(name = "ee.carlrobert.codegpt.state.conversations.ConversationsState", storages = @Storage("ChatGPTConversations_170.xml"))
|
||||
public class ConversationsState implements PersistentStateComponent<ConversationsState> {
|
||||
|
||||
@OptionTag(converter = ConversationsConverter.class)
|
||||
|
|
@ -55,7 +51,7 @@ public class ConversationsState implements PersistentStateComponent<Conversation
|
|||
return getInstance().currentConversation;
|
||||
}
|
||||
|
||||
public Map<ClientCode, List<Conversation>> getConversationsMapping() {
|
||||
public Map<String, List<Conversation>> getConversationsMapping() {
|
||||
return conversationsContainer.getConversationsMapping();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package ee.carlrobert.codegpt.conversations.message;
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import ee.carlrobert.codegpt.completions.SerpResult;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -11,6 +13,7 @@ public class Message {
|
|||
private final String prompt;
|
||||
private String response;
|
||||
private String userMessage;
|
||||
private List<SerpResult> serpResults;
|
||||
|
||||
public Message(String prompt, String response) {
|
||||
this(prompt);
|
||||
|
|
@ -47,6 +50,14 @@ public class Message {
|
|||
this.userMessage = userMessage;
|
||||
}
|
||||
|
||||
public List<SerpResult> getSerpResults() {
|
||||
return serpResults;
|
||||
}
|
||||
|
||||
public void setSerpResults(List<SerpResult> serpResults) {
|
||||
this.serpResults = serpResults;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
package ee.carlrobert.codegpt.credentials;
|
||||
|
||||
import com.intellij.credentialStore.CredentialAttributes;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.Service;
|
||||
import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class AzureCredentialsManager {
|
||||
@Service
|
||||
public final class AzureCredentialsManager {
|
||||
|
||||
private static final CredentialAttributes azureOpenAIApiKeyCredentialAttributes =
|
||||
CredentialsUtil.createCredentialAttributes("AZURE_OPENAI_API_KEY");
|
||||
private static final CredentialAttributes azureActiveDirectoryTokenCredentialAttributes =
|
||||
CredentialsUtil.createCredentialAttributes("AZURE_ACTIVE_DIRECTORY_TOKEN");
|
||||
private static AzureCredentialsManager instance;
|
||||
|
||||
private String azureOpenAIApiKey;
|
||||
private String azureActiveDirectoryToken;
|
||||
|
|
@ -21,10 +23,7 @@ public class AzureCredentialsManager {
|
|||
}
|
||||
|
||||
public static AzureCredentialsManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new AzureCredentialsManager();
|
||||
}
|
||||
return instance;
|
||||
return ApplicationManager.getApplication().getService(AzureCredentialsManager.class);
|
||||
}
|
||||
|
||||
public String getSecret() {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
package ee.carlrobert.codegpt.credentials;
|
||||
|
||||
import com.intellij.credentialStore.CredentialAttributes;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.Service;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class OpenAICredentialsManager {
|
||||
@Service
|
||||
public final class OpenAICredentialsManager {
|
||||
|
||||
private static final CredentialAttributes openAIApiKeyCredentialAttributes = CredentialsUtil.createCredentialAttributes("OPENAI_API_KEY");
|
||||
private static OpenAICredentialsManager instance;
|
||||
|
||||
private String openAIApiKey;
|
||||
|
||||
|
|
@ -15,10 +17,7 @@ public class OpenAICredentialsManager {
|
|||
}
|
||||
|
||||
public static OpenAICredentialsManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new OpenAICredentialsManager();
|
||||
}
|
||||
return instance;
|
||||
return ApplicationManager.getApplication().getService(OpenAICredentialsManager.class);
|
||||
}
|
||||
|
||||
public boolean isApiKeySet() {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
package ee.carlrobert.codegpt.credentials;
|
||||
|
||||
import com.intellij.credentialStore.CredentialAttributes;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.Service;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class UserCredentialsManager {
|
||||
@Service
|
||||
public final class UserCredentialsManager {
|
||||
|
||||
private static final CredentialAttributes accountPasswordCredentialAttributes = CredentialsUtil.createCredentialAttributes("ACCOUNT_PASSWORD");
|
||||
|
||||
private static UserCredentialsManager instance;
|
||||
|
||||
private String accountPassword;
|
||||
|
||||
private UserCredentialsManager() {
|
||||
|
|
@ -16,10 +17,7 @@ public class UserCredentialsManager {
|
|||
}
|
||||
|
||||
public static UserCredentialsManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new UserCredentialsManager();
|
||||
}
|
||||
return instance;
|
||||
return ApplicationManager.getApplication().getService(UserCredentialsManager.class);
|
||||
}
|
||||
|
||||
public @Nullable String getAccountPassword() {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package ee.carlrobert.codegpt.indexes;
|
|||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.github.jelmerk.knn.Item;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
|
|
@ -13,12 +14,11 @@ import com.intellij.openapi.util.io.FileUtil;
|
|||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.CodeGPTPlugin;
|
||||
import ee.carlrobert.codegpt.completions.CompletionClientProvider;
|
||||
import ee.carlrobert.codegpt.embeddings.EmbeddingsService;
|
||||
import ee.carlrobert.codegpt.embeddings.VectorStore;
|
||||
import ee.carlrobert.codegpt.embeddings.CheckedFile;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.util.FileUtils;
|
||||
import ee.carlrobert.codegpt.util.OverlayUtils;
|
||||
import ee.carlrobert.embedding.CheckedFile;
|
||||
import ee.carlrobert.embedding.EmbeddingsService;
|
||||
import ee.carlrobert.vector.VectorStore;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
|
@ -34,10 +34,7 @@ public class CodebaseIndexingTask extends Task.Backgroundable {
|
|||
super(project, CodeGPTBundle.get("codebaseIndexing.task.title"), true);
|
||||
this.project = project;
|
||||
this.checkedFiles = checkedFiles;
|
||||
this.embeddingsService = new EmbeddingsService(
|
||||
CompletionClientProvider.getEmbeddingsClient(),
|
||||
CompletionClientProvider.getChatCompletionClient(SettingsState.getInstance()),
|
||||
CodeGPTPlugin.getPluginBasePath());
|
||||
this.embeddingsService = new EmbeddingsService(CompletionClientProvider.getOpenAIClient(), CodeGPTPlugin.getPluginBasePath());
|
||||
}
|
||||
|
||||
public void run() {
|
||||
|
|
@ -63,7 +60,7 @@ public class CodebaseIndexingTask extends Task.Backgroundable {
|
|||
|
||||
try {
|
||||
indicator.setFraction(0);
|
||||
var embeddings = embeddingsService.createEmbeddings(checkedFiles, indicator);
|
||||
List<Item<Object, double[]>> embeddings = embeddingsService.createEmbeddings(checkedFiles, indicator);
|
||||
VectorStore.getInstance(CodeGPTPlugin.getPluginBasePath()).save(embeddings);
|
||||
OverlayUtils.showNotification("Indexing completed", NotificationType.INFORMATION);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import com.intellij.ui.ScrollPaneFactory;
|
|||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.ui.AsyncProcessIcon;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import ee.carlrobert.codegpt.embeddings.CheckedFile;
|
||||
import ee.carlrobert.embedding.CheckedFile;
|
||||
import ee.carlrobert.codegpt.util.FileUtils;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
package ee.carlrobert.codegpt.settings;
|
||||
|
||||
import com.intellij.openapi.ui.ComboBox;
|
||||
import ee.carlrobert.openai.client.completion.CompletionModel;
|
||||
import ee.carlrobert.llm.completion.CompletionModel;
|
||||
import java.awt.Component;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.plaf.basic.BasicComboBoxRenderer;
|
||||
|
||||
public class BaseModelComboBox extends ComboBox<CompletionModel> {
|
||||
public class ModelComboBox extends ComboBox<CompletionModel> {
|
||||
|
||||
public BaseModelComboBox(CompletionModel[] options, CompletionModel selectedModel) {
|
||||
public ModelComboBox(CompletionModel[] options, CompletionModel selectedModel) {
|
||||
super(options);
|
||||
setSelectedItem(selectedModel);
|
||||
setRenderer(getBasicComboBoxRenderer());
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
package ee.carlrobert.codegpt.settings;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.ui.ComboBox;
|
||||
import com.intellij.ui.components.JBRadioButton;
|
||||
import com.intellij.util.ui.FormBuilder;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.settings.state.ModelSettingsState;
|
||||
import ee.carlrobert.codegpt.util.SwingUtils;
|
||||
import ee.carlrobert.openai.client.completion.CompletionModel;
|
||||
import ee.carlrobert.openai.client.completion.chat.ChatCompletionModel;
|
||||
import ee.carlrobert.openai.client.completion.text.TextCompletionModel;
|
||||
import java.util.NoSuchElementException;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
public class ModelSelectionForm {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(ModelSelectionForm.class);
|
||||
|
||||
private static final String modelSelectionLabel = CodeGPTBundle.get("settingsConfigurable.section.model.selectionFieldLabel");
|
||||
|
||||
private final ComboBox<CompletionModel> chatCompletionBaseModelComboBox;
|
||||
private final ComboBox<CompletionModel> textCompletionBaseModelComboBox;
|
||||
private final JBRadioButton useChatCompletionRadioButton;
|
||||
private final JBRadioButton useTextCompletionRadioButton;
|
||||
private final JPanel chatCompletionModelsPanel;
|
||||
private final JPanel textCompletionModelsPanel;
|
||||
|
||||
private CompletionModel findChatCompletionModelOrGetDefault(ModelSettingsState settings) {
|
||||
try {
|
||||
return ChatCompletionModel.findByCode(settings.getChatCompletionModel());
|
||||
} catch (NoSuchElementException e) {
|
||||
LOG.warn("Couldn't find completion model with code: " + settings.getChatCompletionModel());
|
||||
return ChatCompletionModel.GPT_3_5;
|
||||
}
|
||||
}
|
||||
|
||||
private CompletionModel findTextCompletionModelOrGetDefault(ModelSettingsState settings) {
|
||||
try {
|
||||
return TextCompletionModel.findByCode(settings.getTextCompletionModel());
|
||||
} catch (NoSuchElementException e) {
|
||||
LOG.warn("Couldn't find completion model with code: " + settings.getTextCompletionModel());
|
||||
return TextCompletionModel.DAVINCI;
|
||||
}
|
||||
}
|
||||
|
||||
public ModelSelectionForm() {
|
||||
var settings = ModelSettingsState.getInstance();
|
||||
chatCompletionBaseModelComboBox = new BaseModelComboBox(
|
||||
new ChatCompletionModel[] {
|
||||
ChatCompletionModel.GPT_3_5,
|
||||
ChatCompletionModel.GPT_3_5_16k,
|
||||
ChatCompletionModel.GPT_4,
|
||||
ChatCompletionModel.GPT_4_32k
|
||||
},
|
||||
findChatCompletionModelOrGetDefault(settings));
|
||||
chatCompletionModelsPanel = SwingUtils.createPanel(
|
||||
chatCompletionBaseModelComboBox, modelSelectionLabel, false);
|
||||
chatCompletionModelsPanel.setBorder(JBUI.Borders.emptyLeft(16));
|
||||
textCompletionBaseModelComboBox = new BaseModelComboBox(
|
||||
new TextCompletionModel[] {
|
||||
TextCompletionModel.DAVINCI,
|
||||
TextCompletionModel.CURIE,
|
||||
TextCompletionModel.BABBAGE,
|
||||
TextCompletionModel.ADA,
|
||||
},
|
||||
findTextCompletionModelOrGetDefault(settings));
|
||||
textCompletionModelsPanel = SwingUtils.createPanel(textCompletionBaseModelComboBox, modelSelectionLabel);
|
||||
textCompletionModelsPanel.setBorder(JBUI.Borders.emptyLeft(16));
|
||||
useChatCompletionRadioButton = new JBRadioButton(
|
||||
CodeGPTBundle.get("settingsConfigurable.section.model.useChatCompletionRadioButtonLabel"),
|
||||
settings.isUseChatCompletion());
|
||||
useTextCompletionRadioButton = new JBRadioButton(
|
||||
CodeGPTBundle.get("settingsConfigurable.section.model.useTextCompletionRadioButtonLabel"),
|
||||
settings.isUseTextCompletion());
|
||||
|
||||
registerFields();
|
||||
registerRadioButtons();
|
||||
}
|
||||
|
||||
public JPanel getForm() {
|
||||
var form = FormBuilder.createFormBuilder()
|
||||
.addComponent(useChatCompletionRadioButton)
|
||||
.addComponent(chatCompletionModelsPanel)
|
||||
.addComponent(useTextCompletionRadioButton)
|
||||
.addComponent(textCompletionModelsPanel)
|
||||
.getPanel();
|
||||
form.setBorder(JBUI.Borders.emptyLeft(16));
|
||||
return form;
|
||||
}
|
||||
|
||||
public boolean isChatCompletionOptionSelected() {
|
||||
return useChatCompletionRadioButton.isSelected();
|
||||
}
|
||||
|
||||
public void setUseChatCompletionSelected(boolean isSelected) {
|
||||
useChatCompletionRadioButton.setSelected(isSelected);
|
||||
}
|
||||
|
||||
public boolean isTextCompletionOptionSelected() {
|
||||
return useTextCompletionRadioButton.isSelected();
|
||||
}
|
||||
|
||||
public void setUseTextCompletionSelected(boolean isSelected) {
|
||||
useTextCompletionRadioButton.setSelected(isSelected);
|
||||
}
|
||||
|
||||
public TextCompletionModel getTextCompletionBaseModel() {
|
||||
return (TextCompletionModel) textCompletionBaseModelComboBox.getSelectedItem();
|
||||
}
|
||||
|
||||
public void setTextCompletionBaseModel(String modelCode) {
|
||||
textCompletionBaseModelComboBox.setSelectedItem(TextCompletionModel.findByCode(modelCode));
|
||||
}
|
||||
|
||||
public ChatCompletionModel getChatCompletionBaseModel() {
|
||||
return (ChatCompletionModel) chatCompletionBaseModelComboBox.getSelectedItem();
|
||||
}
|
||||
|
||||
public void setChatCompletionBaseModel(String modelCode) {
|
||||
chatCompletionBaseModelComboBox.setSelectedItem(ChatCompletionModel.findByCode(modelCode));
|
||||
}
|
||||
|
||||
private void registerRadioButtons() {
|
||||
var completionButtonGroup = new ButtonGroup();
|
||||
completionButtonGroup.add(useChatCompletionRadioButton);
|
||||
completionButtonGroup.add(useTextCompletionRadioButton);
|
||||
useChatCompletionRadioButton.addActionListener(e -> enableModelFields(true));
|
||||
useTextCompletionRadioButton.addActionListener(e -> enableModelFields(false));
|
||||
}
|
||||
|
||||
private void registerFields() {
|
||||
enableModelFields(useChatCompletionRadioButton.isSelected());
|
||||
}
|
||||
|
||||
private void enableModelFields(boolean isChatCompletionModel) {
|
||||
chatCompletionBaseModelComboBox.setEnabled(isChatCompletionModel);
|
||||
textCompletionBaseModelComboBox.setEnabled(!isChatCompletionModel);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
package ee.carlrobert.codegpt.settings;public class ServiceChangeNotifier {
|
||||
}
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
package ee.carlrobert.codegpt.settings;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.ui.ComboBox;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.ui.TitledSeparator;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.ui.components.JBPasswordField;
|
||||
import com.intellij.ui.components.JBRadioButton;
|
||||
import com.intellij.ui.components.JBTextField;
|
||||
|
|
@ -13,20 +17,37 @@ import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
|
|||
import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.user.UserManager;
|
||||
import ee.carlrobert.codegpt.user.auth.AuthenticationNotifier;
|
||||
import ee.carlrobert.codegpt.util.SwingUtils;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.OpenAIChatCompletionModel;
|
||||
import ee.carlrobert.llm.completion.CompletionModel;
|
||||
import java.awt.FlowLayout;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
public class ServiceSelectionForm {
|
||||
|
||||
private static final OpenAIChatCompletionModel[] DEFAULT_OPENAI_MODELS = new OpenAIChatCompletionModel[] {
|
||||
OpenAIChatCompletionModel.GPT_3_5,
|
||||
OpenAIChatCompletionModel.GPT_3_5_16k,
|
||||
OpenAIChatCompletionModel.GPT_4,
|
||||
OpenAIChatCompletionModel.GPT_4_32k
|
||||
};
|
||||
|
||||
private final JBRadioButton useOpenAIServiceRadioButton;
|
||||
private final JBRadioButton useAzureServiceRadioButton;
|
||||
|
||||
private final JBPasswordField openAIApiKey;
|
||||
private final JBPasswordField openAIApiKeyField;
|
||||
private final JBTextField openAIBaseHostField;
|
||||
private final JBTextField openAIPathField;
|
||||
private final JBTextField openAIOrganizationField;
|
||||
private final JPanel openAIServiceSectionPanel;
|
||||
private final ComboBox<CompletionModel> openAICompletionModelComboBox;
|
||||
|
||||
private final JBRadioButton useAzureApiKeyAuthenticationRadioButton;
|
||||
private final JBPasswordField azureApiKeyField;
|
||||
|
|
@ -35,17 +56,23 @@ public class ServiceSelectionForm {
|
|||
private final JBPasswordField azureActiveDirectoryTokenField;
|
||||
private final JPanel azureActiveDirectoryTokenFieldPanel;
|
||||
private final JBTextField azureBaseHostField;
|
||||
private final JBTextField azurePathField;
|
||||
private final JBTextField azureResourceNameField;
|
||||
private final JBTextField azureDeploymentIdField;
|
||||
private final JBTextField azureApiVersionField;
|
||||
private final JPanel azureServiceSectionPanel;
|
||||
private final ComboBox<CompletionModel> azureCompletionModelComboBox;
|
||||
|
||||
private final JBRadioButton useYouServiceRadioButton;
|
||||
private final JPanel youServiceSectionPanel;
|
||||
private final JBCheckBox displayWebSearchResultsCheckBox;
|
||||
|
||||
public ServiceSelectionForm(SettingsState settings) {
|
||||
var openAISettings = OpenAISettingsState.getInstance();
|
||||
var azureSettings = AzureSettingsState.getInstance();
|
||||
openAIApiKey = new JBPasswordField();
|
||||
openAIApiKey.setColumns(30);
|
||||
openAIApiKey.setText(OpenAICredentialsManager.getInstance().getApiKey());
|
||||
openAIApiKeyField = new JBPasswordField();
|
||||
openAIApiKeyField.setColumns(30);
|
||||
openAIApiKeyField.setText(OpenAICredentialsManager.getInstance().getApiKey());
|
||||
|
||||
azureApiKeyField = new JBPasswordField();
|
||||
azureApiKeyField.setColumns(30);
|
||||
|
|
@ -76,31 +103,188 @@ public class ServiceSelectionForm {
|
|||
CodeGPTBundle.get("settingsConfigurable.section.service.useOpenAIServiceRadioButtonLabel"), settings.isUseOpenAIService());
|
||||
useAzureServiceRadioButton = new JBRadioButton(
|
||||
CodeGPTBundle.get("settingsConfigurable.section.service.useAzureServiceRadioButtonLabel"), settings.isUseAzureService());
|
||||
useYouServiceRadioButton = new JBRadioButton(
|
||||
CodeGPTBundle.get("settingsConfigurable.section.service.useYouServiceRadioButtonLabel"), settings.isUseYouService());
|
||||
|
||||
openAIBaseHostField = new JBTextField(openAISettings.getBaseHost(), 30);
|
||||
openAIPathField = new JBTextField(openAISettings.getPath(), 30);
|
||||
openAIOrganizationField = new JBTextField(openAISettings.getOrganization(), 30);
|
||||
openAICompletionModelComboBox = new ModelComboBox(DEFAULT_OPENAI_MODELS, OpenAIChatCompletionModel.findByCode(openAISettings.getModel()));
|
||||
|
||||
azureBaseHostField = new JBTextField(azureSettings.getBaseHost(), 30);
|
||||
azureResourceNameField = new JBTextField(azureSettings.getResourceName(), 30);
|
||||
azureDeploymentIdField = new JBTextField(azureSettings.getDeploymentId(), 30);
|
||||
azureApiVersionField = new JBTextField(azureSettings.getApiVersion(), 30);
|
||||
azureBaseHostField = new JBTextField(azureSettings.getBaseHost(), 35);
|
||||
azurePathField = new JBTextField(azureSettings.getPath(), 35);
|
||||
azureResourceNameField = new JBTextField(azureSettings.getResourceName(), 35);
|
||||
azureDeploymentIdField = new JBTextField(azureSettings.getDeploymentId(), 35);
|
||||
azureApiVersionField = new JBTextField(azureSettings.getApiVersion(), 35);
|
||||
azureCompletionModelComboBox = new ModelComboBox(DEFAULT_OPENAI_MODELS, OpenAIChatCompletionModel.findByCode(azureSettings.getModel()));
|
||||
azureCompletionModelComboBox.getEditor().getEditorComponent().setMaximumSize(azureBaseHostField.getPreferredSize());
|
||||
|
||||
displayWebSearchResultsCheckBox = new JBCheckBox("Display web search results", settings.isDisplayWebSearchResults());
|
||||
displayWebSearchResultsCheckBox.setEnabled(UserManager.getInstance().isAuthenticated());
|
||||
|
||||
openAIServiceSectionPanel = createOpenAIServiceSectionPanel();
|
||||
azureServiceSectionPanel = createAzureServiceSectionPanel();
|
||||
youServiceSectionPanel = createYouServiceSectionPanel();
|
||||
|
||||
registerPanelsVisibility(settings, azureSettings);
|
||||
registerRadioButtons();
|
||||
|
||||
ApplicationManager.getApplication()
|
||||
.getMessageBus()
|
||||
.connect()
|
||||
.subscribe(AuthenticationNotifier.AUTHENTICATION_TOPIC, (AuthenticationNotifier) () -> displayWebSearchResultsCheckBox.setEnabled(true));
|
||||
}
|
||||
|
||||
public JPanel getForm() {
|
||||
var form = FormBuilder.createFormBuilder()
|
||||
.addComponent(useOpenAIServiceRadioButton)
|
||||
var panel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
|
||||
panel.add(useOpenAIServiceRadioButton);
|
||||
// flow layout's horizontal gap adds annoying horizontal padding on each sides
|
||||
panel.add(Box.createHorizontalStrut(16));
|
||||
panel.add(useAzureServiceRadioButton);
|
||||
panel.add(Box.createHorizontalStrut(16));
|
||||
panel.add(useYouServiceRadioButton);
|
||||
|
||||
return FormBuilder.createFormBuilder()
|
||||
.addComponent(withEmptyLeftBorder(panel))
|
||||
.addComponent(openAIServiceSectionPanel)
|
||||
.addComponent(useAzureServiceRadioButton)
|
||||
.addComponent(azureServiceSectionPanel)
|
||||
.addComponent(youServiceSectionPanel)
|
||||
.getPanel();
|
||||
form.setBorder(JBUI.Borders.emptyLeft(16));
|
||||
return form;
|
||||
}
|
||||
|
||||
private JPanel createOpenAIServiceSectionPanel() {
|
||||
var requestConfigurationPanel = UI.PanelFactory.grid()
|
||||
.add(UI.PanelFactory.panel(openAIOrganizationField)
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.section.service.openai.organizationField.label"))
|
||||
.resizeX(false)
|
||||
.withComment(CodeGPTBundle.get("settingsConfigurable.section.service.openai.organizationField.comment")))
|
||||
.add(UI.PanelFactory.panel(openAIBaseHostField)
|
||||
.withLabel("Base host:")
|
||||
.resizeX(false))
|
||||
.add(UI.PanelFactory.panel(openAIPathField)
|
||||
.withLabel("Path:")
|
||||
.resizeX(false))
|
||||
.add(UI.PanelFactory.panel(openAICompletionModelComboBox)
|
||||
.withLabel("Model:")
|
||||
.resizeX(false))
|
||||
.createPanel();
|
||||
|
||||
var apiKeyFieldPanel = UI.PanelFactory.panel(openAIApiKeyField)
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.section.integration.apiKeyField.label"))
|
||||
.resizeX(false)
|
||||
.withComment(CodeGPTBundle.get("settingsConfigurable.section.integration.apiKeyField.comment"))
|
||||
.withCommentHyperlinkListener(SwingUtils::handleHyperlinkClicked)
|
||||
.createPanel();
|
||||
|
||||
return FormBuilder.createFormBuilder()
|
||||
.addComponent(new TitledSeparator("Authentication"))
|
||||
.addComponent(withEmptyLeftBorder(apiKeyFieldPanel))
|
||||
.addComponent(new TitledSeparator("Request Configuration"))
|
||||
.addComponent(withEmptyLeftBorder(requestConfigurationPanel))
|
||||
.addComponentFillVertically(new JPanel(), 0)
|
||||
.getPanel();
|
||||
}
|
||||
|
||||
private JPanel createAzureServiceSectionPanel() {
|
||||
var authPanel = withEmptyLeftBorder(FormBuilder.createFormBuilder()
|
||||
.addComponent(UI.PanelFactory
|
||||
.panel(useAzureApiKeyAuthenticationRadioButton)
|
||||
.resizeX(false)
|
||||
.createPanel())
|
||||
.addComponent(withEmptyLeftBorder(azureApiKeyFieldPanel))
|
||||
.addComponent(UI.PanelFactory
|
||||
.panel(useAzureActiveDirectoryAuthenticationRadioButton)
|
||||
.resizeX(false)
|
||||
.createPanel())
|
||||
.addComponent(withEmptyLeftBorder(azureActiveDirectoryTokenFieldPanel))
|
||||
.getPanel());
|
||||
|
||||
var configPanel = withEmptyLeftBorder(UI.PanelFactory.grid()
|
||||
.add(UI.PanelFactory.panel(azureResourceNameField)
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.section.service.azure.resourceNameField.label"))
|
||||
.resizeX(false)
|
||||
.withComment(CodeGPTBundle.get("settingsConfigurable.section.service.azure.resourceNameField.comment")))
|
||||
.add(UI.PanelFactory.panel(azureDeploymentIdField)
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.section.service.azure.deploymentIdField.label"))
|
||||
.resizeX(false)
|
||||
.withComment(CodeGPTBundle.get("settingsConfigurable.section.service.azure.deploymentIdField.comment")))
|
||||
.add(UI.PanelFactory.panel(azureApiVersionField)
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.section.service.azure.apiVersionField.label"))
|
||||
.resizeX(false)
|
||||
.withComment(CodeGPTBundle.get("settingsConfigurable.section.service.azure.apiVersionField.comment")))
|
||||
.add(UI.PanelFactory.panel(azureBaseHostField)
|
||||
.withLabel("Base host:")
|
||||
.resizeX(false))
|
||||
.add(UI.PanelFactory.panel(azurePathField)
|
||||
.withLabel("Path:")
|
||||
.resizeX(false))
|
||||
.add(UI.PanelFactory.panel(azureCompletionModelComboBox)
|
||||
.withLabel("Model:")
|
||||
.resizeX(false))
|
||||
.createPanel());
|
||||
|
||||
return FormBuilder.createFormBuilder()
|
||||
.addComponent(new TitledSeparator("Authentication"))
|
||||
.addComponent(authPanel)
|
||||
.addComponent(new TitledSeparator("Request Configuration"))
|
||||
.addComponent(configPanel)
|
||||
.getPanel();
|
||||
}
|
||||
|
||||
private JPanel createYouServiceSectionPanel() {
|
||||
return FormBuilder.createFormBuilder()
|
||||
.addComponent(new UserDetailsSettingsPanel(Disposer.newDisposable()))
|
||||
.addComponent(new TitledSeparator("Chat Preferences"))
|
||||
.addComponent(withEmptyLeftBorder(displayWebSearchResultsCheckBox))
|
||||
.getPanel();
|
||||
}
|
||||
|
||||
private JComponent withEmptyLeftBorder(JComponent component) {
|
||||
component.setBorder(JBUI.Borders.emptyLeft(16));
|
||||
return component;
|
||||
}
|
||||
|
||||
private void registerPanelsVisibility(SettingsState settings, AzureSettingsState azureSettings) {
|
||||
openAIServiceSectionPanel.setVisible(settings.isUseOpenAIService());
|
||||
azureServiceSectionPanel.setVisible(settings.isUseAzureService());
|
||||
azureApiKeyFieldPanel.setVisible(azureSettings.isUseAzureApiKeyAuthentication());
|
||||
azureActiveDirectoryTokenFieldPanel.setVisible(azureSettings.isUseAzureActiveDirectoryAuthentication());
|
||||
youServiceSectionPanel.setVisible(settings.isUseYouService());
|
||||
}
|
||||
|
||||
private void registerRadioButtons() {
|
||||
registerRadioButtons(
|
||||
List.of(
|
||||
Map.entry(useOpenAIServiceRadioButton, openAIServiceSectionPanel),
|
||||
Map.entry(useAzureServiceRadioButton, azureServiceSectionPanel),
|
||||
Map.entry(useYouServiceRadioButton, youServiceSectionPanel)));
|
||||
registerRadioButtons(
|
||||
List.of(
|
||||
Map.entry(useAzureApiKeyAuthenticationRadioButton, azureApiKeyFieldPanel),
|
||||
Map.entry(useAzureActiveDirectoryAuthenticationRadioButton, azureActiveDirectoryTokenFieldPanel)));
|
||||
}
|
||||
|
||||
private void registerRadioButtons(List<Map.Entry<JBRadioButton, JPanel>> entries) {
|
||||
var buttonGroup = new ButtonGroup();
|
||||
entries.forEach(entry -> buttonGroup.add(entry.getKey()));
|
||||
|
||||
entries.forEach(entry -> entry.getKey().addActionListener((e) -> {
|
||||
for (Map.Entry<JBRadioButton, JPanel> innerEntry : entries) {
|
||||
innerEntry.getValue().setVisible(innerEntry.equals(entry));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public OpenAIChatCompletionModel getSelectedCompletionModel() {
|
||||
return (OpenAIChatCompletionModel) (isOpenAIServiceSelected() ?
|
||||
openAICompletionModelComboBox.getSelectedItem() :
|
||||
azureCompletionModelComboBox.getSelectedItem());
|
||||
}
|
||||
|
||||
public void setSelectedChatCompletionModel(OpenAIChatCompletionModel chatCompletionModel) {
|
||||
if (isOpenAIServiceSelected()) {
|
||||
openAICompletionModelComboBox.setSelectedItem(chatCompletionModel);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOpenAIServiceSelected(boolean selected) {
|
||||
|
|
@ -119,12 +303,20 @@ public class ServiceSelectionForm {
|
|||
return useAzureServiceRadioButton.isSelected();
|
||||
}
|
||||
|
||||
public boolean isYouServiceSelected() {
|
||||
return useYouServiceRadioButton.isSelected();
|
||||
}
|
||||
|
||||
public void setYouServiceSelected(boolean selected) {
|
||||
useYouServiceRadioButton.setSelected(selected);
|
||||
}
|
||||
|
||||
public void setOpenAIApiKey(String apiKey) {
|
||||
openAIApiKey.setText(apiKey);
|
||||
openAIApiKeyField.setText(apiKey);
|
||||
}
|
||||
|
||||
public String getOpenAIApiKey() {
|
||||
return new String(openAIApiKey.getPassword());
|
||||
return new String(openAIApiKeyField.getPassword());
|
||||
}
|
||||
|
||||
public void setOpenAIBaseHost(String baseHost) {
|
||||
|
|
@ -143,6 +335,14 @@ public class ServiceSelectionForm {
|
|||
return openAIOrganizationField.getText();
|
||||
}
|
||||
|
||||
public void setOpenAIModel(String model) {
|
||||
openAICompletionModelComboBox.setSelectedItem(OpenAIChatCompletionModel.findByCode(model));
|
||||
}
|
||||
|
||||
public String getOpenAIModel() {
|
||||
return ((OpenAIChatCompletionModel) (openAICompletionModelComboBox.getModel().getSelectedItem())).getCode();
|
||||
}
|
||||
|
||||
public void setAzureActiveDirectoryAuthenticationSelected(boolean selected) {
|
||||
useAzureActiveDirectoryAuthenticationRadioButton.setSelected(selected);
|
||||
}
|
||||
|
|
@ -207,94 +407,35 @@ public class ServiceSelectionForm {
|
|||
return azureBaseHostField.getText();
|
||||
}
|
||||
|
||||
private JPanel createOpenAIServiceSectionPanel() {
|
||||
var panel = UI.PanelFactory.grid()
|
||||
.add(UI.PanelFactory.panel(openAIApiKey)
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.section.integration.apiKeyField.label"))
|
||||
.resizeX(false)
|
||||
.withComment(CodeGPTBundle.get("settingsConfigurable.section.integration.apiKeyField.comment"))
|
||||
.withCommentHyperlinkListener(SwingUtils::handleHyperlinkClicked))
|
||||
.add(UI.PanelFactory.panel(openAIOrganizationField)
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.section.service.openai.organizationField.label"))
|
||||
.resizeX(false)
|
||||
.withComment(CodeGPTBundle.get("settingsConfigurable.section.service.openai.organizationField.comment")))
|
||||
.add(UI.PanelFactory.panel(openAIBaseHostField)
|
||||
.withLabel("Base host:")
|
||||
.resizeX(false))
|
||||
.createPanel();
|
||||
panel.setBorder(JBUI.Borders.emptyLeft(16));
|
||||
return panel;
|
||||
public void setAzureModel(String model) {
|
||||
azureCompletionModelComboBox.setSelectedItem(OpenAIChatCompletionModel.findByCode(model));
|
||||
}
|
||||
|
||||
private JPanel createAzureServiceSectionPanel() {
|
||||
var gridPanel = UI.PanelFactory.grid()
|
||||
.add(UI.PanelFactory.panel(azureResourceNameField)
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.section.service.azure.resourceNameField.label"))
|
||||
.resizeX(false)
|
||||
.withComment(CodeGPTBundle.get("settingsConfigurable.section.service.azure.resourceNameField.comment")))
|
||||
.add(UI.PanelFactory.panel(azureDeploymentIdField)
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.section.service.azure.deploymentIdField.label"))
|
||||
.resizeX(false)
|
||||
.withComment(CodeGPTBundle.get("settingsConfigurable.section.service.azure.deploymentIdField.comment")))
|
||||
.add(UI.PanelFactory.panel(azureApiVersionField)
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.section.service.azure.apiVersionField.label"))
|
||||
.resizeX(false)
|
||||
.withComment(CodeGPTBundle.get("settingsConfigurable.section.service.azure.apiVersionField.comment")))
|
||||
.add(UI.PanelFactory.panel(azureBaseHostField)
|
||||
.withLabel("Base host:")
|
||||
.resizeX(false))
|
||||
.createPanel();
|
||||
gridPanel.setBorder(JBUI.Borders.emptyLeft(16));
|
||||
|
||||
azureApiKeyFieldPanel.setBorder(JBUI.Borders.emptyLeft(16));
|
||||
azureActiveDirectoryTokenFieldPanel.setBorder(JBUI.Borders.emptyLeft(16));
|
||||
|
||||
var authenticationPanel = FormBuilder.createFormBuilder()
|
||||
.addComponent(UI.PanelFactory.panel(useAzureApiKeyAuthenticationRadioButton).resizeX(false).createPanel())
|
||||
.addComponent(azureApiKeyFieldPanel)
|
||||
.addComponent(UI.PanelFactory.panel(useAzureActiveDirectoryAuthenticationRadioButton).resizeX(false).createPanel())
|
||||
.addComponent(azureActiveDirectoryTokenFieldPanel)
|
||||
.getPanel();
|
||||
authenticationPanel.setBorder(JBUI.Borders.emptyLeft(16));
|
||||
|
||||
var form = FormBuilder.createFormBuilder()
|
||||
.addComponent(new TitledSeparator("Authentication"))
|
||||
.addComponent(authenticationPanel)
|
||||
.addComponent(new TitledSeparator("Request Configuration"))
|
||||
.addComponent(gridPanel)
|
||||
.getPanel();
|
||||
form.setBorder(JBUI.Borders.emptyLeft(16));
|
||||
return form;
|
||||
public String getAzureModel() {
|
||||
return ((OpenAIChatCompletionModel) (azureCompletionModelComboBox.getModel().getSelectedItem())).getCode();
|
||||
}
|
||||
|
||||
private void registerPanelsVisibility(SettingsState settings, AzureSettingsState azureSettings) {
|
||||
openAIServiceSectionPanel.setVisible(settings.isUseOpenAIService());
|
||||
azureServiceSectionPanel.setVisible(settings.isUseAzureService());
|
||||
azureApiKeyFieldPanel.setVisible(azureSettings.isUseAzureApiKeyAuthentication());
|
||||
azureActiveDirectoryTokenFieldPanel.setVisible(azureSettings.isUseAzureActiveDirectoryAuthentication());
|
||||
public void setDisplayWebSearchResults(boolean displayWebSearchResults) {
|
||||
displayWebSearchResultsCheckBox.setSelected(displayWebSearchResults);
|
||||
}
|
||||
|
||||
private void registerRadioButtons() {
|
||||
registerRadioButtons(
|
||||
Map.entry(useOpenAIServiceRadioButton, openAIServiceSectionPanel),
|
||||
Map.entry(useAzureServiceRadioButton, azureServiceSectionPanel));
|
||||
registerRadioButtons(
|
||||
Map.entry(useAzureApiKeyAuthenticationRadioButton, azureApiKeyFieldPanel),
|
||||
Map.entry(useAzureActiveDirectoryAuthenticationRadioButton, azureActiveDirectoryTokenFieldPanel));
|
||||
public boolean isDisplayWebSearchResults() {
|
||||
return displayWebSearchResultsCheckBox.isSelected();
|
||||
}
|
||||
|
||||
private void registerRadioButtons(Map.Entry<JBRadioButton, JPanel> firstEntry, Map.Entry<JBRadioButton, JPanel> secondEntry) {
|
||||
var buttonGroup = new ButtonGroup();
|
||||
buttonGroup.add(firstEntry.getKey());
|
||||
buttonGroup.add(secondEntry.getKey());
|
||||
public void setOpenAIPath(String path) {
|
||||
openAIPathField.setText(path);
|
||||
}
|
||||
|
||||
firstEntry.getKey().addActionListener(e -> {
|
||||
firstEntry.getValue().setVisible(true);
|
||||
secondEntry.getValue().setVisible(false);
|
||||
});
|
||||
secondEntry.getKey().addActionListener(e -> {
|
||||
firstEntry.getValue().setVisible(false);
|
||||
secondEntry.getValue().setVisible(true);
|
||||
});
|
||||
public String getOpenAIPath() {
|
||||
return openAIPathField.getText();
|
||||
}
|
||||
|
||||
public void setAzurePath(String path) {
|
||||
azurePathField.setText(path);
|
||||
}
|
||||
|
||||
public String getAzurePath() {
|
||||
return azurePathField.getText();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,19 +15,13 @@ public class SettingsComponent {
|
|||
private final JPanel mainPanel;
|
||||
private final JBTextField displayNameField;
|
||||
private final ServiceSelectionForm serviceSelectionForm;
|
||||
private final ModelSelectionForm modelSelectionForm;
|
||||
private final UserDetailsSettingsPanel userDetailsSettingsPanel;
|
||||
|
||||
public SettingsComponent(Disposable parentDisposable, SettingsState settings) {
|
||||
modelSelectionForm = new ModelSelectionForm();
|
||||
serviceSelectionForm = new ServiceSelectionForm(settings);
|
||||
|
||||
displayNameField = new JBTextField(settings.getDisplayName(), 20);
|
||||
|
||||
userDetailsSettingsPanel = new UserDetailsSettingsPanel(parentDisposable, settings);
|
||||
|
||||
userDetailsSettingsPanel = new UserDetailsSettingsPanel(parentDisposable);
|
||||
mainPanel = FormBuilder.createFormBuilder()
|
||||
// .addComponent(userDetailsSettingsPanel)
|
||||
.addComponent(UI.PanelFactory.panel(displayNameField)
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.section.integration.displayNameFieldLabel"))
|
||||
.resizeX(false)
|
||||
|
|
@ -35,8 +29,6 @@ public class SettingsComponent {
|
|||
.addComponent(new TitledSeparator(CodeGPTBundle.get("settingsConfigurable.section.service.title")))
|
||||
.addComponent(serviceSelectionForm.getForm())
|
||||
.addVerticalGap(8)
|
||||
.addComponent(new TitledSeparator(CodeGPTBundle.get("settingsConfigurable.section.model.title")))
|
||||
.addComponent(modelSelectionForm.getForm())
|
||||
.addComponentFillVertically(new JPanel(), 0)
|
||||
.getPanel();
|
||||
}
|
||||
|
|
@ -65,10 +57,6 @@ public class SettingsComponent {
|
|||
return serviceSelectionForm;
|
||||
}
|
||||
|
||||
public ModelSelectionForm getModelSelectionForm() {
|
||||
return modelSelectionForm;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayNameField.getText();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,17 +3,13 @@ package ee.carlrobert.codegpt.settings;
|
|||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.EncodingManager;
|
||||
import ee.carlrobert.codegpt.conversations.ConversationsState;
|
||||
import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
|
||||
import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
|
||||
import ee.carlrobert.codegpt.credentials.UserCredentialsManager;
|
||||
import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.ModelSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.standard.StandardChatToolWindowContentManager;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.standard.StandardChatToolWindowTabPanel;
|
||||
import ee.carlrobert.codegpt.util.ApplicationUtils;
|
||||
import javax.swing.JComponent;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
|
|
@ -47,90 +43,41 @@ public class SettingsConfigurable implements Configurable, Disposable {
|
|||
var settings = SettingsState.getInstance();
|
||||
var openAISettings = OpenAISettingsState.getInstance();
|
||||
var azureSettings = AzureSettingsState.getInstance();
|
||||
var modelSettings = ModelSettingsState.getInstance();
|
||||
|
||||
var serviceSelectionForm = settingsComponent.getServiceSelectionForm();
|
||||
return !settingsComponent.getEmail().equals(settings.getEmail()) ||
|
||||
!settingsComponent.getDisplayName().equals(settings.getDisplayName()) ||
|
||||
|
||||
serviceSelectionForm.isOpenAIServiceSelected() != settings.isUseOpenAIService() ||
|
||||
serviceSelectionForm.isAzureServiceSelected() != settings.isUseAzureService() ||
|
||||
!serviceSelectionForm.getOpenAIApiKey().equals(OpenAICredentialsManager.getInstance().getApiKey()) ||
|
||||
!serviceSelectionForm.getOpenAIOrganization().equals(openAISettings.getOrganization()) ||
|
||||
!serviceSelectionForm.getOpenAIBaseHost().equals(openAISettings.getBaseHost()) ||
|
||||
|
||||
serviceSelectionForm.isAzureActiveDirectoryAuthenticationSelected() != azureSettings.isUseAzureActiveDirectoryAuthentication() ||
|
||||
serviceSelectionForm.isAzureApiKeyAuthenticationSelected() != azureSettings.isUseAzureApiKeyAuthentication() ||
|
||||
!serviceSelectionForm.getAzureActiveDirectoryToken().equals(AzureCredentialsManager.getInstance().getAzureActiveDirectoryToken()) ||
|
||||
!serviceSelectionForm.getAzureOpenAIApiKey().equals(AzureCredentialsManager.getInstance().getAzureOpenAIApiKey()) ||
|
||||
!serviceSelectionForm.getAzureResourceName().equals(azureSettings.getResourceName()) ||
|
||||
!serviceSelectionForm.getAzureDeploymentId().equals(azureSettings.getDeploymentId()) ||
|
||||
!serviceSelectionForm.getAzureApiVersion().equals(azureSettings.getApiVersion()) ||
|
||||
!serviceSelectionForm.getAzureBaseHost().equals(azureSettings.getBaseHost()) ||
|
||||
|
||||
isModelChanged(modelSettings) ||
|
||||
isCompletionOptionChanged(modelSettings);
|
||||
return !settingsComponent.getDisplayName().equals(settings.getDisplayName()) ||
|
||||
isServiceChanged(serviceSelectionForm, settings) ||
|
||||
openAISettings.isModified(serviceSelectionForm) ||
|
||||
azureSettings.isModified(serviceSelectionForm) ||
|
||||
serviceSelectionForm.isDisplayWebSearchResults() != settings.isDisplayWebSearchResults();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
var serviceSelectionForm = settingsComponent.getServiceSelectionForm();
|
||||
var settings = SettingsState.getInstance();
|
||||
var openAISettings = OpenAISettingsState.getInstance();
|
||||
var azureSettings = AzureSettingsState.getInstance();
|
||||
var modelSettings = ModelSettingsState.getInstance();
|
||||
var isModelChanged = isModelChanged(modelSettings);
|
||||
var serviceChanged = isServiceChanged(serviceSelectionForm, settings);
|
||||
var modelChanged = openAISettings.getModel().equals(serviceSelectionForm.getOpenAIModel()) ||
|
||||
azureSettings.getModel().equals(serviceSelectionForm.getAzureModel());
|
||||
|
||||
if (isModelChanged) {
|
||||
EncodingManager.getInstance().setEncoding(modelSettings.isUseChatCompletion() ?
|
||||
modelSettings.getChatCompletionModel() :
|
||||
modelSettings.getTextCompletionModel());
|
||||
}
|
||||
|
||||
if (isCompletionOptionChanged(modelSettings) || isModelChanged) {
|
||||
ConversationsState.getInstance().setCurrentConversation(null);
|
||||
var project = ApplicationUtils.findCurrentProject();
|
||||
if (project == null) {
|
||||
throw new RuntimeException("Could not find current project.");
|
||||
}
|
||||
|
||||
StandardChatToolWindowContentManager.getInstance(project)
|
||||
.tryFindChatTabbedPane()
|
||||
.ifPresent(tabbedPane -> {
|
||||
tabbedPane.clearAll();
|
||||
var tabPanel = new StandardChatToolWindowTabPanel(project);
|
||||
tabPanel.displayLandingView();
|
||||
tabbedPane.addNewTab(tabPanel);
|
||||
});
|
||||
}
|
||||
|
||||
var serviceSelectionForm = settingsComponent.getServiceSelectionForm();
|
||||
var modelSelectionForm = settingsComponent.getModelSelectionForm();
|
||||
|
||||
UserCredentialsManager.getInstance().setAccountPassword(settingsComponent.getPassword());
|
||||
OpenAICredentialsManager.getInstance().setApiKey(serviceSelectionForm.getOpenAIApiKey());
|
||||
AzureCredentialsManager.getInstance().setApiKey(serviceSelectionForm.getAzureOpenAIApiKey());
|
||||
AzureCredentialsManager.getInstance().setAzureActiveDirectoryToken(serviceSelectionForm.getAzureActiveDirectoryToken());
|
||||
|
||||
settings.setEmail(settingsComponent.getEmail());
|
||||
settings.setDisplayName(settingsComponent.getDisplayName());
|
||||
|
||||
settings.setUseOpenAIService(serviceSelectionForm.isOpenAIServiceSelected());
|
||||
settings.setUseAzureService(serviceSelectionForm.isAzureServiceSelected());
|
||||
settings.setUseYouService(serviceSelectionForm.isYouServiceSelected());
|
||||
settings.setDisplayWebSearchResults(serviceSelectionForm.isDisplayWebSearchResults());
|
||||
|
||||
openAISettings.setOrganization(serviceSelectionForm.getOpenAIOrganization());
|
||||
openAISettings.setBaseHost(serviceSelectionForm.getOpenAIBaseHost());
|
||||
openAISettings.apply(serviceSelectionForm);
|
||||
azureSettings.apply(serviceSelectionForm);
|
||||
|
||||
azureSettings.setUseAzureActiveDirectoryAuthentication(serviceSelectionForm.isAzureActiveDirectoryAuthenticationSelected());
|
||||
azureSettings.setUseAzureApiKeyAuthentication(serviceSelectionForm.isAzureApiKeyAuthenticationSelected());
|
||||
azureSettings.setResourceName(serviceSelectionForm.getAzureResourceName());
|
||||
azureSettings.setDeploymentId(serviceSelectionForm.getAzureDeploymentId());
|
||||
azureSettings.setApiVersion(serviceSelectionForm.getAzureApiVersion());
|
||||
azureSettings.setBaseHost(serviceSelectionForm.getAzureBaseHost());
|
||||
|
||||
modelSettings.setUseChatCompletion(modelSelectionForm.isChatCompletionOptionSelected());
|
||||
modelSettings.setUseTextCompletion(modelSelectionForm.isTextCompletionOptionSelected());
|
||||
modelSettings.setChatCompletionModel(modelSelectionForm.getChatCompletionBaseModel().getCode());
|
||||
modelSettings.setTextCompletionModel(modelSelectionForm.getTextCompletionBaseModel().getCode());
|
||||
if (serviceChanged || modelChanged) {
|
||||
resetActiveTab();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -138,33 +85,19 @@ public class SettingsConfigurable implements Configurable, Disposable {
|
|||
var settings = SettingsState.getInstance();
|
||||
var openAISettings = OpenAISettingsState.getInstance();
|
||||
var azureSettings = AzureSettingsState.getInstance();
|
||||
var modelSettings = ModelSettingsState.getInstance();
|
||||
var serviceSelectionForm = settingsComponent.getServiceSelectionForm();
|
||||
var modelSelectionForm = settingsComponent.getModelSelectionForm();
|
||||
|
||||
settingsComponent.setEmail(settings.getEmail());
|
||||
settingsComponent.setDisplayName(settings.getDisplayName());
|
||||
|
||||
serviceSelectionForm.setOpenAIServiceSelected(settings.isUseOpenAIService());
|
||||
serviceSelectionForm.setAzureServiceSelected(settings.isUseAzureService());
|
||||
serviceSelectionForm.setYouServiceSelected(settings.isUseYouService());
|
||||
|
||||
serviceSelectionForm.setOpenAIApiKey(OpenAICredentialsManager.getInstance().getApiKey());
|
||||
serviceSelectionForm.setOpenAIOrganization(openAISettings.getOrganization());
|
||||
serviceSelectionForm.setOpenAIBaseHost(openAISettings.getBaseHost());
|
||||
openAISettings.reset(serviceSelectionForm);
|
||||
azureSettings.reset(serviceSelectionForm);
|
||||
|
||||
serviceSelectionForm.setAzureApiKeyAuthenticationSelected(azureSettings.isUseAzureApiKeyAuthentication());
|
||||
serviceSelectionForm.setAzureApiKey(AzureCredentialsManager.getInstance().getAzureOpenAIApiKey());
|
||||
serviceSelectionForm.setAzureActiveDirectoryAuthenticationSelected(azureSettings.isUseAzureActiveDirectoryAuthentication());
|
||||
serviceSelectionForm.setAzureActiveDirectoryToken(AzureCredentialsManager.getInstance().getAzureActiveDirectoryToken());
|
||||
serviceSelectionForm.setAzureResourceName(azureSettings.getResourceName());
|
||||
serviceSelectionForm.setAzureDeploymentId(azureSettings.getDeploymentId());
|
||||
serviceSelectionForm.setAzureApiVersion(azureSettings.getApiVersion());
|
||||
serviceSelectionForm.setAzureBaseHost(azureSettings.getBaseHost());
|
||||
|
||||
modelSelectionForm.setUseChatCompletionSelected(modelSettings.isUseChatCompletion());
|
||||
modelSelectionForm.setUseTextCompletionSelected(modelSettings.isUseTextCompletion());
|
||||
modelSelectionForm.setChatCompletionBaseModel(modelSettings.getChatCompletionModel());
|
||||
modelSelectionForm.setTextCompletionBaseModel(modelSettings.getTextCompletionModel());
|
||||
serviceSelectionForm.setDisplayWebSearchResults(settings.isDisplayWebSearchResults());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -172,19 +105,23 @@ public class SettingsConfigurable implements Configurable, Disposable {
|
|||
settingsComponent = null;
|
||||
}
|
||||
|
||||
private boolean isCompletionOptionChanged(ModelSettingsState settings) {
|
||||
var modelSelectionForm = settingsComponent.getModelSelectionForm();
|
||||
return modelSelectionForm.isChatCompletionOptionSelected() != settings.isUseChatCompletion() ||
|
||||
modelSelectionForm.isTextCompletionOptionSelected() != settings.isUseTextCompletion();
|
||||
}
|
||||
|
||||
private boolean isModelChanged(ModelSettingsState settings) {
|
||||
var modelSelectionForm = settingsComponent.getModelSelectionForm();
|
||||
return !modelSelectionForm.getChatCompletionBaseModel().getCode().equals(settings.getChatCompletionModel()) ||
|
||||
!modelSelectionForm.getTextCompletionBaseModel().getCode().equals(settings.getTextCompletionModel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
private boolean isServiceChanged(ServiceSelectionForm serviceSelectionForm, SettingsState settings) {
|
||||
return serviceSelectionForm.isOpenAIServiceSelected() != settings.isUseOpenAIService() ||
|
||||
serviceSelectionForm.isAzureServiceSelected() != settings.isUseAzureService() ||
|
||||
serviceSelectionForm.isYouServiceSelected() != settings.isUseYouService();
|
||||
}
|
||||
|
||||
private void resetActiveTab() {
|
||||
ConversationsState.getInstance().setCurrentConversation(null);
|
||||
var project = ApplicationUtils.findCurrentProject();
|
||||
if (project == null) {
|
||||
throw new RuntimeException("Could not find current project.");
|
||||
}
|
||||
|
||||
StandardChatToolWindowContentManager.getInstance(project).resetActiveTab();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package ee.carlrobert.codegpt.settings;
|
||||
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.ui.ComponentValidator;
|
||||
import com.intellij.openapi.ui.ValidationInfo;
|
||||
|
|
@ -17,17 +18,23 @@ import ee.carlrobert.codegpt.CodeGPTBundle;
|
|||
import ee.carlrobert.codegpt.credentials.UserCredentialsManager;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.user.UserManager;
|
||||
import ee.carlrobert.codegpt.user.auth.AuthenticationError;
|
||||
import ee.carlrobert.codegpt.user.auth.AuthenticationHandler;
|
||||
import ee.carlrobert.codegpt.user.auth.AuthenticationService;
|
||||
import ee.carlrobert.codegpt.user.auth.response.AuthenticationResponse;
|
||||
import ee.carlrobert.codegpt.user.auth.response.User;
|
||||
import ee.carlrobert.codegpt.util.OverlayUtils;
|
||||
import ee.carlrobert.codegpt.util.SwingUtils;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPasswordField;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class UserDetailsSettingsPanel extends JPanel {
|
||||
|
||||
|
|
@ -37,8 +44,9 @@ public class UserDetailsSettingsPanel extends JPanel {
|
|||
private final JTextPane signUpTextPane;
|
||||
private final AsyncProcessIcon loadingSpinner;
|
||||
|
||||
public UserDetailsSettingsPanel(Disposable parentDisposable, SettingsState settings) {
|
||||
public UserDetailsSettingsPanel(Disposable parentDisposable) {
|
||||
super(new BorderLayout());
|
||||
var settings = SettingsState.getInstance();
|
||||
emailField = new JBTextField(settings.getEmail(), 25);
|
||||
passwordField = new JBPasswordField();
|
||||
passwordField.setColumns(25);
|
||||
|
|
@ -65,10 +73,10 @@ public class UserDetailsSettingsPanel extends JPanel {
|
|||
}
|
||||
});
|
||||
|
||||
if (UserManager.getInstance().getSession() == null) {
|
||||
add(createUserAuthenticationPanel(emailField, passwordField, false));
|
||||
if (UserManager.getInstance().getAuthenticationResponse() == null) {
|
||||
add(createUserAuthenticationPanel(emailField, passwordField, null));
|
||||
} else {
|
||||
add(createUserInformationPanel());
|
||||
add(createUserInformationPanel(UserManager.getInstance().getAuthenticationResponse().getData().getUser()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -90,6 +98,9 @@ public class UserDetailsSettingsPanel extends JPanel {
|
|||
String value;
|
||||
if (component instanceof JBTextField) {
|
||||
value = ((JBTextField) component).getText();
|
||||
if (!isValidEmail(value)) {
|
||||
return new ValidationInfo("The email you entered is invalid.", component).withOKEnabled();
|
||||
}
|
||||
} else {
|
||||
value = new String(((JPasswordField) component).getPassword());
|
||||
}
|
||||
|
|
@ -106,9 +117,15 @@ public class UserDetailsSettingsPanel extends JPanel {
|
|||
return validator;
|
||||
}
|
||||
|
||||
private boolean isValidEmail(String email) {
|
||||
// RFC 5322
|
||||
return Pattern.compile("^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$")
|
||||
.matcher(email)
|
||||
.matches();
|
||||
}
|
||||
|
||||
private JTextPane createSignUpTextPane() {
|
||||
var textPane = createTextPane(
|
||||
"<html><a href=\"https://subscription-starter-etdkim4m3-carlrobertoh-gmailcom.vercel.app/signin#auth-sign-up\">Don't have an account? Sign up</a></html>");
|
||||
var textPane = createTextPane("<html><a href=\"https://you.com/code\">Don't have an account? Sign up</a></html>");
|
||||
textPane.setBorder(JBUI.Borders.emptyLeft(4));
|
||||
return textPane;
|
||||
}
|
||||
|
|
@ -132,17 +149,16 @@ public class UserDetailsSettingsPanel extends JPanel {
|
|||
return panel;
|
||||
}
|
||||
|
||||
private JPanel createUserAuthenticationPanel(JBTextField emailAddressField, JBPasswordField passwordField, boolean withInvalidCredentials) {
|
||||
private JPanel createUserAuthenticationPanel(JBTextField emailAddressField, JBPasswordField passwordField, @Nullable AuthenticationError error) {
|
||||
var contentPanelBuilder = FormBuilder.createFormBuilder()
|
||||
.addVerticalGap(8)
|
||||
.addLabeledComponent("Email address:", emailAddressField)
|
||||
.addLabeledComponent("Password:", passwordField)
|
||||
.addVerticalGap(4)
|
||||
.addComponentToRightColumn(createFooterPanel())
|
||||
.addVerticalGap(4);
|
||||
|
||||
if (withInvalidCredentials) {
|
||||
var invalidCredentialsLabel = new JBLabel("Invalid login credentials");
|
||||
if (error != null) {
|
||||
var invalidCredentialsLabel = new JBLabel(error.getErrorMessage());
|
||||
invalidCredentialsLabel.setForeground(JBColor.red);
|
||||
invalidCredentialsLabel.setBorder(JBUI.Borders.emptyLeft(4));
|
||||
|
||||
|
|
@ -157,29 +173,15 @@ public class UserDetailsSettingsPanel extends JPanel {
|
|||
.getPanel();
|
||||
}
|
||||
|
||||
private JPanel createUserInformationPanel() {
|
||||
private JPanel createUserInformationPanel(User user) {
|
||||
var userManager = UserManager.getInstance();
|
||||
var contentPanelBuilder = FormBuilder.createFormBuilder()
|
||||
.addLabeledComponent("Email address:",
|
||||
new JBLabel(userManager.getSession()
|
||||
.getUser()
|
||||
.getEmail()).withFont(JBFont.label().asBold()));
|
||||
|
||||
if (userManager.isSubscribed()) {
|
||||
contentPanelBuilder.addLabeledComponent("Subscription:",
|
||||
new JBLabel(userManager.getSubscription()
|
||||
.getPrices()
|
||||
.getProducts()
|
||||
.getName()).withFont(JBFont.label().asBold()));
|
||||
} else {
|
||||
contentPanelBuilder.addComponent(createTextPane(
|
||||
"<html>You haven't subscribed to any plan yet. Subscribe <a href=\"https://codegpt-starter.vercel.app\">now</a>.</html>"));
|
||||
}
|
||||
.addLabeledComponent("Email address:", new JBLabel(user.getEmails().get(0).getEmail()).withFont(JBFont.label().asBold()));
|
||||
|
||||
var signOutButton = new JButton("Sign Out");
|
||||
signOutButton.addActionListener(e -> {
|
||||
userManager.clearSession();
|
||||
refreshView(createUserAuthenticationPanel(emailField, passwordField, false));
|
||||
refreshView(createUserAuthenticationPanel(emailField, passwordField, null));
|
||||
});
|
||||
|
||||
return FormBuilder.createFormBuilder()
|
||||
|
|
@ -196,18 +198,25 @@ public class UserDetailsSettingsPanel extends JPanel {
|
|||
class UserAuthenticationHandler implements AuthenticationHandler {
|
||||
|
||||
@Override
|
||||
public void handleAuthenticated() {
|
||||
SwingUtilities.invokeLater(() -> refreshView(createUserInformationPanel()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleInvalidCredentials() {
|
||||
SwingUtilities.invokeLater(() -> refreshView(createUserAuthenticationPanel(emailField, passwordField, true)));
|
||||
public void handleAuthenticated(AuthenticationResponse authenticationResponse) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
var email = emailField.getText();
|
||||
var password = passwordField.getPassword();
|
||||
SettingsState.getInstance().setEmail(email);
|
||||
UserCredentialsManager.getInstance().setAccountPassword(new String(password));
|
||||
refreshView(createUserInformationPanel(authenticationResponse.getData().getUser()));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleGenericError() {
|
||||
// TODO
|
||||
SwingUtilities.invokeLater(() -> refreshView(
|
||||
createUserAuthenticationPanel(emailField, passwordField, new AuthenticationError("unknown", "Something went wrong."))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(AuthenticationError error) {
|
||||
SwingUtilities.invokeLater(() -> refreshView(createUserAuthenticationPanel(emailField, passwordField, error)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ public class AdvancedSettingsComponent {
|
|||
|
||||
private JPanel createConnectionSettingsForm() {
|
||||
var panel = FormBuilder.createFormBuilder()
|
||||
.addVerticalGap(4)
|
||||
.addLabeledComponent("Connection timeout (s):", connectionTimeoutField)
|
||||
.addLabeledComponent("Read timeout (s):", readTimeoutField)
|
||||
.getPanel();
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import java.net.Proxy;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@State(name = "CodeGPT_AdvancedSettings", storages = @Storage("CodeGPT_AdvancedSettings.xml"))
|
||||
@State(name = "CodeGPT_AdvancedSettings_210", storages = @Storage("CodeGPT_AdvancedSettings_210.xml"))
|
||||
public class AdvancedSettingsState implements PersistentStateComponent<AdvancedSettingsState> {
|
||||
|
||||
private String proxyHost = "";
|
||||
|
|
|
|||
|
|
@ -100,7 +100,6 @@ public class ConfigurationComponent {
|
|||
.addComponent(openNewTabCheckBox)
|
||||
.addVerticalGap(4)
|
||||
.addComponent(new TitledSeparator(CodeGPTBundle.get("configurationConfigurable.section.assistant.title")))
|
||||
.addVerticalGap(4)
|
||||
.addComponent(createAssistantConfigurationForm())
|
||||
.addComponentFillVertically(new JPanel(), 0)
|
||||
.getPanel();
|
||||
|
|
|
|||
|
|
@ -10,10 +10,7 @@ import java.util.Map;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@State(
|
||||
name = "ee.carlrobert.codegpt.state.settings.configuration.ConfigurationState",
|
||||
storages = @Storage("CodeGPTConfiguration.xml")
|
||||
)
|
||||
@State(name = "CodeGPT_ConfigurationSettings_210", storages = @Storage("CodeGPT_ConfigurationSettings_210.xml"))
|
||||
public class ConfigurationState implements PersistentStateComponent<ConfigurationState> {
|
||||
|
||||
private String systemPrompt = "";
|
||||
|
|
|
|||
|
|
@ -5,15 +5,22 @@ import com.intellij.openapi.components.PersistentStateComponent;
|
|||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
|
||||
import ee.carlrobert.codegpt.settings.ServiceSelectionForm;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.OpenAIChatCompletionModel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@State(name = "CodeGPT_AzureSettings", storages = @Storage("CodeGPT_AzureSettings.xml"))
|
||||
@State(name = "CodeGPT_AzureSettings_210", storages = @Storage("CodeGPT_AzureSettings_210.xml"))
|
||||
public class AzureSettingsState implements PersistentStateComponent<AzureSettingsState> {
|
||||
|
||||
private final String BASE_PATH = "/openai/deployments/%s/chat/completions?api-version=%s";
|
||||
|
||||
private String resourceName = "";
|
||||
private String deploymentId = "";
|
||||
private String apiVersion = "";
|
||||
private String baseHost = "https://%s.openai.azure.com";
|
||||
private String path = BASE_PATH;
|
||||
private String model = OpenAIChatCompletionModel.GPT_3_5.getCode();
|
||||
private boolean useAzureApiKeyAuthentication = true;
|
||||
private boolean useAzureActiveDirectoryAuthentication;
|
||||
|
||||
|
|
@ -31,6 +38,48 @@ public class AzureSettingsState implements PersistentStateComponent<AzureSetting
|
|||
XmlSerializerUtil.copyBean(state, this);
|
||||
}
|
||||
|
||||
public boolean isModified(ServiceSelectionForm serviceSelectionForm) {
|
||||
return serviceSelectionForm.isAzureActiveDirectoryAuthenticationSelected() != isUseAzureActiveDirectoryAuthentication() ||
|
||||
serviceSelectionForm.isAzureApiKeyAuthenticationSelected() != isUseAzureApiKeyAuthentication() ||
|
||||
!serviceSelectionForm.getAzureActiveDirectoryToken().equals(AzureCredentialsManager.getInstance().getAzureActiveDirectoryToken()) ||
|
||||
!serviceSelectionForm.getAzureOpenAIApiKey().equals(AzureCredentialsManager.getInstance().getAzureOpenAIApiKey()) ||
|
||||
!serviceSelectionForm.getAzureResourceName().equals(resourceName) ||
|
||||
!serviceSelectionForm.getAzureDeploymentId().equals(deploymentId) ||
|
||||
!serviceSelectionForm.getAzureApiVersion().equals(apiVersion) ||
|
||||
!serviceSelectionForm.getAzureBaseHost().equals(baseHost) ||
|
||||
!serviceSelectionForm.getAzurePath().equals(path) ||
|
||||
!serviceSelectionForm.getAzureModel().equals(model);
|
||||
}
|
||||
|
||||
public void apply(ServiceSelectionForm serviceSelectionForm) {
|
||||
useAzureActiveDirectoryAuthentication = serviceSelectionForm.isAzureActiveDirectoryAuthenticationSelected();
|
||||
useAzureApiKeyAuthentication = serviceSelectionForm.isAzureApiKeyAuthenticationSelected();
|
||||
|
||||
resourceName = serviceSelectionForm.getAzureResourceName();
|
||||
deploymentId = serviceSelectionForm.getAzureDeploymentId();
|
||||
apiVersion = serviceSelectionForm.getAzureApiVersion();
|
||||
baseHost = serviceSelectionForm.getAzureBaseHost();
|
||||
path = serviceSelectionForm.getAzurePath();
|
||||
model = serviceSelectionForm.getAzureModel();
|
||||
}
|
||||
|
||||
public void reset(ServiceSelectionForm serviceSelectionForm) {
|
||||
serviceSelectionForm.setAzureApiKey(AzureCredentialsManager.getInstance().getAzureOpenAIApiKey());
|
||||
serviceSelectionForm.setAzureActiveDirectoryToken(AzureCredentialsManager.getInstance().getAzureActiveDirectoryToken());
|
||||
serviceSelectionForm.setAzureApiKeyAuthenticationSelected(useAzureApiKeyAuthentication);
|
||||
serviceSelectionForm.setAzureActiveDirectoryAuthenticationSelected(useAzureActiveDirectoryAuthentication);
|
||||
serviceSelectionForm.setAzureResourceName(resourceName);
|
||||
serviceSelectionForm.setAzureDeploymentId(deploymentId);
|
||||
serviceSelectionForm.setAzureApiVersion(apiVersion);
|
||||
serviceSelectionForm.setAzureBaseHost(baseHost);
|
||||
serviceSelectionForm.setAzurePath(path);
|
||||
serviceSelectionForm.setAzureModel(serviceSelectionForm.getAzureModel());
|
||||
}
|
||||
|
||||
public boolean isUsingCustomPath() {
|
||||
return !BASE_PATH.equals(path);
|
||||
}
|
||||
|
||||
public String getResourceName() {
|
||||
return resourceName;
|
||||
}
|
||||
|
|
@ -63,6 +112,22 @@ public class AzureSettingsState implements PersistentStateComponent<AzureSetting
|
|||
this.baseHost = baseHost;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public boolean isUseAzureApiKeyAuthentication() {
|
||||
return useAzureApiKeyAuthentication;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,82 +0,0 @@
|
|||
package ee.carlrobert.codegpt.settings.state;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import ee.carlrobert.openai.client.ClientCode;
|
||||
import ee.carlrobert.openai.client.completion.chat.ChatCompletionModel;
|
||||
import ee.carlrobert.openai.client.completion.text.TextCompletionModel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@State(name = "CodeGPT_ModelSettings", storages = @Storage("CodeGPT_ModelSettings.xml"))
|
||||
public class ModelSettingsState implements PersistentStateComponent<ModelSettingsState> {
|
||||
|
||||
private String textCompletionModel = TextCompletionModel.DAVINCI.getCode();
|
||||
private String chatCompletionModel = ChatCompletionModel.GPT_3_5.getCode();
|
||||
private boolean useChatCompletion = true;
|
||||
private boolean useTextCompletion;
|
||||
|
||||
public static ModelSettingsState getInstance() {
|
||||
return ApplicationManager.getApplication().getService(ModelSettingsState.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelSettingsState getState() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(@NotNull ModelSettingsState state) {
|
||||
XmlSerializerUtil.copyBean(state, this);
|
||||
}
|
||||
|
||||
public void sync(Conversation conversation) {
|
||||
var isChatCompletion = ClientCode.CHAT_COMPLETION.equals(conversation.getClientCode());
|
||||
if (isChatCompletion) {
|
||||
chatCompletionModel = conversation.getModel();
|
||||
} else {
|
||||
textCompletionModel = conversation.getModel();
|
||||
}
|
||||
useChatCompletion = isChatCompletion;
|
||||
useTextCompletion = !isChatCompletion;
|
||||
}
|
||||
|
||||
public String getCompletionModel() {
|
||||
return useChatCompletion ? chatCompletionModel : textCompletionModel;
|
||||
}
|
||||
|
||||
public String getTextCompletionModel() {
|
||||
return textCompletionModel;
|
||||
}
|
||||
|
||||
public void setTextCompletionModel(String textCompletionModel) {
|
||||
this.textCompletionModel = textCompletionModel;
|
||||
}
|
||||
|
||||
public String getChatCompletionModel() {
|
||||
return chatCompletionModel;
|
||||
}
|
||||
|
||||
public void setChatCompletionModel(String chatCompletionModel) {
|
||||
this.chatCompletionModel = chatCompletionModel;
|
||||
}
|
||||
|
||||
public boolean isUseChatCompletion() {
|
||||
return useChatCompletion;
|
||||
}
|
||||
|
||||
public void setUseChatCompletion(boolean useChatCompletion) {
|
||||
this.useChatCompletion = useChatCompletion;
|
||||
}
|
||||
|
||||
public boolean isUseTextCompletion() {
|
||||
return useTextCompletion;
|
||||
}
|
||||
|
||||
public void setUseTextCompletion(boolean useTextCompletion) {
|
||||
this.useTextCompletion = useTextCompletion;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,13 +5,20 @@ import com.intellij.openapi.components.PersistentStateComponent;
|
|||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
|
||||
import ee.carlrobert.codegpt.settings.ServiceSelectionForm;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.OpenAIChatCompletionModel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@State(name = "CodeGPT_OpenAISettings", storages = @Storage("CodeGPT_OpenAISettings.xml"))
|
||||
@State(name = "CodeGPT_OpenAISettings_210", storages = @Storage("CodeGPT_OpenAISettings_210.xml"))
|
||||
public class OpenAISettingsState implements PersistentStateComponent<OpenAISettingsState> {
|
||||
|
||||
private final String BASE_PATH = "/v1/chat/completions";
|
||||
|
||||
private String organization = "";
|
||||
private String baseHost = "https://api.openai.com";
|
||||
private String path = BASE_PATH;
|
||||
private String model = OpenAIChatCompletionModel.GPT_3_5.getCode();
|
||||
|
||||
public static OpenAISettingsState getInstance() {
|
||||
return ApplicationManager.getApplication().getService(OpenAISettingsState.class);
|
||||
|
|
@ -27,6 +34,33 @@ public class OpenAISettingsState implements PersistentStateComponent<OpenAISetti
|
|||
XmlSerializerUtil.copyBean(state, this);
|
||||
}
|
||||
|
||||
public boolean isModified(ServiceSelectionForm serviceSelectionForm) {
|
||||
return !serviceSelectionForm.getOpenAIApiKey().equals(OpenAICredentialsManager.getInstance().getApiKey()) ||
|
||||
!serviceSelectionForm.getOpenAIOrganization().equals(organization) ||
|
||||
!serviceSelectionForm.getOpenAIBaseHost().equals(baseHost) ||
|
||||
!serviceSelectionForm.getOpenAIPath().equals(path) ||
|
||||
!serviceSelectionForm.getOpenAIModel().equals(model);
|
||||
}
|
||||
|
||||
public void apply(ServiceSelectionForm serviceSelectionForm) {
|
||||
organization = serviceSelectionForm.getOpenAIOrganization();
|
||||
baseHost = serviceSelectionForm.getOpenAIBaseHost();
|
||||
path = serviceSelectionForm.getOpenAIPath();
|
||||
model = serviceSelectionForm.getOpenAIModel();
|
||||
}
|
||||
|
||||
public void reset(ServiceSelectionForm serviceSelectionForm) {
|
||||
serviceSelectionForm.setOpenAIApiKey(OpenAICredentialsManager.getInstance().getApiKey());
|
||||
serviceSelectionForm.setOpenAIOrganization(organization);
|
||||
serviceSelectionForm.setOpenAIBaseHost(baseHost);
|
||||
serviceSelectionForm.setOpenAIPath(path);
|
||||
serviceSelectionForm.setOpenAIModel(model);
|
||||
}
|
||||
|
||||
public boolean isUsingCustomPath() {
|
||||
return !BASE_PATH.equals(path);
|
||||
}
|
||||
|
||||
public String getOrganization() {
|
||||
return organization;
|
||||
}
|
||||
|
|
@ -42,4 +76,20 @@ public class OpenAISettingsState implements PersistentStateComponent<OpenAISetti
|
|||
public void setBaseHost(String openAIBaseHost) {
|
||||
this.baseHost = openAIBaseHost;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,10 @@ import com.intellij.openapi.components.PersistentStateComponent;
|
|||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@State(
|
||||
name = "ee.carlrobert.codegpt.state.settings.SettingsState",
|
||||
storages = @Storage("CodeGPTSettings_210.xml")
|
||||
)
|
||||
@State(name = "CodeGPT_GeneralSettings_210", storages = @Storage("CodeGPT_GeneralSettings_210.xml"))
|
||||
public class SettingsState implements PersistentStateComponent<SettingsState> {
|
||||
|
||||
private String email = "";
|
||||
|
|
@ -18,6 +16,8 @@ public class SettingsState implements PersistentStateComponent<SettingsState> {
|
|||
private boolean previouslySignedIn;
|
||||
private boolean useOpenAIService = true;
|
||||
private boolean useAzureService;
|
||||
private boolean useYouService;
|
||||
private boolean displayWebSearchResults = true;
|
||||
|
||||
public SettingsState() {
|
||||
}
|
||||
|
|
@ -36,6 +36,20 @@ public class SettingsState implements PersistentStateComponent<SettingsState> {
|
|||
XmlSerializerUtil.copyBean(state, this);
|
||||
}
|
||||
|
||||
public void sync(Conversation conversation) {
|
||||
var clientCode = conversation.getClientCode();
|
||||
if ("chat.completion".equals(clientCode)) {
|
||||
OpenAISettingsState.getInstance().setModel(conversation.getModel());
|
||||
}
|
||||
if ("azure.chat.completion".equals(clientCode)) {
|
||||
AzureSettingsState.getInstance().setModel(conversation.getModel());
|
||||
}
|
||||
|
||||
setUseOpenAIService("chat.completion".equals(clientCode));
|
||||
setUseAzureService("azure.chat.completion".equals(clientCode));
|
||||
setUseYouService("you.chat.completion".equals(clientCode));
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
|
@ -82,4 +96,20 @@ public class SettingsState implements PersistentStateComponent<SettingsState> {
|
|||
public void setUseAzureService(boolean useAzureService) {
|
||||
this.useAzureService = useAzureService;
|
||||
}
|
||||
|
||||
public boolean isUseYouService() {
|
||||
return useYouService;
|
||||
}
|
||||
|
||||
public void setUseYouService(boolean useYouService) {
|
||||
this.useYouService = useYouService;
|
||||
}
|
||||
|
||||
public boolean isDisplayWebSearchResults() {
|
||||
return displayWebSearchResults;
|
||||
}
|
||||
|
||||
public void setDisplayWebSearchResults(boolean displayWebSearchResults) {
|
||||
this.displayWebSearchResults = displayWebSearchResults;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package ee.carlrobert.codegpt.toolwindow;
|
||||
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.ui.JBFont;
|
||||
import ee.carlrobert.codegpt.Icons;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.OpenAIChatCompletionModel;
|
||||
import java.util.NoSuchElementException;
|
||||
import javax.swing.SwingConstants;
|
||||
|
||||
public class ModelIconLabel extends JBLabel {
|
||||
|
||||
public ModelIconLabel(String clientCode, String modelCode) {
|
||||
if ("you.chat.completion".equals(clientCode)) {
|
||||
setIcon(Icons.YouIcon);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("chat.completion".equals(clientCode)) {
|
||||
setIcon(Icons.OpenAIIcon);
|
||||
}
|
||||
if ("azure.chat.completion".equals(clientCode)) {
|
||||
setIcon(Icons.AzureIcon);
|
||||
}
|
||||
setText(formatModelName(modelCode));
|
||||
setFont(JBFont.small().asBold());
|
||||
setHorizontalAlignment(SwingConstants.LEADING);
|
||||
}
|
||||
|
||||
private String formatModelName(String modelCode) {
|
||||
try {
|
||||
return OpenAIChatCompletionModel.findByCode(modelCode).getDescription();
|
||||
} catch (NoSuchElementException e) {
|
||||
return modelCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.intellij.openapi.wm.ToolWindow;
|
|||
import com.intellij.openapi.wm.ToolWindowFactory;
|
||||
import com.intellij.ui.content.ContentManagerEvent;
|
||||
import com.intellij.ui.content.ContentManagerListener;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.contextual.ContextualChatToolWindowPanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.standard.StandardChatToolWindowPanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.conversations.ConversationsToolWindow;
|
||||
import javax.swing.JComponent;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package ee.carlrobert.codegpt.toolwindow.chat;
|
|||
import static com.intellij.openapi.ui.Messages.OK;
|
||||
import static ee.carlrobert.codegpt.util.ThemeUtils.getPanelBackgroundColor;
|
||||
import static java.lang.String.format;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.intellij.openapi.editor.impl.EditorImpl;
|
||||
import com.intellij.openapi.project.Project;
|
||||
|
|
@ -11,12 +12,21 @@ import com.intellij.ui.JBColor;
|
|||
import com.intellij.ui.components.JBScrollPane;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import ee.carlrobert.codegpt.completions.CompletionRequestHandler;
|
||||
import ee.carlrobert.codegpt.completions.SerpResult;
|
||||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import ee.carlrobert.codegpt.conversations.ConversationService;
|
||||
import ee.carlrobert.codegpt.conversations.message.Message;
|
||||
import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
|
||||
import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
|
||||
import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.toolwindow.ModelIconLabel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.ChatMessageResponseBody;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.ResponsePanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.UserMessagePanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.UserPromptTextArea;
|
||||
import ee.carlrobert.codegpt.user.UserManager;
|
||||
import ee.carlrobert.codegpt.util.EditorUtils;
|
||||
import ee.carlrobert.codegpt.util.FileUtils;
|
||||
import ee.carlrobert.codegpt.util.OverlayUtils;
|
||||
|
|
@ -25,6 +35,7 @@ import java.awt.GridBagConstraints;
|
|||
import java.awt.GridBagLayout;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import javax.swing.BoxLayout;
|
||||
|
|
@ -41,9 +52,10 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
private final JPanel rootPanel;
|
||||
private final ScrollablePanel scrollablePanel;
|
||||
private final Map<UUID, JPanel> visibleMessagePanels = new HashMap<>();
|
||||
private final Map<UUID, List<SerpResult>> serpResultsMapping = new HashMap<>();
|
||||
|
||||
protected final Project project;
|
||||
protected final UserTextArea userTextArea;
|
||||
protected final UserPromptTextArea userPromptTextArea;
|
||||
protected final ConversationService conversationService;
|
||||
protected @Nullable Conversation conversation;
|
||||
|
||||
|
|
@ -55,12 +67,12 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
this.conversationService = ConversationService.getInstance();
|
||||
this.rootPanel = new JPanel(new GridBagLayout());
|
||||
this.scrollablePanel = new ScrollablePanel();
|
||||
this.userTextArea = new UserTextArea(this::handleSubmit);
|
||||
this.userPromptTextArea = new UserPromptTextArea(this::handleSubmit);
|
||||
init();
|
||||
}
|
||||
|
||||
public void requestFocusForTextArea() {
|
||||
userTextArea.focus();
|
||||
userPromptTextArea.focus();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -113,6 +125,9 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
}
|
||||
|
||||
private boolean isCredentialSet() {
|
||||
if (SettingsState.getInstance().isUseYouService()) {
|
||||
return UserManager.getInstance().isAuthenticated();
|
||||
}
|
||||
if (SettingsState.getInstance().isUseAzureService()) {
|
||||
return AzureCredentialsManager.getInstance().isCredentialSet();
|
||||
}
|
||||
|
|
@ -141,6 +156,20 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
responsePanel.enableActions();
|
||||
conversationService.saveMessage(completeMessage, message, conversation, isRetry);
|
||||
stopStreaming(responseContainer);
|
||||
|
||||
var serpResults = serpResultsMapping.get(message.getId());
|
||||
var containsResults = serpResults != null && !serpResults.isEmpty();
|
||||
if (SettingsState.getInstance().isDisplayWebSearchResults()) {
|
||||
if (containsResults) {
|
||||
responseContainer.displaySerpResults(serpResults);
|
||||
}
|
||||
}
|
||||
|
||||
if (containsResults) {
|
||||
message.setSerpResults(serpResults.stream()
|
||||
.map(result -> new SerpResult(result.getUrl(), result.getName(), result.getSnippet(), result.getSnippetSource()))
|
||||
.collect(toList()));
|
||||
}
|
||||
});
|
||||
requestHandler.addTokensExceededListener(() -> SwingUtilities.invokeLater(() -> {
|
||||
var answer = OverlayUtils.showTokenLimitExceededDialog();
|
||||
|
|
@ -156,8 +185,11 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
responseContainer.displayError(error.getMessage());
|
||||
stopStreaming(responseContainer);
|
||||
});
|
||||
userTextArea.setRequestHandler(requestHandler);
|
||||
userTextArea.setSubmitEnabled(false);
|
||||
requestHandler.addSerpResultsListener(serpResults -> serpResultsMapping.put(message.getId(), serpResults.stream()
|
||||
.map(result -> new SerpResult(result.getUrl(), result.getName(), result.getSnippet(), result.getSnippetSource()))
|
||||
.collect(toList())));
|
||||
userPromptTextArea.setRequestHandler(requestHandler);
|
||||
userPromptTextArea.setSubmitEnabled(false);
|
||||
requestHandler.call(conversation, message, isRetry);
|
||||
}
|
||||
|
||||
|
|
@ -215,7 +247,7 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
|
||||
private void stopStreaming(ChatMessageResponseBody responseContainer) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
userTextArea.setSubmitEnabled(true);
|
||||
userPromptTextArea.setSubmitEnabled(true);
|
||||
responseContainer.hideCarets();
|
||||
});
|
||||
}
|
||||
|
|
@ -261,15 +293,52 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.gridy = 1;
|
||||
|
||||
// JBUI.Panels.simplePanel(8, 0).add();
|
||||
JPanel chatTextAreaWrapper = new JPanel(new BorderLayout());
|
||||
chatTextAreaWrapper.setBorder(JBUI.Borders.compound(
|
||||
|
||||
var model = getModel();
|
||||
var modelIconWrapper = JBUI.Panels.simplePanel(
|
||||
new ModelIconLabel(getClientCode(), model)).withBorder(JBUI.Borders.empty(0, 0, 8, 4));
|
||||
modelIconWrapper.setBackground(getPanelBackgroundColor());
|
||||
|
||||
var wrapper = new JPanel(new BorderLayout());
|
||||
wrapper.setBorder(JBUI.Borders.compound(
|
||||
JBUI.Borders.customLine(JBColor.border(), 1, 0, 0, 0),
|
||||
JBUI.Borders.empty(8)));
|
||||
chatTextAreaWrapper.setBackground(getPanelBackgroundColor());
|
||||
chatTextAreaWrapper.add(userTextArea, BorderLayout.SOUTH);
|
||||
rootPanel.add(chatTextAreaWrapper, gbc);
|
||||
userTextArea.requestFocusInWindow();
|
||||
userTextArea.requestFocus();
|
||||
wrapper.setBackground(getPanelBackgroundColor());
|
||||
wrapper.add(userPromptTextArea, BorderLayout.SOUTH);
|
||||
if (model != null) {
|
||||
wrapper.add(modelIconWrapper, BorderLayout.LINE_END);
|
||||
}
|
||||
rootPanel.add(wrapper, gbc);
|
||||
userPromptTextArea.requestFocusInWindow();
|
||||
userPromptTextArea.requestFocus();
|
||||
}
|
||||
|
||||
private String getClientCode() {
|
||||
var settings = SettingsState.getInstance();
|
||||
if (settings.isUseOpenAIService()) {
|
||||
return "chat.completion";
|
||||
}
|
||||
if (settings.isUseAzureService()) {
|
||||
return "azure.chat.completion";
|
||||
}
|
||||
if (settings.isUseYouService()) {
|
||||
return "you.chat.completion";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private @Nullable String getModel() {
|
||||
var settings = SettingsState.getInstance();
|
||||
if (settings.isUseOpenAIService()) {
|
||||
return OpenAISettingsState.getInstance().getModel();
|
||||
}
|
||||
if (settings.isUseAzureService()) {
|
||||
return AzureSettingsState.getInstance().getModel();
|
||||
}
|
||||
if (settings.isUseYouService()) {
|
||||
return "YouCode";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package ee.carlrobert.codegpt.toolwindow.chat;
|
|||
|
||||
import static ee.carlrobert.codegpt.util.FileUtils.findFileNameExtensionMapping;
|
||||
|
||||
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.ActionGroup;
|
||||
|
|
@ -11,7 +10,6 @@ import com.intellij.openapi.actionSystem.AnAction;
|
|||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.EditorKind;
|
||||
|
|
@ -21,7 +19,6 @@ import com.intellij.openapi.editor.impl.ContextMenuPopupHandler;
|
|||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.testFramework.LightVirtualFile;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
|
|
@ -46,12 +43,10 @@ import org.jetbrains.annotations.NotNull;
|
|||
|
||||
public class ChatToolWindowTabPanelEditor implements Disposable {
|
||||
|
||||
private final Project project;
|
||||
private final Editor editor;
|
||||
private final Map.Entry<String, String> fileNameExtensionMapping;
|
||||
|
||||
public ChatToolWindowTabPanelEditor(Project project, String code, String language, Disposable disposableParent) {
|
||||
this.project = project;
|
||||
this.fileNameExtensionMapping = findFileNameExtensionMapping(language);
|
||||
|
||||
var fileName = "temp_" + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()) + fileNameExtensionMapping.getValue();
|
||||
|
|
@ -60,7 +55,7 @@ public class ChatToolWindowTabPanelEditor implements Disposable {
|
|||
if (document == null) {
|
||||
document = EditorFactory.getInstance().createDocument(code);
|
||||
}
|
||||
disableHighlighting(document);
|
||||
EditorUtils.disableHighlighting(project, document);
|
||||
editor = EditorFactory.getInstance().createEditor(document, project, lightVirtualFile, true, EditorKind.UNTYPED);
|
||||
String originalGroupId = ((EditorEx) editor).getContextMenuGroupId();
|
||||
AnAction originalGroup = originalGroupId == null ? null : ActionManager.getInstance().getAction(originalGroupId);
|
||||
|
|
@ -95,13 +90,6 @@ public class ChatToolWindowTabPanelEditor implements Disposable {
|
|||
return editor;
|
||||
}
|
||||
|
||||
private void disableHighlighting(Document document) {
|
||||
var psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
|
||||
if (psiFile != null) {
|
||||
DaemonCodeAnalyzer.getInstance(project).setHighlightingEnabled(psiFile, false);
|
||||
}
|
||||
}
|
||||
|
||||
private JPanel createHeaderComponent(String language) {
|
||||
var headerComponent = new JPanel(new BorderLayout());
|
||||
headerComponent.setBorder(JBUI.Borders.compound(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat;
|
||||
package ee.carlrobert.codegpt.toolwindow.chat.components;
|
||||
|
||||
import static ee.carlrobert.codegpt.util.ThemeUtils.getPanelBackgroundColor;
|
||||
import static java.lang.String.format;
|
||||
|
|
@ -19,12 +19,20 @@ import com.vladsch.flexmark.ast.FencedCodeBlock;
|
|||
import com.vladsch.flexmark.html.HtmlRenderer;
|
||||
import com.vladsch.flexmark.parser.Parser;
|
||||
import com.vladsch.flexmark.util.data.MutableDataSet;
|
||||
import ee.carlrobert.codegpt.completions.SerpResult;
|
||||
import ee.carlrobert.codegpt.settings.SettingsConfigurable;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ChatToolWindowTabPanelEditor;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ResponseNodeRenderer;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.StreamParser;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.StreamResponseType;
|
||||
import ee.carlrobert.codegpt.util.MarkdownUtils;
|
||||
import ee.carlrobert.codegpt.util.SwingUtils;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextPane;
|
||||
|
|
@ -84,8 +92,10 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
}
|
||||
|
||||
public void displayMissingCredential() {
|
||||
currentlyProcessedTextPane.setText(
|
||||
"<html><p style=\"margin-top: 4px; margin-bottom: 8px;\">API key not provided. Open <a href=\"#\">Settings</a> to set one.</p></html>");
|
||||
var message = SettingsState.getInstance().isUseYouService() ?
|
||||
"Please <a href=\"#\">log in</a> to access the chat feature." :
|
||||
"API key not provided. Open <a href=\"#\">Settings</a> to set one.";
|
||||
currentlyProcessedTextPane.setText(String.format("<html><p style=\"margin-top: 4px; margin-bottom: 8px;\">%s</p></html>", message));
|
||||
currentlyProcessedTextPane.addHyperlinkListener(e -> {
|
||||
if (e.getEventType() == ACTIVATED) {
|
||||
ShowSettingsUtil.getInstance().showSettingsDialog(project, SettingsConfigurable.class);
|
||||
|
|
@ -104,7 +114,6 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public void displayError(String message) {
|
||||
var errorText = format("<html><p style=\"margin-top: 4px; margin-bottom: 8px;\">%s</p></html>", message);
|
||||
if (responseReceived) {
|
||||
|
|
@ -120,6 +129,24 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
displayError("Something went wrong.");
|
||||
}
|
||||
|
||||
public void displaySerpResults(List<SerpResult> serpResults) {
|
||||
var titles = serpResults.stream()
|
||||
.map(result -> format("<li style=\"margin-bottom: 4px;\"><a href=\"%s\">%s</a></li>", result.getUrl(), result.getName()))
|
||||
.collect(Collectors.joining());
|
||||
var html = format(
|
||||
"<html>" +
|
||||
"<p><strong>Search results:</strong></p>" +
|
||||
"<ol>%s</ol>" +
|
||||
"</html>", titles);
|
||||
if (responseReceived) {
|
||||
var textPane = createTextPane();
|
||||
textPane.setText(html);
|
||||
add(new ResponseWrapper().add(textPane));
|
||||
} else {
|
||||
currentlyProcessedTextPane.setText(html);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
removeAll();
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat;
|
||||
package ee.carlrobert.codegpt.toolwindow.chat.components;
|
||||
|
||||
import static ee.carlrobert.codegpt.util.ThemeUtils.getPanelBackgroundColor;
|
||||
|
||||
|
|
@ -13,6 +13,7 @@ import ee.carlrobert.codegpt.Icons;
|
|||
import ee.carlrobert.codegpt.toolwindow.IconActionButton;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingConstants;
|
||||
|
|
@ -29,7 +30,7 @@ public class ResponsePanel extends JPanel {
|
|||
header = new Header();
|
||||
body = new Body();
|
||||
add(header, BorderLayout.NORTH);
|
||||
add(body, BorderLayout.SOUTH);
|
||||
add(body, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
public void enableActions() {
|
||||
|
|
@ -69,7 +70,7 @@ public class ResponsePanel extends JPanel {
|
|||
setBorder(JBUI.Borders.empty(12, 8, 4, 8));
|
||||
add(getIconLabel(), BorderLayout.LINE_START);
|
||||
|
||||
iconsWrapper = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 0));
|
||||
iconsWrapper = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0));
|
||||
iconsWrapper.setBackground(getBackground());
|
||||
add(iconsWrapper, BorderLayout.LINE_END);
|
||||
}
|
||||
|
|
@ -81,7 +82,7 @@ public class ResponsePanel extends JPanel {
|
|||
}
|
||||
|
||||
public void addReloadAction(Runnable onReload) {
|
||||
iconsWrapper.add(new IconActionButton("Reload response", Actions.Refresh, new AnAction() {
|
||||
addIconActionButton(new IconActionButton("Reload response", Actions.Refresh, new AnAction() {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
enableActions(false);
|
||||
|
|
@ -91,7 +92,7 @@ public class ResponsePanel extends JPanel {
|
|||
}
|
||||
|
||||
public void addDeleteAction(Runnable onDelete) {
|
||||
iconsWrapper.add(new IconActionButton("Delete response", Actions.GC, new AnAction() {
|
||||
addIconActionButton(new IconActionButton("Delete response", Actions.GC, new AnAction() {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
onDelete.run();
|
||||
|
|
@ -99,6 +100,13 @@ public class ResponsePanel extends JPanel {
|
|||
}));
|
||||
}
|
||||
|
||||
private void addIconActionButton(IconActionButton iconActionButton) {
|
||||
if (iconsWrapper.getComponents() != null && iconsWrapper.getComponents().length > 0) {
|
||||
iconsWrapper.add(Box.createHorizontalStrut(8));
|
||||
}
|
||||
iconsWrapper.add(iconActionButton);
|
||||
}
|
||||
|
||||
private JBLabel getIconLabel() {
|
||||
return new JBLabel("CodeGPT", Icons.DefaultIcon, SwingConstants.LEADING)
|
||||
.setAllowAutoWrapping(true)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat;
|
||||
package ee.carlrobert.codegpt.toolwindow.chat.components;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat;
|
||||
package ee.carlrobert.codegpt.toolwindow.chat.components;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
|
|
@ -33,7 +33,7 @@ import javax.swing.event.DocumentListener;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class UserTextArea extends JPanel {
|
||||
public class UserPromptTextArea extends JPanel {
|
||||
|
||||
private static final String TEXT_SUBMIT = "text-submit";
|
||||
private static final String INSERT_BREAK = "insert-break";
|
||||
|
|
@ -47,7 +47,7 @@ public class UserTextArea extends JPanel {
|
|||
private JPanel iconsPanel;
|
||||
private boolean submitEnabled = true;
|
||||
|
||||
public UserTextArea(Consumer<String> onSubmit) {
|
||||
public UserPromptTextArea(Consumer<String> onSubmit) {
|
||||
this.onSubmit = onSubmit;
|
||||
|
||||
textArea = new JBTextArea();
|
||||
|
|
@ -68,12 +68,12 @@ public class UserTextArea extends JPanel {
|
|||
textArea.addFocusListener(new FocusListener() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
UserTextArea.super.paintBorder(UserTextArea.super.getGraphics());
|
||||
UserPromptTextArea.super.paintBorder(UserPromptTextArea.super.getGraphics());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusLost(FocusEvent e) {
|
||||
UserTextArea.super.paintBorder(UserTextArea.super.getGraphics());
|
||||
UserPromptTextArea.super.paintBorder(UserPromptTextArea.super.getGraphics());
|
||||
}
|
||||
});
|
||||
textArea.getDocument().addDocumentListener(new DocumentListener() {
|
||||
|
|
@ -139,7 +139,7 @@ public class UserTextArea extends JPanel {
|
|||
}
|
||||
|
||||
private void handleSubmit() {
|
||||
if (submitEnabled && textArea.getText().length() > 0) {
|
||||
if (submitEnabled && !textArea.getText().isEmpty()) {
|
||||
// Replacing each newline with two newlines to ensure proper Markdown formatting
|
||||
var text = textArea.getText().replace("\n", "\n\n");
|
||||
onSubmit.accept(text);
|
||||
|
|
@ -13,13 +13,13 @@ import ee.carlrobert.codegpt.indexes.CodebaseIndexingCompletedNotifier;
|
|||
import ee.carlrobert.codegpt.indexes.CodebaseIndexingTask;
|
||||
import ee.carlrobert.codegpt.indexes.FolderStructureTreePanel;
|
||||
import ee.carlrobert.codegpt.settings.SettingsConfigurable;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ResponsePanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.ResponsePanel;
|
||||
import ee.carlrobert.codegpt.user.UserManager;
|
||||
import ee.carlrobert.codegpt.user.auth.AuthenticationNotifier;
|
||||
import ee.carlrobert.codegpt.user.auth.SignedOutNotifier;
|
||||
import ee.carlrobert.codegpt.util.OverlayUtils;
|
||||
import ee.carlrobert.codegpt.util.SwingUtils;
|
||||
import ee.carlrobert.codegpt.embeddings.VectorStore;
|
||||
import ee.carlrobert.vector.VectorStore;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ class ContextualChatToolWindowLandingPanel extends ResponsePanel {
|
|||
var description = createTextPane();
|
||||
var userManager = UserManager.getInstance();
|
||||
|
||||
if (userManager.getSession() == null) {
|
||||
if (userManager.getAuthenticationResponse() == null) {
|
||||
description.setText("<html>" +
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\">It looks like you haven't logged in. Please <a href=\"LOGIN\">log in</a> to use the feature.</p>" +
|
||||
"</html>");
|
||||
|
|
|
|||
|
|
@ -17,17 +17,17 @@ public class ContextualChatToolWindowTabPanel extends BaseChatToolWindowTabPanel
|
|||
public ContextualChatToolWindowTabPanel(@NotNull Project project) {
|
||||
super(project, true);
|
||||
displayLandingView();
|
||||
userTextArea.setTextAreaEnabled(UserManager.getInstance().isSubscribed());
|
||||
userPromptTextArea.setTextAreaEnabled(UserManager.getInstance().isSubscribed());
|
||||
|
||||
project.getMessageBus()
|
||||
.connect()
|
||||
.subscribe(CodebaseIndexingCompletedNotifier.INDEXING_COMPLETED_TOPIC,
|
||||
(CodebaseIndexingCompletedNotifier) () -> userTextArea.setTextAreaEnabled(UserManager.getInstance().isSubscribed()));
|
||||
(CodebaseIndexingCompletedNotifier) () -> userPromptTextArea.setTextAreaEnabled(UserManager.getInstance().isSubscribed()));
|
||||
|
||||
var messageBusConnection = ApplicationManager.getApplication().getMessageBus().connect();
|
||||
messageBusConnection.subscribe(AuthenticationNotifier.AUTHENTICATION_TOPIC,
|
||||
(AuthenticationNotifier) () -> userTextArea.setTextAreaEnabled(UserManager.getInstance().isSubscribed()));
|
||||
messageBusConnection.subscribe(SignedOutNotifier.SIGNED_OUT_TOPIC, (SignedOutNotifier) () -> userTextArea.setTextAreaEnabled(false));
|
||||
(AuthenticationNotifier) () -> userPromptTextArea.setTextAreaEnabled(UserManager.getInstance().isSubscribed()));
|
||||
messageBusConnection.subscribe(SignedOutNotifier.SIGNED_OUT_TOPIC, (SignedOutNotifier) () -> userPromptTextArea.setTextAreaEnabled(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.intellij.openapi.project.Project;
|
|||
import com.intellij.openapi.wm.ToolWindow;
|
||||
import com.intellij.openapi.wm.ToolWindowManager;
|
||||
import com.intellij.ui.content.Content;
|
||||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import ee.carlrobert.codegpt.conversations.ConversationsState;
|
||||
import ee.carlrobert.codegpt.conversations.message.Message;
|
||||
import ee.carlrobert.codegpt.settings.configuration.ConfigurationState;
|
||||
|
|
@ -55,6 +56,15 @@ public class StandardChatToolWindowContentManager {
|
|||
}
|
||||
}
|
||||
|
||||
public void displayConversation(Conversation conversation) {
|
||||
displayChatTab();
|
||||
tryFindChatTabbedPane()
|
||||
.ifPresent(tabbedPane -> tabbedPane.tryFindActiveConversationTitle(conversation.getId())
|
||||
.ifPresentOrElse(
|
||||
title -> tabbedPane.setSelectedIndex(tabbedPane.indexOfTab(title)),
|
||||
() -> tabbedPane.addNewTab(new StandardChatToolWindowTabPanel(project, conversation))));
|
||||
}
|
||||
|
||||
public StandardChatToolWindowTabPanel createNewTabPanel() {
|
||||
displayChatTab();
|
||||
var tabbedPane = tryFindChatTabbedPane();
|
||||
|
|
@ -90,12 +100,10 @@ public class StandardChatToolWindowContentManager {
|
|||
return Optional.empty();
|
||||
}
|
||||
|
||||
public void resetTabbedPane() {
|
||||
public void resetActiveTab() {
|
||||
tryFindChatTabbedPane().ifPresent(tabbedPane -> {
|
||||
tabbedPane.clearAll();
|
||||
var tabPanel = new StandardChatToolWindowTabPanel(project);
|
||||
tabPanel.displayLandingView();
|
||||
tabbedPane.addNewTab(tabPanel);
|
||||
tabbedPane.addNewTab(new StandardChatToolWindowTabPanel(project));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED;
|
|||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ResponsePanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.ResponsePanel;
|
||||
import ee.carlrobert.codegpt.util.SwingUtils;
|
||||
import java.awt.event.MouseEvent;
|
||||
import javax.swing.JTextPane;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ package ee.carlrobert.codegpt.toolwindow.chat.standard;
|
|||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.actionSystem.ActionToolbar;
|
||||
import com.intellij.openapi.actionSystem.Constraints;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.actionSystem.DefaultCompactActionGroup;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.SimpleToolWindowPanel;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
|
|
@ -11,6 +13,7 @@ import ee.carlrobert.codegpt.actions.toolwindow.ClearChatWindowAction;
|
|||
import ee.carlrobert.codegpt.actions.toolwindow.CreateNewConversationAction;
|
||||
import ee.carlrobert.codegpt.actions.toolwindow.OpenInEditorAction;
|
||||
import ee.carlrobert.codegpt.conversations.ConversationsState;
|
||||
import java.awt.FlowLayout;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class StandardChatToolWindowPanel extends SimpleToolWindowPanel {
|
||||
|
|
@ -21,27 +24,22 @@ public class StandardChatToolWindowPanel extends SimpleToolWindowPanel {
|
|||
}
|
||||
|
||||
private void initialize(Project project, Disposable parentDisposable) {
|
||||
var tabPanel = new StandardChatToolWindowTabPanel(project);
|
||||
var conversation = ConversationsState.getCurrentConversation();
|
||||
if (conversation == null) {
|
||||
tabPanel.displayLandingView();
|
||||
} else {
|
||||
tabPanel.displayConversation(conversation);
|
||||
}
|
||||
|
||||
var tabPanel = new StandardChatToolWindowTabPanel(project, conversation);
|
||||
var tabbedPane = createTabbedPane(tabPanel, parentDisposable);
|
||||
setToolbar(createActionToolbar(project, tabbedPane).getComponent());
|
||||
var toolbarComponent = createActionToolbar(project, tabbedPane).getComponent();
|
||||
toolbarComponent.setLayout(new FlowLayout());
|
||||
|
||||
setToolbar(toolbarComponent);
|
||||
setContent(tabbedPane);
|
||||
|
||||
Disposer.register(parentDisposable, tabPanel);
|
||||
}
|
||||
|
||||
private ActionToolbar createActionToolbar(Project project, StandardChatToolWindowTabbedPane tabbedPane) {
|
||||
var actionGroup = new DefaultActionGroup("TOOLBAR_ACTION_GROUP", false);
|
||||
var actionGroup = new DefaultCompactActionGroup("TOOLBAR_ACTION_GROUP", false);
|
||||
actionGroup.add(new CreateNewConversationAction(() -> {
|
||||
var panel = new StandardChatToolWindowTabPanel(project);
|
||||
panel.displayLandingView();
|
||||
tabbedPane.addNewTab(panel);
|
||||
tabbedPane.addNewTab(new StandardChatToolWindowTabPanel(project));
|
||||
repaint();
|
||||
revalidate();
|
||||
}));
|
||||
|
|
@ -50,7 +48,7 @@ public class StandardChatToolWindowPanel extends SimpleToolWindowPanel {
|
|||
actionGroup.add(new OpenInEditorAction());
|
||||
|
||||
var toolbar = ActionManager.getInstance()
|
||||
.createActionToolbar("NAVIGATION_BAR_TOOLBAR", actionGroup, false);
|
||||
.createActionToolbar("NAVIGATION_BAR_TOOLBAR", actionGroup, true);
|
||||
toolbar.setTargetComponent(this);
|
||||
return toolbar;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,20 +6,31 @@ import com.intellij.openapi.editor.impl.EditorImpl;
|
|||
import com.intellij.openapi.project.Project;
|
||||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import ee.carlrobert.codegpt.conversations.message.Message;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.BaseChatToolWindowTabPanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ChatMessageResponseBody;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ResponsePanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.UserMessagePanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.ChatMessageResponseBody;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.ResponsePanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.UserMessagePanel;
|
||||
import ee.carlrobert.codegpt.util.EditorUtils;
|
||||
import ee.carlrobert.codegpt.util.FileUtils;
|
||||
import ee.carlrobert.codegpt.util.OverlayUtils;
|
||||
import javax.swing.JComponent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class StandardChatToolWindowTabPanel extends BaseChatToolWindowTabPanel {
|
||||
|
||||
public StandardChatToolWindowTabPanel(@NotNull Project project) {
|
||||
this(project, null);
|
||||
}
|
||||
|
||||
public StandardChatToolWindowTabPanel(@NotNull Project project, @Nullable Conversation conversation) {
|
||||
super(project, false);
|
||||
if (conversation == null) {
|
||||
displayLandingView();
|
||||
} else {
|
||||
displayConversation(conversation);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -51,12 +62,20 @@ public class StandardChatToolWindowTabPanel extends BaseChatToolWindowTabPanel {
|
|||
public void displayConversation(@NotNull Conversation conversation) {
|
||||
clearWindow();
|
||||
conversation.getMessages().forEach(message -> {
|
||||
var messageResponseBody = new ChatMessageResponseBody(project, this)
|
||||
.withResponse(message.getResponse());
|
||||
|
||||
var serpResults = message.getSerpResults();
|
||||
if (SettingsState.getInstance().isDisplayWebSearchResults() && serpResults != null && !serpResults.isEmpty()) {
|
||||
messageResponseBody.displaySerpResults(serpResults);
|
||||
}
|
||||
|
||||
var messageWrapper = createNewMessageWrapper(message.getId());
|
||||
messageWrapper.add(new UserMessagePanel(project, message, false, this));
|
||||
messageWrapper.add(new ResponsePanel()
|
||||
.withReloadAction(() -> reloadMessage(message, conversation))
|
||||
.withDeleteAction(() -> deleteMessage(message.getId(), messageWrapper, conversation))
|
||||
.addContent(new ChatMessageResponseBody(project, this).withResponse(message.getResponse())));
|
||||
.addContent(messageResponseBody));
|
||||
});
|
||||
setConversation(conversation);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import com.intellij.ui.components.JBLabel;
|
|||
import com.intellij.ui.components.JBTabbedPane;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import ee.carlrobert.codegpt.conversations.ConversationsState;
|
||||
import ee.carlrobert.codegpt.settings.state.ModelSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
|
|
@ -36,7 +36,6 @@ public class StandardChatToolWindowTabbedPane extends JBTabbedPane {
|
|||
this.parentDisposable = parentDisposable;
|
||||
setTabComponentInsets(null);
|
||||
setComponentPopupMenu(new TabPopupMenu());
|
||||
|
||||
addChangeListener(e -> refreshTabState());
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +103,7 @@ public class StandardChatToolWindowTabbedPane extends JBTabbedPane {
|
|||
var conversation = toolWindowPanel.getConversation();
|
||||
if (conversation != null) {
|
||||
ConversationsState.getInstance().setCurrentConversation(conversation);
|
||||
ModelSettingsState.getInstance().sync(conversation);
|
||||
SettingsState.getInstance().sync(conversation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,94 +1,111 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.conversations;
|
||||
|
||||
import static ee.carlrobert.codegpt.util.SwingUtils.justifyLeft;
|
||||
import static ee.carlrobert.codegpt.util.ThemeUtils.getPanelBackgroundColor;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.ui.JBFont;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import ee.carlrobert.codegpt.actions.toolwindow.DeleteConversationAction;
|
||||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import ee.carlrobert.codegpt.conversations.ConversationsState;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.toolwindow.IconActionButton;
|
||||
import ee.carlrobert.codegpt.toolwindow.ModelIconLabel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.standard.StandardChatToolWindowContentManager;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Font;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class ConversationPanel extends JPanel {
|
||||
|
||||
private final Conversation conversation;
|
||||
|
||||
ConversationPanel(Conversation conversation, boolean isSelected) {
|
||||
this.conversation = conversation;
|
||||
addStyles(isSelected);
|
||||
|
||||
var constraints = new GridBagConstraints();
|
||||
constraints.insets = JBUI.insets(0, 10);
|
||||
addChatIcon(constraints);
|
||||
addTextPanel(constraints);
|
||||
ConversationPanel(@NotNull Project project, @NotNull Conversation conversation, @NotNull Runnable onDelete) {
|
||||
super(new BorderLayout());
|
||||
setBackground(JBColor.background());
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
SettingsState.getInstance().sync(conversation);
|
||||
StandardChatToolWindowContentManager.getInstance(project).displayConversation(conversation);
|
||||
}
|
||||
});
|
||||
addStyles(isSelected(conversation));
|
||||
addTextPanel(conversation, onDelete);
|
||||
setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
}
|
||||
|
||||
private boolean isSelected(Conversation conversation) {
|
||||
var currentConversation = ConversationsState.getCurrentConversation();
|
||||
return currentConversation != null && currentConversation.getId().equals(conversation.getId());
|
||||
}
|
||||
|
||||
private void addStyles(boolean isSelected) {
|
||||
setBackground(JBColor.background().darker());
|
||||
if (isSelected) {
|
||||
setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createMatteBorder(4, 4, 4, 4, JBColor.green),
|
||||
JBUI.Borders.empty(10)));
|
||||
} else {
|
||||
setBorder(JBUI.Borders.empty(10));
|
||||
}
|
||||
var border = isSelected ?
|
||||
JBUI.Borders.customLine(JBUI.CurrentTheme.ActionButton.focusedBorder(), 2, 2, 2, 2) :
|
||||
JBUI.Borders.customLine(JBColor.border(), 1, 0, 1, 0);
|
||||
setBackground(getPanelBackgroundColor());
|
||||
setBorder(JBUI.Borders.compound(border, JBUI.Borders.empty(8)));
|
||||
setLayout(new GridBagLayout());
|
||||
setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
}
|
||||
|
||||
private void addChatIcon(GridBagConstraints constraints) {
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = 0;
|
||||
constraints.weightx = 0.0;
|
||||
constraints.fill = GridBagConstraints.NONE;
|
||||
add(new JLabel(AllIcons.Actions.Annotate), constraints);
|
||||
}
|
||||
|
||||
private void addTextPanel(GridBagConstraints constraints) {
|
||||
private void addTextPanel(Conversation conversation, Runnable onDelete) {
|
||||
var constraints = new GridBagConstraints();
|
||||
constraints.gridx = 1;
|
||||
constraints.weightx = 1.0;
|
||||
constraints.fill = GridBagConstraints.HORIZONTAL;
|
||||
add(createTextPanel(), constraints);
|
||||
add(createTextPanel(conversation, onDelete), constraints);
|
||||
}
|
||||
|
||||
private JPanel createTextPanel() {
|
||||
var title = new JLabel(getFirstPrompt());
|
||||
title.setBorder(JBUI.Borders.emptyBottom(8));
|
||||
title.setFont(title.getFont().deriveFont(title.getFont().getStyle() | Font.BOLD));
|
||||
private JPanel createTextPanel(Conversation conversation, Runnable onDelete) {
|
||||
var headerPanel = new JPanel(new GridBagLayout());
|
||||
headerPanel.setBorder(JBUI.Borders.emptyBottom(12));
|
||||
|
||||
var textPanel = new JPanel();
|
||||
textPanel.setBackground(getBackground());
|
||||
textPanel.setLayout(new BoxLayout(textPanel, BoxLayout.PAGE_AXIS));
|
||||
textPanel.add(justifyLeft(title));
|
||||
var gbc = new GridBagConstraints();
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.weightx = 1.0;
|
||||
gbc.gridx = 0;
|
||||
|
||||
var bottomPanel = new JPanel();
|
||||
bottomPanel.setBackground(getBackground());
|
||||
bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.X_AXIS));
|
||||
headerPanel.setBackground(getPanelBackgroundColor());
|
||||
headerPanel.add(new JBLabel(getFirstPrompt(conversation))
|
||||
.withFont(JBFont.label().asBold()), gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 0;
|
||||
headerPanel.add(new IconActionButton("Delete conversation", AllIcons.Actions.GC, new DeleteConversationAction(onDelete)), gbc);
|
||||
|
||||
var bottomPanel = new JPanel(new BorderLayout());
|
||||
bottomPanel.setBackground(getPanelBackgroundColor());
|
||||
bottomPanel.add(new JLabel(conversation.getUpdatedOn()
|
||||
.format(DateTimeFormatter.ofPattern("M/d/yyyy, h:mm:ss a"))));
|
||||
bottomPanel.add(Box.createHorizontalGlue());
|
||||
.format(DateTimeFormatter.ofPattern("M/d/yyyy, h:mm:ss a"))), BorderLayout.WEST);
|
||||
if (conversation.getModel() != null) {
|
||||
bottomPanel.add(new JLabel(conversation.getModel()));
|
||||
bottomPanel.add(new ModelIconLabel(conversation.getClientCode(), conversation.getModel()), BorderLayout.EAST);
|
||||
}
|
||||
textPanel.add(bottomPanel);
|
||||
|
||||
var textPanel = new JPanel(new BorderLayout());
|
||||
textPanel.setBackground(getPanelBackgroundColor());
|
||||
textPanel.add(headerPanel, BorderLayout.NORTH);
|
||||
textPanel.add(bottomPanel, BorderLayout.SOUTH);
|
||||
return textPanel;
|
||||
}
|
||||
|
||||
private String getFirstPrompt() {
|
||||
private String getFirstPrompt(Conversation conversation) {
|
||||
var messages = conversation.getMessages();
|
||||
var prompt = "";
|
||||
if (!messages.isEmpty()) {
|
||||
prompt = conversation.getMessages().get(0).getPrompt();
|
||||
if (messages.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return prompt;
|
||||
return messages.get(0).getPrompt();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="ee.carlrobert.codegpt.toolwindow.conversations.ConversationsToolWindow">
|
||||
<grid id="27dc6" binding="conversationsToolWindowContent" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="500" height="400"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<scrollpane id="77046" binding="scrollPane" custom-create="true">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children/>
|
||||
</scrollpane>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
|
|
@ -9,15 +9,10 @@ import com.intellij.ui.components.JBScrollPane;
|
|||
import com.intellij.util.ui.JBFont;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import ee.carlrobert.codegpt.actions.toolwindow.DeleteAllConversationsAction;
|
||||
import ee.carlrobert.codegpt.actions.toolwindow.DeleteConversationAction;
|
||||
import ee.carlrobert.codegpt.actions.toolwindow.MoveDownAction;
|
||||
import ee.carlrobert.codegpt.actions.toolwindow.MoveUpAction;
|
||||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import ee.carlrobert.codegpt.conversations.ConversationService;
|
||||
import ee.carlrobert.codegpt.conversations.ConversationsState;
|
||||
import ee.carlrobert.codegpt.settings.state.ModelSettingsState;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.standard.StandardChatToolWindowContentManager;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.standard.StandardChatToolWindowTabPanel;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
|
|
@ -25,29 +20,35 @@ import javax.swing.JScrollPane;
|
|||
import javax.swing.ScrollPaneConstants;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ConversationsToolWindow {
|
||||
public class ConversationsToolWindow extends JPanel {
|
||||
|
||||
private final Project project;
|
||||
private final ConversationService conversationService;
|
||||
private JPanel conversationsToolWindowContent;
|
||||
private JScrollPane scrollPane;
|
||||
private ScrollablePanel scrollablePanel;
|
||||
private final ScrollablePanel scrollablePanel;
|
||||
private final JScrollPane scrollPane;
|
||||
|
||||
public ConversationsToolWindow(@NotNull Project project) {
|
||||
this.project = project;
|
||||
this.conversationService = ConversationService.getInstance();
|
||||
scrollablePanel = new ScrollablePanel();
|
||||
scrollablePanel.setLayout(new BoxLayout(scrollablePanel, BoxLayout.Y_AXIS));
|
||||
|
||||
scrollPane = new JBScrollPane();
|
||||
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
scrollPane.setViewportView(scrollablePanel);
|
||||
scrollPane.setBorder(null);
|
||||
scrollPane.setViewportBorder(null);
|
||||
refresh();
|
||||
}
|
||||
|
||||
public JPanel getContent() {
|
||||
SimpleToolWindowPanel panel = new SimpleToolWindowPanel(true);
|
||||
panel.setContent(conversationsToolWindowContent);
|
||||
panel.setContent(scrollPane);
|
||||
|
||||
var actionGroup = new DefaultActionGroup("TOOLBAR_ACTION_GROUP", false);
|
||||
actionGroup.add(new MoveDownAction(this::refresh));
|
||||
actionGroup.add(new MoveUpAction(this::refresh));
|
||||
actionGroup.addSeparator();
|
||||
actionGroup.add(new DeleteConversationAction(this::refresh));
|
||||
actionGroup.add(new DeleteAllConversationsAction(this::refresh));
|
||||
|
||||
var toolbar = ActionManager.getInstance()
|
||||
|
|
@ -67,46 +68,13 @@ public class ConversationsToolWindow {
|
|||
emptyLabel.setBorder(JBUI.Borders.empty(8));
|
||||
scrollablePanel.add(emptyLabel);
|
||||
} else {
|
||||
sortedConversations.forEach(this::addContent);
|
||||
sortedConversations.forEach(conversation -> {
|
||||
scrollablePanel.add(Box.createVerticalStrut(8));
|
||||
scrollablePanel.add(new ConversationPanel(project, conversation, this::refresh));
|
||||
});
|
||||
}
|
||||
|
||||
scrollablePanel.revalidate();
|
||||
scrollablePanel.repaint();
|
||||
}
|
||||
|
||||
private void addContent(Conversation conversation) {
|
||||
var mainPanel = new RootConversationPanel(() -> {
|
||||
ModelSettingsState.getInstance().sync(conversation);
|
||||
|
||||
var toolWindowContentManager = StandardChatToolWindowContentManager.getInstance(project);
|
||||
toolWindowContentManager.displayChatTab();
|
||||
toolWindowContentManager.tryFindChatTabbedPane()
|
||||
.ifPresent(tabbedPane -> tabbedPane.tryFindActiveConversationTitle(conversation.getId())
|
||||
.ifPresentOrElse(
|
||||
title -> tabbedPane.setSelectedIndex(tabbedPane.indexOfTab(title)),
|
||||
() -> {
|
||||
var panel = new StandardChatToolWindowTabPanel(project);
|
||||
panel.displayConversation(conversation);
|
||||
tabbedPane.addNewTab(panel);
|
||||
}));
|
||||
});
|
||||
|
||||
var currentConversation = ConversationsState.getCurrentConversation();
|
||||
var isSelected =
|
||||
currentConversation != null && currentConversation.getId().equals(conversation.getId());
|
||||
mainPanel.add(new ConversationPanel(conversation, isSelected));
|
||||
mainPanel.setBackground(conversationsToolWindowContent.getBackground());
|
||||
scrollablePanel.add(mainPanel);
|
||||
}
|
||||
|
||||
private void createUIComponents() {
|
||||
scrollablePanel = new ScrollablePanel();
|
||||
scrollablePanel.setLayout(new BoxLayout(scrollablePanel, BoxLayout.Y_AXIS));
|
||||
|
||||
scrollPane = new JBScrollPane();
|
||||
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
scrollPane.setViewportView(scrollablePanel);
|
||||
scrollPane.setBorder(null);
|
||||
scrollPane.setViewportBorder(null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.conversations;
|
||||
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
class RootConversationPanel extends JPanel {
|
||||
|
||||
RootConversationPanel(Runnable onClick) {
|
||||
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
||||
setBorder(JBUI.Borders.empty(10, 20));
|
||||
setBackground(JBColor.background());
|
||||
addMouseListener(getMouseListener(onClick));
|
||||
}
|
||||
|
||||
private MouseListener getMouseListener(Runnable onClick) {
|
||||
return new MouseListener() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent mouseEvent) {
|
||||
onClick.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent mouseEvent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent mouseEvent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent mouseEvent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent mouseEvent) {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +1,51 @@
|
|||
package ee.carlrobert.codegpt.user;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.Service;
|
||||
import ee.carlrobert.codegpt.user.subscription.Subscription;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@Service
|
||||
public final class ApiClient {
|
||||
|
||||
private static final String API_BASE_URL = "http://localhost:8000/api";
|
||||
private final OkHttpClient httpClient;
|
||||
|
||||
private ApiClient() {
|
||||
httpClient = new OkHttpClient();
|
||||
}
|
||||
private static final String API_BASE_URL = "https://web.stytch.com/sdk";
|
||||
private static final String publicToken = "public-token-live-507a52ad-7e69-496b-aee0-1c9863c7c819";
|
||||
|
||||
public static ApiClient getInstance() {
|
||||
return ApplicationManager.getApplication().getService(ApiClient.class);
|
||||
}
|
||||
|
||||
public @Nullable Subscription getSubscription(String accessToken) {
|
||||
try {
|
||||
var body = httpClient.newCall(new Request.Builder()
|
||||
.url(API_BASE_URL + "/subscriptions")
|
||||
.header("Authorization", accessToken)
|
||||
.get()
|
||||
.build())
|
||||
.execute()
|
||||
.body();
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
return new ObjectMapper().readValue(body.string(), new TypeReference<>() {});
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unable to obtain subscriptions", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void authenticate(String email, String password, Callback callback) {
|
||||
try {
|
||||
httpClient.newCall(new Request.Builder()
|
||||
.url(API_BASE_URL + "/auth")
|
||||
.post(RequestBody.create(
|
||||
new ObjectMapper()
|
||||
.writerWithDefaultPrettyPrinter()
|
||||
.writeValueAsString(Map.of(
|
||||
"email", email,
|
||||
"password", password)),
|
||||
MediaType.parse("application/json")))
|
||||
new OkHttpClient()
|
||||
.newCall(new Request.Builder()
|
||||
.url(API_BASE_URL + "/v1/passwords/authenticate")
|
||||
.headers(Headers.of(
|
||||
"content-type", "application/json",
|
||||
"authority", "web.stytch.com",
|
||||
"authorization", "Basic " + Base64.getEncoder().encodeToString((publicToken + ":" + publicToken).getBytes()),
|
||||
"x-sdk-client", "eyJldmVudF9pZCI6ImV2ZW50LWlkLWY5YmU4YWU5LWE3MjctNGFlYy1hNzY0LTk4NDg1NDFkZjcwYSIsImFwcF9zZXNzaW9uX2lkIjoiYXBwLXNlc3Npb24taWQtYjY1NzcwZjMtMWFkMy00YjlhLWFjYzctMzJjNWQyMGMxNGU0IiwicGVyc2lzdGVudF9pZCI6InBlcnNpc3RlbnQtaWQtYzY0M2M0YTMtZDg5MC00ZGJkLTk3YjQtMjY0MmFlODdkMTZhIiwiY2xpZW50X3NlbnRfYXQiOiIyMDIzLTA5LTAxVDIyOjMwOjU1LjIzNFoiLCJ0aW1lem9uZSI6IkV1cm9wZS9UYWxsaW5uIiwiYXBwIjp7ImlkZW50aWZpZXIiOiJ5b3UuY29tIn0sInNkayI6eyJpZGVudGlmaWVyIjoiU3R5dGNoLmpzIEphdmFzY3JpcHQgU0RLIC0gWU9VLkNPTSBERUJVRyBCVUlMRCIsInZlcnNpb24iOiI0LjAuMCJ9fQ==",
|
||||
"x-sdk-parent-host", "https://you.com"
|
||||
))
|
||||
.post(RequestBody.create(new ObjectMapper()
|
||||
.writerWithDefaultPrettyPrinter()
|
||||
.writeValueAsString(Map.of(
|
||||
"email", email,
|
||||
"password", password,
|
||||
"session_duration_minutes", 129_600))
|
||||
.getBytes(StandardCharsets.UTF_8)))
|
||||
.build())
|
||||
.enqueue(callback);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException("Could not process request", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshToken(String refreshToken, Callback callback) {
|
||||
try {
|
||||
httpClient.newCall(new Request.Builder()
|
||||
.url(API_BASE_URL + "/refresh-token")
|
||||
.patch(RequestBody.create(
|
||||
new ObjectMapper()
|
||||
.writerWithDefaultPrettyPrinter()
|
||||
.writeValueAsString(Map.of(
|
||||
"refreshToken", refreshToken)),
|
||||
MediaType.parse("application/json")))
|
||||
.build())
|
||||
.enqueue(callback);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException("Unable to refresh token", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,13 @@ package ee.carlrobert.codegpt.user;
|
|||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.Service;
|
||||
import ee.carlrobert.codegpt.user.auth.Session;
|
||||
import ee.carlrobert.codegpt.user.auth.SignedOutNotifier;
|
||||
import ee.carlrobert.codegpt.user.subscription.Subscription;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import ee.carlrobert.codegpt.user.auth.response.AuthenticationResponse;
|
||||
|
||||
@Service
|
||||
public final class UserManager {
|
||||
|
||||
private Session session;
|
||||
private Subscription subscription;
|
||||
private AuthenticationResponse authenticationResponse;
|
||||
|
||||
private UserManager() {
|
||||
}
|
||||
|
|
@ -20,32 +17,27 @@ public final class UserManager {
|
|||
return ApplicationManager.getApplication().getService(UserManager.class);
|
||||
}
|
||||
|
||||
public @Nullable Session getSession() {
|
||||
return session;
|
||||
public AuthenticationResponse getAuthenticationResponse() {
|
||||
return authenticationResponse;
|
||||
}
|
||||
|
||||
public void setSession(@Nullable Session session) {
|
||||
this.session = session;
|
||||
public void setAuthenticationResponse(AuthenticationResponse authenticationResponse) {
|
||||
this.authenticationResponse = authenticationResponse;
|
||||
}
|
||||
|
||||
public void clearSession() {
|
||||
session = null;
|
||||
subscription = null;
|
||||
authenticationResponse = null;
|
||||
|
||||
ApplicationManager.getApplication().getMessageBus()
|
||||
.syncPublisher(SignedOutNotifier.SIGNED_OUT_TOPIC)
|
||||
.signedOut();
|
||||
}
|
||||
|
||||
public @Nullable Subscription getSubscription() {
|
||||
return subscription;
|
||||
}
|
||||
|
||||
public void setSubscription(@Nullable Subscription subscription) {
|
||||
this.subscription = subscription;
|
||||
}
|
||||
|
||||
public boolean isSubscribed() {
|
||||
return session != null && subscription != null;
|
||||
return true; // TODO
|
||||
}
|
||||
|
||||
public boolean isAuthenticated() {
|
||||
return authenticationResponse != null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package ee.carlrobert.codegpt.user.auth;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class AuthenticationError {
|
||||
|
||||
private final String errorType;
|
||||
private final String errorMessage;
|
||||
|
||||
public AuthenticationError(@JsonProperty("error_type") String errorType, @JsonProperty("error_message") String errorMessage) {
|
||||
this.errorType = errorType;
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public String getErrorType() {
|
||||
return errorType;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
package ee.carlrobert.codegpt.user.auth;
|
||||
|
||||
import ee.carlrobert.codegpt.user.auth.response.AuthenticationResponse;
|
||||
|
||||
public interface AuthenticationHandler {
|
||||
|
||||
void handleAuthenticated();
|
||||
|
||||
void handleInvalidCredentials();
|
||||
void handleAuthenticated(AuthenticationResponse authenticationResponse);
|
||||
|
||||
void handleGenericError();
|
||||
|
||||
void handleError(AuthenticationError authenticationError);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@ import com.intellij.notification.NotificationType;
|
|||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.Service;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import ee.carlrobert.codegpt.credentials.UserCredentialsManager;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.user.ApiClient;
|
||||
import ee.carlrobert.codegpt.user.UserManager;
|
||||
import ee.carlrobert.codegpt.user.auth.response.AuthenticationResponse;
|
||||
import ee.carlrobert.codegpt.util.OverlayUtils;
|
||||
import java.io.IOException;
|
||||
import okhttp3.Call;
|
||||
|
|
@ -33,56 +32,7 @@ public final class AuthenticationService {
|
|||
client.authenticate(email, password, new AuthenticationCallback(authenticationHandler, email, password));
|
||||
}
|
||||
|
||||
public void refreshToken() {
|
||||
var userManager = UserManager.getInstance();
|
||||
var session = userManager.getSession();
|
||||
|
||||
if (session == null) {
|
||||
throw new IllegalStateException("Tried to revalidate unauthenticated user");
|
||||
}
|
||||
|
||||
client.refreshToken(session.getRefreshToken(), new Callback() {
|
||||
@Override
|
||||
public void onFailure(@NotNull Call call, @NotNull IOException e) {
|
||||
userManager.clearSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(@NotNull Call call, @NotNull Response response) {
|
||||
if (response.code() == 200) {
|
||||
var body = response.body();
|
||||
if (body != null) {
|
||||
try {
|
||||
handleSuccessfulAuthentication(new ObjectMapper().readValue(body.string(), Session.class));
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unable to deserialize session", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
userManager.clearSession();
|
||||
throw new RuntimeException("Internal server error. " + response.message());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleSuccessfulAuthentication(Session session) {
|
||||
SettingsState.getInstance().setPreviouslySignedIn(true);
|
||||
|
||||
var userManager = UserManager.getInstance();
|
||||
userManager.setSession(session);
|
||||
|
||||
var subscription = client.getSubscription(session.getAccessToken());
|
||||
if (subscription != null) {
|
||||
userManager.setSubscription(subscription);
|
||||
}
|
||||
ApplicationManager.getApplication().getMessageBus()
|
||||
.syncPublisher(AuthenticationNotifier.AUTHENTICATION_TOPIC)
|
||||
.authenticationSuccessful();
|
||||
}
|
||||
|
||||
class AuthenticationCallback implements Callback {
|
||||
static class AuthenticationCallback implements Callback {
|
||||
|
||||
private final AuthenticationHandler authenticationHandler;
|
||||
private final String email;
|
||||
|
|
@ -102,27 +52,33 @@ public final class AuthenticationService {
|
|||
|
||||
@Override
|
||||
public void onResponse(@NotNull Call call, @NotNull Response response) {
|
||||
if (response.code() == 401) {
|
||||
authenticationHandler.handleInvalidCredentials();
|
||||
var body = response.body();
|
||||
if (body == null) {
|
||||
authenticationHandler.handleGenericError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.code() == 200) {
|
||||
var body = response.body();
|
||||
if (body != null) {
|
||||
try {
|
||||
var session = new ObjectMapper().readValue(body.string(), Session.class);
|
||||
handleSuccessfulAuthentication(session);
|
||||
SettingsState.getInstance().setEmail(email);
|
||||
UserCredentialsManager.getInstance().setAccountPassword(password);
|
||||
authenticationHandler.handleAuthenticated();
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unable to deserialize session", e);
|
||||
}
|
||||
try {
|
||||
var authenticationResponse = new ObjectMapper().readValue(body.string(), AuthenticationResponse.class);
|
||||
UserManager.getInstance().setAuthenticationResponse(authenticationResponse);
|
||||
authenticationHandler.handleAuthenticated(authenticationResponse);
|
||||
|
||||
ApplicationManager.getApplication().getMessageBus()
|
||||
.syncPublisher(AuthenticationNotifier.AUTHENTICATION_TOPIC)
|
||||
.authenticationSuccessful();
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unable to deserialize session", e);
|
||||
}
|
||||
}
|
||||
|
||||
authenticationHandler.handleGenericError();
|
||||
throw new RuntimeException("Internal server error. " + response.message());
|
||||
try {
|
||||
authenticationHandler.handleError(new ObjectMapper().readValue(body.string(), AuthenticationError.class));
|
||||
} catch (Throwable ex) {
|
||||
authenticationHandler.handleGenericError();
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue