change the scope of blockModelSet from during process to during one time request; add the feature to specify a fixed model in test dialog

This commit is contained in:
geekgeekrun
2025-04-23 02:00:03 +08:00
parent bbd670b674
commit d23b0475dc
4 changed files with 177 additions and 43 deletions
@@ -1,9 +1,9 @@
import { saveGptCompletionRequestRecord } from '@geekgeekrun/sqlite-plugin/dist/handlers' import { saveGptCompletionRequestRecord } from '@geekgeekrun/sqlite-plugin/dist/handlers'
export const RequestSceneEnum = { export enum RequestSceneEnum {
testing: 1, testing = 1,
readNoReplyAutoReminder: 2, readNoReplyAutoReminder = 2,
geekAutoStartChatWithBoss: 3 geekAutoStartChatWithBoss = 3
} }
let dbInitPromise let dbInitPromise
@@ -555,7 +555,8 @@ export default function initIpc() {
}) })
async function requestLlm(_, requestPayload) { async function requestLlm(_, requestPayload) {
return await requestNewMessageContent(requestPayload.messageList, { return await requestNewMessageContent(requestPayload.messageList, {
requestScene: RequestSceneEnum.testing requestScene: RequestSceneEnum.testing,
llmConfigIdForPick: requestPayload.llmConfigIdForPick ?? null
}) })
} }
ipcMain.handle('request-llm-for-test', requestLlm) ipcMain.handle('request-llm-for-test', requestLlm)
@@ -27,8 +27,7 @@ export const sendLookForwardReplyEmotion = async (page: Page) => {
await lookForwardReplyEmojiProxy!.click() await lookForwardReplyEmojiProxy!.click()
} }
const blockModelSet = new Set() const pickLlmConfigFromList = (llmConfigList, blockModelSet) => {
const pickLlmConfigFromList = (llmConfigList) => {
if (llmConfigList.length === 1) { if (llmConfigList.length === 1) {
llmConfigList[0].enabled = true llmConfigList[0].enabled = true
llmConfigList[0].serveWeight = SINGLE_ITEM_DEFAULT_SERVE_WEIGHT llmConfigList[0].serveWeight = SINGLE_ITEM_DEFAULT_SERVE_WEIGHT
@@ -115,7 +114,13 @@ export const writeDefaultAutoRemindPrompt = async () => {
await writeStorageFile(autoReminderPromptTemplateFileName, defaultPrompt, { isJson: false }) await writeStorageFile(autoReminderPromptTemplateFileName, defaultPrompt, { isJson: false })
} }
export const requestNewMessageContent = async (chatRecords, { requestScene } = {}) => { export const requestNewMessageContent = async (
chatRecords,
{
requestScene,
llmConfigIdForPick
}: { requestScene?: RequestSceneEnum; llmConfigIdForPick?: string[] } = {}
) => {
const template = await getValidTemplate() const template = await getValidTemplate()
const resumeObject = (await readConfigFile('resumes.json'))?.[0] const resumeObject = (await readConfigFile('resumes.json'))?.[0]
const resumeContent = formatResumeJsonToMarkdown(resumeObject) const resumeContent = formatResumeJsonToMarkdown(resumeObject)
@@ -151,9 +156,15 @@ export const requestNewMessageContent = async (chatRecords, { requestScene } = {
const llmRequestRecord: Omit<LlmModelUsageRecord, 'id' | 'providerApiSecretMd5'> & { const llmRequestRecord: Omit<LlmModelUsageRecord, 'id' | 'providerApiSecretMd5'> & {
providerApiSecret: string providerApiSecret: string
} = {} } = {}
const blockModelSet = new Set()
while (!res) { while (!res) {
const llmConfigList = await readConfigFile('llm.json') let llmConfigList = await readConfigFile('llm.json')
llmConfig = pickLlmConfigFromList(llmConfigList) if (llmConfigIdForPick?.length) {
llmConfigList = llmConfigList.filter((it) => {
return llmConfigIdForPick.includes(it.id)
})
}
llmConfig = pickLlmConfigFromList(llmConfigList, blockModelSet)
if (!llmConfig) { if (!llmConfig) {
throw new Error(`CANNOT_FIND_A_USABLE_MODEL`) throw new Error(`CANNOT_FIND_A_USABLE_MODEL`)
} }
@@ -1,41 +1,129 @@
<template> <template>
<div class="h100vh flex flex-col"> <div class="h100vh flex flex-col">
<div <div
ref="scrollElRef"
:style="{ :style="{
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
flex: 1, flex: 1,
overflow: `auto`, overflow: `auto`,
margin: `0 auto`, margin: `0 auto`,
alignItems: `flex-end` alignItems: `flex-end`,
width: '100%'
}" }"
> >
<div class="pb20px"></div> <div
<div v-for="(item, index) in messageList" :key="index" class="message-item"> v-if="messageList.length"
{{ item.text }} :style="{
width: '480px',
margin: '0 auto'
}"
>
<div class="pb20px"></div>
<div v-for="(item, index) in messageList" :key="index" flex flex-col flex-items-end>
<div class="message-item-wrap flex flex-col">
<div class="message-item">
{{ item.text }}
</div>
<div
:style="{
width: 'fit-content',
alignSelf: 'flex-end'
}"
font-size-10px
>
{{ item.usedLlmConfig.model }}
</div>
<div
v-if="item?.usedLlmConfig?.providerCompleteApiUrl?.trim()"
:style="{
width: 'fit-content',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
alignSelf: 'flex-end',
color: '#bbb'
}"
font-size-10px
w-fit-content
max-w-20em
>
{{ item.usedLlmConfig.providerCompleteApiUrl }}
</div>
</div>
</div>
<div class="pb20px"></div>
</div>
<div v-else w-full h-full flex flex-item-center justify-center>
<el-empty>
<template #description>
<template v-if="!isLoading">
点击下方 <el-button
font-size-16px
h-fit-content
align-baseline
p0
type="text"
@click.prevent="sendLlmGeneratedContent"
>发送开场白</el-button
> 以开始模拟聊天
</template>
<template v-else>请稍候第一条消息正在回复的路上~</template>
</template>
</el-empty>
</div> </div>
<div class="pb20px"></div>
</div> </div>
<div <div
:style="{ :style="{
display: 'grid', display: 'grid',
gridTemplateColumns: '100px 1fr', gridTemplateColumns: 'min-content 1fr min-content',
height: `fit-content`, height: `fit-content`,
paddingTop: `10px`, paddingTop: `10px`,
paddingBottom: `10px`, paddingBottom: `10px`,
backgroundColor: `#f0f0f0` backgroundColor: `#f0f0f0`
}" }"
> >
<!-- <el-select v-model="selectedLlmConfig"> <el-select v-model="selectedLlmConfig" ml10px w160px placeholder="随机使用一个模型">
<el-option v-for="(it, index) in llmConfigList" :key="index" :label="it"> <el-option
<div>{{ it.model }}</div> v-for="(it, index) in llmConfigListForRender"
<div>{{ it.providerCompleteApiUrl }}</div> :key="index"
:value="it.id"
:label="it.model"
:disabled="!it.enabled"
:style="{
paddingTop: '10px',
paddingBottom: '10px',
height: 'auto',
lineHeight: '1.25em'
}"
>
<div
:style="{
display: 'flex',
justifyContent: 'space-between'
}"
>
<div>{{ it.model }}</div>
<div class="font-size-12px color-#bbb">
{{ formatApiSecret(it.providerApiSecret) || '' }}
</div>
</div>
<div
v-if="it?.providerCompleteApiUrl?.trim?.()"
:style="{
color: '#bbb',
width: '35em',
fontSize: '12px',
overflow: 'hidden',
textOverflow: 'ellipsis'
}"
>
{{ it.providerCompleteApiUrl }}
</div>
</el-option> </el-option>
</el-select> --> </el-select>
<el-button ml20px type="text" @click="closeWindow">关闭对话框</el-button>
<el-button <el-button
:loading="isLoading" :loading="isLoading"
mr10px
width-fit-content width-fit-content
type="primary" type="primary"
@click="sendLlmGeneratedContent" @click="sendLlmGeneratedContent"
@@ -44,38 +132,57 @@
<template v-else-if="!messageList.length">发送开场白</template> <template v-else-if="!messageList.length">发送开场白</template>
<template v-else>发送下一句提醒内容</template> <template v-else>发送下一句提醒内容</template>
</el-button> </el-button>
<div></div> <el-button mr10px type="text" @click="closeWindow">关闭对话框</el-button>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue' import { computed, ref } from 'vue'
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
type MessageItem = { type MessageItem = {
text: string text: string
generatedBy: string usedLlmConfig: string
// recordInfo: any
} }
const messageList = ref<MessageItem[]>([]) const messageList = ref<MessageItem[]>([])
// const llmConfigList = ref([]) const llmConfigList = ref([])
// async function getLlmConfigList() { const llmConfigListForRender = computed(() => {
// debugger return [
// llmConfigList.value = await electron.ipcRenderer.invoke('get-llm-config-for-test') {
// } id: null,
// getLlmConfigList().catch(() => {}) model: '随机使用一个模型',
// const selectedLlmConfig = ref() providerCompleteApiUrl: null,
enabled: true
},
...(llmConfigList.value ?? [])
]
})
async function getLlmConfigList() {
llmConfigList.value = await electron.ipcRenderer.invoke('get-llm-config-for-test')
}
getLlmConfigList().catch(() => {})
const selectedLlmConfig = ref(null)
const scrollElRef = ref(null)
const isLoading = ref(false) const isLoading = ref(false)
async function sendLlmGeneratedContent() { async function sendLlmGeneratedContent() {
isLoading.value = true isLoading.value = true
try { try {
const response = await electron.ipcRenderer.invoke('request-llm-for-test', { const response = await electron.ipcRenderer.invoke('request-llm-for-test', {
messageList: JSON.parse(JSON.stringify(messageList.value ?? [])) messageList: JSON.parse(JSON.stringify(messageList.value ?? [])),
llmConfigIdForPick: selectedLlmConfig.value ? [selectedLlmConfig.value] : null
}) })
console.log(response) console.log(response)
messageList.value.push({ messageList.value.push({
text: response.responseText, text: response.responseText,
generatedBy: response.usedLlmConfig usedLlmConfig: response.usedLlmConfig
})
await sleep(50)
;(scrollElRef.value as any as HTMLDivElement)?.scrollTo({
top: scrollElRef.value?.scrollHeight,
behavior: 'smooth'
}) })
} finally { } finally {
isLoading.value = false isLoading.value = false
@@ -85,17 +192,32 @@ async function sendLlmGeneratedContent() {
function closeWindow() { function closeWindow() {
electron.ipcRenderer.send(`close-read-no-reply-reminder-llm-mock-window`) electron.ipcRenderer.send(`close-read-no-reply-reminder-llm-mock-window`)
} }
function formatApiSecret(text) {
if (typeof text !== 'string' || !text?.trim()) {
return ''
}
if (text === 'ollama') {
return text
}
if (text.length >= 8) {
return `${text.slice(0, 4)}***${text.slice(-4)}`
}
return `***`
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.message-item { .message-item-wrap {
line-height: 1.25em;
font-size: 14px;
background-color: #d1f0ef;
color: #333;
padding: 10px;
border-radius: 8px 8px 0 8px;
margin-top: 20px;
max-width: 420px; max-width: 420px;
margin-top: 20px;
.message-item {
line-height: 1.25em;
font-size: 14px;
background-color: #d1f0ef;
color: #333;
padding: 10px;
border-radius: 8px 8px 0 8px;
}
} }
</style> </style>