wip: entrance of ChatGPT

This commit is contained in:
0xJacky 2023-03-22 00:10:37 +08:00
parent af1be98575
commit 8c1cdda305
No known key found for this signature in database
GPG key ID: B6E4A6E4A561BAF0
8 changed files with 407 additions and 227 deletions

View file

@ -46,6 +46,7 @@
"unplugin-vue-components": "^0.22.12", "unplugin-vue-components": "^0.22.12",
"vite": "^4.1.4", "vite": "^4.1.4",
"vite-plugin-html": "^3.2.0", "vite-plugin-html": "^3.2.0",
"vite-svg-loader": "^4.0.0",
"vue-tsc": "^1.0.24" "vue-tsc": "^1.0.24"
} }
} }

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2406 2406"><path d="M1 578.4C1 259.5 259.5 1 578.4 1h1249.1c319 0 577.5 258.5 577.5 577.4V2406H578.4C259.5 2406 1 2147.5 1 1828.6V578.4z" fill="#74aa9c"/><path d="M1107.3 299.1c-198 0-373.9 127.3-435.2 315.3C544.8 640.6 434.9 720.2 370.5 833c-99.3 171.4-76.6 386.9 56.4 533.8-41.1 123.1-27 257.7 38.6 369.2 98.7 172 297.3 260.2 491.6 219.2 86.1 97 209.8 152.3 339.6 151.8 198 0 373.9-127.3 435.3-315.3 127.5-26.3 237.2-105.9 301-218.5 99.9-171.4 77.2-386.9-55.8-533.9v-.6c41.1-123.1 27-257.8-38.6-369.8-98.7-171.4-297.3-259.6-491-218.6-86.6-96.8-210.5-151.8-340.3-151.2zm0 117.5-.6.6c79.7 0 156.3 27.5 217.6 78.4-2.5 1.2-7.4 4.3-11 6.1L952.8 709.3c-18.4 10.4-29.4 30-29.4 51.4V1248l-155.1-89.4V755.8c-.1-187.1 151.6-338.9 339-339.2zm434.2 141.9c121.6-.2 234 64.5 294.7 169.8 39.2 68.6 53.9 148.8 40.4 226.5-2.5-1.8-7.3-4.3-10.4-6.1l-360.4-208.2c-18.4-10.4-41-10.4-59.4 0L1024 984.2V805.4L1372.7 604c51.3-29.7 109.5-45.4 168.8-45.5zM650 743.5v427.9c0 21.4 11 40.4 29.4 51.4l421.7 243-155.7 90L597.2 1355c-162-93.8-217.4-300.9-123.8-462.8C513.1 823.6 575.5 771 650 743.5zm807.9 106 348.8 200.8c162.5 93.7 217.6 300.6 123.8 462.8l.6.6c-39.8 68.6-102.4 121.2-176.5 148.2v-428c0-21.4-11-41-29.4-51.4l-422.3-243.7 155-89.3zM1201.7 997l177.8 102.8v205.1l-177.8 102.8-177.8-102.8v-205.1L1201.7 997zm279.5 161.6 155.1 89.4v402.2c0 187.3-152 339.2-339 339.2v-.6c-79.1 0-156.3-27.6-217-78.4 2.5-1.2 8-4.3 11-6.1l360.4-207.5c18.4-10.4 30-30 29.4-51.4l.1-486.8zM1380 1421.9v178.8l-348.8 200.8c-162.5 93.1-369.6 38-463.4-123.7h.6c-39.8-68-54-148.8-40.5-226.5 2.5 1.8 7.4 4.3 10.4 6.1l360.4 208.2c18.4 10.4 41 10.4 59.4 0l421.9-243.7z" fill="white"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import {computed, ref, watch} from 'vue' import {computed, onMounted, ref, watch} from 'vue'
import {useGettext} from 'vue3-gettext' import {useGettext} from 'vue3-gettext'
import {useUserStore} from '@/pinia' import {useUserStore} from '@/pinia'
import {storeToRefs} from 'pinia' import {storeToRefs} from 'pinia'
@ -10,12 +10,20 @@ import 'highlight.js/styles/vs2015.css'
import {SendOutlined} from '@ant-design/icons-vue' import {SendOutlined} from '@ant-design/icons-vue'
import Template from '@/views/template/Template.vue' import Template from '@/views/template/Template.vue'
import openai from '@/api/openai' import openai from '@/api/openai'
import ChatGPT_logo from '@/assets/svg/ChatGPT_logo.svg'
import Icon from '@ant-design/icons-vue'
const {$gettext} = useGettext() const {$gettext} = useGettext()
const props = defineProps(['content', 'path', 'history_messages']) const props = defineProps(['content', 'path', 'history_messages'])
const emit = defineEmits(['update:history_messages'])
const history_messages = computed(() => props.history_messages)
watch(computed(() => props.history_messages), () => { onMounted(() => {
messages.value = props.history_messages
})
watch(history_messages, () => {
messages.value = props.history_messages messages.value = props.history_messages
}) })
@ -99,6 +107,9 @@ async function request() {
} }
async function send() { async function send() {
if (!messages.value) {
messages.value = []
}
if (messages.value.length === 0) { if (messages.value.length === 0) {
messages.value.push({ messages.value.push({
role: 'user', role: 'user',
@ -145,6 +156,7 @@ function clear_record() {
messages: [] messages: []
}) })
messages.value = [] messages.value = []
emit('update:history_messages', [])
} }
async function regenerate(index: number) { async function regenerate(index: number) {
@ -154,73 +166,88 @@ async function regenerate(index: number) {
} }
const editing_idx = ref(-1) const editing_idx = ref(-1)
const button_shape = computed(() => loading.value ? 'square' : 'circle')
</script> </script>
<template> <template>
<a-card title="ChatGPT"> <a-card class="chatgpt" title="ChatGPT" v-if="messages?.length>1">
<div class="chatgpt-container"> <div class="chatgpt-container">
<template v-if="messages?.length>0"> <a-list
<a-list class="chatgpt-log"
class="chatgpt-log" item-layout="horizontal"
item-layout="horizontal" :data-source="messages"
:data-source="messages" >
> <template #renderItem="{ item, index }">
<template #renderItem="{ item, index }"> <a-list-item>
<a-list-item> <a-comment :author="item.role" :avatar="item.avatar">
<a-comment :author="item.role" :avatar="item.avatar"> <template #content>
<template #content> <div class="content" v-if="item.role==='assistant'||editing_idx!=index"
<div class="content" v-if="item.role==='assistant'||editing_idx!=index" v-html="marked.parse(item.content)"></div>
v-html="marked.parse(item.content)"></div> <a-input style="padding: 0" v-else v-model:value="item.content"
<a-input style="padding: 0" v-else v-model:value="item.content" :bordered="false"/>
:bordered="false"/> </template>
</template> <template #actions>
<template #actions>
<span v-if="item.role==='user'&&editing_idx!==index" @click="editing_idx=index"> <span v-if="item.role==='user'&&editing_idx!==index" @click="editing_idx=index">
{{ $gettext('Modify') }} {{ $gettext('Modify') }}
</span> </span>
<template v-else-if="editing_idx==index"> <template v-else-if="editing_idx==index">
<span @click="regenerate(index+1)">{{ $gettext('Save') }}</span> <span @click="regenerate(index+1)">{{ $gettext('Save') }}</span>
<span @click="editing_idx=-1">{{ $gettext('Cancel') }}</span> <span @click="editing_idx=-1">{{ $gettext('Cancel') }}</span>
</template> </template>
<span v-else-if="!loading" @click="regenerate(index)" :disabled="loading"> <span v-else-if="!loading" @click="regenerate(index)" :disabled="loading">
{{ $gettext('Reload') }} {{ $gettext('Reload') }}
</span> </span>
</template> </template>
</a-comment> </a-comment>
</a-list-item> </a-list-item>
</template> </template>
</a-list> </a-list>
<div class="input-msg"> <div class="input-msg">
<div class="control-btn"> <div class="control-btn">
<a-space v-show="!loading"> <a-space v-show="!loading">
<a-popconfirm <a-popconfirm
:cancelText="$gettext('No')" :cancelText="$gettext('No')"
:okText="$gettext('OK')" :okText="$gettext('OK')"
:title="$gettext('Are you sure you want to clear the record of chat?')" :title="$gettext('Are you sure you want to clear the record of chat?')"
@confirm="clear_record"> @confirm="clear_record">
<a-button type="text">{{ $gettext('Clear') }}</a-button> <a-button type="text">{{ $gettext('Clear') }}</a-button>
</a-popconfirm> </a-popconfirm>
<a-button type="text" @click="regenerate(messages?.length-1)"> <a-button type="text" @click="regenerate(messages?.length-1)">
{{ $gettext('Regenerate response') }} {{ $gettext('Regenerate response') }}
</a-button>
</a-space>
</div>
<a-textarea auto-size v-model:value="ask_buffer"/>
<div class="sned-btn">
<a-button size="small" type="text" :loading="loading" @click="send">
<send-outlined/>
</a-button> </a-button>
</div> </a-space>
</div> </div>
</template> <a-textarea auto-size v-model:value="ask_buffer"/>
<template v-else> <div class="sned-btn">
<a-button @click="send">{{ $gettext('Chat with ChatGPT') }}</a-button> <a-button size="small" type="text" :loading="loading" @click="send">
</template> <send-outlined/>
</a-button>
</div>
</div>
</div> </div>
</a-card> </a-card>
<template v-else>
<div class="chat-start">
<a-button size="large" shape="circle" @click="send" :loading="loading">
<Icon v-if="!loading" :component="ChatGPT_logo"/>
</a-button>
</div>
</template>
</template> </template>
<style lang="less" scoped> <style lang="less" scoped>
.chatgpt {
position: sticky;
top: 78px;
}
.chat-start {
position: fixed !important;
right: 36px;
bottom: 78px;
}
.chatgpt-container { .chatgpt-container {
margin: 0 auto; margin: 0 auto;
max-width: 800px; max-width: 800px;

View file

@ -162,10 +162,13 @@ function on_change_enabled(checked: boolean) {
disable() disable()
} }
} }
const editor_md = computed(() => history_chatgpt_record?.value?.length > 1 ? 16 : 24)
const chat_md = computed(() => history_chatgpt_record?.value?.length > 1 ? 8 : 24)
</script> </script>
<template> <template>
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :xs="24" :sm="24" :md="16"> <a-col :xs="24" :sm="24" :md="editor_md">
<a-card :bordered="false"> <a-card :bordered="false">
<template #title> <template #title>
<span style="margin-right: 10px">{{ interpolate($gettext('Edit %{n}'), {n: name}) }}</span> <span style="margin-right: 10px">{{ interpolate($gettext('Edit %{n}'), {n: name}) }}</span>
@ -225,9 +228,9 @@ function on_change_enabled(checked: boolean) {
</a-card> </a-card>
</a-col> </a-col>
<a-col class="col-right" :xs="24" :sm="24" :md="8"> <a-col class="col-right" :xs="24" :sm="24" :md="chat_md">
<chat-g-p-t class="chatgpt" :content="configText" :path="ngx_config.file_name" <chat-g-p-t :content="configText" :path="ngx_config.file_name"
:history_messages="history_chatgpt_record"/> v-model:history_messages="history_chatgpt_record"/>
</a-col> </a-col>
<footer-tool-bar> <footer-tool-bar>
@ -250,11 +253,6 @@ function on_change_enabled(checked: boolean) {
<style lang="less" scoped> <style lang="less" scoped>
.col-right { .col-right {
position: relative; position: relative;
.chatgpt {
position: sticky;
top: 78px;
}
} }
.ant-card { .ant-card {

View file

@ -6,6 +6,7 @@ import {AntDesignVueResolver} from 'unplugin-vue-components/resolvers'
import {fileURLToPath, URL} from 'url' import {fileURLToPath, URL} from 'url'
import vueJsx from '@vitejs/plugin-vue-jsx' import vueJsx from '@vitejs/plugin-vue-jsx'
import vitePluginBuildId from 'vite-plugin-build-id' import vitePluginBuildId from 'vite-plugin-build-id'
import svgLoader from 'vite-svg-loader'
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
@ -25,7 +26,7 @@ export default defineConfig({
'.less' '.less'
] ]
}, },
plugins: [vue(), vueJsx(), vitePluginBuildId(), plugins: [vue(), vueJsx(), vitePluginBuildId(), svgLoader(),
Components({ Components({
resolvers: [AntDesignVueResolver({importStyle: false})], resolvers: [AntDesignVueResolver({importStyle: false})],
directoryAsNamespace: true directoryAsNamespace: true

View file

@ -864,6 +864,16 @@
estree-walker "^2.0.2" estree-walker "^2.0.2"
source-map "^0.6.1" source-map "^0.6.1"
"@vue/compiler-core@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.47.tgz#3e07c684d74897ac9aa5922c520741f3029267f8"
integrity sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==
dependencies:
"@babel/parser" "^7.16.4"
"@vue/shared" "3.2.47"
estree-walker "^2.0.2"
source-map "^0.6.1"
"@vue/compiler-dom@3.2.37": "@vue/compiler-dom@3.2.37":
version "3.2.37" version "3.2.37"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz#10d2427a789e7c707c872da9d678c82a0c6582b5" resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz#10d2427a789e7c707c872da9d678c82a0c6582b5"
@ -880,6 +890,14 @@
"@vue/compiler-core" "3.2.45" "@vue/compiler-core" "3.2.45"
"@vue/shared" "3.2.45" "@vue/shared" "3.2.45"
"@vue/compiler-dom@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz#a0b06caf7ef7056939e563dcaa9cbde30794f305"
integrity sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==
dependencies:
"@vue/compiler-core" "3.2.47"
"@vue/shared" "3.2.47"
"@vue/compiler-sfc@3.2.37": "@vue/compiler-sfc@3.2.37":
version "3.2.37" version "3.2.37"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz#3103af3da2f40286edcd85ea495dcb35bc7f5ff4" resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz#3103af3da2f40286edcd85ea495dcb35bc7f5ff4"
@ -912,6 +930,22 @@
postcss "^8.1.10" postcss "^8.1.10"
source-map "^0.6.1" source-map "^0.6.1"
"@vue/compiler-sfc@^3.2.20":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz#1bdc36f6cdc1643f72e2c397eb1a398f5004ad3d"
integrity sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==
dependencies:
"@babel/parser" "^7.16.4"
"@vue/compiler-core" "3.2.47"
"@vue/compiler-dom" "3.2.47"
"@vue/compiler-ssr" "3.2.47"
"@vue/reactivity-transform" "3.2.47"
"@vue/shared" "3.2.47"
estree-walker "^2.0.2"
magic-string "^0.25.7"
postcss "^8.1.10"
source-map "^0.6.1"
"@vue/compiler-ssr@3.2.37": "@vue/compiler-ssr@3.2.37":
version "3.2.37" version "3.2.37"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz#4899d19f3a5fafd61524a9d1aee8eb0505313cff" resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz#4899d19f3a5fafd61524a9d1aee8eb0505313cff"
@ -928,6 +962,14 @@
"@vue/compiler-dom" "3.2.45" "@vue/compiler-dom" "3.2.45"
"@vue/shared" "3.2.45" "@vue/shared" "3.2.45"
"@vue/compiler-ssr@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz#35872c01a273aac4d6070ab9d8da918ab13057ee"
integrity sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==
dependencies:
"@vue/compiler-dom" "3.2.47"
"@vue/shared" "3.2.47"
"@vue/devtools-api@^6.1.4": "@vue/devtools-api@^6.1.4":
version "6.2.1" version "6.2.1"
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.2.1.tgz#6f2948ff002ec46df01420dfeff91de16c5b4092" resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.2.1.tgz#6f2948ff002ec46df01420dfeff91de16c5b4092"
@ -960,6 +1002,17 @@
estree-walker "^2.0.2" estree-walker "^2.0.2"
magic-string "^0.25.7" magic-string "^0.25.7"
"@vue/reactivity-transform@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz#e45df4d06370f8abf29081a16afd25cffba6d84e"
integrity sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==
dependencies:
"@babel/parser" "^7.16.4"
"@vue/compiler-core" "3.2.47"
"@vue/shared" "3.2.47"
estree-walker "^2.0.2"
magic-string "^0.25.7"
"@vue/reactivity@3.2.37": "@vue/reactivity@3.2.37":
version "3.2.37" version "3.2.37"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.37.tgz#5bc3847ac58828e2b78526e08219e0a1089f8848" resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.37.tgz#5bc3847ac58828e2b78526e08219e0a1089f8848"
@ -1034,6 +1087,11 @@
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.45.tgz#a3fffa7489eafff38d984e23d0236e230c818bc2" resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.45.tgz#a3fffa7489eafff38d984e23d0236e230c818bc2"
integrity sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg== integrity sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==
"@vue/shared@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.47.tgz#e597ef75086c6e896ff5478a6bfc0a7aa4bbd14c"
integrity sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==
"@zougt/some-loader-utils@^1.4.3": "@zougt/some-loader-utils@^1.4.3":
version "1.4.3" version "1.4.3"
resolved "https://registry.yarnpkg.com/@zougt/some-loader-utils/-/some-loader-utils-1.4.3.tgz#41cf762b291ab9697f8c008bdeebaf80eaee4714" resolved "https://registry.yarnpkg.com/@zougt/some-loader-utils/-/some-loader-utils-1.4.3.tgz#41cf762b291ab9697f8c008bdeebaf80eaee4714"
@ -1462,6 +1520,17 @@ css-select@^4.1.3, css-select@^4.2.1:
domutils "^2.8.0" domutils "^2.8.0"
nth-check "^2.0.1" nth-check "^2.0.1"
css-select@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6"
integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==
dependencies:
boolbase "^1.0.0"
css-what "^6.1.0"
domhandler "^5.0.2"
domutils "^3.0.1"
nth-check "^2.0.1"
css-selector-parser@^1.3: css-selector-parser@^1.3:
version "1.4.1" version "1.4.1"
resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-1.4.1.tgz#03f9cb8a81c3e5ab2c51684557d5aaf6d2569759" resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-1.4.1.tgz#03f9cb8a81c3e5ab2c51684557d5aaf6d2569759"
@ -1475,7 +1544,23 @@ css-tree@^1.1.2, css-tree@^1.1.3:
mdn-data "2.0.14" mdn-data "2.0.14"
source-map "^0.6.1" source-map "^0.6.1"
css-what@^6.0.1: css-tree@^2.2.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20"
integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==
dependencies:
mdn-data "2.0.30"
source-map-js "^1.0.1"
css-tree@~2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032"
integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==
dependencies:
mdn-data "2.0.28"
source-map-js "^1.0.1"
css-what@^6.0.1, css-what@^6.1.0:
version "6.1.0" version "6.1.0"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
@ -1551,6 +1636,13 @@ csso@^4.2.0:
dependencies: dependencies:
css-tree "^1.1.2" css-tree "^1.1.2"
csso@^5.0.5:
version "5.0.5"
resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6"
integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==
dependencies:
css-tree "~2.2.0"
csstype@^2.6.8: csstype@^2.6.8:
version "2.6.20" version "2.6.20"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.20.tgz#9229c65ea0b260cf4d3d997cb06288e36a8d6dda" resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.20.tgz#9229c65ea0b260cf4d3d997cb06288e36a8d6dda"
@ -1609,7 +1701,16 @@ dom-serializer@^1.0.1:
domhandler "^4.2.0" domhandler "^4.2.0"
entities "^2.0.0" entities "^2.0.0"
domelementtype@^2.0.1, domelementtype@^2.2.0: dom-serializer@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
dependencies:
domelementtype "^2.3.0"
domhandler "^5.0.2"
entities "^4.2.0"
domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
@ -1621,6 +1722,13 @@ domhandler@^4.2.0, domhandler@^4.3.1:
dependencies: dependencies:
domelementtype "^2.2.0" domelementtype "^2.2.0"
domhandler@^5.0.1, domhandler@^5.0.2:
version "5.0.3"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
dependencies:
domelementtype "^2.3.0"
domutils@^2.8.0: domutils@^2.8.0:
version "2.8.0" version "2.8.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
@ -1630,6 +1738,15 @@ domutils@^2.8.0:
domelementtype "^2.2.0" domelementtype "^2.2.0"
domhandler "^4.2.0" domhandler "^4.2.0"
domutils@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c"
integrity sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==
dependencies:
dom-serializer "^2.0.0"
domelementtype "^2.3.0"
domhandler "^5.0.1"
dot-case@^3.0.4: dot-case@^3.0.4:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
@ -1670,6 +1787,11 @@ entities@^2.0.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
entities@^4.2.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174"
integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==
errno@^0.1.1: errno@^0.1.1:
version "0.1.8" version "0.1.8"
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
@ -2168,6 +2290,16 @@ mdn-data@2.0.14:
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
mdn-data@2.0.28:
version "2.0.28"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba"
integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==
mdn-data@2.0.30:
version "2.0.30"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc"
integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==
merge2@^1.3.0: merge2@^1.3.0:
version "1.4.1" version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
@ -2791,7 +2923,7 @@ sortablejs@1.14.0:
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.14.0.tgz#6d2e17ccbdb25f464734df621d4f35d4ab35b3d8" resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.14.0.tgz#6d2e17ccbdb25f464734df621d4f35d4ab35b3d8"
integrity sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w== integrity sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==
source-map-js@^1.0.2: source-map-js@^1.0.1, source-map-js@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
@ -2924,6 +3056,18 @@ svgo@^2.7.0:
picocolors "^1.0.0" picocolors "^1.0.0"
stable "^0.1.8" stable "^0.1.8"
svgo@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.0.2.tgz#5e99eeea42c68ee0dc46aa16da093838c262fe0a"
integrity sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==
dependencies:
"@trysound/sax" "0.2.0"
commander "^7.2.0"
css-select "^5.1.0"
css-tree "^2.2.1"
csso "^5.0.5"
picocolors "^1.0.0"
terser@^5.10.0: terser@^5.10.0:
version "5.14.2" version "5.14.2"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10" resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10"
@ -3051,6 +3195,14 @@ vite-plugin-html@^3.2.0:
node-html-parser "^5.3.3" node-html-parser "^5.3.3"
pathe "^0.2.0" pathe "^0.2.0"
vite-svg-loader@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/vite-svg-loader/-/vite-svg-loader-4.0.0.tgz#1cec4337dba3c23ab13bcabb111896e251b047ac"
integrity sha512-0MMf1yzzSYlV4MGePsLVAOqXsbF5IVxbn4EEzqRnWxTQl8BJg/cfwIzfQNmNQxZp5XXwd4kyRKF1LytuHZTnqA==
dependencies:
"@vue/compiler-sfc" "^3.2.20"
svgo "^3.0.2"
vite@^4.0.4: vite@^4.0.4:
version "4.0.4" version "4.0.4"
resolved "https://registry.npmmirror.com/vite/-/vite-4.0.4.tgz#4612ce0b47bbb233a887a54a4ae0c6e240a0da31" resolved "https://registry.npmmirror.com/vite/-/vite-4.0.4.tgz#4612ce0b47bbb233a887a54a4ae0c6e240a0da31"

View file

@ -1,160 +1,160 @@
package api package api
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/0xJacky/Nginx-UI/server/model" "github.com/0xJacky/Nginx-UI/server/model"
"github.com/0xJacky/Nginx-UI/server/query" "github.com/0xJacky/Nginx-UI/server/query"
"github.com/0xJacky/Nginx-UI/server/settings" "github.com/0xJacky/Nginx-UI/server/settings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sashabaranov/go-openai" "github.com/sashabaranov/go-openai"
"io" "io"
"log" "log"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
) )
const ChatGPTInitPrompt = "You are a assistant who can help users write and optimise the configurations of Nginx, the first user message contains the content of the configuration file which is currently opened by the user and the current language code(CLC). You suppose to use the language corresponding to the CLC to give the first reply. Later the language environment depends on the user message. The first reply should involve the key information of the file and ask user what can you help them." const ChatGPTInitPrompt = "You are a assistant who can help users write and optimise the configurations of Nginx, the first user message contains the content of the configuration file which is currently opened by the user and the current language code(CLC). You suppose to use the language corresponding to the CLC to give the first reply. Later the language environment depends on the user message. The first reply should involve the key information of the file and ask user what can you help them."
func MakeChatCompletionRequest(c *gin.Context) { func MakeChatCompletionRequest(c *gin.Context) {
var json struct { var json struct {
Messages []openai.ChatCompletionMessage `json:"messages"` Messages []openai.ChatCompletionMessage `json:"messages"`
} }
if !BindAndValid(c, &json) { if !BindAndValid(c, &json) {
return return
} }
messages := []openai.ChatCompletionMessage{ messages := []openai.ChatCompletionMessage{
{ {
Role: openai.ChatMessageRoleSystem, Role: openai.ChatMessageRoleSystem,
Content: ChatGPTInitPrompt, Content: ChatGPTInitPrompt,
}, },
} }
messages = append(messages, json.Messages...) messages = append(messages, json.Messages...)
// sse server // sse server
c.Writer.Header().Set("Content-Type", "text/event-stream") c.Writer.Header().Set("Content-Type", "text/event-stream")
c.Writer.Header().Set("Cache-Control", "no-cache") c.Writer.Header().Set("Cache-Control", "no-cache")
c.Writer.Header().Set("Connection", "keep-alive") c.Writer.Header().Set("Connection", "keep-alive")
c.Writer.Header().Set("Access-Control-Allow-Origin", "*") c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
log.Println(settings.OpenAISettings.Token) log.Println(settings.OpenAISettings.Token)
config := openai.DefaultConfig(settings.OpenAISettings.Token) config := openai.DefaultConfig(settings.OpenAISettings.Token)
if settings.OpenAISettings.Proxy != "" { if settings.OpenAISettings.Proxy != "" {
proxyUrl, err := url.Parse(settings.OpenAISettings.Proxy) proxyUrl, err := url.Parse(settings.OpenAISettings.Proxy)
if err != nil { if err != nil {
c.Stream(func(w io.Writer) bool { c.Stream(func(w io.Writer) bool {
c.SSEvent("message", gin.H{ c.SSEvent("message", gin.H{
"type": "error", "type": "error",
"content": err.Error(), "content": err.Error(),
}) })
return false return false
}) })
return return
} }
transport := &http.Transport{ transport := &http.Transport{
Proxy: http.ProxyURL(proxyUrl), Proxy: http.ProxyURL(proxyUrl),
} }
config.HTTPClient = &http.Client{ config.HTTPClient = &http.Client{
Transport: transport, Transport: transport,
} }
} }
if settings.OpenAISettings.BaseUrl != "" { if settings.OpenAISettings.BaseUrl != "" {
config.BaseURL = settings.OpenAISettings.BaseUrl config.BaseURL = settings.OpenAISettings.BaseUrl
} }
openaiClient := openai.NewClientWithConfig(config) openaiClient := openai.NewClientWithConfig(config)
ctx := context.Background() ctx := context.Background()
req := openai.ChatCompletionRequest{ req := openai.ChatCompletionRequest{
Model: settings.OpenAISettings.Model, Model: settings.OpenAISettings.Model,
Messages: messages, Messages: messages,
Stream: true, Stream: true,
} }
stream, err := openaiClient.CreateChatCompletionStream(ctx, req) stream, err := openaiClient.CreateChatCompletionStream(ctx, req)
if err != nil { if err != nil {
fmt.Printf("CompletionStream error: %v\n", err) fmt.Printf("CompletionStream error: %v\n", err)
c.Stream(func(w io.Writer) bool { c.Stream(func(w io.Writer) bool {
c.SSEvent("message", gin.H{ c.SSEvent("message", gin.H{
"type": "error", "type": "error",
"content": err.Error(), "content": err.Error(),
}) })
return false return false
}) })
return return
} }
defer stream.Close() defer stream.Close()
msgChan := make(chan string) msgChan := make(chan string)
go func() { go func() {
for { for {
response, err := stream.Recv() response, err := stream.Recv()
if errors.Is(err, io.EOF) { if errors.Is(err, io.EOF) {
close(msgChan) close(msgChan)
fmt.Println() fmt.Println()
return return
} }
if err != nil { if err != nil {
fmt.Printf("Stream error: %v\n", err) fmt.Printf("Stream error: %v\n", err)
close(msgChan) close(msgChan)
return return
} }
// Send SSE to client // Send SSE to client
message := fmt.Sprintf("%s", response.Choices[0].Delta.Content) message := fmt.Sprintf("%s", response.Choices[0].Delta.Content)
fmt.Printf("%s", response.Choices[0].Delta.Content) fmt.Printf("%s", response.Choices[0].Delta.Content)
_ = os.Stdout.Sync() _ = os.Stdout.Sync()
msgChan <- message msgChan <- message
} }
}() }()
c.Stream(func(w io.Writer) bool { c.Stream(func(w io.Writer) bool {
if m, ok := <-msgChan; ok { if m, ok := <-msgChan; ok {
c.SSEvent("message", gin.H{ c.SSEvent("message", gin.H{
"type": "message", "type": "message",
"content": m, "content": m,
}) })
return true return true
} }
return false return false
}) })
} }
func StoreChatGPTRecord(c *gin.Context) { func StoreChatGPTRecord(c *gin.Context) {
var json struct { var json struct {
FileName string `json:"file_name"` FileName string `json:"file_name"`
Messages []openai.ChatCompletionMessage `json:"messages"` Messages []openai.ChatCompletionMessage `json:"messages"`
} }
if !BindAndValid(c, &json) { if !BindAndValid(c, &json) {
return return
} }
name := json.FileName name := json.FileName
g := query.ChatGPTLog g := query.ChatGPTLog
_, err := g.Where(g.Name.Eq(name)).FirstOrCreate() _, err := g.Where(g.Name.Eq(name)).FirstOrCreate()
if err != nil { if err != nil {
ErrHandler(c, err) ErrHandler(c, err)
return return
} }
_, err = g.Where(g.Name.Eq(name)).Updates(&model.ChatGPTLog{ _, err = g.Where(g.Name.Eq(name)).Updates(&model.ChatGPTLog{
Name: name, Name: name,
Content: json.Messages, Content: json.Messages,
}) })
if err != nil { if err != nil {
ErrHandler(c, err) ErrHandler(c, err)
return return
} }
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"message": "ok", "message": "ok",
}) })
} }

View file

@ -1,45 +1,45 @@
package api package api
import ( import (
"github.com/0xJacky/Nginx-UI/server/settings" "github.com/0xJacky/Nginx-UI/server/settings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http" "net/http"
) )
func GetSettings(c *gin.Context) { func GetSettings(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"server": settings.ServerSettings, "server": settings.ServerSettings,
"nginx_log": settings.NginxLogSettings, "nginx_log": settings.NginxLogSettings,
"openai": settings.OpenAISettings, "openai": settings.OpenAISettings,
}) })
} }
func SaveSettings(c *gin.Context) { func SaveSettings(c *gin.Context) {
var json struct { var json struct {
Server settings.Server `json:"server"` Server settings.Server `json:"server"`
NginxLog settings.NginxLog `json:"nginx_log"` NginxLog settings.NginxLog `json:"nginx_log"`
Openai settings.OpenAI `json:"openai"` Openai settings.OpenAI `json:"openai"`
} }
if !BindAndValid(c, &json) { if !BindAndValid(c, &json) {
return return
} }
settings.Conf.Section("server").Key("Email").SetValue(json.Server.Email) settings.Conf.Section("server").Key("Email").SetValue(json.Server.Email)
settings.Conf.Section("server").Key("HTTPChallengePort").SetValue(json.Server.HTTPChallengePort) settings.Conf.Section("server").Key("HTTPChallengePort").SetValue(json.Server.HTTPChallengePort)
settings.Conf.Section("nginx_log").Key("AccessLogPath").SetValue(json.NginxLog.AccessLogPath) settings.Conf.Section("nginx_log").Key("AccessLogPath").SetValue(json.NginxLog.AccessLogPath)
settings.Conf.Section("nginx_log").Key("ErrorLogPath").SetValue(json.NginxLog.ErrorLogPath) settings.Conf.Section("nginx_log").Key("ErrorLogPath").SetValue(json.NginxLog.ErrorLogPath)
settings.Conf.Section("openai").Key("Model").SetValue(json.Openai.Model) settings.Conf.Section("openai").Key("Model").SetValue(json.Openai.Model)
settings.Conf.Section("openai").Key("BaseUrl").SetValue(json.Openai.BaseUrl) settings.Conf.Section("openai").Key("BaseUrl").SetValue(json.Openai.BaseUrl)
settings.Conf.Section("openai").Key("Proxy").SetValue(json.Openai.Proxy) settings.Conf.Section("openai").Key("Proxy").SetValue(json.Openai.Proxy)
settings.Conf.Section("openai").Key("Token").SetValue(json.Openai.Token) settings.Conf.Section("openai").Key("Token").SetValue(json.Openai.Token)
err := settings.Save() err := settings.Save()
if err != nil { if err != nil {
ErrHandler(c, err) ErrHandler(c, err)
return return
} }
GetSettings(c) GetSettings(c)
} }