refactor midjourney service, use api key in database

This commit is contained in:
RockYang
2024-08-06 18:30:57 +08:00
parent cc551ba266
commit f9b809801d
29 changed files with 585 additions and 1203 deletions

View File

@@ -6,7 +6,7 @@
</div>
<div class="chat-item">
<div class="content" v-html="data.content"></div>
<div class="content" v-html="md.render(processContent(data.content))"></div>
<div class="bar" v-if="data.created_at">
<span class="bar-item"><el-icon><Clock/></el-icon> {{ dateFormat(data.created_at) }}</span>
<span class="bar-item">tokens: {{ data.tokens }}</span>
@@ -17,7 +17,7 @@
content="复制回答"
placement="bottom"
>
<el-icon class="copy-reply" :data-clipboard-text="data.orgContent">
<el-icon class="copy-reply" :data-clipboard-text="data.content">
<DocumentCopy/>
</el-icon>
</el-tooltip>
@@ -34,7 +34,7 @@
</el-tooltip>
</span>
<span class="bar-item" @click="synthesis(data.orgContent)">
<span class="bar-item" @click="synthesis(data.content)">
<el-tooltip
class="box-item"
effect="dark"
@@ -69,7 +69,7 @@
</div>
<div class="chat-item">
<div class="content-wrapper">
<div class="content" v-html="data.content"></div>
<div class="content" v-html="md.render(processContent(data.content))"></div>
</div>
<div class="bar" v-if="data.created_at">
<span class="bar-item"><el-icon><Clock/></el-icon> {{ dateFormat(data.created_at) }}</span>
@@ -81,7 +81,7 @@
content="复制回答"
placement="bottom"
>
<el-icon class="copy-reply" :data-clipboard-text="data.orgContent">
<el-icon class="copy-reply" :data-clipboard-text="data.content">
<DocumentCopy/>
</el-icon>
</el-tooltip>
@@ -98,7 +98,7 @@
</el-tooltip>
</span>
<span class="bar-item bg" @click="synthesis(data.orgContent)">
<span class="bar-item bg" @click="synthesis(data.content)">
<el-tooltip
class="box-item"
effect="dark"
@@ -118,7 +118,8 @@
<script setup>
import {Clock, DocumentCopy, Refresh} from "@element-plus/icons-vue";
import {ElMessage} from "element-plus";
import {dateFormat} from "@/utils/libs";
import {dateFormat, processContent} from "@/utils/libs";
import hl from "highlight.js";
// eslint-disable-next-line no-undef,no-unused-vars
const props = defineProps({
data: {
@@ -128,7 +129,6 @@ const props = defineProps({
content: "",
created_at: "",
tokens: 0,
orgContent: ""
},
},
readOnly: {
@@ -141,6 +141,33 @@ const props = defineProps({
},
})
const mathjaxPlugin = require('markdown-it-mathjax3')
const md = require('markdown-it')({
breaks: true,
html: true,
linkify: true,
typographer: true,
highlight: function (str, lang) {
const codeIndex = parseInt(Date.now()) + Math.floor(Math.random() * 10000000)
// 显示复制代码按钮
const copyBtn = `<span class="copy-code-btn" data-clipboard-action="copy" data-clipboard-target="#copy-target-${codeIndex}">复制</span>
<textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy-target-${codeIndex}">${str.replace(/<\/textarea>/g, '&lt;/textarea>')}</textarea>`
if (lang && hl.getLanguage(lang)) {
const langHtml = `<span class="lang-name">${lang}</span>`
// 处理代码高亮
const preCode = hl.highlight(lang, str, true).value
// 将代码包裹在 pre 中
return `<pre class="code-container"><code class="language-${lang} hljs">${preCode}</code>${copyBtn} ${langHtml}</pre>`
}
// 处理代码高亮
const preCode = md.utils.escapeHtml(str)
// 将代码包裹在 pre 中
return `<pre class="code-container"><code class="language-${lang} hljs">${preCode}</code>${copyBtn}</pre>`
}
});
md.use(mathjaxPlugin)
const emits = defineEmits(['regen']);
if (!props.data.icon) {

View File

@@ -6,8 +6,6 @@
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
import {createRouter, createWebHistory} from "vue-router";
import {ref} from "vue";
import {httpGet} from "@/utils/http";
const routes = [
{

View File

@@ -204,13 +204,11 @@ import {Delete, Edit, More, Plus, Promotion, Search, Share, VideoPause} from '@e
import 'highlight.js/styles/a11y-dark.css'
import {
isMobile,
processContent,
randString,
removeArrayItem,
UUID
} from "@/utils/libs";
import {ElMessage, ElMessageBox} from "element-plus";
import hl from "highlight.js";
import {getSessionId, getUserToken, removeUserToken} from "@/store/session";
import {httpGet, httpPost} from "@/utils/http";
import {useRouter} from "vue-router";
@@ -221,7 +219,6 @@ import {useSharedStore} from "@/store/sharedata";
import FileSelect from "@/components/FileSelect.vue";
import FileList from "@/components/FileList.vue";
import ChatSetting from "@/components/ChatSetting.vue";
import axios from "axios";
import BackTop from "@/components/BackTop.vue";
import {showMessageError} from "@/utils/dialog";
@@ -547,33 +544,6 @@ const removeChat = function (chat) {
}
const mathjaxPlugin = require('markdown-it-mathjax3')
const md = require('markdown-it')({
breaks: true,
html: true,
linkify: true,
typographer: true,
highlight: function (str, lang) {
const codeIndex = parseInt(Date.now()) + Math.floor(Math.random() * 10000000)
// 显示复制代码按钮
const copyBtn = `<span class="copy-code-btn" data-clipboard-action="copy" data-clipboard-target="#copy-target-${codeIndex}">复制</span>
<textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy-target-${codeIndex}">${str.replace(/<\/textarea>/g, '&lt;/textarea>')}</textarea>`
if (lang && hl.getLanguage(lang)) {
const langHtml = `<span class="lang-name">${lang}</span>`
// 处理代码高亮
const preCode = hl.highlight(lang, str, true).value
// 将代码包裹在 pre 中
return `<pre class="code-container"><code class="language-${lang} hljs">${preCode}</code>${copyBtn} ${langHtml}</pre>`
}
// 处理代码高亮
const preCode = md.utils.escapeHtml(str)
// 将代码包裹在 pre 中
return `<pre class="code-container"><code class="language-${lang} hljs">${preCode}</code>${copyBtn}</pre>`
}
});
md.use(mathjaxPlugin)
// 创建 socket 连接
const prompt = ref('');
const showStopGenerate = ref(false); // 停止生成
@@ -622,7 +592,6 @@ const connect = function (chat_id, role_id) {
id: randString(32),
icon: _role['icon'],
content: _role['hello_msg'],
orgContent: _role['hello_msg'],
})
ElMessage.success({message: "对话连接成功!", duration: 1000})
} else { // 加载聊天记录
@@ -645,7 +614,6 @@ const connect = function (chat_id, role_id) {
icon: _role['icon'],
prompt:prePrompt,
content: "",
orgContent: "",
});
} else if (data.type === 'end') { // 消息接收完毕
// 追加当前会话到会话列表
@@ -680,8 +648,7 @@ const connect = function (chat_id, role_id) {
lineBuffer.value += data.content;
const reply = chatData.value[chatData.value.length - 1]
if (reply) {
reply['orgContent'] = lineBuffer.value;
reply['content'] = md.render(processContent(lineBuffer.value));
reply['content'] = lineBuffer.value;
}
}
// 将聊天框的滚动条滑动到最底部
@@ -845,12 +812,8 @@ const loadChatHistory = function (chatId) {
}
showHello.value = false
for (let i = 0; i < data.length; i++) {
data[i].orgContent = data[i].content;
if (data[i].type === 'reply') {
data[i].content = md.render(processContent(data[i].content))
if (i > 0) {
data[i].prompt = data[i - 1].orgContent
}
if (data[i].type === 'reply' && i > 0) {
data[i].prompt = data[i - 1].content
}
chatData.value.push(data[i]);
}

View File

@@ -1,173 +0,0 @@
<template>
<el-form label-width="150px" label-position="right" class="draw-config">
<el-tabs type="border-card">
<el-tab-pane label="MJ-PLUS">
<div v-if="mjPlusConfigs">
<div class="config-item" v-for="(v,k) in mjPlusConfigs">
<el-form-item label="是否启用">
<el-switch v-model="v['Enabled']"/>
</el-form-item>
<el-form-item label="API 地址">
<el-input v-model="v['ApiURL']" placeholder="API 地址"/>
</el-form-item>
<el-form-item label="API 令牌">
<el-input v-model="v['ApiKey']" placeholder="API KEY"/>
</el-form-item>
<el-form-item label="绘画模式">
<el-select v-model="v['Mode']" placeholder="请选择模式">
<el-option v-for="item in mjModels" :value="item.value" :label="item.name" :key="item.value">{{
item.name
}}
</el-option>
</el-select>
</el-form-item>
<el-button class="remove" type="danger" :icon="Delete" circle @click="removeItem(mjPlusConfigs,k)"/>
</div>
</div>
<el-empty v-else></el-empty>
<el-row style="justify-content: center; padding: 10px">
<el-button round @click="addConfig(mjPlusConfigs)">
<el-icon><Plus /></el-icon>
<span>新增配置</span>
</el-button>
</el-row>
</el-tab-pane>
<el-tab-pane label="MJ-PROXY">
<div v-if="mjProxyConfigs">
<div class="config-item" v-for="(v,k) in mjProxyConfigs">
<el-form-item label="是否启用">
<el-switch v-model="v['Enabled']"/>
</el-form-item>
<el-form-item label="API 地址">
<el-input v-model="v['ApiURL']" placeholder="API 地址"/>
</el-form-item>
<el-form-item label="API 令牌">
<el-input v-model="v['ApiKey']" placeholder="API KEY"/>
</el-form-item>
<el-button class="remove" type="danger" :icon="Delete" circle @click="removeItem(mjProxyConfigs,k)"/>
</div>
</div>
<el-empty v-else />
<el-row style="justify-content: center; padding: 10px">
<el-button round @click="addConfig(mjProxyConfigs)">
<el-icon>
<Plus/>
</el-icon>
<span>新增配置</span>
</el-button>
</el-row>
</el-tab-pane>
<el-tab-pane label="Stable-Diffusion">
<div v-if="sdConfigs">
<div class="config-item" v-for="(v,k) in sdConfigs">
<el-form-item label="是否启用">
<el-switch v-model="v['Enabled']"/>
</el-form-item>
<el-form-item label="API 地址">
<el-input v-model="v['ApiURL']" placeholder="API 地址"/>
</el-form-item>
<el-form-item label="API 令牌">
<el-input v-model="v['ApiKey']" placeholder="API KEY"/>
</el-form-item>
<el-form-item label="模型">
<el-input v-model="v['Model']" placeholder="绘画模型"/>
</el-form-item>
<el-button class="remove" type="danger" :icon="Delete" circle @click="removeItem(sdConfigs,k)"/>
</div>
</div>
<el-empty v-else/>
<el-row style="justify-content: center; padding: 10px">
<el-button round @click="addConfig(sdConfigs)">
<el-icon>
<Plus/>
</el-icon>
<span>新增配置</span>
</el-button>
</el-row>
</el-tab-pane>
</el-tabs>
<div style="padding: 10px;">
<el-form-item>
<el-button type="primary" @click="saveConfig()">保存</el-button>
</el-form-item>
</div>
</el-form>
</template>
<script setup>
import {ref} from "vue";
import {httpGet, httpPost} from "@/utils/http";
import {ElMessage} from "element-plus";
import {Delete, Plus} from "@element-plus/icons-vue";
// 变量定义
const sdConfigs = ref([])
const mjPlusConfigs = ref([])
const mjProxyConfigs = ref([])
const mjModels = ref([
{name: "慢速Relax", value: "relax"},
{name: "快速Fast", value: "fast"},
{name: "急速Turbo", value: "turbo"},
])
httpGet("/api/admin/config/get/app").then(res => {
sdConfigs.value = res.data.sd
mjPlusConfigs.value = res.data.mj_plus
mjProxyConfigs.value = res.data.mj_proxy
}).catch(e =>{
ElMessage.error("获取配置失败:"+e.message)
})
const addConfig = (configs) => {
configs.push({
Enabled: true,
ApiKey: '',
ApiURL: '',
Mode: 'fast'
})
}
const saveConfig = () => {
httpPost('/api/admin/config/update/draw', {
'sd': sdConfigs.value,
'mj_plus': mjPlusConfigs.value,
'mj_proxy': mjProxyConfigs.value
}).then(() => {
ElMessage.success("配置更新成功")
}).catch(e => {
ElMessage.error("操作失败:" + e.message)
})
}
const removeItem = (arr, k) => {
arr.splice(k, 1)
}
</script>
<style lang="stylus" scoped>
.draw-config {
.config-item {
position relative
padding 15px 10px 10px 10px
border 1px solid var(--el-border-color)
border-radius 10px
margin-bottom 10px
.remove {
position absolute
right 15px
top 15px
}
}
}
</style>

View File

@@ -139,6 +139,7 @@ const title = ref("")
const types = ref([
{label: "对话", value:"chat"},
{label: "Midjourney", value:"mj"},
{label: "Stable-Diffusion", value:"sd"},
{label: "DALL-E", value:"dalle"},
{label: "Suno文生歌", value:"suno"},
{label: "Luma视频", value:"luma"},

View File

@@ -157,11 +157,7 @@
<div v-for="item in messages" :key="item.id">
<chat-prompt
v-if="item.type==='prompt'"
:icon="item.icon"
:created-at="dateFormat(item['created_at'])"
:tokens="item['tokens']"
:model="item.model"
:content="item.content"/>
:data="item"/>
<chat-reply v-else-if="item.type==='reply'"
:read-only="true"
:data="item"/>
@@ -288,33 +284,11 @@ const removeMessage = function (row) {
})
}
const mathjaxPlugin = require('markdown-it-mathjax3')
const md = require('markdown-it')({
breaks: true,
html: true,
linkify: true,
typographer: true,
highlight: function (str, lang) {
if (lang && hl.getLanguage(lang)) {
// 处理代码高亮
const preCode = hl.highlight(lang, str, true).value
// 将代码包裹在 pre 中
return `<pre class="code-container"><code class="language-${lang} hljs">${preCode}</code></pre>`
}
// 处理代码高亮
const preCode = md.utils.escapeHtml(str)
// 将代码包裹在 pre 中
return `<pre class="code-container"><code class="language-${lang} hljs">${preCode}</code></pre>`
}
});
md.use(mathjaxPlugin)
const showContentDialog = ref(false)
const dialogContent = ref("")
const showContent = (content) => {
showContentDialog.value = true
dialogContent.value = md.render(processContent(content))
dialogContent.value = processContent(content)
}
const showChatItemDialog = ref(false)
@@ -325,8 +299,6 @@ const showMessages = (row) => {
httpGet('/api/admin/chat/history?chat_id=' + row.chat_id).then(res => {
const data = res.data
for (let i = 0; i < data.length; i++) {
data[i].orgContent = data[i].content;
data[i].content = md.render(processContent(data[i].content))
messages.value.push(data[i]);
}
}).catch(e => {

View File

@@ -194,6 +194,15 @@
</div>
</div>
</el-form-item>
<el-form-item label="MJ默认API模式" prop="mj_mode">
<el-select v-model="system['mj_mode']" placeholder="请选择模式">
<el-option v-for="item in mjModels" :value="item.value" :label="item.name" :key="item.value">{{
item.name
}}
</el-option>
</el-select>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="算力配置">
@@ -359,10 +368,6 @@
<Menu/>
</el-tab-pane>
<el-tab-pane label="AI绘图配置" name="AIDrawing">
<AIDrawing/>
</el-tab-pane>
<el-tab-pane label="授权激活" name="license">
<div class="container">
<el-descriptions
@@ -431,7 +436,6 @@ import MdEditor from "md-editor-v3";
import 'md-editor-v3/lib/style.css';
import Menu from "@/views/admin/Menu.vue";
import {copyObj, dateFormat} from "@/utils/libs";
import AIDrawing from "@/views/admin/AIDrawing.vue";
const activeName = ref('basic')
const system = ref({models: []})
@@ -439,10 +443,14 @@ const configBak = ref({})
const loading = ref(true)
const systemFormRef = ref(null)
const models = ref([])
const openAIModels = ref([])
const notice = ref("")
const license = ref({is_active: false})
const menus = ref([])
const mjModels = ref([
{name: "慢速Relax", value: "relax"},
{name: "快速Fast", value: "fast"},
{name: "急速Turbo", value: "turbo"},
])
onMounted(() => {
// 加载系统配置
@@ -461,7 +469,6 @@ onMounted(() => {
httpGet('/api/admin/model/list').then(res => {
models.value = res.data
openAIModels.value = models.value.filter(v => v.platform === "OpenAI")
loading.value = false
}).catch(e => {
ElMessage.error("获取模型失败:" + e.message)