mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-12 10:55:51 +02:00
enhance: ChatGPT stream reception
This commit is contained in:
parent
11d1d59725
commit
94408cd21f
8 changed files with 70 additions and 32 deletions
1
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
|
@ -56,7 +56,6 @@ declare module '@vue/runtime-core' {
|
||||||
ATabs: typeof import('ant-design-vue/es')['Tabs']
|
ATabs: typeof import('ant-design-vue/es')['Tabs']
|
||||||
ATag: typeof import('ant-design-vue/es')['Tag']
|
ATag: typeof import('ant-design-vue/es')['Tag']
|
||||||
ATextarea: typeof import('ant-design-vue/es')['Textarea']
|
ATextarea: typeof import('ant-design-vue/es')['Textarea']
|
||||||
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
|
||||||
BreadcrumbBreadcrumb: typeof import('./src/components/Breadcrumb/Breadcrumb.vue')['default']
|
BreadcrumbBreadcrumb: typeof import('./src/components/Breadcrumb/Breadcrumb.vue')['default']
|
||||||
ChartAreaChart: typeof import('./src/components/Chart/AreaChart.vue')['default']
|
ChartAreaChart: typeof import('./src/components/Chart/AreaChart.vue')['default']
|
||||||
ChartRadialBarChart: typeof import('./src/components/Chart/RadialBarChart.vue')['default']
|
ChartRadialBarChart: typeof import('./src/components/Chart/RadialBarChart.vue')['default']
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "nginx-ui-frontend-next",
|
"name": "nginx-ui-frontend-next",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.7.7",
|
"version": "1.7.8",
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|
|
@ -46,13 +46,15 @@ async function request() {
|
||||||
|
|
||||||
console.log('fetching...')
|
console.log('fetching...')
|
||||||
|
|
||||||
|
messages.value.push(t.value)
|
||||||
|
|
||||||
|
emit('update:history_messages', messages.value)
|
||||||
|
|
||||||
let res = await fetch(urlJoin(window.location.pathname, '/api/chat_gpt'), {
|
let res = await fetch(urlJoin(window.location.pathname, '/api/chat_gpt'), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'Accept': 'text/event-stream', Authorization: token.value},
|
headers: {'Accept': 'text/event-stream', Authorization: token.value},
|
||||||
body: JSON.stringify({messages: messages.value})
|
body: JSON.stringify({messages: messages.value.slice(0, messages.value?.length - 1)})
|
||||||
})
|
})
|
||||||
|
|
||||||
messages.value.push(t.value)
|
|
||||||
// read body as stream
|
// read body as stream
|
||||||
console.log('reading...')
|
console.log('reading...')
|
||||||
let reader = res.body!.getReader()
|
let reader = res.body!.getReader()
|
||||||
|
@ -62,6 +64,8 @@ async function request() {
|
||||||
|
|
||||||
let buffer = ''
|
let buffer = ''
|
||||||
|
|
||||||
|
let hasCodeBlockIndicator = false
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
let {done, value} = await reader.read()
|
let {done, value} = await reader.read()
|
||||||
if (done) {
|
if (done) {
|
||||||
|
@ -78,17 +82,32 @@ async function request() {
|
||||||
const decoder = new TextDecoder('utf-8')
|
const decoder = new TextDecoder('utf-8')
|
||||||
const raw = decoder.decode(input)
|
const raw = decoder.decode(input)
|
||||||
|
|
||||||
const regex = /{"content":"(.+?)"}/g
|
// console.log(input, raw)
|
||||||
const matches = raw.match(regex)
|
|
||||||
|
const line = raw.split('\n\n')
|
||||||
|
|
||||||
|
line?.forEach(v => {
|
||||||
|
const data = v.slice('event:message\ndata:'.length)
|
||||||
|
if (!data) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const content = JSON.parse(data).content
|
||||||
|
|
||||||
|
if (!hasCodeBlockIndicator) {
|
||||||
|
hasCodeBlockIndicator = content.indexOf('`') > -1
|
||||||
|
}
|
||||||
|
|
||||||
matches?.forEach(v => {
|
|
||||||
const content = JSON.parse(v).content
|
|
||||||
for (let c of content) {
|
for (let c of content) {
|
||||||
buffer += c
|
buffer += c
|
||||||
if (isCodeBlockComplete(buffer)) {
|
if (hasCodeBlockIndicator) {
|
||||||
t.value.content = buffer
|
if (isCodeBlockComplete(buffer)) {
|
||||||
|
t.value.content = buffer
|
||||||
|
hasCodeBlockIndicator = false
|
||||||
|
} else {
|
||||||
|
t.value.content = buffer + '\n```'
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
t.value.content = buffer + '\n```'
|
t.value.content = buffer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -167,11 +186,12 @@ async function regenerate(index: number) {
|
||||||
|
|
||||||
const editing_idx = ref(-1)
|
const editing_idx = ref(-1)
|
||||||
|
|
||||||
const button_shape = computed(() => loading.value ? 'square' : 'circle')
|
const show = computed(() => messages?.value?.length > 1)
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-card class="chatgpt" title="ChatGPT" v-if="messages?.length>1">
|
<a-card class="chatgpt" title="ChatGPT" v-if="show">
|
||||||
<div class="chatgpt-container">
|
<div class="chatgpt-container">
|
||||||
<a-list
|
<a-list
|
||||||
class="chatgpt-log"
|
class="chatgpt-log"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"version":"1.7.7","build_id":85,"total_build":155}
|
{"version":"1.7.8","build_id":86,"total_build":156}
|
|
@ -8,6 +8,7 @@ import {message} from 'ant-design-vue'
|
||||||
import CodeEditor from '@/components/CodeEditor/CodeEditor.vue'
|
import CodeEditor from '@/components/CodeEditor/CodeEditor.vue'
|
||||||
import ngx from '@/api/ngx'
|
import ngx from '@/api/ngx'
|
||||||
import InspectConfig from '@/views/config/InspectConfig.vue'
|
import InspectConfig from '@/views/config/InspectConfig.vue'
|
||||||
|
import ChatGPT from '@/components/ChatGPT/ChatGPT.vue'
|
||||||
|
|
||||||
const {$gettext, interpolate} = gettext
|
const {$gettext, interpolate} = gettext
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
@ -23,16 +24,22 @@ const name = computed(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const configText = ref('')
|
const configText = ref('')
|
||||||
|
const history_chatgpt_record = ref([])
|
||||||
|
const file_path = ref('')
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
if (name.value) {
|
if (name.value) {
|
||||||
config.get(name.value).then(r => {
|
config.get(name.value).then(r => {
|
||||||
configText.value = r.config
|
configText.value = r.config
|
||||||
|
history_chatgpt_record.value = r.chatgpt_messages
|
||||||
|
file_path.value = r.file_path
|
||||||
}).catch(r => {
|
}).catch(r => {
|
||||||
message.error(r.message ?? $gettext('Server error'))
|
message.error(r.message ?? $gettext('Server error'))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
configText.value = ''
|
configText.value = ''
|
||||||
|
history_chatgpt_record.value = []
|
||||||
|
file_path.value = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,28 +65,39 @@ function format_code() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
<inspect-config ref="inspect_config"/>
|
<inspect-config ref="inspect_config"/>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :xs="24" :sm="24" :md="editor_md">
|
||||||
|
<a-card :title="$gettext('Edit Configuration')">
|
||||||
|
<code-editor v-model:content="configText"/>
|
||||||
|
<footer-tool-bar>
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="$router.go(-1)">
|
||||||
|
<translate>Back</translate>
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="format_code">
|
||||||
|
<translate>Format Code</translate>
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" @click="save">
|
||||||
|
<translate>Save</translate>
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</footer-tool-bar>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
<a-card :title="$gettext('Edit Configuration')">
|
<a-col class="col-right" :xs="24" :sm="24" :md="chat_md">
|
||||||
<code-editor v-model:content="configText"/>
|
<chat-g-p-t :content="configText" :path="file_path"
|
||||||
<footer-tool-bar>
|
v-model:history_messages="history_chatgpt_record"/>
|
||||||
<a-space>
|
</a-col>
|
||||||
<a-button @click="$router.go(-1)">
|
</a-row>
|
||||||
<translate>Back</translate>
|
|
||||||
</a-button>
|
|
||||||
<a-button @click="format_code">
|
|
||||||
<translate>Format Code</translate>
|
|
||||||
</a-button>
|
|
||||||
<a-button type="primary" @click="save">
|
|
||||||
<translate>Save</translate>
|
|
||||||
</a-button>
|
|
||||||
</a-space>
|
|
||||||
</footer-tool-bar>
|
|
||||||
</a-card>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"version":"1.7.7","build_id":85,"total_build":155}
|
{"version":"1.7.8","build_id":86,"total_build":156}
|
Binary file not shown.
|
@ -96,6 +96,7 @@ func GetConfig(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"config": string(content),
|
"config": string(content),
|
||||||
"chatgpt_messages": chatgpt.Content,
|
"chatgpt_messages": chatgpt.Content,
|
||||||
|
"file_path": path,
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue