From 56e35a40b6b1f36ec3cf98df6a53af1c7bca2b49 Mon Sep 17 00:00:00 2001 From: Nuno Boavida <45330362+nmboavida@users.noreply.github.com> Date: Tue, 7 Nov 2023 11:15:07 +0000 Subject: [PATCH 1/5] Renamed DisplayName --- vsce/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vsce/package.json b/vsce/package.json index c6c7f1f..aa5ca13 100644 --- a/vsce/package.json +++ b/vsce/package.json @@ -1,8 +1,8 @@ { "name": "neatcoder", - "displayName": "Neatwork AI - GPT4 on Steroids", + "displayName": "Neatwork AI - GPT4 Turbo on Steroids", "description": "Turn your IDE into an AI Sofware engineer.", - "version": "0.2.3", + "version": "0.2.4", "publisher": "NeatworkAi", "repository": { "url": "https://github.com/neatwork-ai/neatcoder-issues.git", From f9c3a71e0a2658f212f9e67a06a67d8660ae9596 Mon Sep 17 00:00:00 2001 From: Nuno Boavida <45330362+nmboavida@users.noreply.github.com> Date: Wed, 8 Nov 2023 00:19:54 +0000 Subject: [PATCH 2/5] Init assistant client (wip) --- crates/neatcoder/src/consts.rs | 2 + .../src/openai/assistant/assistant.rs | 123 ++++++++++++++++++ .../neatcoder/src/openai/assistant/message.rs | 0 crates/neatcoder/src/openai/assistant/mod.rs | 1 + .../neatcoder/src/openai/assistant/thread.rs | 0 crates/neatcoder/src/openai/mod.rs | 1 + crates/neatcoder/src/openai/params.rs | 44 ++++++- 7 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 crates/neatcoder/src/openai/assistant/assistant.rs create mode 100644 crates/neatcoder/src/openai/assistant/message.rs create mode 100644 crates/neatcoder/src/openai/assistant/mod.rs create mode 100644 crates/neatcoder/src/openai/assistant/thread.rs diff --git a/crates/neatcoder/src/consts.rs b/crates/neatcoder/src/consts.rs index f2a3b7c..4038279 100644 --- a/crates/neatcoder/src/consts.rs +++ b/crates/neatcoder/src/consts.rs @@ -15,3 +15,5 @@ pub const CONFIG_FILES: [&str; 9] = [ "Package.swift", // Swift ".gitignore", ]; + +pub const BASE_BETA_URL: &str = "https://api.openai.com/v1/beta"; diff --git a/crates/neatcoder/src/openai/assistant/assistant.rs b/crates/neatcoder/src/openai/assistant/assistant.rs new file mode 100644 index 0000000..19d347a --- /dev/null +++ b/crates/neatcoder/src/openai/assistant/assistant.rs @@ -0,0 +1,123 @@ +use anyhow::{anyhow, Result}; +use reqwest::{header::HeaderMap, Client}; +use serde::{ + de::{self, Visitor}, + Deserialize, Deserializer, Serialize, +}; +use serde_json::json; +use std::{collections::HashMap, fmt}; + +use crate::{consts::BASE_BETA_URL, openai::params::OpenAIModels}; + +#[derive(Serialize, Debug)] +pub struct AssistantRequest { + pub name: String, + pub instructions: String, + pub tools: Vec, // TODO: "tools": [{"type": "code_interpreter"}] + pub model: OpenAIModels, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Assistant { + id: String, + object: String, + created_at: u32, // TODO: Should be a timestamp + name: String, + description: Option, + model: OpenAIModels, + instructions: Option, + tools: Vec, + file_ids: Vec, + metadata: HashMap, +} + +#[derive(Debug, Serialize)] +pub enum Tool { + CodeInterpreter, + Retrieval, + FunctionCall, +} + +impl Tool { + pub fn new(tool: String) -> Self { + let tool = match tool.as_str() { + "code_interpreter" => Tool::CodeInterpreter, + "retrieval" => Tool::Retrieval, + "function" => Tool::FunctionCall, + _ => panic!("Invalid tool {}", tool), + }; + + tool + } + + pub fn as_string(&self) -> String { + match self { + Tool::CodeInterpreter => String::from("code_interpreter"), + Tool::Retrieval => String::from("retrieval"), + Tool::FunctionCall => String::from("function"), + } + } +} + +impl<'de> Deserialize<'de> for Tool { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct ToolVisitor; + + impl<'de> Visitor<'de> for ToolVisitor { + type Value = Tool; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string representing an OpenAI model") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + match value { + "code_interpreter" => Ok(Tool::CodeInterpreter), + "retrieval" => Ok(Tool::Retrieval), + "function" => Ok(Tool::FunctionCall), + _ => Err(E::custom(format!( + "unexpected OpenAI tool: {}", + value + ))), + } + } + } + + deserializer.deserialize_str(ToolVisitor) + } +} + +impl AssistantRequest { + pub async fn create_assistant( + self, + client: &Client, + headers: &HeaderMap, + ) -> Result { + let response = client + .post(&format!("{}/assistants", BASE_BETA_URL)) + .headers(headers.clone()) + .json(&json!({ + "name": self.name, // "Math Tutor", + "instructions": self.instructions, // "You are a personal math tutor. Write and run code to answer math questions.", + "tools": self.tools, // [{"type": "code_interpreter"}], + "model": self.model, // "gpt-4-1106-preview" + })) + .send() + .await?; + + if response.status().is_success() { + let assistant = response.json::().await?; + println!("Create Assistant response: {:?}", assistant); + Ok(assistant) + } else { + // If not successful, perhaps you want to parse it differently or handle the error + Err(anyhow!(response.status())) + } + } +} diff --git a/crates/neatcoder/src/openai/assistant/message.rs b/crates/neatcoder/src/openai/assistant/message.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/neatcoder/src/openai/assistant/mod.rs b/crates/neatcoder/src/openai/assistant/mod.rs new file mode 100644 index 0000000..09db8a7 --- /dev/null +++ b/crates/neatcoder/src/openai/assistant/mod.rs @@ -0,0 +1 @@ +pub mod assistant; diff --git a/crates/neatcoder/src/openai/assistant/thread.rs b/crates/neatcoder/src/openai/assistant/thread.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/neatcoder/src/openai/mod.rs b/crates/neatcoder/src/openai/mod.rs index ed79442..e173aa0 100644 --- a/crates/neatcoder/src/openai/mod.rs +++ b/crates/neatcoder/src/openai/mod.rs @@ -1,3 +1,4 @@ +pub mod assistant; ///< Client for interacting with the OpenAI API. pub mod msg; pub mod params; diff --git a/crates/neatcoder/src/openai/params.rs b/crates/neatcoder/src/openai/params.rs index b344618..3daf3f2 100644 --- a/crates/neatcoder/src/openai/params.rs +++ b/crates/neatcoder/src/openai/params.rs @@ -1,6 +1,9 @@ use anyhow::Result; -use serde::Serialize; -use std::collections::HashMap; +use serde::{ + de::{self, Visitor}, + Deserialize, Deserializer, Serialize, +}; +use std::{collections::HashMap, fmt}; use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; use crate::{ @@ -245,6 +248,43 @@ impl OpenAIModels { } } +impl<'de> Deserialize<'de> for OpenAIModels { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct OpenAIModelsVisitor; + + impl<'de> Visitor<'de> for OpenAIModelsVisitor { + type Value = OpenAIModels; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string representing an OpenAI model") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + match value { + "gpt-4-32k" => Ok(OpenAIModels::Gpt432k), + "gpt-4" => Ok(OpenAIModels::Gpt4), + "gpt-3.5-turbo" => Ok(OpenAIModels::Gpt35Turbo), + "gpt-3.5-turbo-16k" => Ok(OpenAIModels::Gpt35Turbo16k), + "gpt-3.5-turbo-1106" => Ok(OpenAIModels::Gpt35Turbo1106), + "gpt-4-1106-preview" => Ok(OpenAIModels::Gpt41106Preview), + _ => Err(E::custom(format!( + "unexpected OpenAI model: {}", + value + ))), + } + } + } + + deserializer.deserialize_str(OpenAIModelsVisitor) + } +} + impl Default for OpenAIParams { fn default() -> Self { Self { From 8c642f1da700c8980c3ff6b0df3855e005c09fe3 Mon Sep 17 00:00:00 2001 From: Nuno Boavida <45330362+nmboavida@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:16:47 +0000 Subject: [PATCH 3/5] BUGFIX: Notify user when Chat openAI call fails --- vsce/src/chat/handlers.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/vsce/src/chat/handlers.ts b/vsce/src/chat/handlers.ts index 7f55842..f171274 100644 --- a/vsce/src/chat/handlers.ts +++ b/vsce/src/chat/handlers.ts @@ -1,7 +1,9 @@ import * as vscode from "vscode"; +import { window } from "vscode"; import { getOrSetApiKey } from "../utils"; import * as wasm from "../../pkg/neatcoder"; import * as https from "https"; +import * as http from "http"; import * as url from "url"; import { MessageBuffer } from "../utils/httpClient"; import { getLLMParams } from "../utils/utils"; @@ -55,6 +57,22 @@ export async function promptLLM( const req = https.request(options, async (res) => { console.log(`STATUS: ${res.statusCode}`); + if (res.statusCode !== 202) { + const statusMessage = + http.STATUS_CODES[res.statusCode!] || "Unknown status code"; + + console.log(`STATUS: ${res.statusCode} ${statusMessage}`); + // Here the use of `window` and `webviewPanel` assumes this is within a VS Code extension + window.showErrorMessage( + `HTTP error: STATUS: ${res.statusCode} ${statusMessage}` + ); + + reject( + new Error(`HTTP error: STATUS: ${res.statusCode} ${statusMessage}`) + ); + return; // Stop further processing + } + res.setEncoding("utf8"); res.pause(); From 3fe9991ce85dec7487549b39b3a166aafc7ae167 Mon Sep 17 00:00:00 2001 From: Nuno Boavida <45330362+nmboavida@users.noreply.github.com> Date: Wed, 8 Nov 2023 17:42:24 +0000 Subject: [PATCH 4/5] Reinstate gpt3.5 and gpt4 classic --- vsce/src/utils/utils.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/vsce/src/utils/utils.ts b/vsce/src/utils/utils.ts index 455a9fc..14f3406 100644 --- a/vsce/src/utils/utils.ts +++ b/vsce/src/utils/utils.ts @@ -280,7 +280,12 @@ export async function getOrSetModelVersion(): Promise if (!modelVersion) { const value = await vscode.window.showQuickPick( - ["gpt-3.5-turbo-1106", "gpt-4-1106-preview"], + [ + "gpt-3.5-turbo-1106", + "gpt-4-1106-preview", + "gpt-3.5-turbo-16k", + "gpt-4", + ], { canPickMany: false, placeHolder: "Select an OpenAI model", // This is the placeholder text @@ -306,11 +311,12 @@ export async function getOrSetModelVersion(): Promise return fromModelVersionToEnum(modelVersion); } +// TODO: Remove dulplicated logic... export async function setModelVersion() { let config = vscode.workspace.getConfiguration("extension"); const value = await vscode.window.showQuickPick( - ["gpt-3.5-turbo-1106", "gpt-4-1106-preview"], + ["gpt-3.5-turbo-1106", "gpt-4-1106-preview", "gpt-3.5-turbo-16k", "gpt-4"], { canPickMany: false, placeHolder: "Select an OpenAI model", // This is the placeholder text From 350989772bbec59dafd23fbc0f51d62b0e4625ab Mon Sep 17 00:00:00 2001 From: Nuno Boavida <45330362+nmboavida@users.noreply.github.com> Date: Wed, 8 Nov 2023 17:51:55 +0000 Subject: [PATCH 5/5] CHANGELOG --- vsce/CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vsce/CHANGELOG.md b/vsce/CHANGELOG.md index 53a1959..fcd715f 100644 --- a/vsce/CHANGELOG.md +++ b/vsce/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to the "neatcoder" extension will be documented in this file Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. +## [0.2.4] - 08/11/2023 + +### Changed +- Reinstated classic Open AI models `gpt-3.5` and `gpt-4` models + +### Fixed +- Chat http request error handling + ## [0.2.3] - 07/11/2023 ### Added