feat: azure openai api (#80)

This commit is contained in:
josc146
2023-04-05 16:55:16 +08:00
parent 64fe14db79
commit 9e91ea6b18
10 changed files with 173 additions and 36 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ Deep ChatGPT integrations in your browser, completely for free.
[![license][license-image]][license-url]
[![release][release-image]][release-url]
[![size](https://img.shields.io/badge/minified%20size-350%20kB-blue)][release-url]
[![size](https://img.shields.io/badge/minified%20size-360%20kB-blue)][release-url]
[![verfiy][verify-image]][verify-url]
English | [Indonesia](README_IN.md) | [简体中文](README_ZH.md)
+1 -1
View File
@@ -10,7 +10,7 @@ Integrasi Deep ChatGPT di browser Anda, sepenuhnya gratis.
[![license][license-image]][license-url]
[![release][release-image]][release-url]
[![size](https://img.shields.io/badge/minified%20size-350%20kB-blue)][release-url]
[![size](https://img.shields.io/badge/minified%20size-360%20kB-blue)][release-url]
[![verfiy][verify-image]][verify-url]
[Inggris](README.md) | Indonesia | [简体中文](README_ZH.md)
+1 -1
View File
@@ -10,7 +10,7 @@
[![license][license-image]][license-url]
[![release][release-image]][release-url]
[![size](https://img.shields.io/badge/minified%20size-350%20kB-blue)][release-url]
[![size](https://img.shields.io/badge/minified%20size-360%20kB-blue)][release-url]
[![verfiy][verify-image]][verify-url]
[English](README.md) | [Indonesia](README_IN.md) | 简体中文
+32 -7
View File
@@ -9,6 +9,7 @@
"@nem035/gpt-3-encoder": "^1.1.7",
"@picocss/pico": "^1.5.7",
"@primer/octicons-react": "^18.2.0",
"azure-openai": "^0.9.4",
"countries-list": "^2.6.1",
"eventsource-parser": "^0.1.0",
"expiry-map": "^2.0.0",
@@ -2751,8 +2752,7 @@
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/available-typed-arrays": {
"version": "1.0.5",
@@ -2763,6 +2763,23 @@
"node": ">= 0.4"
}
},
"node_modules/axios": {
"version": "0.26.1",
"resolved": "https://registry.npmmirror.com/axios/-/axios-0.26.1.tgz",
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
"dependencies": {
"follow-redirects": "^1.14.8"
}
},
"node_modules/azure-openai": {
"version": "0.9.4",
"resolved": "https://registry.npmmirror.com/azure-openai/-/azure-openai-0.9.4.tgz",
"integrity": "sha512-7uii4ZInxzu2zjLg45PdvgOaw3ps18tEAw0Yux9mo8anX4PwnCMSS9xdlKNiNQyyEKPogvAcxH2PIufHXFLx6Q==",
"dependencies": {
"axios": "^0.26.0",
"form-data": "^4.0.0"
}
},
"node_modules/babel-loader": {
"version": "9.1.2",
"resolved": "https://registry.npmmirror.com/babel-loader/-/babel-loader-9.1.2.tgz",
@@ -3081,7 +3098,6 @@
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"dependencies": {
"delayed-stream": "~1.0.0"
},
@@ -3581,7 +3597,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true,
"engines": {
"node": ">=0.4.0"
}
@@ -4349,6 +4364,19 @@
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/for-each": {
"version": "0.3.3",
"resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.3.tgz",
@@ -4362,7 +4390,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dev": true,
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
@@ -6295,7 +6322,6 @@
"version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
@@ -6304,7 +6330,6 @@
"version": "2.1.35",
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"dependencies": {
"mime-db": "1.52.0"
},
+1
View File
@@ -22,6 +22,7 @@
"@nem035/gpt-3-encoder": "^1.1.7",
"@picocss/pico": "^1.5.7",
"@primer/octicons-react": "^18.2.0",
"azure-openai": "^0.9.4",
"countries-list": "^2.6.1",
"eventsource-parser": "^0.1.0",
"expiry-map": "^2.0.0",
+75
View File
@@ -0,0 +1,75 @@
import { Configuration, OpenAIApi } from 'azure-openai'
import { getUserConfig, maxResponseTokenLength } from '../../config/index.mjs'
import { getChatSystemPromptBase, pushRecord, setAbortController } from './shared.mjs'
import { getConversationPairs } from '../../utils/get-conversation-pairs'
/**
* @param {Runtime.Port} port
* @param {string} question
* @param {Session} session
*/
export async function generateAnswersWithAzureOpenaiApi(port, question, session) {
const { controller, messageListener } = setAbortController(port)
const config = await getUserConfig()
const prompt = getConversationPairs(session.conversationRecords, false)
prompt.unshift({ role: 'system', content: await getChatSystemPromptBase() })
prompt.push({ role: 'user', content: question })
const openAiApi = new OpenAIApi(
new Configuration({
apiKey: config.azureApiKey,
azure: {
apiKey: config.azureApiKey,
endpoint: config.azureEndpoint,
deploymentName: config.azureDeploymentName,
},
}),
)
let answer = ''
const response = await openAiApi
.createChatCompletion(
{
messages: prompt,
stream: true,
max_tokens: maxResponseTokenLength,
},
{
signal: controller.signal,
responseType: 'stream',
},
)
.catch((err) => {
port.onMessage.removeListener(messageListener)
throw err
})
for await (const chunk of response.data) {
const lines = chunk
.toString('utf8')
.split('\n')
.filter((line) => line.trim().startsWith('data: '))
for (const line of lines) {
const message = line.replace(/^data: /, '')
console.debug('sse message', message)
if (message === '[DONE]') {
pushRecord(session, question, answer)
console.debug('conversation history', { content: session.conversationRecords })
port.postMessage({ answer: null, done: true, session: session })
break
}
let data
try {
data = JSON.parse(message)
} catch (error) {
console.debug('json error', error)
continue
}
answer += data.choices[0].text
port.postMessage({ answer: answer, done: false, session: null })
}
}
port.onMessage.removeListener(messageListener)
}
+1 -9
View File
@@ -7,16 +7,8 @@ import { pushRecord, setAbortController } from './shared.mjs'
* @param {string} question
* @param {Session} session
* @param {string} accessToken
* @param {string} modelName
*/
export async function generateAnswersWithBingWebApi(
port,
question,
session,
accessToken,
// eslint-disable-next-line
modelName,
) {
export async function generateAnswersWithBingWebApi(port, question, session, accessToken) {
const { controller, messageListener } = setAbortController(port)
const bingAIClient = new BingAIClient({ userToken: accessToken })
+5 -7
View File
@@ -11,7 +11,9 @@ import {
generateAnswersWithGptCompletionApi,
} from './apis/openai-api'
import { generateAnswersWithCustomApi } from './apis/custom-api.mjs'
import { generateAnswersWithAzureOpenaiApi } from './apis/azure-openai-api.mjs'
import {
azureOpenAiApiModelKeys,
bingWebModelKeys,
chatgptApiModelKeys,
chatgptWebModelKeys,
@@ -81,13 +83,7 @@ Browser.runtime.onConnect.addListener((port) => {
await generateAnswersWithChatgptWebApi(port, session.question, session, accessToken)
} else if (bingWebModelKeys.includes(session.modelName)) {
const accessToken = await getBingAccessToken()
await generateAnswersWithBingWebApi(
port,
session.question,
session,
accessToken,
session.modelName,
)
await generateAnswersWithBingWebApi(port, session.question, session, accessToken)
} else if (gptApiModelKeys.includes(session.modelName)) {
await generateAnswersWithGptCompletionApi(
port,
@@ -112,6 +108,8 @@ Browser.runtime.onConnect.addListener((port) => {
'',
config.customModelName,
)
} else if (azureOpenAiApiModelKeys.includes(session.modelName)) {
await generateAnswersWithAzureOpenaiApi(port, session.question, session)
}
} catch (err) {
console.error(err)
+17 -9
View File
@@ -19,6 +19,7 @@ export const Models = {
chatgptApi4_32k: { value: 'gpt-4-32k', desc: 'ChatGPT (GPT-4-32k)' },
gptApiDavinci: { value: 'text-davinci-003', desc: 'GPT-3.5' },
customModel: { value: '', desc: 'Custom Model' },
azureOpenAi: { value: '', desc: 'ChatGPT (Azure)' },
}
export const chatgptWebModelKeys = ['chatgptFree35', 'chatgptPlus4']
@@ -26,6 +27,7 @@ export const bingWebModelKeys = ['bingFree4']
export const gptApiModelKeys = ['gptApiDavinci']
export const chatgptApiModelKeys = ['chatgptApi35', 'chatgptApi4_8k', 'chatgptApi4_32k']
export const customApiModelKeys = ['customModel']
export const azureOpenAiApiModelKeys = ['azureOpenAi']
export const TriggerMode = {
always: 'Always',
@@ -60,13 +62,21 @@ export const defaultConfig = {
themeMode: 'auto',
/** @type {keyof Models}*/
modelName: 'chatgptFree35',
apiKey: '',
/** @type {keyof ModelMode}*/
modelMode: 'balanced',
preferredLanguage: getNavigatorLanguage(),
insertAtTop: isMobile(),
lockWhenAnswer: false,
autoRegenAfterSwitchModel: false,
apiKey: '', // openai ApiKey
azureApiKey: '',
azureEndpoint: '',
azureDeploymentName: '',
/** @type {keyof ModelMode}*/
modelMode: 'balanced',
customModelApiUrl: 'http://localhost:8000/chat/completions',
customModelName: 'llama-7b-int4',
@@ -154,6 +164,10 @@ export function isUsingCustomModel(configOrSession) {
return customApiModelKeys.includes(configOrSession.modelName)
}
export function isUsingAzureOpenAi(configOrSession) {
return azureOpenAiApiModelKeys.includes(configOrSession.modelName)
}
export async function getPreferredLanguageKey() {
const config = await getUserConfig()
if (config.preferredLanguage === 'auto') return config.userLanguage
@@ -166,12 +180,6 @@ export async function getPreferredLanguageKey() {
*/
export async function getUserConfig() {
const options = await Browser.storage.local.get(Object.keys(defaultConfig))
// version compatibility
if (options.modelName === 'chatgptFree') options.modelName = 'chatgptFree35'
else if (options.modelName === 'chatgptApi') options.modelName = 'chatgptApi35'
else if (options.modelName === 'gptDavinci') options.modelName = 'gptApiDavinci'
return defaults(options, defaultConfig)
}
+39 -1
View File
@@ -5,6 +5,7 @@ import {
getPreferredLanguageKey,
getUserConfig,
isUsingApiKey,
isUsingAzureOpenAi,
isUsingCustomModel,
isUsingMultiModeModel,
ModelMode,
@@ -85,7 +86,10 @@ function GeneralPart({ config, updateConfig }) {
<span style="display: flex; gap: 15px;">
<select
style={
isUsingApiKey(config) || isUsingMultiModeModel(config) || isUsingCustomModel(config)
isUsingApiKey(config) ||
isUsingMultiModeModel(config) ||
isUsingCustomModel(config) ||
isUsingAzureOpenAi(config)
? 'width: 50%;'
: undefined
}
@@ -165,6 +169,18 @@ function GeneralPart({ config, updateConfig }) {
}}
/>
)}
{isUsingAzureOpenAi(config) && (
<input
type="password"
style="width: 50%;"
value={config.azureApiKey}
placeholder={t('Azure API Key')}
onChange={(e) => {
const apiKey = e.target.value
updateConfig({ azureApiKey: apiKey })
}}
/>
)}
</span>
{isUsingCustomModel(config) && (
<input
@@ -177,6 +193,28 @@ function GeneralPart({ config, updateConfig }) {
}}
/>
)}
{isUsingAzureOpenAi(config) && (
<input
type="password"
value={config.azureEndpoint}
placeholder={t('Azure Endpoint')}
onChange={(e) => {
const endpoint = e.target.value
updateConfig({ azureEndpoint: endpoint })
}}
/>
)}
{isUsingAzureOpenAi(config) && (
<input
type="text"
value={config.azureDeploymentName}
placeholder={t('Azure Deployment Name')}
onChange={(e) => {
const deploymentName = e.target.value
updateConfig({ azureDeploymentName: deploymentName })
}}
/>
)}
</label>
<label>
<legend>{t('Preferred Language')}</legend>