Skip to content

Commit

Permalink
feat: add gpt-3.5-turbo-instruct, gpt-4-32k, and gpt-3.5-turbo-16k
Browse files Browse the repository at this point in the history
  • Loading branch information
ericrallen committed Oct 30, 2023
1 parent fed4734 commit f6cc31a
Show file tree
Hide file tree
Showing 12 changed files with 233 additions and 60 deletions.
20 changes: 16 additions & 4 deletions src/components/SidebarView.tsx
Expand Up @@ -10,7 +10,10 @@ import { useApp } from '../hooks/useApp'

import type { Conversation } from '../services/conversation'

import { OPEN_AI_CHAT_COMPLETION_OBJECT_TYPE } from '../services/openai/constants'
import {
OPEN_AI_CHAT_COMPLETION_OBJECT_TYPE,
OPEN_AI_COMPLETION_OBJECT_TYPE
} from '../services/openai/constants'

export interface ChatFormProps {
onChatUpdate?: () => Promise<void>
Expand Down Expand Up @@ -41,7 +44,9 @@ const SidebarView = ({ onChatUpdate }: ChatFormProps): React.ReactElement => {
setPrompt('')

chat
?.send(prompt, { signal: cancelPromptController.signal })
?.send(prompt, {
signal: cancelPromptController.signal
})
.then(async (responseStream: Stream<OpenAI.Chat.ChatCompletionChunk>) => {
let accumulatedMessage = ''

Expand Down Expand Up @@ -72,7 +77,10 @@ const SidebarView = ({ onChatUpdate }: ChatFormProps): React.ReactElement => {
id,
model,
created,
object: OPEN_AI_CHAT_COMPLETION_OBJECT_TYPE,
object:
conversation?.model?.adapter?.engine === 'chat'
? OPEN_AI_CHAT_COMPLETION_OBJECT_TYPE
: OPEN_AI_COMPLETION_OBJECT_TYPE,
choices: [
{
message: {
Expand Down Expand Up @@ -111,7 +119,11 @@ const SidebarView = ({ onChatUpdate }: ChatFormProps): React.ReactElement => {
}

const cancelPromptSubmit = (event: React.FormEvent): void => {
logger.debug(`Cancelling streaming response from ${conversation?.model?.adapter?.name as string}...`)
logger.debug(
`Cancelling streaming response from ${
conversation?.model?.adapter?.name as string
}...`
)

cancelPromptController.abort()
}
Expand Down
9 changes: 9 additions & 0 deletions src/services/openai/adapters/chat.ts
@@ -0,0 +1,9 @@
import type { ChatAdapter } from 'src/types'

const OpenAIModelChatAdapter: ChatAdapter = {
name: 'openai',
engine: 'chat',
endpoint: '/v1/chat/completions'
}

export default OpenAIModelChatAdapter
9 changes: 9 additions & 0 deletions src/services/openai/adapters/completion.ts
@@ -0,0 +1,9 @@
import type { ChatAdapter } from 'src/types'

const OpenAIModelCompletionAdapter: ChatAdapter = {
name: 'openai',
engine: 'completion',
endpoint: '/v1/completions'
}

export default OpenAIModelCompletionAdapter
171 changes: 138 additions & 33 deletions src/services/openai/index.ts
@@ -1,17 +1,25 @@
import { requestUrl as obsidianRequest, type RequestUrlParam } from 'obsidian'

import OpenAI from 'openai'
import type { Stream } from 'openai/streaming'

import formatChat from './utils/formatChat'

import {
OPEN_AI_BASE_URL,
OPEN_AI_DEFAULT_MODEL,
OPEN_AI_RESPONSE_TOKENS,
OPEN_AI_DEFAULT_TEMPERATURE
} from './constants'

import {
OPEN_AI_GPT_START_WORD,
OPEN_AI_GPT3_STOP_WORD
} from './models/constants'

import { PLUGIN_SETTINGS } from '../../constants'

import type { OpenAICompletionRequest } from './types'
import type { OpenAICompletionRequest, OpenAICompletion } from './types'
import type { Conversation } from '../conversation'
import type { PluginSettings } from '../../types'
import type Logger from '../logger'
Expand All @@ -36,45 +44,142 @@ export const openAICompletion = async (
{ signal }: { signal?: AbortSignal },
settings: PluginSettings = PLUGIN_SETTINGS,
logger: Logger
): Promise<Stream<OpenAI.Chat.ChatCompletionChunk>> => {
let { openAiApiKey: apiKey } = settings
// @ts-expect-error
): Promise<Stream<OpenAICompletion | OpenAI.Chat.ChatCompletionChunk>> => {
let { openAiApiKey: apiKey, userHandle, botHandle } = settings

if (safeStorage.isEncryptionAvailable() === true) {
apiKey = await safeStorage.decryptString(Buffer.from(apiKey))
}

try {
const openai = new OpenAI({
apiKey,
dangerouslyAllowBrowser: true
})

const messages = formatChat(input as Conversation)

const stream = await openai.chat.completions.create(
{
model: model.model,
messages,
stream: true,
temperature,
max_tokens: maxTokens,
top_p: topP,
frequency_penalty: frequencyPenalty,
presence_penalty: presencePenalty
},
{
signal
if (model.adapter.engine === 'chat') {
try {
const openai = new OpenAI({
apiKey,
dangerouslyAllowBrowser: true
})

const messages = formatChat(input as Conversation)

const stream = await openai.chat.completions.create(
{
model: model.model,
messages,
stream: true,
temperature,
max_tokens: maxTokens,
top_p: topP,
frequency_penalty: frequencyPenalty,
presence_penalty: presencePenalty
},
{
signal
}
)

return stream
} catch (error) {
if (typeof error?.response !== 'undefined') {
logger.error(error.response.status, error.response.data)
} else {
logger.error(error.message)
}
)

return stream
} catch (error) {
if (typeof error?.response !== 'undefined') {
logger.error(error.response.status, error.response.data)
} else {
logger.error(error.message)

throw error
}
} else if (typeof model?.adapter?.endpoint !== 'undefined') {
// TODO: remove this now that non-chat models are being deprecated
const requestUrl = new URL(model?.adapter?.endpoint, OPEN_AI_BASE_URL)

const requestHeaders = {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
Accept: 'text/event-stream'
}

const prompt = formatChat(input as Conversation)

const stopWords: string[] = []

if (typeof model.stopWord !== 'undefined' && model.stopWord !== '') {
stopWords.push(model.stopWord)
}

if (typeof userHandle !== 'undefined' && userHandle !== '') {
stopWords.push(userHandle)
}

if (typeof botHandle !== 'undefined' && botHandle !== '') {
stopWords.push(botHandle)
}

const requestBody = {
prompt: prompt
.reduce((promptString, message) => {
return (
`${promptString}${OPEN_AI_GPT_START_WORD}[${message.role[0].toUpperCase()}${message.role.slice(
1
)}]\n` +
`${message?.content as string}\n${OPEN_AI_GPT3_STOP_WORD}`.trim()
)
}, '')
.trim(),
model: model.model,
stream: true,
temperature,
max_tokens: maxTokens,
stop: stopWords,
top_p: topP,
frequency_penalty: frequencyPenalty,
presence_penalty: presencePenalty
}

const request: RequestUrlParam = {
url: requestUrl.toString(),
headers: requestHeaders,
method: 'POST',
body: JSON.stringify(requestBody),
throw: false
}

throw error
try {
// borrowed from: https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams
const stream = await obsidianRequest(request)
.then(async (response) => {
// eslint-disable-next-line @typescript-eslint/return-await
return await response?.json?.()
})
.then((response) => {
const reader = response.body.getReader()
return new ReadableStream({
start(controller) {
return pump()
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function pump() {
// @ts-expect-error
return reader.read().then(({ done, value }) => {
// When no more data needs to be consumed, close the stream
if (done === true) {
controller.close()
return
}
// Enqueue the next data chunk into our target stream
controller.enqueue(value)
return pump()
})
}
}
})
})
// Create a new response out of the stream
.then((stream) => new Response(stream))

// @ts-expect-error
return stream
} catch (error) {
logger.error(error)

throw error
}
}
}
13 changes: 13 additions & 0 deletions src/services/openai/models/gpt-3.5-turbo-16k.ts
@@ -0,0 +1,13 @@
import OpenAIModelChatAdapter from '../adapters/chat'

import type { ModelDefinition } from '../types'

const GPT35Turbo16K: ModelDefinition = {
name: 'GPT-3.5 Turbo 16K',
adapter: OpenAIModelChatAdapter,
model: 'gpt-3.5-turbo-16k',
maxTokens: 16385,
tokenType: 'gpt4'
}

export default GPT35Turbo16K
13 changes: 13 additions & 0 deletions src/services/openai/models/gpt-3.5-turbo-instruct.ts
@@ -0,0 +1,13 @@
import OpenAIModelCompletionAdapter from '../adapters/completion'

import type { ModelDefinition } from '../types'

const GPT35TurboInstruct: ModelDefinition = {
name: 'GPT-3.5 Turbo Instruct',
adapter: OpenAIModelCompletionAdapter,
model: 'gpt-3.5-turbo-instruct',
maxTokens: 8192,
tokenType: 'gpt4'
}

export default GPT35TurboInstruct
14 changes: 4 additions & 10 deletions src/services/openai/models/gpt-3.5-turbo.ts
@@ -1,18 +1,12 @@
import type { ChatAdapter } from '../../../types'
import OpenAIModelChatAdapter from '../adapters/chat'

import type { ModelDefinition } from '../types'

const GPT35TurboAdapter: ChatAdapter = {
name: 'openai',
engine: 'chat',
endpoint: '/v1/chat/completions'
}

const GPT35Turbo: ModelDefinition = {
name: 'GPT-3.5',
adapter: GPT35TurboAdapter,
name: 'GPT-3.5 Turbo',
adapter: OpenAIModelChatAdapter,
model: 'gpt-3.5-turbo',
maxTokens: 4000,
maxTokens: 4097,
tokenType: 'gpt4'
}

Expand Down
13 changes: 13 additions & 0 deletions src/services/openai/models/gpt-4-32k.ts
@@ -0,0 +1,13 @@
import OpenAIModelChatAdapter from '../adapters/chat'

import type { ModelDefinition } from '../types'

const GPT432K: ModelDefinition = {
name: 'GPT-4 32K',
adapter: OpenAIModelChatAdapter,
model: 'gpt-4-32k',
maxTokens: 32768,
tokenType: 'gpt4'
}

export default GPT432K
12 changes: 3 additions & 9 deletions src/services/openai/models/gpt-4.ts
@@ -1,18 +1,12 @@
import type { ChatAdapter } from '../../../types'
import OpenAIModelChatAdapter from '../adapters/chat'

import type { ModelDefinition } from '../types'

const GPT4Adapter: ChatAdapter = {
name: 'openai',
engine: 'chat',
endpoint: '/v1/chat/completions'
}

const GPT4: ModelDefinition = {
name: 'GPT-4',
adapter: GPT4Adapter,
adapter: OpenAIModelChatAdapter,
model: 'gpt-4',
maxTokens: 8000,
maxTokens: 8192,
tokenType: 'gpt4'
}

Expand Down
8 changes: 7 additions & 1 deletion src/services/openai/models/index.ts
@@ -1,9 +1,15 @@
import GPT35Turbo from './gpt-3.5-turbo'
import GPT35TurboInstruct from './gpt-3.5-turbo-instruct'
import GPT35Turbo16K from './gpt-3.5-turbo-16k'
import GPT4 from './gpt-4'
import GPT432K from './gpt-4-32k'

const models = {
'gpt-4': GPT4,
'gpt-3.5-turbo': GPT35Turbo
'gpt-3.5-turbo': GPT35Turbo,
'gpt-3.5-turbo-instruct': GPT35TurboInstruct,
'gpt-3.5-turbo-16k': GPT35Turbo16K,
'gpt-4-32k': GPT432K
}

export default models
9 changes: 7 additions & 2 deletions src/services/openai/types.ts
Expand Up @@ -5,10 +5,15 @@ import type { ChatAdapter } from '../../types'
import type { TokenCounterType } from '../../utils/tokenCounter'

// TODO: add other models
export type OpenAIModel = 'gpt-3.5-turbo' | 'gpt-4'
// | 'code-davinci-002'
export type OpenAIModel =
| 'gpt-3.5-turbo'
| 'gpt-3.5-turbo-16k'
| 'gpt-3.5-turbo-instruct'
| 'gpt-4'
| 'gpt-4-32k'

// TODO: find out what other valid object values are
// tODO: It think this was deprecated with the older completion models
export type OpenAICompletionObject = 'text_completion'

export interface ModelDefinition {
Expand Down

0 comments on commit f6cc31a

Please sign in to comment.