From 213426d58f7a64dcc952f4285ab2c013e3c4cb85 Mon Sep 17 00:00:00 2001 From: TDC13 <32446533+TDC13@users.noreply.github.com> Date: Sat, 29 Apr 2023 12:22:25 -0500 Subject: [PATCH] Added a way to use azure openai api. --- main.ts | 82 +++++++++++++++++++++++++++++++++++++++++++---- package-lock.json | 14 ++++++-- package.json | 1 + 3 files changed, 88 insertions(+), 9 deletions(-) diff --git a/main.ts b/main.ts index ba85c32..61d7c8c 100644 --- a/main.ts +++ b/main.ts @@ -28,13 +28,14 @@ import * as cohere from "cohere-ai"; import * as fs from "fs"; import GPT3Tokenizer from "gpt3-tokenizer"; import { Configuration, OpenAIApi } from "openai"; +import { Configuration as AzureConfiguration, OpenAIApi as AzureOpenAiApi} from "azure-openai"; import { v4 as uuidv4 } from "uuid"; const dialog = require("electron").remote.dialog; const untildify = require("untildify") as any; const tokenizer = new GPT3Tokenizer({ type: "codex" }); // TODO depends on model -const PROVIDERS = ["cohere", "textsynth", "ocp", "openai", "openai-chat"]; +const PROVIDERS = ["cohere", "textsynth", "ocp", "openai", "openai-chat", "azure", "azure-chat"]; type Provider = (typeof PROVIDERS)[number]; interface LoomSettings { @@ -42,6 +43,10 @@ interface LoomSettings { cohereApiKey: string; textsynthApiKey: string; + azureApiKey: string; + azureEndpoint: string; + + ocpApiKey: string; ocpUrl: string; @@ -68,6 +73,9 @@ const DEFAULT_SETTINGS: LoomSettings = { cohereApiKey: "", textsynthApiKey: "", + azureApiKey: "", + azureEndpoint: "", + ocpApiKey: "", ocpUrl: "", @@ -110,6 +118,7 @@ export default class LoomPlugin extends Plugin { statusBarItem: HTMLElement; openai: OpenAIApi; + azureopenai: AzureOpenAiApi; withFile(callback: (file: TFile) => T): T | null { const file = this.app.workspace.getActiveFile(); @@ -152,6 +161,19 @@ export default class LoomPlugin extends Plugin { cohere.init(this.settings.cohereApiKey); } + setAzureOpenAI() { + const configuration = new AzureConfiguration({ + apiKey: this.settings.azureApiKey, + // add azure info into configuration + azure: { + apiKey: this.settings.azureApiKey, + endpoint: this.settings.azureEndpoint + } + }); + this.azureopenai = new AzureOpenAiApi(configuration); + } + + async onload() { await this.loadSettings(); await this.loadState(); @@ -160,6 +182,7 @@ export default class LoomPlugin extends Plugin { this.setOpenAI(); this.setCohere(); + this.setAzureOpenAI(); this.statusBarItem = this.addStatusBarItem(); this.statusBarItem.setText("Completing..."); @@ -180,6 +203,12 @@ export default class LoomPlugin extends Plugin { !this.settings.openaiApiKey ) return false; + if ( + ["azure", "azure-chat"].includes(this.settings.provider) && + !this.settings.azureApiKey + ) + return false; + if (this.settings.provider === "cohere" && !this.settings.cohereApiKey) return false; if ( @@ -189,7 +218,7 @@ export default class LoomPlugin extends Plugin { return false; if (this.settings.provider === "ocp" && !this.settings.ocpApiKey) return false; - + if (!checking) { if (file.extension === "md") this.mdComplete(file); @@ -1067,7 +1096,7 @@ export default class LoomPlugin extends Plugin { // complete, or visually display an error and return if that fails let rawCompletions; try { - if (this.settings.provider === "openai-chat") { + if (["openai-chat"].includes(this.settings.provider)) { rawCompletions = ( await this.openai.createChatCompletion({ model: this.settings.model, @@ -1078,7 +1107,7 @@ export default class LoomPlugin extends Plugin { top_p: this.settings.topP, }) ).data.choices.map((choice) => choice.message?.content); - } else if (this.settings.provider === "openai") { + } else if (["openai"].includes(this.settings.provider)) { rawCompletions = ( await this.openai.createCompletion({ model: this.settings.model, @@ -1089,18 +1118,40 @@ export default class LoomPlugin extends Plugin { top_p: this.settings.topP, }) ).data.choices.map((choice) => choice.text); + } else if (["azure-chat"].includes(this.settings.provider)) { + rawCompletions = ( + await this.azureopenai.createChatCompletion({ + model: this.settings.model, + messages: [{ role: "assistant", content: prompt }], + max_tokens: this.settings.maxTokens, + n: this.settings.n, + temperature: this.settings.temperature, + top_p: this.settings.topP, + }) + ).data.choices.map((choice) => choice.message?.content); + } else if (["azure"].includes(this.settings.provider)) { + rawCompletions = ( + await this.azureopenai.createCompletion({ + model: this.settings.model, + prompt, + max_tokens: this.settings.maxTokens, + n: this.settings.n, + temperature: this.settings.temperature, + top_p: this.settings.topP, + }) + ).data.choices.map((choice) => choice.text); } } catch (e) { if ( e.response.status === 401 && - ["openai", "openai-chat"].includes(this.settings.provider) + ["openai", "openai-chat", "azure", "azure-chat"].includes(this.settings.provider) ) new Notice( "OpenAI API key is invalid. Please provide a valid key in the settings." ); else if ( e.response.status === 429 && - ["openai", "openai-chat"].includes(this.settings.provider) + ["openai", "openai-chat", "azure", "azure-chat"].includes(this.settings.provider) ) new Notice("OpenAI API rate limit exceeded."); else new Notice("Unknown API error: " + e.response.data.error.message); @@ -1203,7 +1254,7 @@ export default class LoomPlugin extends Plugin { completion = completion.replace(/ { const optionEl = providerSelect.createEl("option", { @@ -2238,6 +2291,8 @@ class LoomSettingTab extends PluginSettingTab { dropdown.addOption("ocp", "OpenAI code-davinci-002 proxy"); dropdown.addOption("openai", "OpenAI (Completion)"); dropdown.addOption("openai-chat", "OpenAI (Chat)"); + dropdown.addOption("azure", "Azure (Completion)"); + dropdown.addOption("azure-chat", "Azure (Chat)"); dropdown.setValue(this.plugin.settings.provider); dropdown.onChange(async (value) => { if (PROVIDERS.find((provider) => provider === value)) @@ -2276,6 +2331,19 @@ class LoomSettingTab extends PluginSettingTab { apiKeySetting("OpenAI", "openaiApiKey"); + apiKeySetting("Azure", "azureApiKey") + + new Setting(containerEl) + .setName("Azure-Openai Resource Endpoint") + .setDesc("Required if using Azure") + .addText((text) => + text.setValue(this.plugin.settings.azureEndpoint).onChange(async (value) => { + this.plugin.settings.azureEndpoint = value; + await this.plugin.save(); + }) + ); + + // TODO: reduce duplication of other settings new Setting(containerEl).setName("Model").addText((text) => diff --git a/package-lock.json b/package-lock.json index c9f2d4e..d248c91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,18 @@ { "name": "obsidian-loom", - "version": "0.1.0", + "version": "1.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "obsidian-loom", - "version": "0.1.0", + "version": "1.3.0", "license": "MIT", "dependencies": { "@codemirror/state": "^6.2.0", "@codemirror/view": "^6.9.3", "@types/lodash": "^4.14.191", + "azure-openai": "^0.9.4", "cohere-ai": "^6.1.0", "gpt3-tokenizer": "^1.1.5", "openai": "^3.2.0", @@ -497,6 +498,15 @@ "follow-redirects": "^1.14.8" } }, + "node_modules/azure-openai": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/azure-openai/-/azure-openai-0.9.4.tgz", + "integrity": "sha512-7uii4ZInxzu2zjLg45PdvgOaw3ps18tEAw0Yux9mo8anX4PwnCMSS9xdlKNiNQyyEKPogvAcxH2PIufHXFLx6Q==", + "dependencies": { + "axios": "^0.26.0", + "form-data": "^4.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", diff --git a/package.json b/package.json index a3d1829..ecc77d6 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@codemirror/state": "^6.2.0", "@codemirror/view": "^6.9.3", "@types/lodash": "^4.14.191", + "azure-openai": "^0.9.4", "cohere-ai": "^6.1.0", "gpt3-tokenizer": "^1.1.5", "openai": "^3.2.0",