Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AI-32] Add option for custom OpenAI-compatible endpoint #1

Merged
merged 5 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/lib/ApiUtil.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
<script context="module" lang="ts">
// This makes it possible to override the OpenAI API base URL in the .env file
const apiBase = import.meta.env.VITE_API_BASE || 'https://api.openai.com'
const endpointCompletions = import.meta.env.VITE_ENDPOINT_COMPLETIONS || '/v1/chat/completions'
const endpointGenerations = import.meta.env.VITE_ENDPOINT_GENERATIONS || '/v1/images/generations'
const endpointModels = import.meta.env.VITE_ENDPOINT_MODELS || '/v1/models'
const endpointEmbeddings = import.meta.env.VITE_ENDPOINT_EMBEDDINGS || '/v1/embeddings'
const petalsBase = import.meta.env.VITE_PEDALS_WEBSOCKET || 'wss://chat.petals.dev'
const endpointPetals = import.meta.env.VITE_PEDALS_WEBSOCKET || '/api/v2/generate'

export const getApiBase = ():string => apiBase
export const getEndpointCompletions = ():string => endpointCompletions
export const getEndpointGenerations = ():string => endpointGenerations
export const getEndpointModels = ():string => endpointModels
Expand Down
10 changes: 5 additions & 5 deletions src/lib/Chat.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@
let showSettingsModal

