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; + } + +}