Change the model cache to local storage and support caching multiple models
This commit is contained in:
11
app.vue
11
app.vue
@@ -1,8 +1,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
||||||
import ApiKeyEditor from "./components/ApiKeyEditor";
|
|
||||||
|
|
||||||
const runtimeConfig = useRuntimeConfig()
|
const runtimeConfig = useRuntimeConfig()
|
||||||
|
const currentModel = useCurrentModel()
|
||||||
const fetchingResponse = ref(false)
|
const fetchingResponse = ref(false)
|
||||||
const fetchReply = async (message, parentMessageId) => {
|
const fetchReply = async (message, parentMessageId) => {
|
||||||
const ctrl = new AbortController()
|
const ctrl = new AbortController()
|
||||||
@@ -14,6 +14,7 @@ const fetchReply = async (message, parentMessageId) => {
|
|||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
|
model: currentModel.value,
|
||||||
message: message,
|
message: message,
|
||||||
parentMessageId: parentMessageId,
|
parentMessageId: parentMessageId,
|
||||||
conversationId: currentConversation.value.id
|
conversationId: currentConversation.value.id
|
||||||
@@ -114,18 +115,18 @@ onNuxtReady(() => {
|
|||||||
:theme="theme"
|
:theme="theme"
|
||||||
>
|
>
|
||||||
<v-navigation-drawer
|
<v-navigation-drawer
|
||||||
theme="dark"
|
|
||||||
permanent
|
permanent
|
||||||
>
|
>
|
||||||
<v-list>
|
<v-list>
|
||||||
<ModelNameEditor/>
|
<ClientOnly>
|
||||||
<ApiKeyEditor/>
|
<ModelDialog/>
|
||||||
|
</ClientOnly>
|
||||||
</v-list>
|
</v-list>
|
||||||
|
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
<v-list>
|
<v-list>
|
||||||
<!-- <v-list-item title="Clear conversations"></v-list-item>-->
|
<ApiKeyEditor/>
|
||||||
<v-list-item
|
<v-list-item
|
||||||
:prepend-icon="theme === 'light' ? 'dark_mode' : 'light_mode'"
|
:prepend-icon="theme === 'light' ? 'dark_mode' : 'light_mode'"
|
||||||
:title="(theme === 'light' ? 'Dark' : 'Light') + ' mode'"
|
:title="(theme === 'light' ? 'Dark' : 'Light') + ' mode'"
|
||||||
|
|||||||
121
components/ModelDialog.vue
Normal file
121
components/ModelDialog.vue
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
<template>
|
||||||
|
<v-dialog
|
||||||
|
v-model="dialog"
|
||||||
|
persistent
|
||||||
|
scrollable
|
||||||
|
>
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<v-list-item
|
||||||
|
rounded="xl"
|
||||||
|
v-bind="props"
|
||||||
|
prepend-icon="smart_toy"
|
||||||
|
active
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
{{ currentModel }}
|
||||||
|
</v-list-item>
|
||||||
|
</template>
|
||||||
|
<v-card>
|
||||||
|
<v-card-title>
|
||||||
|
<span class="text-h5">OpenAI Models</span>
|
||||||
|
<div>
|
||||||
|
About the models:
|
||||||
|
<a target="_blank" href="https://platform.openai.com/docs/models/overview">https://platform.openai.com/docs/models/overview</a>
|
||||||
|
</div>
|
||||||
|
</v-card-title>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-card-text>
|
||||||
|
<div
|
||||||
|
v-for="(model, index) in models"
|
||||||
|
:key="index"
|
||||||
|
class="d-flex align-center"
|
||||||
|
>
|
||||||
|
<v-switch
|
||||||
|
v-model="currentModel"
|
||||||
|
color="primary"
|
||||||
|
:label="model"
|
||||||
|
:value="model"
|
||||||
|
hide-details
|
||||||
|
></v-switch>
|
||||||
|
<v-icon
|
||||||
|
icon="delete_outline"
|
||||||
|
@click="removeModel(index)"
|
||||||
|
></v-icon>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<v-btn
|
||||||
|
variant="outlined"
|
||||||
|
v-if="!showInputModel"
|
||||||
|
@click="showInputModel = true"
|
||||||
|
>
|
||||||
|
Add a model
|
||||||
|
</v-btn>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="d-flex align-center"
|
||||||
|
>
|
||||||
|
<v-text-field
|
||||||
|
v-model="inputModel"
|
||||||
|
label="Enter a model name"
|
||||||
|
hide-details
|
||||||
|
></v-text-field>
|
||||||
|
<v-btn class="ml-3" icon="done" @click="createNewModel"></v-btn>
|
||||||
|
<v-btn class="ml-3" icon="close" @click="showInputModel = false"></v-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</v-card-text>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-alert
|
||||||
|
v-if="warningText"
|
||||||
|
density="compact"
|
||||||
|
type="warning"
|
||||||
|
:text="warningText"
|
||||||
|
></v-alert>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn
|
||||||
|
color="primary"
|
||||||
|
@click="save"
|
||||||
|
>
|
||||||
|
Save & Close
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const dialog = ref(false)
|
||||||
|
const models = useModels()
|
||||||
|
const currentModel = useCurrentModel()
|
||||||
|
const inputModel = ref('')
|
||||||
|
const showInputModel = ref(false)
|
||||||
|
const warningText = ref(null)
|
||||||
|
const showWarning = (text) => {
|
||||||
|
warningText.value = text
|
||||||
|
setTimeout(() => {
|
||||||
|
warningText.value = null
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
const createNewModel = () => {
|
||||||
|
models.value.push(inputModel.value)
|
||||||
|
inputModel.value = ''
|
||||||
|
showInputModel.value = false
|
||||||
|
}
|
||||||
|
const removeModel = (index) => {
|
||||||
|
if (currentModel.value === models.value[index]) {
|
||||||
|
currentModel.value = null
|
||||||
|
}
|
||||||
|
models.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
const save = async () => {
|
||||||
|
console.log(currentModel.value)
|
||||||
|
if (!currentModel.value) {
|
||||||
|
showWarning('Please select at least one model.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setModels(models.value)
|
||||||
|
setCurrentModel(currentModel.value)
|
||||||
|
dialog.value = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
<template>
|
|
||||||
<v-list-item v-if="showModelNameEditor">
|
|
||||||
<v-text-field
|
|
||||||
label="Model name"
|
|
||||||
v-model="modelNameInput"
|
|
||||||
hide-details
|
|
||||||
variant="outlined"
|
|
||||||
></v-text-field>
|
|
||||||
<template v-slot:append>
|
|
||||||
<v-icon icon="done" size="small" @click="submitModelName"></v-icon>
|
|
||||||
<v-icon icon="close" size="small" @click="showModelNameEditor = false"></v-icon>
|
|
||||||
</template>
|
|
||||||
</v-list-item>
|
|
||||||
<v-list-item
|
|
||||||
v-else
|
|
||||||
:title="currentModelName"
|
|
||||||
subtitle="Current model"
|
|
||||||
>
|
|
||||||
<template v-slot:append>
|
|
||||||
<v-icon icon="edit" @click="showModelNameEditor = true"></v-icon>
|
|
||||||
</template>
|
|
||||||
</v-list-item>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
const { data } = await useFetch('/api/settings/?key=modelName')
|
|
||||||
const currentModelName = ref(data.value.data)
|
|
||||||
const modelNameInput = ref(currentModelName.value)
|
|
||||||
const showModelNameEditor = ref(false)
|
|
||||||
const submitModelName = async () => {
|
|
||||||
try {
|
|
||||||
const { data } = await useFetch('/api/settings', {
|
|
||||||
method: 'POST',
|
|
||||||
body: { key: 'modelName', value: modelNameInput.value }
|
|
||||||
})
|
|
||||||
if (data.value.status === 'success') {
|
|
||||||
currentModelName.value = modelNameInput.value
|
|
||||||
showModelNameEditor.value = false
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
3
composables/states.js
Normal file
3
composables/states.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
export const useModels = () => useState('models', () => getStoredModels())
|
||||||
|
export const useCurrentModel = () => useState('currentModel', () => getCurrentModel())
|
||||||
@@ -38,7 +38,6 @@ export default defineEventHandler(async (event) => {
|
|||||||
'Connection': 'keep-alive'
|
'Connection': 'keep-alive'
|
||||||
})
|
})
|
||||||
|
|
||||||
const modelName = await getSetting('modelName')
|
|
||||||
const apiKey = await getSetting('apiKey')
|
const apiKey = await getSetting('apiKey')
|
||||||
|
|
||||||
if (!apiKey) {
|
if (!apiKey) {
|
||||||
@@ -57,7 +56,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
modelOptions: {
|
modelOptions: {
|
||||||
// The model is set to text-chat-davinci-002-20221122 by default, but you can override
|
// The model is set to text-chat-davinci-002-20221122 by default, but you can override
|
||||||
// it and any other parameters here
|
// it and any other parameters here
|
||||||
model: modelName,
|
model: body.model,
|
||||||
},
|
},
|
||||||
// (Optional) Set custom instructions instead of "You are ChatGPT...".
|
// (Optional) Set custom instructions instead of "You are ChatGPT...".
|
||||||
// promptPrefix: 'You are Bob, a cowboy in Western times...',
|
// promptPrefix: 'You are Bob, a cowboy in Western times...',
|
||||||
|
|||||||
5
utils/enums.js
Normal file
5
utils/enums.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
export const STORAGE_KEY = {
|
||||||
|
OPENAI_MODELS: 'openai_models',
|
||||||
|
CURRENT_OPENAI_MODEL: 'current_openai_model',
|
||||||
|
}
|
||||||
43
utils/localStorage.js
Normal file
43
utils/localStorage.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import {useCurrentModel, useModels} from "~/composables/states";
|
||||||
|
|
||||||
|
const get = (key) => {
|
||||||
|
let val = localStorage.getItem(key)
|
||||||
|
if (val) {
|
||||||
|
val = JSON.parse(val)
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
const set = (key, val) => {
|
||||||
|
localStorage.setItem(key, JSON.stringify(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_OPENAI_MODEL = 'text-davinci-003'
|
||||||
|
|
||||||
|
export const setModels = (val) => {
|
||||||
|
const models = useModels()
|
||||||
|
set(STORAGE_KEY.OPENAI_MODELS, val)
|
||||||
|
models.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getStoredModels = () => {
|
||||||
|
let models = get(STORAGE_KEY.OPENAI_MODELS)
|
||||||
|
if (!models) {
|
||||||
|
models = [DEFAULT_OPENAI_MODEL]
|
||||||
|
}
|
||||||
|
return models
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setCurrentModel = (val) => {
|
||||||
|
const model = useCurrentModel()
|
||||||
|
set(STORAGE_KEY.CURRENT_OPENAI_MODEL, val)
|
||||||
|
model.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCurrentModel = () => {
|
||||||
|
let model = get(STORAGE_KEY.CURRENT_OPENAI_MODEL)
|
||||||
|
if (!model) {
|
||||||
|
model = DEFAULT_OPENAI_MODEL
|
||||||
|
}
|
||||||
|
return model
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user