From cc12f4a59afd25b56aaa8b28129000436173c06e Mon Sep 17 00:00:00 2001
From: Tu Thanh Nguyen <138571181+tutn-axonivy@users.noreply.github.com>
Date: Tue, 7 May 2024 11:24:55 +0700
Subject: [PATCH] MARP-174-Error-using-ChatGPT-assistant (#43)
---
openai-assistant/META-INF/MANIFEST.MF | 3 +-
openai-assistant/plugin.xml | 6 ++
openai-assistant/res/variables.yaml | 4 +-
.../openai/assistant/ChatGptRequest.java | 23 +++++--
.../openai/assistant/OpenAiConfig.java | 1 +
.../openai/assistant/models/Model.java | 44 +++++++++++++
.../assistant/models/ResponseModel.java | 25 ++++++++
.../assistant/ui/ChatGPTAssistantHandler.java | 1 +
.../openai/assistant/ui/ChatGPTquest.java | 3 +-
.../openai/assistant/ui/ChatGptUiFlow.java | 24 +++++++
.../assistant/ui/SelectModelDialog.java | 64 +++++++++++++++++++
11 files changed, 191 insertions(+), 7 deletions(-)
create mode 100644 openai-assistant/src/com/axonivy/connector/openai/assistant/models/Model.java
create mode 100644 openai-assistant/src/com/axonivy/connector/openai/assistant/models/ResponseModel.java
create mode 100644 openai-assistant/src/com/axonivy/connector/openai/assistant/ui/SelectModelDialog.java
diff --git a/openai-assistant/META-INF/MANIFEST.MF b/openai-assistant/META-INF/MANIFEST.MF
index a08d527..bb122a8 100644
--- a/openai-assistant/META-INF/MANIFEST.MF
+++ b/openai-assistant/META-INF/MANIFEST.MF
@@ -14,6 +14,7 @@ Require-Bundle: org.eclipse.core.runtime,
ch.ivyteam.ivy.webservice.execution;resolution:=optional,
ch.ivyteam.lib.jackson,
ch.ivyteam.ivy.jersey.client,
- javax.ws.rs
+ javax.ws.rs,
+ ch.ivyteam.ivy.rest.client.exec;resolution:=optional
Bundle-ClassPath: .
Bundle-ActivationPolicy: lazy
diff --git a/openai-assistant/plugin.xml b/openai-assistant/plugin.xml
index 94504e5..2f0d83a 100644
--- a/openai-assistant/plugin.xml
+++ b/openai-assistant/plugin.xml
@@ -46,6 +46,9 @@
+
+
+
@@ -73,6 +76,9 @@
+
+
+
diff --git a/openai-assistant/res/variables.yaml b/openai-assistant/res/variables.yaml
index 0504586..fab9759 100644
--- a/openai-assistant/res/variables.yaml
+++ b/openai-assistant/res/variables.yaml
@@ -11,4 +11,6 @@ Variables:
# tokens to spend on analyzation and response; at max 4097
maxTokens: 1024
# base uri to use for chatGPT calls
- platformUri: 'https://api.openai.com/v1'
\ No newline at end of file
+ platformUri: 'https://api.openai.com/v1'
+ # model to request
+ model: 'gpt-3.5-turbo'
\ No newline at end of file
diff --git a/openai-assistant/src/com/axonivy/connector/openai/assistant/ChatGptRequest.java b/openai-assistant/src/com/axonivy/connector/openai/assistant/ChatGptRequest.java
index 5a8f9b6..b670a3c 100644
--- a/openai-assistant/src/com/axonivy/connector/openai/assistant/ChatGptRequest.java
+++ b/openai-assistant/src/com/axonivy/connector/openai/assistant/ChatGptRequest.java
@@ -8,6 +8,7 @@
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status.Family;
+import com.axonivy.connector.openai.assistant.OpenAiConfig.Key;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
@@ -26,11 +27,21 @@ public ChatGptRequest maxTokens(int tokens) {
this.maxTokens = tokens;
return this;
}
+
+ public String getModels() {
+ WebTarget chat = client.get().path("models");
+ Response resp = chat.request().get();
+ return read(resp);
+ }
public String ask(String context, String question) {
- WebTarget chat = client.get().path("completions");
- ObjectNode request = completion()
- .put("prompt", context + "\n\n"+question);
+ WebTarget chat = client.get().path("chat/completions");
+ ObjectNode message = JsonNodeFactory.instance.objectNode();
+ ArrayNode arrayNode = JsonNodeFactory.instance.arrayNode();
+ message.put("role", "user");
+ message.put("content", context + "\n\n"+question);
+ arrayNode.add(message);
+ ObjectNode request = completion().set("messages", arrayNode);
var payload = Entity.entity(request, MediaType.APPLICATION_JSON);
Response resp = chat.request().post(payload);
return read(resp);
@@ -40,6 +51,9 @@ private String read(Response resp) {
JsonNode result = resp.readEntity(JsonNode.class);
if (resp.getStatusInfo().getFamily() == Family.SUCCESSFUL) {
if (result.get("choices") instanceof ArrayNode choices) {
+ if (choices.get(0).get("message") instanceof ObjectNode message) {
+ return message.get("content").asText();
+ }
return choices.get(0).get("text").asText();
}
}
@@ -47,8 +61,9 @@ private String read(Response resp) {
}
private ObjectNode completion() {
+ OpenAiConfig repo = new OpenAiConfig();
ObjectNode request = JsonNodeFactory.instance.objectNode();
- request.put("model", "text-davinci-003");
+ request.put("model", repo.getValue(Key.MODEL).orElse("gpt-3.5-turbo"));
request.put("max_tokens", maxTokens);
request.put("temperature", 1);
request.put("top_p", 1);
diff --git a/openai-assistant/src/com/axonivy/connector/openai/assistant/OpenAiConfig.java b/openai-assistant/src/com/axonivy/connector/openai/assistant/OpenAiConfig.java
index f917051..2b82a97 100644
--- a/openai-assistant/src/com/axonivy/connector/openai/assistant/OpenAiConfig.java
+++ b/openai-assistant/src/com/axonivy/connector/openai/assistant/OpenAiConfig.java
@@ -17,6 +17,7 @@ public interface Key {
String CONNECT_TIMEOUT_SECONDS = OPENAI_PREFIX + "connectTimeoutSeconds";
String MAX_TOKENS = OPENAI_PREFIX + "maxTokens";
String PLATFORM_URI = OPENAI_PREFIX + "platformUri";
+ String MODEL = OPENAI_PREFIX + "model";
}
private final Config config;
diff --git a/openai-assistant/src/com/axonivy/connector/openai/assistant/models/Model.java b/openai-assistant/src/com/axonivy/connector/openai/assistant/models/Model.java
new file mode 100644
index 0000000..8389357
--- /dev/null
+++ b/openai-assistant/src/com/axonivy/connector/openai/assistant/models/Model.java
@@ -0,0 +1,44 @@
+package com.axonivy.connector.openai.assistant.models;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Model {
+ private String id;
+ private String object;
+ private long created;
+ @JsonProperty("owned_by")
+ private String ownedBy;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getObject() {
+ return object;
+ }
+
+ public void setObject(String object) {
+ this.object = object;
+ }
+
+ public long getCreated() {
+ return created;
+ }
+
+ public void setCreated(long created) {
+ this.created = created;
+ }
+
+ public String getOwnedBy() {
+ return ownedBy;
+ }
+
+ public void setOwnedBy(String ownedBy) {
+ this.ownedBy = ownedBy;
+ }
+
+}
diff --git a/openai-assistant/src/com/axonivy/connector/openai/assistant/models/ResponseModel.java b/openai-assistant/src/com/axonivy/connector/openai/assistant/models/ResponseModel.java
new file mode 100644
index 0000000..3e71292
--- /dev/null
+++ b/openai-assistant/src/com/axonivy/connector/openai/assistant/models/ResponseModel.java
@@ -0,0 +1,25 @@
+package com.axonivy.connector.openai.assistant.models;
+
+import java.util.List;
+
+public class ResponseModel {
+ private String object;
+ private List data;
+
+ public String getObject() {
+ return object;
+ }
+
+ public void setObject(String object) {
+ this.object = object;
+ }
+
+ public List getData() {
+ return data;
+ }
+
+ public void setData(List data) {
+ this.data = data;
+ }
+
+}
diff --git a/openai-assistant/src/com/axonivy/connector/openai/assistant/ui/ChatGPTAssistantHandler.java b/openai-assistant/src/com/axonivy/connector/openai/assistant/ui/ChatGPTAssistantHandler.java
index 2d14f88..0c9e00a 100644
--- a/openai-assistant/src/com/axonivy/connector/openai/assistant/ui/ChatGPTAssistantHandler.java
+++ b/openai-assistant/src/com/axonivy/connector/openai/assistant/ui/ChatGPTAssistantHandler.java
@@ -37,5 +37,6 @@ public interface Quests {
String EDIT = "edit";
String CHAT = "chat";
String KEY = "apiKey";
+ String MODEL = "model";
}
}
diff --git a/openai-assistant/src/com/axonivy/connector/openai/assistant/ui/ChatGPTquest.java b/openai-assistant/src/com/axonivy/connector/openai/assistant/ui/ChatGPTquest.java
index f361151..67e0b68 100644
--- a/openai-assistant/src/com/axonivy/connector/openai/assistant/ui/ChatGPTquest.java
+++ b/openai-assistant/src/com/axonivy/connector/openai/assistant/ui/ChatGPTquest.java
@@ -19,6 +19,7 @@ public Map getParameterValues() {
"Insert", Quests.INSERT,
"Edit (beta)", Quests.EDIT,
"Chat", Quests.CHAT,
- "Api Key", Quests.KEY);
+ "Api Key", Quests.KEY,
+ "API Model", Quests.MODEL);
}
}
diff --git a/openai-assistant/src/com/axonivy/connector/openai/assistant/ui/ChatGptUiFlow.java b/openai-assistant/src/com/axonivy/connector/openai/assistant/ui/ChatGptUiFlow.java
index d9ddf9d..2960ffd 100644
--- a/openai-assistant/src/com/axonivy/connector/openai/assistant/ui/ChatGptUiFlow.java
+++ b/openai-assistant/src/com/axonivy/connector/openai/assistant/ui/ChatGptUiFlow.java
@@ -2,11 +2,13 @@
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
+import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
import org.eclipse.compare.CompareUI;
import org.eclipse.core.resources.IResource;
@@ -25,7 +27,11 @@
import com.axonivy.connector.openai.assistant.ChatGptRequest;
import com.axonivy.connector.openai.assistant.OpenAiConfig;
import com.axonivy.connector.openai.assistant.OpenAiConfig.Key;
+import com.axonivy.connector.openai.assistant.models.Model;
+import com.axonivy.connector.openai.assistant.models.ResponseModel;
import com.axonivy.connector.openai.assistant.ui.ChatGPTAssistantHandler.Quests;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import ch.ivyteam.swt.dialogs.SwtCommonDialogs;
@@ -84,6 +90,24 @@ public void run() {
}
return;
}
+
+ if (quest.equalsIgnoreCase(Quests.MODEL)) {
+ String supportedModels = runWithProgress(() -> chatGpt.getModels());
+
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ List models = mapper.readValue(supportedModels, ResponseModel.class).getData();
+ String selectedModel = SelectModelDialog.open(site.getShell(), "OpenAI model",
+ "Please select model", repo.getValue(Key.MODEL).orElse(""),
+ models.stream().map(Model::getId).collect(Collectors.toList()));
+ if (selectedModel != null) {
+ repo.storeSecret(Key.MODEL, selectedModel);
+ }
+ return;
+ } catch (JsonProcessingException e) {
+ return;
+ }
+ }
boolean assist = SwtCommonDialogs.openQuestionDialog(site.getShell(), "need assistance?", """
ready for asking chat GPT on ?
diff --git a/openai-assistant/src/com/axonivy/connector/openai/assistant/ui/SelectModelDialog.java b/openai-assistant/src/com/axonivy/connector/openai/assistant/ui/SelectModelDialog.java
new file mode 100644
index 0000000..658fbb9
--- /dev/null
+++ b/openai-assistant/src/com/axonivy/connector/openai/assistant/ui/SelectModelDialog.java
@@ -0,0 +1,64 @@
+package com.axonivy.connector.openai.assistant.ui;
+
+import java.util.List;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+
+public class SelectModelDialog extends MessageDialog {
+
+ private String value;
+ private Combo comboField;
+ private List models;
+
+ public SelectModelDialog(Shell parent, String message, String title, String value, List models) {
+ super(parent, title, null, message, MessageDialog.CONFIRM,
+ new String[] { IDialogConstants.OK_LABEL, IDialogConstants.CANCEL_LABEL }, 0);
+ this.value = value;
+ this.models = models;
+ }
+
+ @Override
+ protected Control createCustomArea(Composite parent) {
+ comboField = new Combo(parent, SWT.READ_ONLY);
+ String items[] = models.toArray(new String[0]);
+ comboField.setItems(items);
+ int defaultModelIndex = models.indexOf(value);
+ comboField.select(defaultModelIndex);
+ return comboField;
+ }
+
+ @Override
+ protected void buttonPressed(int buttonId) {
+ if (buttonId == 0) {
+ value = comboField.getText();
+ } else {
+ value = null;
+ }
+ super.buttonPressed(buttonId);
+ }
+
+ public static String open(Shell parent, String title, String message, String value, List models) {
+ var modelDialog = new SelectModelDialog(parent, message, title, value, models);
+ String[] result = new String[1];
+ parent.getDisplay().syncExec(() -> {
+ modelDialog.open();
+ result[0] = modelDialog.getValue();
+ });
+ return result[0];
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+}