Skip to content

Commit

Permalink
perf: optimize the display of session content (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
ayangweb committed Mar 19, 2023
1 parent 6f56529 commit 532245f
Show file tree
Hide file tree
Showing 15 changed files with 338 additions and 291 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
支持的平台与架构:

- **Mac**: Apple Silicon, Intel
- **Windows**: Windows 7 - Window 11
- **Windows**: Windows 7 - Windows 11
- **Linux**: x64

<img src='./images/home.png' />
Expand Down
10 changes: 2 additions & 8 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,8 @@ onMounted(async () => {
<Session />

<div class="flex cursor-default flex-col gap-2 pt-2">
<div class="text-6 relative flex justify-end gap-4">
<!-- 当前聊天角色对象 -->
<!-- <div class="top-50% left-50% text-4 -translate-1/2 absolute">
正在与 <span class="mark">{{ currentRole?.name }}</span> 对话
</div> -->
<!-- 功能按钮 -->
<Function />
</div>
<!-- 功能区域 -->
<Function />
<!-- 输入框 -->
<Input />
</div>
Expand Down
114 changes: 2 additions & 112 deletions src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,112 +1,2 @@
import { fetch, Body } from '@tauri-apps/api/http'
import { dialogErrorMessage } from '@/utils'
import type { FetchOptions } from '@tauri-apps/api/http'
import type { RecordData } from '@/types'
import { useSessionStore, useSettingsStore } from '@/stores'
import {
fetchEventSource,
type EventSourceMessage
} from '@microsoft/fetch-event-source'

/**
* 请求总入口
* @param url 请求地址
* @param options 请求参数
*/
export const request = async (url: string, options?: FetchOptions) => {
try {
const { method, headers } = options || {}

const { data }: Record<string, any> = await fetch(url, {
...options,
method: method || 'GET',
timeout: 1000 * 60,
headers: {
...headers,
'user-agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
}
})

const { error, usage, choices } = data

if (error) throw new Error(error.message)

return {
usage,
message: choices[0].message
}
} catch (error: any) {
console.log('error', error)
dialogErrorMessage('请求出错:' + error.message)
}
}

/**
* 获取 openai 对话消息
* @param messages 消息列表
*/
export const getOpenAIResultApi = async (messages: RecordData[]) => {
if (!messages.length) return

const { apiKey } = useSettingsStore()

return await request(import.meta.env.VITE_OPEN_AI_URL, {
method: 'POST',
body: Body.json({
model: 'gpt-3.5-turbo-0301',
messages,
temperature: 0.6,
stream: false
}),
headers: {
Authorization: `Bearer ${apiKey || import.meta.env.VITE_OPEN_AI_API_KEY}`
}
})
}

/**
* 获取 openai 对话消息(流)
* @param messages 消息列表
*/
export const getOpenAIResultStream = async (messages: RecordData[]) => {
if (!messages.length) return

const { apiKey } = useSettingsStore()
const { addSessionData } = useSessionStore()
const { streamReply } = storeToRefs(useSessionStore())
streamReply.value = ''

await fetchEventSource(import.meta.env.VITE_OPEN_AI_URL, {
method: 'POST',
body: JSON.stringify({
model: 'gpt-3.5-turbo-0301',
messages,
temperature: 0.6,
stream: true
}),
headers: {
Authorization: `Bearer ${apiKey || import.meta.env.VITE_OPEN_AI_API_KEY}`,
'Content-Type': 'application/json',
Accept: 'application/json'
},
onmessage(msg: EventSourceMessage) {
if (msg.data !== '[DONE]') {
const { choices } = JSON.parse(msg.data)

if (!choices[0].delta.content) return
streamReply.value += choices[0].delta.content
}
},
onclose() {
const res: RecordData = {
role: 'assistant',
content: streamReply.value
}
addSessionData(false, '', res)
},
onerror({ message }: any) {
throw new Error(`流输出出错:${message}`)
}
})
}
export * from './request'
export * from './openAi'
166 changes: 166 additions & 0 deletions src/api/openAi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { Body } from '@tauri-apps/api/http'
import { request } from '.'
import { OPEN_AI_MODEL } from '@/constants'
import { dialogErrorMessage } from '@/utils'
import {
fetchEventSource,
type EventSourceMessage
} from '@microsoft/fetch-event-source'
import { useSessionStore, useSettingsStore, useRoleStore } from '@/stores'
import { executeSQL } from '@/sqls'
import type { MessageData, SessionData } from '@/types'

/**
* 获取 openai 对话消息
* @param messages 消息列表
*/
export const getOpenAIResultApi = async (messages: MessageData[]) => {
if (!messages.length) return

const { apiKey } = useSettingsStore()

return await request(import.meta.env.VITE_OPEN_AI_URL, {
method: 'POST',
body: Body.json({
model: OPEN_AI_MODEL,
messages
}),
headers: {
Authorization: `Bearer ${apiKey || import.meta.env.VITE_OPEN_AI_API_KEY}`
}
})
}

/**
* 获取 openai 对话消息(流)
* @param messages 消息列表
*/
export const getOpenAIResultStreamApi = async (messages: MessageData[]) => {
if (!messages.length) return

const { apiKey } = useSettingsStore()
const { updateSessionData } = useSessionStore()
const { sessionDataList } = storeToRefs(useSessionStore())

await fetchEventSource(import.meta.env.VITE_OPEN_AI_URL, {
method: 'POST',
body: JSON.stringify({
model: OPEN_AI_MODEL,
messages,
temperature: 0.6,
stream: true
}),
headers: {
Authorization: `Bearer ${apiKey || import.meta.env.VITE_OPEN_AI_API_KEY}`,
'Content-Type': 'application/json',
Accept: 'application/json'
},
onmessage(msg: EventSourceMessage) {
if (msg.data !== '[DONE]') {
const { choices } = JSON.parse(msg.data)

if (!choices[0].delta.content) return

sessionDataList.value.at(-1)!.message.content +=
choices[0].delta.content
}
},
onclose() {
updateSessionData(sessionDataList.value.at(-1)!)
},
onerror({ message }: any) {
throw new Error(`流输出出错:${message}`)
}
})
}

/**
* 获取 ai 回答
* @param value 消息内容
*/
export const getAiMessage = async (value?: string) => {
const { isThinking } = storeToRefs(useSessionStore())

try {
const { currentRole } = useRoleStore()

if (!currentRole) return

const messages: MessageData[] = []

const { currentSession, sessionDataList } = useSessionStore()
const { isMemory } = useSettingsStore()

const lastQuestion = sessionDataList.filter((item) => item.is_ask).at(-1)

// 记忆模式,或者是第一次对话,都要生成角色描述
if (sessionDataList.length < 3 || isMemory) {
messages.push({
role: 'system',
content: currentRole.description
})
}

// 获取记忆(限制5条),往前推直到出现 is_momery 为 false 的
// TODO 应该进行限流,防止出现过多的记忆,导致token超出
const addMemory = async () => {
if (isMemory) {
// TODO: 优化 sql
const sql = `SELECT * FROM session_data WHERE session_id = '${currentSession?.id}' ORDER BY id DESC LIMIT 5;`
const memoryList = (await executeSQL(sql)) as SessionData[]

let count = 0
const arr = []
while (count < memoryList.length) {
if (!memoryList[count].is_memory) break
arr.push(memoryList[count++].message)
}
messages.push(...arr.reverse())
}
}

// 再次生成上一次问题
if (!value) {
if (!lastQuestion) return

// 为了保证统一,这之后的内容全部删掉
const deleteSql = `DELETE FROM session_data WHERE session_id = '${lastQuestion?.session_id}' AND id >= ${lastQuestion?.id};`
await executeSQL(deleteSql)

await addMemory()
messages.push(lastQuestion?.message)
} else {
await addMemory()
messages.push({
role: 'user',
content: value
})
}

const { isThinking } = storeToRefs(useSessionStore())
const { addSessionData } = useSessionStore()

isThinking.value = true

await addSessionData({
isAsk: true,
data: messages.at(-1)!
})

await addSessionData({
isAsk: false,
data: {
role: 'assistant',
content: ''
}
})

await getOpenAIResultStreamApi(messages)

isThinking.value = false
} catch ({ message }: any) {
dialogErrorMessage(message)

isThinking.value = false
}
}
35 changes: 35 additions & 0 deletions src/api/request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { fetch, type FetchOptions } from '@tauri-apps/api/http'
import { dialogErrorMessage } from '@/utils'

/**
* 普通请求
* @param url 请求地址
* @param options 请求参数
*/
export const request = async (url: string, options?: FetchOptions) => {
try {
const { method, headers } = options || {}

const { data }: Record<string, any> = await fetch(url, {
...options,
method: method || 'GET',
timeout: 1000 * 60,
headers: {
...headers,
'user-agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
}
})

const { error, usage, choices } = data

if (error) throw new Error(error.message)

return {
usage,
message: choices[0].message
}
} catch (error: any) {
dialogErrorMessage('请求出错:' + error.message)
}
}
Loading

0 comments on commit 532245f

Please sign in to comment.