Skip to content

Commit 2e06be5

Browse files
committed
feat: check apikey
1 parent eee31c8 commit 2e06be5

5 files changed

Lines changed: 99 additions & 28 deletions

File tree

packages/plugins/robot/src/Main.vue

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
:prompt-items="promptItems"
1515
:bubble-renderers="bubbleRenderers"
1616
:allowFiles="isVisualModel && robotSettingState.chatMode === CHAT_MODE.Agent"
17+
:beforeSubmit="checkApiKey"
1718
@fileSelected="handleFileSelected"
1819
>
1920
<template #operations>
@@ -63,9 +64,9 @@
6364
</template>
6465

6566
<script setup lang="ts">
66-
import { computed, h, onMounted, ref } from 'vue'
67+
import { computed, h, onMounted, ref, type Component } from 'vue'
6768
import { ToolbarBase } from '@opentiny/tiny-engine-common'
68-
import { TinyPopover } from '@opentiny/vue'
69+
import { TinyNotify, TinyPopover } from '@opentiny/vue'
6970
import { getMetaApi, META_SERVICE } from '@opentiny/tiny-engine-meta-register'
7071
import type { PromptProps } from '@opentiny/tiny-robot'
7172
import { IconThink } from '@opentiny/tiny-robot-svgs'
@@ -88,7 +89,8 @@ const { options } = defineProps({
8889
}
8990
})
9091
91-
const { robotSettingState, CHAT_MODE, getModelCapabilities, saveRobotSettingState } = useModelConfig()
92+
const { robotSettingState, CHAT_MODE, getModelCapabilities, saveRobotSettingState, getAIModelOptions } =
93+
useModelConfig()
9294
9395
const robotChatRef = ref(null)
9496
@@ -169,10 +171,10 @@ const openAIRobot = () => {
169171
robotChatRef.value?.openAIRobot()
170172
}
171173
172-
const bubbleRenderers = computed(() => {
174+
const bubbleRenderers = computed<Record<string, Component>>(() => {
173175
return robotSettingState.chatMode === CHAT_MODE.Agent
174-
? { markdown: AgentRenderer, loading: AgentRenderer, 'collapsible-text': AgentRenderer }
175-
: {}
176+
? { markdown: AgentRenderer, loading: AgentRenderer }
177+
: ({} as Record<string, Component>)
176178
})
177179
178180
const handleFileSelected = (formData: unknown, updateAttachment: (resourceUrl: string) => void) => {
@@ -196,6 +198,28 @@ const handleFileSelected = (formData: unknown, updateAttachment: (resourceUrl: s
196198
}
197199
}
198200
201+
const checkApiKey = () => {
202+
const provider = getAIModelOptions().find((option) => option.baseUrl === robotSettingState.selectedModel.baseUrl)
203+
if (
204+
!robotSettingState.selectedModel.baseUrl ||
205+
(!robotSettingState.selectedModel.apiKey && provider && !provider.allowEmptyApiKey)
206+
) {
207+
TinyNotify({
208+
type: 'warning',
209+
title: '未设置API Key,请检查设置',
210+
message: '请先设置大模型API Key后重试。',
211+
position: 'top-right',
212+
duration: 5000
213+
})
214+
setTimeout(() => {
215+
showSettingPopover.value = true
216+
}, 1000)
217+
return false
218+
}
219+
220+
return true
221+
}
222+
199223
onMounted(async () => {
200224
setTimeout(() => {
201225
showTeleport.value = true

packages/plugins/robot/src/components/chat/RobotChat.vue

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ import MarkdownRenderer from '../renderers/MarkdownRenderer.vue'
121121
import ImgRenderer from '../renderers/ImgRenderer.vue'
122122
import { serializeError } from '../../utils'
123123
124-
const { promptItems, allowFiles, bubbleRenderers } = defineProps({
124+
const { promptItems, allowFiles, bubbleRenderers, beforeSubmit } = defineProps({
125125
promptItems: {
126126
type: Array as PropType<PromptProps[]>,
127127
default: () => []
@@ -133,6 +133,10 @@ const { promptItems, allowFiles, bubbleRenderers } = defineProps({
133133
bubbleRenderers: {
134134
type: Object as PropType<Record<string, Component>>,
135135
default: () => ({})
136+
},
137+
beforeSubmit: {
138+
type: Function,
139+
default: () => true
136140
}
137141
})
138142
@@ -275,6 +279,15 @@ const handleSendMessage = async (content: string) => {
275279
if (!messageContent || (typeof messageContent === 'string' && !messageContent.trim())) {
276280
return
277281
}
282+
283+
let result = beforeSubmit?.()
284+
if (result && typeof result.then === 'function') {
285+
result = await result
286+
}
287+
if (result !== true) {
288+
return
289+
}
290+
278291
const userMessage: ChatMessage = {
279292
role: 'user',
280293
content: messageContent

packages/plugins/robot/src/components/header-extension/RobotSettingPopover.vue

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
<template>
22
<div ref="robotSetting" class="robot-setting">
33
<div class="header">设置</div>
4-
<tiny-tabs tab-style="button-card" class="full-width-tabs" v-model="state.activeName">
4+
<tiny-tabs
5+
tab-style="button-card"
6+
class="full-width-tabs"
7+
v-model="state.activeName"
8+
:before-leave="handleChangeTab"
9+
>
510
<tiny-tab-item title="可选模型" :name="EXISTING_MODELS">
611
<tiny-form
712
:rules="existFormRules"
@@ -116,7 +121,7 @@
116121
</template>
117122
<script lang="ts">
118123
/* metaService: engine.plugins.robot.RobotSettingPopover */
119-
import { ref, reactive, computed } from 'vue'
124+
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
120125
import type { Component } from 'vue'
121126
import {
122127
TinyForm,
@@ -199,12 +204,21 @@ export default {
199204
})
200205
201206
const customizeFormRules = {
202-
baseUrl: [{ required: true, message: '必填', trigger: 'blur' }]
207+
baseUrl: [{ required: true, message: '必填', trigger: 'blur' }],
208+
model: [{ required: true, message: '必填', trigger: 'blur' }]
203209
}
204210
205-
const existFormRules = {
206-
apiKey: [{ required: true, message: '必填', trigger: 'blur' }]
207-
}
211+
const existFormRules = computed(() => {
212+
const rules = {
213+
baseUrl: [{ required: true, message: '必填', trigger: 'change' }],
214+
model: [{ required: true, message: '必填', trigger: 'change' }]
215+
}
216+
const provider = getProviderByBaseUrl(state.existFormData.baseUrl)
217+
if (provider && !provider.allowEmptyApiKey) {
218+
rules.apiKey = [{ required: true, message: '必填', trigger: 'blur' }]
219+
}
220+
return rules
221+
})
208222
209223
const closePanel = () => {
210224
emit('close')
@@ -216,7 +230,7 @@ export default {
216230
const models = provider?.models.map(modelTransformer) || []
217231
218232
state.existFormData.label = provider?.label || ''
219-
state.existFormData.model = models[0]?.name || ''
233+
state.existFormData.model = models[0]?.value || ''
220234
state.existFormData.completeModel = ''
221235
222236
saveRobotSettingState({
@@ -258,6 +272,19 @@ export default {
258272
})
259273
}
260274
275+
const validate = () => {
276+
const form = state.activeName === EXISTING_MODELS ? robotSettingExistForm : robotSettingCustomizeForm
277+
form.value?.validate().catch((_error) => {})
278+
}
279+
280+
onMounted(validate)
281+
282+
const handleChangeTab = (activeName, usedActiveName) => {
283+
if (activeName !== usedActiveName) {
284+
nextTick(validate)
285+
}
286+
}
287+
261288
const initFormData = () => {
262289
const { activeName, existModel, customizeModel } = loadRobotSettingState() || {}
263290
const initModel = robotSettingState.selectedModel
@@ -285,6 +312,7 @@ export default {
285312
initFormData()
286313
287314
return {
315+
handleChangeTab,
288316
baseUrlOptions,
289317
modelOptions,
290318
compactModelOptions,

packages/plugins/robot/src/composables/useChat.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -106,16 +106,11 @@ const updateLLMConfig = (newConfig: Omit<AIModelConfig, 'provider' | 'providerIm
106106
}
107107

108108
const removeLoading = (messages: ChatMessage[], name?: string) => {
109-
const lastMessage = messages.at(-1)
110-
if (name === 'latest' && lastMessage.renderContent?.at(-1)?.type === 'loading') {
111-
lastMessage.renderContent.pop()
112-
return
113-
}
114-
const index = lastMessage.renderContent?.findIndex(
115-
(item) => item.type === 'loading' && (name ? item.content === name : true)
116-
)
109+
const renderContent = messages.at(-1)?.renderContent
110+
if (!renderContent || !renderContent.length) return
111+
const index = renderContent.findLastIndex((item) => item.type === 'loading' && (name ? item.content === name : true))
117112
if (index !== -1) {
118-
lastMessage.renderContent?.splice(index, 1)
113+
renderContent?.splice(index, 1)
119114
}
120115
}
121116

@@ -136,7 +131,7 @@ const events: UseMessageOptions['events'] = {
136131
}
137132
const lastMessage = messages.value.at(-1)
138133
if (choice.delta.reasoning_content || choice.delta.content || choice.delta.tool_calls?.length) {
139-
removeLoading(messages.value, 'latest')
134+
removeLoading(messages.value)
140135
}
141136
handleDeltaReasoning(choice, lastMessage) // eslint-disable-line
142137
handleDeltaContent(choice, lastMessage) // eslint-disable-line
@@ -326,7 +321,7 @@ const handleToolCall = async (
326321
data.choices[0].delta.content ||
327322
data.choices[0].delta.tool_calls?.length
328323
) {
329-
removeLoading(messages, 'latest')
324+
removeLoading(messages)
330325
}
331326
if (data.choices[0].delta.reasoning_content) {
332327
handleDeltaReasoning(data.choices[0], currentMessage)
@@ -346,7 +341,7 @@ const handleToolCall = async (
346341
getMessageManager().messageState.status = STATUS.ERROR
347342
},
348343
onDone: async () => {
349-
removeLoading(messages, 'latest')
344+
removeLoading(messages)
350345
const toolCalls = messages.at(-1)!.tool_calls
351346
if (toolCalls?.length) {
352347
await handleToolCall(toolCalls, messages, toolMessages)
@@ -382,7 +377,7 @@ export default function () {
382377
chatAbortController?.abort()
383378
messageManager.abortRequest()
384379
messageManager.messageState.status = STATUS.ABORTED
385-
removeLoading(messageManager.messages.value, 'latest')
380+
removeLoading(messageManager.messages.value)
386381
},
387382
...rest,
388383
switchConversation: (conversationId: string) => {
@@ -392,12 +387,21 @@ export default function () {
392387
rest.switchConversation(conversationId)
393388
// 切换会话后跟随切换对话模式
394389
if (conversation.metadata?.chatMode) {
395-
robotSettingState.chatMode = conversation.metadata.chatMode as string
390+
robotSettingState.chatMode = robotSettingState.chatMode as string
396391
} else {
397392
robotSettingState.chatMode = CHAT_MODE.Agent
398393
rest.updateMetadata(conversationId, { chatMode: CHAT_MODE.Agent })
399394
rest.saveConversations()
400395
}
396+
if (robotSettingState.chatMode === CHAT_MODE.Agent) {
397+
messageManager.messages.value.at(-1)?.renderContent?.forEach((item) => {
398+
if (item.type === 'loading') {
399+
item.status = 'failed'
400+
}
401+
})
402+
} else {
403+
removeLoading(messageManager.messages.value)
404+
}
401405
},
402406
createConversation: (title?: string) => {
403407
createConversation(title, { chatMode: robotSettingState.chatMode })

packages/plugins/robot/src/constants/model-config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const DEFAULT_LLM_MODELS = [
1313
provider: 'bailian',
1414
label: '阿里云百炼',
1515
baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
16+
allowEmptyApiKey: false,
1617
models: [
1718
// Agent/chat
1819
{
@@ -71,6 +72,7 @@ export const DEFAULT_LLM_MODELS = [
7172
provider: 'deepseek',
7273
label: 'DeepSeek',
7374
baseUrl: 'https://api.deepseek.com/v1',
75+
allowEmptyApiKey: false,
7476
models: [
7577
{
7678
label: 'DeepSeek',

0 commit comments

Comments
 (0)