Skip to content

Commit

Permalink
feat: add ai generate image
Browse files Browse the repository at this point in the history
  • Loading branch information
orangelckc authored and ayangweb committed Mar 28, 2023
1 parent 9300bc6 commit 7cd91dc
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 33 deletions.
2 changes: 2 additions & 0 deletions .eslintrc-auto-import.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@
"executeSQL": true,
"generalKeys": true,
"getActivePinia": true,
"getAiIamge": true,
"getAiMessage": true,
"getContributorsApi": true,
"getCurrentInstance": true,
"getCurrentScope": true,
"getKeySymbol": true,
"getMemoryList": true,
"getOpenAICreditApi": true,
"getOpenAIImage": true,
"getOpenAIKey": true,
"getOpenAIResultApi": true,
"getOpenAIResultStreamApi": true,
Expand Down
22 changes: 21 additions & 1 deletion src/api/openAi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
fetchEventSource,
type EventSourceMessage
} from '@microsoft/fetch-event-source'
import type { MessageData } from '@/types'
import type { MessageData, ImageData } from '@/types'

/**
* 获取 openai 对话消息
Expand Down Expand Up @@ -126,3 +126,23 @@ export const getOpenAICreditApi = async () => {
}
}
}

/**
* 根据提示创建图像
* @param messages 图像参数
*/
export const getOpenAIImage = async (imageData: ImageData) => {
if (!imageData) return

const apiKey = getOpenAIKey()
if (!apiKey) return

return await request('/v1/images/generations', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
HostUrl: HOST_URL.OPENAI
},
body: Body.json(imageData)
})
}
50 changes: 30 additions & 20 deletions src/components/Input/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
import { appWindow } from '@tauri-apps/api/window'
const recordStore = useSessionStore()
const { isThinking } = storeToRefs(recordStore)
const { isThinking, currentSession } = storeToRefs(recordStore)
const roleStore = useRoleStore()
const { currentRole, isEdit, textAreaValue, popoverVisible } =
const { currentRole, isEdit, textAreaValue, imageValue, popoverVisible } =
storeToRefs(roleStore)
const textAreaElement = ref<HTMLTextAreaElement | null>(null)
Expand All @@ -25,11 +25,10 @@ const onKeydown = (event: KeyboardEvent) => {
if (!value) return
// if (popoverVisible) {
popoverVisible.value = false
// }
getAiMessage(value)
if (currentSession.value?.type === 'text') getAiMessage(value)
else getAiIamge(value)
textAreaElement.value?.blur()
textAreaValue.value = ''
Expand Down Expand Up @@ -60,21 +59,32 @@ onMounted(() => {
<template>
<div class="app-input flex items-center gap-2">
<RoleList />

<a-textarea
ref="textAreaElement"
class="bordered bg-transparent!"
:class="!textAreaValue && 'rounded-10'"
:placeholder="isThinking ? 'AI 正在思考...' : '有什么问题尽管问我'"
v-model="textAreaValue"
:disabled="isThinking || isEdit"
:auto-size="{
minRows: 1,
maxRows: 5
}"
clearable
@keydown="onKeydown"
></a-textarea>
<div class="flex w-full flex-col">
<a-textarea
ref="textAreaElement"
class="bordered bg-transparent!"
:class="!textAreaValue && 'rounded-10'"
:placeholder="isThinking ? 'AI 正在思考...' : '有什么问题尽管问我'"
v-model="textAreaValue"
:disabled="isThinking || isEdit"
:auto-size="{
minRows: 1,
maxRows: 5
}"
clearable
@keydown="onKeydown"
></a-textarea>
<div class="flex w-full" v-if="currentSession?.type === 'image'">
<a-select placeholder="选择生成的尺寸" v-model="imageValue.size">
<a-option>256x256</a-option>
<a-option>512x512</a-option>
<a-option>1024x1024</a-option>
</a-select>
<a-select placeholder="选择生成的数量" v-model="imageValue.number">
<a-option v-for="i in 10" :key="i">{{ i + '' }}</a-option>
</a-select>
</div>
</div>
</div>
</template>
Expand Down
7 changes: 6 additions & 1 deletion src/sqls/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const executeSQL = async (sql: string, hideError = false) => {
export const initSQL = async () => {
await executeSQL(
`
CREATE TABLE IF NOT EXISTS session (id TEXT, title TEXT, role_id INTEGER, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
CREATE TABLE IF NOT EXISTS session (id TEXT, title TEXT, role_id INTEGER, type TEXT, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
CREATE TABLE IF NOT EXISTS session_data (id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT, message TEXT, is_ask INTEGER, is_memory INTEGER, message_type TEXT, time TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
CREATE TABLE IF NOT EXISTS role (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, is_default INTEGER DEFAULT false);
CREATE TABLE IF NOT EXISTS credit (id INTEGER PRIMARY KEY AUTOINCREMENT, history_id INTEGER, token_cost INTEGER, api_key TEXT);
Expand All @@ -81,6 +81,11 @@ export const initSQL = async () => {
`ALTER TABLE session ADD COLUMN update_time TIMESTAMP DEFAULT ${Date.now()};`,
true
)
// 2. 2023-03-27 在 session 表中添加 type 列,记录对话的类型
await executeSQL(
`ALTER TABLE session ADD COLUMN type TEXT DEFAULT 'text';`,
true
)
}

/**
Expand Down
5 changes: 5 additions & 0 deletions src/stores/role.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export const useRoleStore = defineStore(
const popoverVisible = ref(false)
// 检索角色列表的输入框值
const textAreaValue = ref('')
const imageValue = reactive({
number: '1',
size: '256x256'
})
// 是否有角色正在编辑
const isEdit = computed(() => roleList.value.some((item) => item.isEdit))

Expand Down Expand Up @@ -118,6 +122,7 @@ export const useRoleStore = defineStore(
filterRoleList,
popoverVisible,
textAreaValue,
imageValue,
isEdit,
getRoleList,
getFilterRoleList,
Expand Down
32 changes: 22 additions & 10 deletions src/stores/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ export const useSessionStore = defineStore(
currentSession.value = {
id: crypto.randomUUID(),
title: '',
role_id: defaultRole.id
role_id: defaultRole.id,
type: 'image'
}
}

Expand All @@ -75,19 +76,30 @@ export const useSessionStore = defineStore(
await insertSQL('session', {
id: currentSession.value.id,
title: data.content,
role_id: currentRole.id
role_id: currentRole.id,
type: currentSession.value.type
})
}

const { isMemory } = useSettingsStore()
if (messageType === 'text') {
const { isMemory } = useSettingsStore()

await insertSQL('session_data', {
session_id: currentSession.value.id,
is_ask: isAsk,
is_memory: isMemory,
message: data,
message_type: messageType
})
await insertSQL('session_data', {
session_id: currentSession.value.id,
is_ask: isAsk,
is_memory: isMemory,
message: data,
message_type: messageType
})
} else if (messageType === 'image') {
await insertSQL('session_data', {
session_id: currentSession.value.id,
is_ask: isAsk,
is_memory: false,
message: data,
message_type: messageType
})
}

await getSessionData()
}
Expand Down
7 changes: 7 additions & 0 deletions src/types/sql.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ export interface MessageData {
role: Role
content: string
}

export interface ImageData {
n: number
prompt: string
size: '256x256' | '512x512' | '1024x1024'
}
export interface RolePayload {
id?: number
name: string
Expand Down Expand Up @@ -33,6 +39,7 @@ export interface SessionPayload {
role_id: number
update_time?: string
name?: string
type: 'text' | 'image'
isEdit?: boolean
}

Expand Down
68 changes: 68 additions & 0 deletions src/utils/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,71 @@ export const getAiMessage = async (value?: string) => {
isThinking.value = false
}
}

/**
* 获取 ai 作图
* @param value 消息内容
*/
export const getAiIamge = async (value?: string) => {
const { isThinking, sessionDataList } = storeToRefs(useSessionStore())
const { updateSessionData } = useSessionStore()

try {
// 获取图像的记忆模式 = 按照上一张图去修改生成
let imageData

if (!value) {
// 重复上一次提问
const { sessionDataList } = useSessionStore()

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

const deleteSql = `DELETE FROM session_data WHERE session_id = '${lastQuestion?.session_id}' AND id >= ${lastQuestion?.id};`
await executeSQL(deleteSql)

imageData = JSON.parse(lastQuestion?.message.content as any)
} else {
// 添加正常提问
const { imageValue } = useRoleStore()

imageData = {
n: parseInt(imageValue.number),
size: imageValue.size,
prompt: value
}
}

const { addSessionData } = useSessionStore()

isThinking.value = true

await addSessionData({
isAsk: true,
data: {
role: 'user',
content: imageData
}
})

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

const res = await getOpenAIImage(imageData)
if (!res) return

sessionDataList.value.at(-1)!.message.content = res.data as string
} catch ({ message }: any) {
sessionDataList.value.at(-1)!.message.content = message as any

updateSessionData(sessionDataList.value.at(-1)!)
} finally {
isThinking.value = false
}
}
2 changes: 1 addition & 1 deletion src/utils/updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const marked = new MarkdownIt({
})

export const checkVersion = async () => {
const updateInfo = await checkUpdate()
const updateInfo = await checkUpdate().catch(() => null)

if (!updateInfo || !updateInfo.manifest) return

Expand Down

0 comments on commit 7cd91dc

Please sign in to comment.