Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion prompto-lab-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"build-only": "vite build",
"type-check": "vue-tsc --build",
"lint": "eslint . --fix",
"format": "prettier --write src/"
"format": "prettier --write src/",
"serve": "vite preview --port 4173 --host"
},
"dependencies": {
"lodash-es": "^4.17.21",
Expand Down
2 changes: 1 addition & 1 deletion prompto-lab-ui/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { RouterView } from 'vue-router'
import { useRoute } from 'vue-router'
import { ref,onMounted,onUnmounted,computed } from 'vue'
import AppHeader from '@/components/AppHeader.vue'
import AppHeader from '@/components/layout/AppHeader.vue'

const route = useRoute()
const appRef = ref<HTMLDivElement>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<ChatMain
:messages="currentBranchMessages"
:is-loading="isLoading"
:streaming-node-id="streamingNodeId"
@send-message="handleSendMessage"
/>
</div>
Expand Down Expand Up @@ -113,6 +114,10 @@ const isResizing = ref(false)
const startX = ref(0)
const startRightWidth = ref(0)

// 添加流式消息状态管理
const streamingNodeId = ref<string>('')
const streamingContent = ref<string>('')

// 会话状态
const session = ref<ConversationSession | null>(null)
const eventSource = ref<EventSource | null>(null)
Expand Down Expand Up @@ -179,28 +184,70 @@ const initializeSession = async () => {
const handleSSEMessage = (response: MessageResponse) => {
console.log('收到SSE消息:', response)

// 根据消息类型处理
switch (response.type) {
case 'AI_QUESTION':
case 'AI_ANSWER': // 添加对AI_ANSWER类型的处理
case 'AI_ANSWER':
addAIMessage(response.nodeId, response.content)
break
case 'AI_SELECTION_QUESTION':
addAISelectionMessage(response.nodeId, response.content, response.options || [])
break
case 'USER_ANSWER':
// 用户消息确认,通常不需要特殊处理
case 'AI_STREAM_START':
// 开始流式响应
startStreamingMessage(response.nodeId)
break
case 'SYSTEM_INFO':
toast.info({
title: '系统消息',
message: response.content,
duration: 3000
})
case 'AI_STREAM_CHUNK':
// 接收流式内容片段
appendStreamingContent(response.nodeId, response.content)
break
default:
console.warn('未知的消息类型:', response.type, response)
case 'AI_STREAM_END':
// 结束流式响应
finishStreamingMessage(response.nodeId)
break
// ... 其他case保持不变
}
}

// 开始流式消息
const startStreamingMessage = (nodeId: string) => {
streamingNodeId.value = nodeId
streamingContent.value = ''

const aiNode: ConversationNode = {
id: nodeId,
content: '',
type: 'assistant',
timestamp: new Date(),
parentId: currentNodeId.value,
children: [],
isActive: true
}

const currentNode = conversationTree.value.get(currentNodeId.value)
if (currentNode) {
currentNode.children.push(nodeId)
}

conversationTree.value.set(nodeId, aiNode)
currentNodeId.value = nodeId
isLoading.value = false
}

// 追加流式内容
const appendStreamingContent = (nodeId: string, chunk: string) => {
if (streamingNodeId.value === nodeId) {
streamingContent.value += chunk

// 更新节点内容
const node = conversationTree.value.get(nodeId)
if (node) {
node.content = streamingContent.value
}
}
}

// 完成流式消息
const finishStreamingMessage = (nodeId: string) => {
if (streamingNodeId.value === nodeId) {
streamingNodeId.value = ''
streamingContent.value = ''
}
}

Expand Down Expand Up @@ -228,7 +275,13 @@ const handleSSEError = (error: Event) => {
}

// 添加AI消息到对话树
const addAIMessage = (nodeId: string, content: string) => {
const addAIMessage = (nodeId: string, content: string, isStreaming = false) => {
const existingNode = conversationTree.value.get(nodeId)
if (existingNode && isStreaming) {
// 如果是流式更新,只更新内容
existingNode.content = content
return
}
const aiNode: ConversationNode = {
id: nodeId,
content,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
:class="{
'configured': operation.modelName,
'disabled': !operation.enabled,
'testing': testing === operationType
'testing': testing === String(operationType)
}"
>
<!-- 卡片头部 -->
Expand Down Expand Up @@ -183,10 +183,10 @@
v-if="operation.modelName"
@click="testOperation(String(operationType))"
class="action-btn test large"
:disabled="testing === operationType"
:title="testing === operationType ? '测试中...' : '测试操作'"
:disabled="testing === String(operationType)"
:title="testing === String(operationType) ? '测试中...' : '测试操作'"
>
<svg v-if="testing === operationType" class="w-5 h-5 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg v-if="testing === String(operationType)" class="w-5 h-5 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
<svg v-else class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
Expand Down
Loading
Loading