Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d89d1e288d | ||
|
|
cd89d11d0b | ||
|
|
cf0053a060 | ||
|
|
019da4399e | ||
|
|
044961bb01 | ||
|
|
2374c81edb | ||
|
|
699760713e | ||
|
|
d75413cc49 | ||
|
|
8175f199d2 | ||
|
|
f8c2f396c1 | ||
|
|
8217647df8 | ||
|
|
288c9eeeca | ||
|
|
4d09ff7c8a | ||
|
|
5fa059017c | ||
|
|
323f10844b | ||
|
|
ee035390db | ||
|
|
be743bf799 | ||
|
|
a59f84f2bf | ||
|
|
ed0cf2997d | ||
|
|
7f00c74097 | ||
|
|
f007417fa4 | ||
|
|
27c5e2a3ac | ||
|
|
e90dc0c12b | ||
|
|
837fd8c9ff | ||
|
|
ce0b1004f3 |
53
README.md
53
README.md
@@ -1,14 +1,29 @@
|
|||||||
<p align="center">
|
<div align="center">
|
||||||
<img alt="demo" src="./demos/demo.gif?v=1">
|
<h1>ChatGPT UI</h1>
|
||||||
</p>
|
</div>
|
||||||
|
|
||||||
[English](./README.md) | [中文](./docs/zh/README.md)
|
[English](./README.md) | [中文](./docs/zh/README.md)
|
||||||
|
|
||||||
# ChatGPT UI
|
|
||||||
|
|
||||||
A ChatGPT web client that supports multiple users, multiple database connections for persistent data storage, supports i18n. Provides Docker images and quick deployment scripts.
|
A ChatGPT web client that supports multiple users, multiple database connections for persistent data storage, supports i18n. Provides Docker images and quick deployment scripts.
|
||||||
|
|
||||||
|
https://user-images.githubusercontent.com/46235412/227156264-ca17ab17-999b-414f-ab06-3f75b5235bfe.mp4
|
||||||
|
|
||||||
|
|
||||||
## 📢Updates
|
## 📢Updates
|
||||||
|
|
||||||
|
<details open>
|
||||||
|
<summary><strong>2023-03-27</strong></summary>
|
||||||
|
🚀 Support gpt-4 model. You can select the model in the "Model Parameters" of the front-end.
|
||||||
|
The GPT-4 model requires whitelist access from OpenAI.
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details open>
|
||||||
|
<summary><strong>2023-03-23</strong></summary>
|
||||||
|
Added web search capability to generate more relevant and up-to-date answers from ChatGPT!
|
||||||
|
This feature is off by default, you can turn it on in `Chat->Settings` in the admin panel, there is a record `open_web_search` in Settings, set its value to True.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
<details open>
|
<details open>
|
||||||
<summary><strong>2023-03-15</strong></summary>
|
<summary><strong>2023-03-15</strong></summary>
|
||||||
|
|
||||||
@@ -17,17 +32,7 @@ Add "open_registration" setting option in the admin panel to control whether use
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details open>
|
<details>
|
||||||
<summary><strong>2023-03-10</strong></summary>
|
|
||||||
|
|
||||||
Add 2 environment variables to control the typewriter effect:
|
|
||||||
|
|
||||||
- `NUXT_PUBLIC_TYPEWRITER=true` to enable/disable the typewriter effect
|
|
||||||
- `NUXT_PUBLIC_TYPEWRITER_DELAY=50` to set the delay time for each character in milliseconds.
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details open>
|
|
||||||
<summary><strong>2023-03-04</strong></summary>
|
<summary><strong>2023-03-04</strong></summary>
|
||||||
|
|
||||||
**Update to the latest official chat model** `gpt-3.5-turbo`
|
**Update to the latest official chat model** `gpt-3.5-turbo`
|
||||||
@@ -90,9 +95,6 @@ services:
|
|||||||
image: wongsaang/chatgpt-ui-client:latest
|
image: wongsaang/chatgpt-ui-client:latest
|
||||||
environment:
|
environment:
|
||||||
- SERVER_DOMAIN=http://backend-web-server
|
- SERVER_DOMAIN=http://backend-web-server
|
||||||
- NUXT_PUBLIC_APP_NAME='ChatGPT UI' # App name
|
|
||||||
- NUXT_PUBLIC_TYPEWRITER=true # Enable typewriter effect, default is false
|
|
||||||
- NUXT_PUBLIC_TYPEWRITER_DELAY=100 # Typewriter effect delay time, default is 50ms
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend-web-server
|
- backend-web-server
|
||||||
ports:
|
ports:
|
||||||
@@ -115,6 +117,7 @@ services:
|
|||||||
# - EMAIL_HOST_USER=
|
# - EMAIL_HOST_USER=
|
||||||
# - EMAIL_HOST_PASSWORD=
|
# - EMAIL_HOST_PASSWORD=
|
||||||
# - EMAIL_USE_TLS=True
|
# - EMAIL_USE_TLS=True
|
||||||
|
# - EMAIL_FROM=no-reply@example.com #Default sender email address
|
||||||
ports:
|
ports:
|
||||||
- '8000:8000'
|
- '8000:8000'
|
||||||
networks:
|
networks:
|
||||||
@@ -156,6 +159,16 @@ Before you can start chatting, you need to add an OpenAI API key. In the Setting
|
|||||||
|
|
||||||
Now you can access the web client at `http(s)://your.domain` or `http://123.123.123.123` to start chatting.
|
Now you can access the web client at `http(s)://your.domain` or `http://123.123.123.123` to start chatting.
|
||||||
|
|
||||||
|
## Donation
|
||||||
|
|
||||||
|
> If it is helpful to you, it is also helping me.
|
||||||
|
|
||||||
|
If you want to support me, Buy me a coffee ❤️ [https://www.buymeacoffee.com/WongSaang](https://www.buymeacoffee.com/WongSaang)
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img height="150" src="https://github.com/WongSaang/chatgpt-ui/blob/main/demos/bmc_qr.png?raw=true"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
@@ -181,4 +194,4 @@ Build the application for production:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn build
|
yarn build
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
|
||||||
const dialog = ref(false)
|
const dialog = ref(false)
|
||||||
const currentModel = useCurrentModel()
|
const currentModel = useCurrentModel()
|
||||||
const availableModels = [
|
const availableModels = [
|
||||||
DEFAULT_MODEL.name
|
'gpt-3.5-turbo',
|
||||||
|
'gpt-4'
|
||||||
]
|
]
|
||||||
|
const currentModelDefault = ref(MODELS[currentModel.value.name])
|
||||||
|
|
||||||
watch(currentModel, (newVal, oldVal) => {
|
watch(currentModel, (newVal, oldVal) => {
|
||||||
|
currentModelDefault.value = MODELS[newVal.name]
|
||||||
saveCurrentModel(newVal)
|
saveCurrentModel(newVal)
|
||||||
}, { deep: true })
|
}, { deep: true })
|
||||||
|
|
||||||
@@ -83,7 +87,7 @@ watch(currentModel, (newVal, oldVal) => {
|
|||||||
single-line
|
single-line
|
||||||
density="compact"
|
density="compact"
|
||||||
type="number"
|
type="number"
|
||||||
max="2048"
|
:max="currentModelDefault.total_tokens"
|
||||||
step="1"
|
step="1"
|
||||||
style="width: 100px"
|
style="width: 100px"
|
||||||
class="flex-grow-0"
|
class="flex-grow-0"
|
||||||
@@ -93,7 +97,7 @@ watch(currentModel, (newVal, oldVal) => {
|
|||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-slider
|
<v-slider
|
||||||
v-model="currentModel.max_tokens"
|
v-model="currentModel.max_tokens"
|
||||||
:max="2048"
|
:max="currentModelDefault.total_tokens"
|
||||||
:step="1"
|
:step="1"
|
||||||
hide-details
|
hide-details
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
const menu = ref(false)
|
const menu = ref(false)
|
||||||
const prompts = ref([])
|
const prompts = ref([])
|
||||||
const editingPrompt = ref(null)
|
const editingPrompt = ref(null)
|
||||||
|
const newTitlePrompt = ref(null)
|
||||||
const newPrompt = ref('')
|
const newPrompt = ref('')
|
||||||
const submittingNewPrompt = ref(false)
|
const submittingNewPrompt = ref(false)
|
||||||
const promptInputErrorMessage = ref('')
|
const promptInputErrorMessage = ref('')
|
||||||
@@ -24,11 +25,13 @@ const addPrompt = async () => {
|
|||||||
const { data, error } = await useAuthFetch('/api/chat/prompts/', {
|
const { data, error } = await useAuthFetch('/api/chat/prompts/', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
|
title: newTitlePrompt.value,
|
||||||
prompt: newPrompt.value
|
prompt: newPrompt.value
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
if (!error.value) {
|
if (!error.value) {
|
||||||
prompts.value.push(data.value)
|
prompts.value.push(data.value)
|
||||||
|
newTitlePrompt.value = null
|
||||||
newPrompt.value = ''
|
newPrompt.value = ''
|
||||||
}
|
}
|
||||||
submittingNewPrompt.value = false
|
submittingNewPrompt.value = false
|
||||||
@@ -43,6 +46,7 @@ const updatePrompt = async (index) => {
|
|||||||
const { data, error } = await useAuthFetch(`/api/chat/prompts/${editingPrompt.value.id}/`, {
|
const { data, error } = await useAuthFetch(`/api/chat/prompts/${editingPrompt.value.id}/`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
|
title: editingPrompt.value.title,
|
||||||
prompt: editingPrompt.value.prompt
|
prompt: editingPrompt.value.prompt
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -96,10 +100,12 @@ onMounted( () => {
|
|||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-btn
|
<v-btn
|
||||||
v-bind="props"
|
v-bind="props"
|
||||||
icon="speaker_notes"
|
icon
|
||||||
title="Common prompts"
|
>
|
||||||
class="mr-3"
|
<v-icon
|
||||||
></v-btn>
|
icon="speaker_notes"
|
||||||
|
></v-icon>
|
||||||
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<v-container>
|
<v-container>
|
||||||
@@ -108,7 +114,7 @@ onMounted( () => {
|
|||||||
max-width="500"
|
max-width="500"
|
||||||
>
|
>
|
||||||
<v-card-title>
|
<v-card-title>
|
||||||
<span class="headline">Frequently prompts</span>
|
<span class="headline">{{ $t('frequentlyPrompts') }}</span>
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
|
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
@@ -125,35 +131,47 @@ onMounted( () => {
|
|||||||
>
|
>
|
||||||
<v-list-item
|
<v-list-item
|
||||||
active-color="primary"
|
active-color="primary"
|
||||||
rounded="xl"
|
|
||||||
v-if="editingPrompt && editingPrompt.id === prompt.id"
|
v-if="editingPrompt && editingPrompt.id === prompt.id"
|
||||||
>
|
>
|
||||||
<v-textarea
|
<div class="d-flex flex-row" :style="{ marginTop: '5px' }">
|
||||||
rows="2"
|
<div class="flex-grow-1">
|
||||||
v-model="editingPrompt.prompt"
|
<v-text-field
|
||||||
:loading="editingPrompt.updating"
|
v-model="editingPrompt.title"
|
||||||
variant="underlined"
|
:loading="editingPrompt.updating"
|
||||||
hide-details
|
:label="$t('titlePrompt')"
|
||||||
density="compact"
|
variant="underlined"
|
||||||
>
|
density="compact"
|
||||||
<template v-slot:append>
|
hide-details
|
||||||
<div class="d-flex flex-column">
|
>
|
||||||
<v-btn
|
</v-text-field>
|
||||||
icon="done"
|
<v-textarea
|
||||||
variant="text"
|
rows="2"
|
||||||
:loading="editingPrompt.updating"
|
v-model="editingPrompt.prompt"
|
||||||
@click="updatePrompt(idx)"
|
:loading="editingPrompt.updating"
|
||||||
>
|
variant="underlined"
|
||||||
</v-btn>
|
density="compact"
|
||||||
<v-btn
|
hide-details
|
||||||
icon="close"
|
>
|
||||||
variant="text"
|
</v-textarea>
|
||||||
@click="cancelEditPrompt()"
|
</div>
|
||||||
>
|
<div>
|
||||||
</v-btn>
|
<div class="d-flex flex-column">
|
||||||
</div>
|
<v-btn
|
||||||
</template>
|
icon="done"
|
||||||
</v-textarea>
|
variant="text"
|
||||||
|
:loading="editingPrompt.updating"
|
||||||
|
@click="updatePrompt(idx)"
|
||||||
|
>
|
||||||
|
</v-btn>
|
||||||
|
<v-btn
|
||||||
|
icon="close"
|
||||||
|
variant="text"
|
||||||
|
@click="cancelEditPrompt()"
|
||||||
|
>
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item
|
<v-list-item
|
||||||
v-if="!editingPrompt || editingPrompt.id !== prompt.id"
|
v-if="!editingPrompt || editingPrompt.id !== prompt.id"
|
||||||
@@ -161,7 +179,7 @@ onMounted( () => {
|
|||||||
active-color="primary"
|
active-color="primary"
|
||||||
@click="selectPrompt(prompt)"
|
@click="selectPrompt(prompt)"
|
||||||
>
|
>
|
||||||
<v-list-item-title>{{ prompt.prompt }}</v-list-item-title>
|
<v-list-item-title>{{ prompt.title ? prompt.title : prompt.prompt }}</v-list-item-title>
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
<v-btn
|
<v-btn
|
||||||
icon="edit"
|
icon="edit"
|
||||||
@@ -182,6 +200,25 @@ onMounted( () => {
|
|||||||
</v-list-item>
|
</v-list-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<v-list-item
|
||||||
|
active-color="primary"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="pt-3"
|
||||||
|
>
|
||||||
|
<v-text-field
|
||||||
|
rows="1"
|
||||||
|
v-model="newTitlePrompt"
|
||||||
|
:label="$t('titlePrompt')"
|
||||||
|
variant="outlined"
|
||||||
|
density="compact"
|
||||||
|
hide-details
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
</v-text-field>
|
||||||
|
</div>
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
<v-list-item
|
<v-list-item
|
||||||
active-color="primary"
|
active-color="primary"
|
||||||
>
|
>
|
||||||
@@ -191,7 +228,7 @@ onMounted( () => {
|
|||||||
<v-textarea
|
<v-textarea
|
||||||
rows="2"
|
rows="2"
|
||||||
v-model="newPrompt"
|
v-model="newPrompt"
|
||||||
label="Add a new prompt"
|
:label="$t('addNewPrompt')"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
density="compact"
|
density="compact"
|
||||||
:error-messages="promptInputErrorMessage"
|
:error-messages="promptInputErrorMessage"
|
||||||
@@ -209,7 +246,7 @@ onMounted( () => {
|
|||||||
@click="addPrompt()"
|
@click="addPrompt()"
|
||||||
>
|
>
|
||||||
<v-icon icon="add"></v-icon>
|
<v-icon icon="add"></v-icon>
|
||||||
Add prompt
|
{{ $t('addPrompt') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
export const useModels = () => useState('models', () => getStoredModels())
|
// export const useModels = () => useState('models', () => getStoredModels())
|
||||||
|
|
||||||
export const useCurrentModel = () => useState('currentModel', () => getCurrentModel())
|
export const useCurrentModel = () => useState('currentModel', () => getCurrentModel())
|
||||||
|
|
||||||
@@ -7,4 +7,6 @@ export const useApiKey = () => useState('apiKey', () => getStoredApiKey())
|
|||||||
|
|
||||||
export const useConversion = () => useState('conversion', () => getDefaultConversionData())
|
export const useConversion = () => useState('conversion', () => getDefaultConversionData())
|
||||||
|
|
||||||
export const useConversions = () => useState('conversions', () => [])
|
export const useConversions = () => useState('conversions', () => [])
|
||||||
|
|
||||||
|
export const useSettings = () => useState('settings', () => {})
|
||||||
BIN
demos/bmc_qr.png
Normal file
BIN
demos/bmc_qr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
BIN
demos/demo.mp4
Normal file
BIN
demos/demo.mp4
Normal file
Binary file not shown.
16
docker-compose.dev.yml
Normal file
16
docker-compose.dev.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
client:
|
||||||
|
platform: linux/x86_64
|
||||||
|
build: .
|
||||||
|
environment:
|
||||||
|
SERVER_DOMAIN: http://web-server
|
||||||
|
ports:
|
||||||
|
- '${CLIENT_PORT:-8080}:80'
|
||||||
|
networks:
|
||||||
|
- chatgpt_network
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
networks:
|
||||||
|
chatgpt_network:
|
||||||
|
external: True
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
version: '3'
|
version: '3'
|
||||||
services:
|
services:
|
||||||
client:
|
client:
|
||||||
|
platform: linux/x86_64
|
||||||
image: wongsaang/chatgpt-ui-client:latest
|
image: wongsaang/chatgpt-ui-client:latest
|
||||||
environment:
|
environment:
|
||||||
- SERVER_DOMAIN=http://backend-web-server
|
- SERVER_DOMAIN=http://backend-web-server
|
||||||
- NUXT_PUBLIC_APP_NAME='ChatGPT UI'
|
|
||||||
- NUXT_PUBLIC_TYPEWRITER=true
|
|
||||||
- NUXT_PUBLIC_TYPEWRITER_DELAY=100
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend-web-server
|
- backend-web-server
|
||||||
ports:
|
ports:
|
||||||
@@ -15,6 +13,7 @@ services:
|
|||||||
- chatgpt_ui_network
|
- chatgpt_ui_network
|
||||||
restart: always
|
restart: always
|
||||||
backend-wsgi-server:
|
backend-wsgi-server:
|
||||||
|
platform: linux/x86_64
|
||||||
image: wongsaang/chatgpt-ui-wsgi-server:latest
|
image: wongsaang/chatgpt-ui-wsgi-server:latest
|
||||||
environment:
|
environment:
|
||||||
- APP_DOMAIN=${APP_DOMAIN:-localhost:9000}
|
- APP_DOMAIN=${APP_DOMAIN:-localhost:9000}
|
||||||
@@ -29,12 +28,14 @@ services:
|
|||||||
# - EMAIL_HOST_USER=
|
# - EMAIL_HOST_USER=
|
||||||
# - EMAIL_HOST_PASSWORD=
|
# - EMAIL_HOST_PASSWORD=
|
||||||
# - EMAIL_USE_TLS=True
|
# - EMAIL_USE_TLS=True
|
||||||
|
# - EMAIL_FROM=no-reply@example.com #Default sender email address
|
||||||
ports:
|
ports:
|
||||||
- '${WSGI_PORT:-8000}:8000'
|
- '${WSGI_PORT:-8000}:8000'
|
||||||
networks:
|
networks:
|
||||||
- chatgpt_ui_network
|
- chatgpt_ui_network
|
||||||
restart: always
|
restart: always
|
||||||
backend-web-server:
|
backend-web-server:
|
||||||
|
platform: linux/x86_64
|
||||||
image: wongsaang/chatgpt-ui-web-server:latest
|
image: wongsaang/chatgpt-ui-web-server:latest
|
||||||
environment:
|
environment:
|
||||||
- BACKEND_URL=http://backend-wsgi-server:8000
|
- BACKEND_URL=http://backend-wsgi-server:8000
|
||||||
@@ -48,4 +49,4 @@ services:
|
|||||||
|
|
||||||
networks:
|
networks:
|
||||||
chatgpt_ui_network:
|
chatgpt_ui_network:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|||||||
@@ -1,14 +1,27 @@
|
|||||||
<p align="center">
|
<div align="center">
|
||||||
<img alt="demo" src="../../demos/demo.gif?v=1">
|
<h1>ChatGPT UI</h1>
|
||||||
</p>
|
</div>
|
||||||
|
|
||||||
[English](../../README.md) | [中文](./docs/zh/README.md)
|
[English](../../README.md) | [中文](./docs/zh/README.md)
|
||||||
|
|
||||||
# ChatGPT UI
|
|
||||||
|
|
||||||
ChatGPT Web 客户端,支持多用户,支持 Mysql、PostgreSQL 等多种数据库连接进行数据持久化存储,支持多语言。提供 Docker 镜像和快速部署脚本。
|
ChatGPT Web 客户端,支持多用户,支持 Mysql、PostgreSQL 等多种数据库连接进行数据持久化存储,支持多语言。提供 Docker 镜像和快速部署脚本。
|
||||||
|
|
||||||
|
https://user-images.githubusercontent.com/46235412/227156264-ca17ab17-999b-414f-ab06-3f75b5235bfe.mp4
|
||||||
|
|
||||||
|
|
||||||
## 📢 更新
|
## 📢 更新
|
||||||
|
|
||||||
|
<details open>
|
||||||
|
<summary><strong>2023-03-27</strong></summary>
|
||||||
|
🚀 支持 gpt-4 模型。你可以在前端的“模型参数”中选择模型,gpt-4 模型需要通过 openai 的白名单才能使用。
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details open>
|
||||||
|
<summary><strong>2023-03-23</strong></summary>
|
||||||
|
增加网页搜索能力,使得 ChatGPT 生成的回答更与时俱进!
|
||||||
|
该功能默认处于关闭状态,你可以在管理后台的 `Chat->Settings` 中开启它,在 Settings 中有一个 `open_web_search` 的记录,把它的值设置为 True。
|
||||||
|
</details>
|
||||||
|
|
||||||
<details open>
|
<details open>
|
||||||
<summary><strong>2023-03-15</strong></summary>
|
<summary><strong>2023-03-15</strong></summary>
|
||||||
|
|
||||||
@@ -16,17 +29,7 @@ ChatGPT Web 客户端,支持多用户,支持 Mysql、PostgreSQL 等多种数
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details open>
|
<details>
|
||||||
<summary><strong>2023-03-10</strong></summary>
|
|
||||||
|
|
||||||
增加 2 个环境变量来控制打字机效果, 详见下方 docker-compose 配置的环境变量说明
|
|
||||||
|
|
||||||
- `NUXT_PUBLIC_TYPEWRITER` 是否开启打字机效果
|
|
||||||
- `NUXT_PUBLIC_TYPEWRITER_DELAY` 每个字的延迟时间,单位:毫秒
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details open>
|
|
||||||
<summary><strong>2023-03-04</strong></summary>
|
<summary><strong>2023-03-04</strong></summary>
|
||||||
|
|
||||||
**使用最新的官方聊天模型** `gpt-3.5-turbo`
|
**使用最新的官方聊天模型** `gpt-3.5-turbo`
|
||||||
@@ -88,9 +91,6 @@ services:
|
|||||||
image: wongsaang/chatgpt-ui-client:latest
|
image: wongsaang/chatgpt-ui-client:latest
|
||||||
environment:
|
environment:
|
||||||
- SERVER_DOMAIN=http://backend-web-server
|
- SERVER_DOMAIN=http://backend-web-server
|
||||||
- NUXT_PUBLIC_APP_NAME='ChatGPT UI' # App 名称,默认为 ChatGPT UI
|
|
||||||
- NUXT_PUBLIC_TYPEWRITER=true # 是否启用打字机效果,默认关闭
|
|
||||||
- NUXT_PUBLIC_TYPEWRITER_DELAY=100 # 打字机效果的延迟时间,默认 50毫秒
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend-web-server
|
- backend-web-server
|
||||||
ports:
|
ports:
|
||||||
@@ -113,6 +113,7 @@ services:
|
|||||||
# - EMAIL_HOST_USER=
|
# - EMAIL_HOST_USER=
|
||||||
# - EMAIL_HOST_PASSWORD=
|
# - EMAIL_HOST_PASSWORD=
|
||||||
# - EMAIL_USE_TLS=True
|
# - EMAIL_USE_TLS=True
|
||||||
|
# - EMAIL_FROM=no-reply@example.com #默认发件邮箱地址
|
||||||
ports:
|
ports:
|
||||||
- '8000:8000'
|
- '8000:8000'
|
||||||
networks:
|
networks:
|
||||||
@@ -154,6 +155,16 @@ networks:
|
|||||||
现在可以访问客户端地址 `http(s)://your.domain` / `http://123.123.123.123` 开始聊天。
|
现在可以访问客户端地址 `http(s)://your.domain` / `http://123.123.123.123` 开始聊天。
|
||||||
|
|
||||||
|
|
||||||
|
## 续杯咖啡
|
||||||
|
|
||||||
|
> 如果对您有帮助,也是在帮助我自己.
|
||||||
|
|
||||||
|
如果你想支持我,给我续杯咖啡吧 ❤️ [https://www.buymeacoffee.com/WongSaang](https://www.buymeacoffee.com/WongSaang)
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img height="150" src="https://github.com/WongSaang/chatgpt-ui/blob/main/demos/bmc_qr.png?raw=true"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
@@ -179,4 +190,4 @@ Build the application for production:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn build
|
yarn build
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
"saveAndClose": "Save & Close",
|
"saveAndClose": "Save & Close",
|
||||||
"pleaseSelectAtLeastOneModelDot": "Please select at least one model.",
|
"pleaseSelectAtLeastOneModelDot": "Please select at least one model.",
|
||||||
"writeAMessage": "Write a message",
|
"writeAMessage": "Write a message",
|
||||||
|
"frequentlyPrompts": "Frequently prompts",
|
||||||
|
"addPrompt": "Add prompt",
|
||||||
|
"titlePrompt": "Title",
|
||||||
|
"addNewPrompt": "Add a new prompt",
|
||||||
"pressEnterToSendYourMessageOrShiftEnterToAddANewLine": "Press Enter to send your message or Shift+Enter to add a new line",
|
"pressEnterToSendYourMessageOrShiftEnterToAddANewLine": "Press Enter to send your message or Shift+Enter to add a new line",
|
||||||
"lightMode": "Light Mode",
|
"lightMode": "Light Mode",
|
||||||
"darkMode": "Dark Mode",
|
"darkMode": "Dark Mode",
|
||||||
@@ -34,6 +38,8 @@
|
|||||||
"copied": "Copied",
|
"copied": "Copied",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"signOut": "Sign out",
|
"signOut": "Sign out",
|
||||||
|
"webSearch": "Web Search",
|
||||||
|
"webSearchDefaultPrompt": "Web search results:\n\n[web_results]\nCurrent date: [current_date]\n\nInstructions: Using the provided web search results, write a comprehensive reply to the given query. Make sure to cite results using [[number](URL)] notation after the reference. If the provided search results refer to multiple subjects with the same name, write separate answers for each subject.\nQuery: [query]",
|
||||||
"welcomeScreen": {
|
"welcomeScreen": {
|
||||||
"introduction1": "is an unofficial client for ChatGPT, but uses the official OpenAI API.",
|
"introduction1": "is an unofficial client for ChatGPT, but uses the official OpenAI API.",
|
||||||
"introduction2": "You will need an OpenAI API Key before you can use this client.",
|
"introduction2": "You will need an OpenAI API Key before you can use this client.",
|
||||||
|
|||||||
65
lang/ru-RU.json
Normal file
65
lang/ru-RU.json
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"welcomeTo": "Добро пожаловать в",
|
||||||
|
"language": "Язык",
|
||||||
|
"setApiKey": "Установить ключ API",
|
||||||
|
"setOpenAIApiKey": "Установить ключ API OpenAI",
|
||||||
|
"openAIApiKey": "Ключ API OpenAI",
|
||||||
|
"getAKey": "Получить ключ",
|
||||||
|
"openAIModels": "Модели OpenAI",
|
||||||
|
"aboutTheModels": "О моделях",
|
||||||
|
"saveAndClose": "Сохранить & Закрыть",
|
||||||
|
"pleaseSelectAtLeastOneModelDot": "Выберите хотя бы одну модель.",
|
||||||
|
"writeAMessage": "Напишите сообщение",
|
||||||
|
"frequentlyPrompts": "Список подсказок",
|
||||||
|
"addPrompt": "Добавить подсказку",
|
||||||
|
"titlePrompt": "Заголовок",
|
||||||
|
"addNewPrompt": "Добавитьте новую подсказку",
|
||||||
|
"pressEnterToSendYourMessageOrShiftEnterToAddANewLine": "Нажмите Enter, чтобы отправить сообщение, или Shift+Enter, чтобы добавить новую строку.",
|
||||||
|
"lightMode": "Светлая",
|
||||||
|
"darkMode": "Темная",
|
||||||
|
"followSystem": "Системная",
|
||||||
|
"themeMode": "Тема",
|
||||||
|
"feedback": "Обратная связь",
|
||||||
|
"newConversation": "Новый чат",
|
||||||
|
"clearConversations": "Очистить чаты",
|
||||||
|
"modelParameters": "Параметры модели",
|
||||||
|
"model": "Модель",
|
||||||
|
"temperature": "Temperature",
|
||||||
|
"topP": "Top P",
|
||||||
|
"frequencyPenalty": "Frequency Penalty",
|
||||||
|
"presencePenalty": "Presence Penalty",
|
||||||
|
"maxTokens": "Max Tokens",
|
||||||
|
"roles": {
|
||||||
|
"me": "Я",
|
||||||
|
"ai": "AI"
|
||||||
|
},
|
||||||
|
"edit": "Редактировать",
|
||||||
|
"copy": "Копировать",
|
||||||
|
"copied": "Скопировано",
|
||||||
|
"delete": "Удалить",
|
||||||
|
"signOut": "Выход",
|
||||||
|
"webSearch": "Поиск в интернете",
|
||||||
|
"webSearchDefaultPrompt": "Результаты веб-поиска:\n\n[web_results]\nТекущая дата: [current_date]\n\nИнструкции: Используя предоставленные результаты веб-поиска, напишите развернутый ответ на заданный запрос. Обязательно цитируйте результаты, используя обозначение [[number](URL)] после ссылки. Если предоставленные результаты поиска относятся к нескольким темам с одинаковым названием, напишите отдельные ответы для каждой темы.\nЗапрос: [query]",
|
||||||
|
"welcomeScreen": {
|
||||||
|
"introduction1": "является неофициальным клиентом для ChatGPT, но использует официальный API OpenAI.",
|
||||||
|
"introduction2": "Вам понадобится ключ API OpenAI, прежде чем вы сможете использовать этот клиент.",
|
||||||
|
"examples": {
|
||||||
|
"title": "Примеры",
|
||||||
|
"item1": "\"Объясни, что такое квантовые вычисления простыми словами\"",
|
||||||
|
"item2": "\"Предложи несколько креативных идей для дня рождения 10-летнего ребенка?\"",
|
||||||
|
"item3": "\"Как сделать HTTP-запрос в Javascript?\""
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"title": "Возможности",
|
||||||
|
"item1": "Помнит, что пользователь сказал ранее в разговоре",
|
||||||
|
"item2": "Позволяет пользователю вносить последующие исправления",
|
||||||
|
"item3": "Научен отклонять неуместные запросы"
|
||||||
|
},
|
||||||
|
"limitations": {
|
||||||
|
"title": "Ограничения",
|
||||||
|
"item1": "Иногда может генерировать неверную информацию",
|
||||||
|
"item2": "Иногда может создавать вредные инструкции или предвзятый контент",
|
||||||
|
"item3": "Ограниченное знание мира и событий после 2021 года"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,10 @@
|
|||||||
"saveAndClose": "保存并关闭",
|
"saveAndClose": "保存并关闭",
|
||||||
"pleaseSelectAtLeastOneModelDot": "请至少选择一个模型",
|
"pleaseSelectAtLeastOneModelDot": "请至少选择一个模型",
|
||||||
"writeAMessage": "输入信息",
|
"writeAMessage": "输入信息",
|
||||||
|
"frequentlyPrompts": "Frequently prompts",
|
||||||
|
"addPrompt": "Add prompt",
|
||||||
|
"titlePrompt": "Title",
|
||||||
|
"addNewPrompt": "Add a new prompt",
|
||||||
"pressEnterToSendYourMessageOrShiftEnterToAddANewLine": "按回车键发送您的信息,或按Shift+Enter键添加新行",
|
"pressEnterToSendYourMessageOrShiftEnterToAddANewLine": "按回车键发送您的信息,或按Shift+Enter键添加新行",
|
||||||
"lightMode": "明亮模式",
|
"lightMode": "明亮模式",
|
||||||
"darkMode": "暗色模式",
|
"darkMode": "暗色模式",
|
||||||
@@ -34,6 +38,8 @@
|
|||||||
"copied": "已复制",
|
"copied": "已复制",
|
||||||
"delete": "删除",
|
"delete": "删除",
|
||||||
"signOut": "退出登录",
|
"signOut": "退出登录",
|
||||||
|
"webSearch": "网页搜索",
|
||||||
|
"webSearchDefaultPrompt": "网络搜索结果:\n\n[web_results]\n当前日期:[current_date]\n\n说明:使用提供的网络搜索结果,对给定的查询写出全面的回复。确保在引用参考文献后使用 [[number](URL)] 符号进行引用结果. 如果提供的搜索结果涉及到多个具有相同名称的主题,请针对每个主题编写单独的答案。\n查询:[query]",
|
||||||
"welcomeScreen": {
|
"welcomeScreen": {
|
||||||
"introduction1": "是一个非官方的ChatGPT客户端,但使用OpenAI的官方API",
|
"introduction1": "是一个非官方的ChatGPT客户端,但使用OpenAI的官方API",
|
||||||
"introduction2": "在使用本客户端之前,您需要一个OpenAI API密钥。",
|
"introduction2": "在使用本客户端之前,您需要一个OpenAI API密钥。",
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ const loadConversations = async () => {
|
|||||||
|
|
||||||
const {mdAndUp} = useDisplay()
|
const {mdAndUp} = useDisplay()
|
||||||
|
|
||||||
|
|
||||||
const drawerPermanent = computed(() => {
|
const drawerPermanent = computed(() => {
|
||||||
return mdAndUp.value
|
return mdAndUp.value
|
||||||
})
|
})
|
||||||
@@ -97,8 +98,18 @@ const signOut = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onNuxtReady(async () => {
|
const settings = useSettings()
|
||||||
|
const showApiKeySetting = ref(false)
|
||||||
|
watchEffect(() => {
|
||||||
|
if (settings.value) {
|
||||||
|
const settingsValue = toRaw(settings.value)
|
||||||
|
showApiKeySetting.value = settingsValue.open_api_key_setting && settingsValue.open_api_key_setting === 'True'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
loadConversations()
|
loadConversations()
|
||||||
|
loadSettings()
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@@ -237,6 +248,10 @@ onNuxtReady(async () => {
|
|||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
|
|
||||||
|
<ApiKeyDialog
|
||||||
|
v-if="showApiKeySetting"
|
||||||
|
/>
|
||||||
|
|
||||||
<ModelParameters/>
|
<ModelParameters/>
|
||||||
|
|
||||||
<v-menu
|
<v-menu
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
const appName = 'ChatGPT UI'
|
const appName = process.env.NUXT_PUBLIC_APP_NAME ?? 'ChatGPT UI'
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
dev: false,
|
dev: false,
|
||||||
@@ -14,6 +14,7 @@ export default defineNuxtConfig({
|
|||||||
appName: appName,
|
appName: appName,
|
||||||
typewriter: false,
|
typewriter: false,
|
||||||
typewriterDelay: 50,
|
typewriterDelay: 50,
|
||||||
|
customApiKey: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
@@ -53,6 +54,12 @@ export default defineNuxtConfig({
|
|||||||
iso: 'zh-CN',
|
iso: 'zh-CN',
|
||||||
name: '简体中文',
|
name: '简体中文',
|
||||||
file: 'zh-CN.json',
|
file: 'zh-CN.json',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'ru',
|
||||||
|
iso: 'ru-RU',
|
||||||
|
name: 'Русский',
|
||||||
|
file: 'ru-RU.json',
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
lazy: true,
|
lazy: true,
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ definePageMeta({
|
|||||||
middleware: ["auth"]
|
middleware: ["auth"]
|
||||||
})
|
})
|
||||||
import {EventStreamContentType, fetchEventSource} from '@microsoft/fetch-event-source'
|
import {EventStreamContentType, fetchEventSource} from '@microsoft/fetch-event-source'
|
||||||
import { nextTick } from 'vue'
|
|
||||||
import MessageActions from "~/components/MessageActions.vue";
|
|
||||||
|
|
||||||
const { $i18n, $auth } = useNuxtApp()
|
const { $i18n, $auth } = useNuxtApp()
|
||||||
const runtimeConfig = useRuntimeConfig()
|
const runtimeConfig = useRuntimeConfig()
|
||||||
@@ -50,15 +48,22 @@ const abortFetch = () => {
|
|||||||
}
|
}
|
||||||
fetchingResponse.value = false
|
fetchingResponse.value = false
|
||||||
}
|
}
|
||||||
const fetchReply = async (message, parentMessageId) => {
|
const fetchReply = async (message) => {
|
||||||
ctrl = new AbortController()
|
ctrl = new AbortController()
|
||||||
|
|
||||||
|
let webSearchParams = {}
|
||||||
|
if (enableWebSearch.value) {
|
||||||
|
webSearchParams['web_search'] = {
|
||||||
|
ua: navigator.userAgent,
|
||||||
|
default_prompt: $i18n.t('webSearchDefaultPrompt')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const data = Object.assign({}, currentModel.value, {
|
const data = Object.assign({}, currentModel.value, {
|
||||||
openaiApiKey: openaiApiKey.value,
|
openaiApiKey: enableCustomApiKey.value ? openaiApiKey.value : null,
|
||||||
message: message,
|
message: message,
|
||||||
// parentMessageId: parentMessageId,
|
|
||||||
conversationId: currentConversation.value.id
|
conversationId: currentConversation.value.id
|
||||||
})
|
}, webSearchParams)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fetchEventSource('/api/conversation/', {
|
await fetchEventSource('/api/conversation/', {
|
||||||
@@ -85,18 +90,17 @@ const fetchReply = async (message, parentMessageId) => {
|
|||||||
throw err;
|
throw err;
|
||||||
},
|
},
|
||||||
async onmessage(message) {
|
async onmessage(message) {
|
||||||
// console.log(message)
|
|
||||||
const event = message.event
|
const event = message.event
|
||||||
const data = JSON.parse(message.data)
|
const data = JSON.parse(message.data)
|
||||||
|
|
||||||
if (event === 'error') {
|
if (event === 'error') {
|
||||||
throw new Error(data.error);
|
abortFetch()
|
||||||
|
showSnackbar(data.error)
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event === 'userMessageId') {
|
if (event === 'userMessageId') {
|
||||||
console.log(currentConversation.value.messages[currentConversation.value.messages.length - 1])
|
|
||||||
currentConversation.value.messages[currentConversation.value.messages.length - 1].id = data.userMessageId
|
currentConversation.value.messages[currentConversation.value.messages.length - 1].id = data.userMessageId
|
||||||
console.log(currentConversation.value.messages[currentConversation.value.messages.length - 1])
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,15 +140,8 @@ const scrollChatWindow = () => {
|
|||||||
|
|
||||||
const send = (message) => {
|
const send = (message) => {
|
||||||
fetchingResponse.value = true
|
fetchingResponse.value = true
|
||||||
let parentMessageId = null
|
currentConversation.value.messages.push({message: message})
|
||||||
if (currentConversation.value.messages.length > 0) {
|
fetchReply(message)
|
||||||
const lastMessage = currentConversation.value.messages[currentConversation.value.messages.length - 1]
|
|
||||||
if (lastMessage.is_bot && lastMessage.id !== null) {
|
|
||||||
parentMessageId = lastMessage.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentConversation.value.messages.push({parentMessageId: parentMessageId, message: message})
|
|
||||||
fetchReply(message, parentMessageId)
|
|
||||||
scrollChatWindow()
|
scrollChatWindow()
|
||||||
}
|
}
|
||||||
const stop = () => {
|
const stop = () => {
|
||||||
@@ -167,6 +164,20 @@ const deleteMessage = (index) => {
|
|||||||
currentConversation.value.messages.splice(index, 1)
|
currentConversation.value.messages.splice(index, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showWebSearchToggle = ref(false)
|
||||||
|
const enableWebSearch = ref(false)
|
||||||
|
const enableCustomApiKey = ref(false)
|
||||||
|
|
||||||
|
const settings = useSettings()
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (settings.value) {
|
||||||
|
const settingsValue = toRaw(settings.value)
|
||||||
|
showWebSearchToggle.value = settingsValue.open_web_search && settingsValue.open_web_search === 'True'
|
||||||
|
enableCustomApiKey.value = settingsValue.open_api_key_setting && settingsValue.open_api_key_setting === 'True'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -207,21 +218,36 @@ const deleteMessage = (index) => {
|
|||||||
<div ref="grab" class="w-100" style="height: 200px;"></div>
|
<div ref="grab" class="w-100" style="height: 200px;"></div>
|
||||||
</div>
|
</div>
|
||||||
<Welcome v-else />
|
<Welcome v-else />
|
||||||
<v-footer app class="d-flex flex-column">
|
<v-footer app>
|
||||||
<div class="px-md-16 w-100 d-flex align-center">
|
<div class="px-md-16 w-100 d-flex flex-column">
|
||||||
<Prompt v-show="!fetchingResponse" :use-prompt="usePrompt" />
|
<div class="d-flex align-center">
|
||||||
<v-btn
|
<v-btn
|
||||||
v-show="fetchingResponse"
|
v-show="fetchingResponse"
|
||||||
icon="close"
|
icon="close"
|
||||||
title="stop"
|
title="stop"
|
||||||
class="mr-3"
|
class="mr-3"
|
||||||
@click="stop"
|
@click="stop"
|
||||||
></v-btn>
|
></v-btn>
|
||||||
<MsgEditor ref="editor" :send-message="send" :disabled="fetchingResponse" :loading="fetchingResponse" />
|
<MsgEditor ref="editor" :send-message="send" :disabled="fetchingResponse" :loading="fetchingResponse" />
|
||||||
</div>
|
</div>
|
||||||
|
<v-toolbar
|
||||||
|
density="comfortable"
|
||||||
|
color="transparent"
|
||||||
|
>
|
||||||
|
<Prompt v-show="!fetchingResponse" :use-prompt="usePrompt" />
|
||||||
|
<v-switch
|
||||||
|
v-if="showWebSearchToggle"
|
||||||
|
v-model="enableWebSearch"
|
||||||
|
hide-details
|
||||||
|
color="primary"
|
||||||
|
:label="$t('webSearch')"
|
||||||
|
></v-switch>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
</v-toolbar>
|
||||||
|
|
||||||
<div class="px-4 py-2 text-disabled text-caption font-weight-light text-center w-100">
|
<!-- <div class="py-2 text-disabled text-caption font-weight-light text-center">-->
|
||||||
© {{ new Date().getFullYear() }} {{ runtimeConfig.public.appName }}
|
<!-- © {{ new Date().getFullYear() }} {{ runtimeConfig.public.appName }}-->
|
||||||
|
<!-- </div>-->
|
||||||
</div>
|
</div>
|
||||||
</v-footer>
|
</v-footer>
|
||||||
<v-snackbar
|
<v-snackbar
|
||||||
|
|||||||
@@ -5,11 +5,25 @@ export const STORAGE_KEY = {
|
|||||||
OPENAI_API_KEY: 'openai_api_key',
|
OPENAI_API_KEY: 'openai_api_key',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_MODEL = {
|
export const MODELS = {
|
||||||
name: 'gpt-3.5-turbo',
|
'gpt-3.5-turbo': {
|
||||||
frequency_penalty: 0.0,
|
name: 'gpt-3.5-turbo',
|
||||||
presence_penalty: 0.0,
|
frequency_penalty: 0.0,
|
||||||
max_tokens: 1000,
|
presence_penalty: 0.0,
|
||||||
temperature: 0.7,
|
total_tokens: 4096,
|
||||||
top_p: 1.0
|
max_tokens: 1000,
|
||||||
}
|
temperature: 0.7,
|
||||||
|
top_p: 1.0
|
||||||
|
},
|
||||||
|
'gpt-4': {
|
||||||
|
name: 'gpt-4',
|
||||||
|
frequency_penalty: 0.0,
|
||||||
|
presence_penalty: 0.0,
|
||||||
|
total_tokens: 8192,
|
||||||
|
max_tokens: 2000,
|
||||||
|
temperature: 0.7,
|
||||||
|
top_p: 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_MODEL_NAME = 'gpt-3.5-turbo'
|
||||||
@@ -50,4 +50,23 @@ export const genTitle = async (conversationId) => {
|
|||||||
return data.value.title
|
return data.value.title
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const transformData = (list) => {
|
||||||
|
const result = {};
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
const item = list[i];
|
||||||
|
result[item.name] = item.value;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const loadSettings = async () => {
|
||||||
|
const settings = useSettings()
|
||||||
|
const { data, error } = await useAuthFetch('/api/chat/settings/', {
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
if (!error.value) {
|
||||||
|
settings.value = transformData(data.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import {MODELS} from "~/utils/enums";
|
||||||
|
|
||||||
const get = (key) => {
|
const get = (key) => {
|
||||||
let val = localStorage.getItem(key)
|
let val = localStorage.getItem(key)
|
||||||
@@ -17,13 +18,13 @@ export const setModels = (val) => {
|
|||||||
models.value = val
|
models.value = val
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getStoredModels = () => {
|
// export const getStoredModels = () => {
|
||||||
let models = get(STORAGE_KEY.MODELS)
|
// let models = get(STORAGE_KEY.MODELS)
|
||||||
if (!models) {
|
// if (!models) {
|
||||||
models = [DEFAULT_MODEL]
|
// models = [DEFAULT_MODEL]
|
||||||
}
|
// }
|
||||||
return models
|
// return models
|
||||||
}
|
// }
|
||||||
|
|
||||||
export const saveCurrentModel = (val) => {
|
export const saveCurrentModel = (val) => {
|
||||||
set(STORAGE_KEY.CURRENT_MODEL, val)
|
set(STORAGE_KEY.CURRENT_MODEL, val)
|
||||||
@@ -32,7 +33,7 @@ export const saveCurrentModel = (val) => {
|
|||||||
export const getCurrentModel = () => {
|
export const getCurrentModel = () => {
|
||||||
let model = get(STORAGE_KEY.CURRENT_MODEL)
|
let model = get(STORAGE_KEY.CURRENT_MODEL)
|
||||||
if (!model) {
|
if (!model) {
|
||||||
model = DEFAULT_MODEL
|
model = MODELS[DEFAULT_MODEL_NAME]
|
||||||
}
|
}
|
||||||
return model
|
return model
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user