let scDelay
const onStateChange = (...args:any) => {
const onStateChange = async (...args:any) => {
if (!chat) return
clearTimeout(scDelay)
setTimeout(() => {
setTimeout(async () => {
if (chat.startSession) {
restartProfile(chatId)
await restartProfile(chatId)
if (chat.startSession) {
chat.startSession = false
saveChatStore()
Expand Down Expand Up @@ -112,7 +112,7 @@
setCurrentChat(chatId)

chatRequest = new ChatRequest()
chatRequest.setChat(chat)
await chatRequest.setChat(chat)

chat.lastAccess = Date.now()
saveChatStore()
Expand Down Expand Up @@ -148,7 +148,7 @@
console.log('Speech recognition not supported')
}
if (chat.startSession) {
restartProfile(chatId)
await restartProfile(chatId)
if (chat.startSession) {
chat.startSession = false
saveChatStore()
Expand Down
20 changes: 10 additions & 10 deletions src/lib/ChatOptionMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@
showChatMenu = false
}

const restartChatSession = () => {
const restartChatSession = async () => {
close()
restartProfile(chatId)
await restartProfile(chatId)
$checkStateChange++ // signal chat page to start profile
}

Expand All @@ -125,17 +125,17 @@
})
}

const importProfileFromFile = (e) => {
const importProfileFromFile = async (e) => {
const image = e.target.files[0]
e.target.value = null
const reader = new FileReader()
reader.onload = e => {
reader.onload = async (e) => {
const json = (e.target || {}).result as string
try {
const profile = JSON.parse(json) as ChatSettings
profile.profileName = newNameForProfile(profile.profileName || '')
profile.profileName = await newNameForProfile(profile.profileName || '')
profile.profile = null as any
saveCustomProfile(profile)
await saveCustomProfile(profile)
openModal(PromptConfirm, {
title: 'Profile Restored',
class: 'is-info',
Expand Down Expand Up @@ -174,17 +174,17 @@
<span class="menu-icon"><Fa icon={faGear}/></span> Chat Profile Settings
</a>
<hr class="dropdown-divider">
<a href={'#'} class:is-disabled={!hasActiveModels()} on:click|preventDefault={() => { hasActiveModels() && close(); hasActiveModels() && startNewChatWithWarning(chatId) }} class="dropdown-item">
<a href={'#'} class:is-disabled={!hasActiveModels()} on:click|preventDefault={async () => { hasActiveModels() && close(); hasActiveModels() && await startNewChatWithWarning(chatId) }} class="dropdown-item">
<span class="menu-icon"><Fa icon={faSquarePlus}/></span> New Chat from Default
</a>
<a href={'#'} class:is-disabled={!chatId} on:click|preventDefault={() => { chatId && close(); chatId && startNewChatFromChatId(chatId) }} class="dropdown-item">
<a href={'#'} class:is-disabled={!chatId} on:click|preventDefault={async () => { chatId && close(); chatId && await startNewChatFromChatId(chatId) }} class="dropdown-item">
<span class="menu-icon"><Fa icon={faSquarePlusOutline}/></span> New Chat from Current
</a>
<a href={'#'} class="dropdown-item" class:is-disabled={!chatId} on:click|preventDefault={() => { if (chatId) close(); copyChat(chatId) }}>
<span class="menu-icon"><Fa icon={faClone}/></span> Clone Chat
</a>
<hr class="dropdown-divider">
<a href={'#'} class="dropdown-item" class:is-disabled={!chatId} on:click|preventDefault={() => { if (chatId) restartChatSession() }}>
<a href={'#'} class="dropdown-item" class:is-disabled={!chatId} on:click|preventDefault={async () => { if (chatId) await restartChatSession() }}>
<span class="menu-icon"><Fa icon={faRotateRight}/></span> Restart Chat Session
</a>
<a href={'#'} class="dropdown-item" class:is-disabled={!chatId} on:click|preventDefault={() => { if (chatId) close(); clearMessages(chatId) }}>
Expand Down Expand Up @@ -231,4 +231,4 @@
</div>

<input style="display:none" type="file" accept=".json" on:change={(e) => importChatFromFile(e)} bind:this={chatFileInput} >
<input style="display:none" type="file" accept=".json" on:change={(e) => importProfileFromFile(e)} bind:this={profileFileInput} >
<input style="display:none" type="file" accept=".json" on:change={async (e) => await importProfileFromFile(e)} bind:this={profileFileInput} >
27 changes: 14 additions & 13 deletions src/lib/ChatRequest.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ export class ChatRequest {
controller:AbortController
providerData: Record<string, any> = {}

setChat (chat: Chat) {
async setChat (chat: Chat) {
this.chat = chat
this.chat.settings.model = this.getModel()
this.chat.settings.model = await this.getModel()
}

getChat (): Chat {
Expand Down Expand Up @@ -63,7 +63,7 @@ export class ChatRequest {
// TODO: Continue to break this method down to smaller chunks
const _this = this
const chat = getChat(_this.chat.id)
this.setChat(chat)
await this.setChat(chat)
const chatSettings = _this.chat.settings
const chatId = chat.id
const imagePromptDetect = /^\s*(please|can\s+you|will\s+you)*\s*(give|generate|create|show|build|design)\s+(me)*\s*(an|a|set|a\s+set\s+of)*\s*([0-9]+|one|two|three|four)*\s+(image|photo|picture|pic)s*\s*(for\s+me)*\s*(of|[^a-z0-9]+|about|that\s+has|showing|with|having|depicting)\s+[^a-z0-9]*(.*)$/i
Expand Down Expand Up @@ -100,7 +100,7 @@ export class ChatRequest {
}
}

const model = this.getModel()
const model = await this.getModel()
const modelDetail = getModelDetail(model)
const maxTokens = getModelMaxTokens(model)

Expand All @@ -117,7 +117,7 @@ export class ChatRequest {

// Inject hidden prompts if requested
// if (!opts.summaryRequest)
this.buildHiddenPromptPrefixMessages(filtered, true)
await this.buildHiddenPromptPrefixMessages(filtered, true)
const messagePayload = filtered
.filter(m => { if (m.skipOnce) { delete m.skipOnce; return false } return true })
.map(m => {
Expand Down Expand Up @@ -231,11 +231,11 @@ export class ChatRequest {
return chatResponse
}

getModel (): Model {
return this.chat.settings.model || getDefaultModel()
async getModel (): Promise<Model> {
return this.chat.settings.model || await getDefaultModel()
}

private buildHiddenPromptPrefixMessages (messages: Message[], insert:boolean = false): Message[] {
private async buildHiddenPromptPrefixMessages (messages: Message[], insert:boolean = false): Promise<Message[]> {
const chat = this.chat
const chatSettings = chat.settings
const hiddenPromptPrefix = mergeProfileFields(chatSettings, chatSettings.hiddenPromptPrefix).trim()
Expand Down Expand Up @@ -271,7 +271,7 @@ export class ChatRequest {
}
if (injectedPrompt) messages.pop()
}
const model = this.getModel()
const model = await this.getModel()
const messageDetail = getModelDetail(model)
if (getLeadPrompt(this.getChat()).trim() && messageDetail.type === 'chat') {
const lastMessage = (results.length && injectedPrompt && !isContinue) ? results[results.length - 1] : messages[messages.length - 1]
Expand All @@ -291,11 +291,12 @@ export class ChatRequest {
* Gets an estimate of how many extra tokens will be added that won't be part of the visible messages
* @param filtered
*/
private getTokenCountPadding (filtered: Message[], chat: Chat): number {
private async getTokenCountPadding (filtered: Message[], chat: Chat): Promise<number> {
const model = await this.getModel()
let result = 0
// add cost of hiddenPromptPrefix
result += this.buildHiddenPromptPrefixMessages(filtered)
.reduce((a, m) => a + countMessageTokens(m, this.getModel(), chat), 0)
result += (await this.buildHiddenPromptPrefixMessages(filtered))
.reduce((a, m) => a + countMessageTokens(m, model, chat), 0)
// more here eventually?
return result
}
Expand All @@ -306,7 +307,7 @@ export class ChatRequest {
const chatSettings = chat.settings
const chatId = chat.id
const reductionMode = chatSettings.continuousChat
const model = _this.getModel()
const model = await _this.getModel()
const maxTokens = getModelMaxTokens(model) // max tokens for model

const continueRequest = async () => {
Expand Down
52 changes: 27 additions & 25 deletions src/lib/ChatSettingsModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
let showSettingsModal = 0
let showProfileMenu:boolean = false
let profileFileInput
let defaultProfile = getDefaultProfileKey()
let defaultProfile
let isDefault = false

const settingsList = getChatSettingList()
Expand All @@ -63,6 +63,8 @@
onMount(async () => {
originalProfile = chatSettings && chatSettings.profile
originalSettings = chatSettings && JSON.parse(JSON.stringify(chatSettings))

defaultProfile = await getDefaultProfileKey()
})

afterUpdate(() => {
Expand Down Expand Up @@ -112,17 +114,17 @@
return profileUri
}

const cloneProfile = () => {
const cloneProfile = async () => {
showProfileMenu = false
const clone = JSON.parse(JSON.stringify(chat.settings))
const name = chat.settings.profileName
clone.profileName = newNameForProfile(name || '')
clone.profileName = await newNameForProfile(name || '')
clone.profile = null
try {
saveCustomProfile(clone)
await saveCustomProfile(clone)
chat.settings.profile = clone.profile
chat.settings.profileName = clone.profileName
applyProfile(chatId, clone.profile)
await applyProfile(chatId, clone.profile)
refreshSettings()
} catch (e) {
errorNotice('Error cloning profile:', e)
Expand All @@ -140,14 +142,14 @@
})
}

const deleteProfile = () => {
const deleteProfile = async () => {
showProfileMenu = false
try {
deleteCustomProfile(chatId, chat.settings.profile)
await deleteCustomProfile(chatId, chat.settings.profile)
chat.settings.profile = globalStore.defaultProfile || ''
saveChatStore()
setGlobalSettingValueByKey('lastProfile', chat.settings.profile)
applyProfile(chatId, chat.settings.profile)
await applyProfile(chatId, chat.settings.profile)
refreshSettings()
} catch (e) {
console.error(e)
Expand All @@ -161,42 +163,42 @@
refreshSettings()
}

const importProfileFromFile = (e) => {
const importProfileFromFile = async (e) => {
const image = e.target.files[0]
e.target.value = null
const reader = new FileReader()
reader.readAsText(image)
reader.onload = e => {
reader.onload = async (e) => {
const json = (e.target || {}).result as string
try {
const profile = JSON.parse(json)
profile.profileName = newNameForProfile(profile.profileName || '')
profile.profileName = await newNameForProfile(profile.profileName || '')
profile.profile = null
saveCustomProfile(profile)
await saveCustomProfile(profile)
refreshSettings()
} catch (e) {
errorNotice('Unable to import profile:', e)
}
}
}

const updateProfileSelectOptions = () => {
const updateProfileSelectOptions = async () => {
const profileSelect = getChatSettingObjectByKey('profile') as ChatSetting & SettingSelect
profileSelect.options = getProfileSelect()
chatDefaults.profile = getDefaultProfileKey()
profileSelect.options = await getProfileSelect()
chatDefaults.profile = await getDefaultProfileKey()
chatDefaults.max_tokens = getModelMaxTokens(chatSettings.model)
// const defaultProfile = globalStore.defaultProfile || profileSelect.options[0].value
defaultProfile = getDefaultProfileKey()
defaultProfile = await getDefaultProfileKey()
isDefault = defaultProfile === chatSettings.profile
}

const showSettings = async () => {
setDirty()
await setDirty()
// Show settings modal
showSettingsModal++

// Get profile options
updateProfileSelectOptions()
await updateProfileSelectOptions()

// Refresh settings modal
showSettingsModal++
Expand All @@ -215,20 +217,20 @@
setTimeout(() => sizeTextElements(profileChanged))
}

const saveProfile = () => {
const saveProfile = async () => {
showProfileMenu = false
try {
saveCustomProfile(chat.settings)
await saveCustomProfile(chat.settings)
refreshSettings()
} catch (e) {
errorNotice('Error saving profile:', e)
}
}

const startNewChat = () => {
const startNewChat = async () => {
const differentProfile = originalSettings.profile !== chatSettings.profile
// start new
const newChatId = addChat(chatSettings)
const newChatId = await addChat(chatSettings)
// restore original
if (differentProfile) {
chat.settings = originalSettings
Expand All @@ -247,13 +249,13 @@
: (x === y || ((x === undefined || x === null || x === false) && (y === undefined || y === null || y === false)))
}

const setDirty = (e:CustomEvent|undefined = undefined) => {
const setDirty = async (e:CustomEvent|undefined = undefined) => {
if (e) {
const setting = e.detail as ChatSetting
const key = setting.key
if (key === 'profile') return
}
const profile = getProfile(chatSettings.profile)
const profile = await getProfile(chatSettings.profile)
chatSettings.isDirty = !deepEqual(profile, chatSettings)
}

Expand Down Expand Up @@ -343,4 +345,4 @@
</div>
</div>

<input style="display:none" type="file" accept=".json" on:change={(e) => importProfileFromFile(e)} bind:this={profileFileInput} >
<input style="display:none" type="file" accept=".json" on:change={async (e) => await importProfileFromFile(e)} bind:this={profileFileInput} >
Loading