mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-28 07:17:04 +02:00
fix(editor): Fix outstanding chat UI bugs (no-changelog) (#21413)
This commit is contained in:
parent
a6e27e1926
commit
c46d121bb4
|
|
@ -243,5 +243,6 @@ export interface EnrichedStructuredChunk extends StructuredChunk {
|
|||
messageId: ChatMessageId;
|
||||
previousMessageId: ChatMessageId | null;
|
||||
retryOfMessageId: ChatMessageId | null;
|
||||
executionId: number | null;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -916,6 +916,7 @@ export class ChatHubService {
|
|||
messageId: message.id,
|
||||
previousMessageId: message.previousMessageId,
|
||||
retryOfMessageId: message.retryOfMessageId,
|
||||
executionId: executionId ? +executionId : null,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { LOCAL_STORAGE_CHAT_HUB_SELECTED_MODEL } from '@/constants';
|
||||
import {
|
||||
findOneFromModelsResponse,
|
||||
restoreConversationModelFromMessageOrSession,
|
||||
} from '@/features/ai/chatHub/chat.utils';
|
||||
import { findOneFromModelsResponse, unflattenModel } from '@/features/ai/chatHub/chat.utils';
|
||||
import ChatConversationHeader from '@/features/ai/chatHub/components/ChatConversationHeader.vue';
|
||||
import ChatMessage from '@/features/ai/chatHub/components/ChatMessage.vue';
|
||||
import ChatPrompt from '@/features/ai/chatHub/components/ChatPrompt.vue';
|
||||
|
|
@ -29,7 +26,7 @@ import {
|
|||
import { N8nIconButton, N8nScrollArea } from '@n8n/design-system';
|
||||
import { useLocalStorage, useMediaQuery, useScroll } from '@vueuse/core';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { computed, ref, useTemplateRef, watch } from 'vue';
|
||||
import { computed, nextTick, ref, useTemplateRef, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useChatStore } from './chat.store';
|
||||
import { useDocumentTitle } from '@/composables/useDocumentTitle';
|
||||
|
|
@ -60,9 +57,12 @@ const currentConversation = computed(() =>
|
|||
: undefined,
|
||||
);
|
||||
const currentConversationTitle = computed(() => currentConversation.value?.title);
|
||||
const readyToShowMessages = computed(() => chatStore.agentsReady);
|
||||
|
||||
const { arrivedState } = useScroll(scrollContainerRef, { throttle: 100, offset: { bottom: 100 } });
|
||||
|
||||
const { arrivedState, measure } = useScroll(scrollContainerRef, {
|
||||
throttle: 100,
|
||||
offset: { bottom: 100 },
|
||||
});
|
||||
const defaultModel = useLocalStorage<ChatHubConversationModel | null>(
|
||||
LOCAL_STORAGE_CHAT_HUB_SELECTED_MODEL(usersStore.currentUserId ?? 'anonymous'),
|
||||
null,
|
||||
|
|
@ -109,13 +109,17 @@ const selectedModel = computed<ChatModelDto | undefined>(() => {
|
|||
return modelFromQuery.value;
|
||||
}
|
||||
|
||||
if (!currentConversation.value?.provider) {
|
||||
return defaultModel.value ? chatStore.getAgent(defaultModel.value) : undefined;
|
||||
if (currentConversation.value?.provider) {
|
||||
const model = unflattenModel(currentConversation.value);
|
||||
|
||||
return model ? chatStore.getAgent(model) : undefined;
|
||||
}
|
||||
|
||||
const model = restoreConversationModelFromMessageOrSession(currentConversation.value);
|
||||
if (chatStore.streaming?.sessionId === sessionId.value) {
|
||||
return chatStore.getAgent(chatStore.streaming.model);
|
||||
}
|
||||
|
||||
return model ? chatStore.getAgent(model) : undefined;
|
||||
return defaultModel.value ? chatStore.getAgent(defaultModel.value) : undefined;
|
||||
});
|
||||
|
||||
const { credentialsByProvider, selectCredential } = useChatCredentials(
|
||||
|
|
@ -170,26 +174,22 @@ function scrollToMessage(messageId: ChatMessageId) {
|
|||
|
||||
// Scroll to the bottom when a new message is added
|
||||
watch(
|
||||
() => chatMessages.value[chatMessages.value.length - 1]?.id,
|
||||
(lastMessageId) => {
|
||||
if (!lastMessageId) {
|
||||
[readyToShowMessages, () => chatMessages.value[chatMessages.value.length - 1]?.id],
|
||||
([ready, lastMessageId]) => {
|
||||
if (!ready || !lastMessageId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentMessage = chatStore.lastMessage(sessionId.value);
|
||||
if (lastMessageId !== currentMessage?.id) {
|
||||
scrollToBottom(currentMessage !== null);
|
||||
return;
|
||||
}
|
||||
// Prevent "scroll to bottom" button from appearing when not necessary
|
||||
void nextTick(measure);
|
||||
|
||||
const message = chatStore
|
||||
.getActiveMessages(sessionId.value)
|
||||
.find((m) => m.id === lastMessageId);
|
||||
|
||||
if (message?.previousMessageId) {
|
||||
if (chatStore.streaming?.sessionId === sessionId.value) {
|
||||
// Scroll to user's prompt when the message is being generated
|
||||
scrollToMessage(message.previousMessageId);
|
||||
scrollToMessage(chatStore.streaming.promptId);
|
||||
return;
|
||||
}
|
||||
|
||||
scrollToBottom(false);
|
||||
},
|
||||
{ immediate: true, flush: 'post' },
|
||||
);
|
||||
|
|
@ -286,7 +286,7 @@ function handleCancelEditMessage() {
|
|||
|
||||
function handleEditMessage(message: ChatHubMessageDto) {
|
||||
if (
|
||||
chatStore.isResponding(message.sessionId) ||
|
||||
isResponding.value ||
|
||||
!['human', 'ai'].includes(message.type) ||
|
||||
!selectedModel.value ||
|
||||
!credentialsForSelectedProvider.value
|
||||
|
|
@ -308,7 +308,7 @@ function handleEditMessage(message: ChatHubMessageDto) {
|
|||
|
||||
function handleRegenerateMessage(message: ChatHubMessageDto) {
|
||||
if (
|
||||
chatStore.isResponding(message.sessionId) ||
|
||||
isResponding.value ||
|
||||
message.type !== 'ai' ||
|
||||
!selectedModel.value ||
|
||||
!credentialsForSelectedProvider.value
|
||||
|
|
@ -400,7 +400,7 @@ function closeAgentEditor() {
|
|||
/>
|
||||
|
||||
<N8nScrollArea
|
||||
v-if="chatStore.agentsReady"
|
||||
v-if="readyToShowMessages"
|
||||
type="scroll"
|
||||
:enable-vertical-scroll="true"
|
||||
:enable-horizontal-scroll="false"
|
||||
|
|
|
|||
|
|
@ -36,14 +36,21 @@ import {
|
|||
type ChatHubMessageStatus,
|
||||
type ChatModelDto,
|
||||
} from '@n8n/api-types';
|
||||
import type { CredentialsMap, ChatMessage, ChatConversation } from './chat.types';
|
||||
import type {
|
||||
CredentialsMap,
|
||||
ChatMessage,
|
||||
ChatConversation,
|
||||
ChatStreamingState,
|
||||
} from './chat.types';
|
||||
import { retry } from '@n8n/utils/retry';
|
||||
import { createAiMessageFromStreamingState, flattenModel } from './chat.utils';
|
||||
|
||||
export const useChatStore = defineStore(CHAT_STORE, () => {
|
||||
const rootStore = useRootStore();
|
||||
const agents = ref<ChatModelsResponse>();
|
||||
const sessions = ref<ChatHubSessionDto[]>();
|
||||
const currentEditingAgent = ref<ChatHubAgentDto | null>(null);
|
||||
const streaming = ref<ChatStreamingState>();
|
||||
|
||||
const conversationsBySession = ref<Map<ChatSessionId, ChatConversation>>(new Map());
|
||||
|
||||
|
|
@ -159,12 +166,6 @@ export const useChatStore = defineStore(CHAT_STORE, () => {
|
|||
const a = messagesGraph[first];
|
||||
const b = messagesGraph[second];
|
||||
|
||||
// TODO: Disabled for now, messages retried don't get this at the FE before reload
|
||||
// TOOD: Do we even need runIndex at all?
|
||||
// if (a.runIndex !== b.runIndex) {
|
||||
// return a.runIndex - b.runIndex;
|
||||
// }
|
||||
|
||||
if (a.createdAt !== b.createdAt) {
|
||||
return a.createdAt < b.createdAt ? -1 : 1;
|
||||
}
|
||||
|
|
@ -270,7 +271,6 @@ export const useChatStore = defineStore(CHAT_STORE, () => {
|
|||
|
||||
const messages = linkMessages(Object.values(conversation.messages));
|
||||
|
||||
// TOOD: Do we need 'state' column at all?
|
||||
const latestMessage = Object.values(messages)
|
||||
.sort((a, b) => (a.createdAt < b.createdAt ? -1 : 1))
|
||||
.pop();
|
||||
|
|
@ -281,106 +281,107 @@ export const useChatStore = defineStore(CHAT_STORE, () => {
|
|||
});
|
||||
}
|
||||
|
||||
function onBeginMessage(
|
||||
sessionId: ChatSessionId,
|
||||
messageId: ChatMessageId,
|
||||
previousMessageId: ChatMessageId | null,
|
||||
retryOfMessageId: ChatMessageId | null,
|
||||
status: ChatHubMessageStatus = 'running',
|
||||
) {
|
||||
addMessage(sessionId, {
|
||||
id: messageId,
|
||||
sessionId,
|
||||
type: 'ai',
|
||||
name: 'AI',
|
||||
content: '',
|
||||
provider: null,
|
||||
model: null,
|
||||
workflowId: null,
|
||||
executionId: null,
|
||||
agentId: null,
|
||||
status,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
previousMessageId,
|
||||
retryOfMessageId,
|
||||
revisionOfMessageId: null,
|
||||
responses: [],
|
||||
alternatives: [],
|
||||
});
|
||||
function onBeginMessage() {
|
||||
if (!streaming.value?.messageId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = createAiMessageFromStreamingState(
|
||||
streaming.value.sessionId,
|
||||
streaming.value.messageId,
|
||||
streaming.value,
|
||||
);
|
||||
|
||||
addMessage(streaming.value.sessionId, message);
|
||||
|
||||
if (sessions.value?.some((session) => session.id === streaming.value?.sessionId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sessions.value = [
|
||||
...(sessions.value ?? []),
|
||||
{
|
||||
id: streaming.value.sessionId,
|
||||
title: 'New Chat',
|
||||
ownerId: '',
|
||||
lastMessageAt: new Date().toISOString(),
|
||||
credentialId: null,
|
||||
agentName: null,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
...flattenModel(streaming.value.model),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function ensureMessage(
|
||||
sessionId: ChatSessionId,
|
||||
messageId: ChatMessageId,
|
||||
previousMessageId: ChatMessageId | null,
|
||||
retryOfMessageId: ChatMessageId | null,
|
||||
): ChatMessage {
|
||||
function ensureMessage(sessionId: ChatSessionId, messageId: ChatMessageId): ChatMessage {
|
||||
const conversation = ensureConversation(sessionId);
|
||||
const message = conversation.messages[messageId];
|
||||
|
||||
if (message) {
|
||||
return message;
|
||||
}
|
||||
|
||||
return addMessage(sessionId, {
|
||||
id: messageId,
|
||||
sessionId,
|
||||
type: 'ai',
|
||||
name: 'AI',
|
||||
content: '',
|
||||
provider: null,
|
||||
model: null,
|
||||
workflowId: null,
|
||||
executionId: null,
|
||||
status: 'running',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
previousMessageId,
|
||||
retryOfMessageId,
|
||||
revisionOfMessageId: null,
|
||||
responses: [],
|
||||
alternatives: [],
|
||||
agentId: null,
|
||||
});
|
||||
const newMessage = createAiMessageFromStreamingState(sessionId, messageId, streaming.value);
|
||||
|
||||
return addMessage(sessionId, newMessage);
|
||||
}
|
||||
|
||||
function onChunk(sessionId: string, messageId: string, chunk: string) {
|
||||
appendMessage(sessionId, messageId, chunk);
|
||||
function onChunk(chunk: string) {
|
||||
if (streaming.value?.messageId) {
|
||||
appendMessage(streaming.value.sessionId, streaming.value.messageId, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
function onEndMessage(sessionId: ChatSessionId, messageId: ChatMessageId) {
|
||||
updateMessage(sessionId, messageId, 'success');
|
||||
function onEndMessage() {
|
||||
if (streaming.value?.messageId) {
|
||||
updateMessage(streaming.value.sessionId, streaming.value.messageId, 'success');
|
||||
}
|
||||
}
|
||||
|
||||
function onStreamMessage(sessionId: string, chunk: EnrichedStructuredChunk) {
|
||||
const { messageId, previousMessageId, retryOfMessageId } = chunk.metadata;
|
||||
function onStreamMessage(chunk: EnrichedStructuredChunk) {
|
||||
if (!streaming.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { sessionId } = streaming.value;
|
||||
|
||||
streaming.value = { ...streaming.value, ...chunk.metadata };
|
||||
|
||||
switch (chunk.type) {
|
||||
case 'begin':
|
||||
onBeginMessage(sessionId, messageId, previousMessageId, retryOfMessageId);
|
||||
onBeginMessage();
|
||||
break;
|
||||
case 'item':
|
||||
onChunk(sessionId, messageId, chunk.content ?? '');
|
||||
onChunk(chunk.content ?? '');
|
||||
break;
|
||||
case 'end':
|
||||
onEndMessage(sessionId, messageId);
|
||||
onEndMessage();
|
||||
break;
|
||||
case 'error': {
|
||||
// Ignore errors after cancellation
|
||||
const message = ensureMessage(sessionId, messageId, previousMessageId, retryOfMessageId);
|
||||
const message = ensureMessage(sessionId, chunk.metadata.messageId);
|
||||
|
||||
if (message.status === 'cancelled') {
|
||||
return;
|
||||
}
|
||||
|
||||
updateMessage(sessionId, messageId, 'error');
|
||||
onChunk(sessionId, messageId, message.content ?? '');
|
||||
updateMessage(sessionId, chunk.metadata.messageId, 'error');
|
||||
onChunk(message.content ?? '');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function onStreamDone(sessionId: ChatSessionId) {
|
||||
async function onStreamDone() {
|
||||
if (!streaming.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { sessionId } = streaming.value;
|
||||
|
||||
streaming.value = undefined;
|
||||
|
||||
// wait up to 3 seconds until conversation title is generated
|
||||
await retry(
|
||||
async () => {
|
||||
|
|
@ -396,8 +397,15 @@ export const useChatStore = defineStore(CHAT_STORE, () => {
|
|||
await fetchSessions();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function onStreamError(sessionId: ChatSessionId, _e: Error) {
|
||||
function onStreamError() {
|
||||
if (!streaming.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { sessionId } = streaming.value;
|
||||
|
||||
streaming.value = undefined;
|
||||
|
||||
const conversation = getConversation(sessionId);
|
||||
if (!conversation) {
|
||||
return;
|
||||
|
|
@ -445,6 +453,8 @@ export const useChatStore = defineStore(CHAT_STORE, () => {
|
|||
alternatives: [],
|
||||
});
|
||||
|
||||
streaming.value = { promptId: messageId, sessionId, model };
|
||||
|
||||
sendMessageApi(
|
||||
rootStore.restApiContext,
|
||||
{
|
||||
|
|
@ -455,9 +465,9 @@ export const useChatStore = defineStore(CHAT_STORE, () => {
|
|||
credentials,
|
||||
previousMessageId,
|
||||
},
|
||||
(chunk: EnrichedStructuredChunk) => onStreamMessage(sessionId, chunk),
|
||||
async () => await onStreamDone(sessionId),
|
||||
(e) => onStreamError(sessionId, e),
|
||||
onStreamMessage,
|
||||
onStreamDone,
|
||||
onStreamError,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -468,7 +478,7 @@ export const useChatStore = defineStore(CHAT_STORE, () => {
|
|||
model: ChatHubConversationModel,
|
||||
credentials: ChatHubSendMessageRequest['credentials'],
|
||||
) {
|
||||
const messageId = uuidv4();
|
||||
const promptId = uuidv4();
|
||||
|
||||
const conversation = ensureConversation(sessionId);
|
||||
const message = conversation.messages[editId];
|
||||
|
|
@ -476,7 +486,7 @@ export const useChatStore = defineStore(CHAT_STORE, () => {
|
|||
|
||||
if (message?.type === 'human') {
|
||||
addMessage(sessionId, {
|
||||
id: messageId,
|
||||
id: promptId,
|
||||
sessionId,
|
||||
type: 'human',
|
||||
name: message.name ?? 'User',
|
||||
|
|
@ -499,19 +509,21 @@ export const useChatStore = defineStore(CHAT_STORE, () => {
|
|||
replaceMessageContent(sessionId, editId, content);
|
||||
}
|
||||
|
||||
streaming.value = { promptId, sessionId, model };
|
||||
|
||||
editMessageApi(
|
||||
rootStore.restApiContext,
|
||||
sessionId,
|
||||
editId,
|
||||
{
|
||||
model,
|
||||
messageId,
|
||||
messageId: promptId,
|
||||
message: content,
|
||||
credentials,
|
||||
},
|
||||
(chunk: EnrichedStructuredChunk) => onStreamMessage(sessionId, chunk),
|
||||
async () => await onStreamDone(sessionId),
|
||||
(e) => onStreamError(sessionId, e),
|
||||
onStreamMessage,
|
||||
onStreamDone,
|
||||
onStreamError,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -528,6 +540,8 @@ export const useChatStore = defineStore(CHAT_STORE, () => {
|
|||
throw new Error('No previous message to base regeneration on');
|
||||
}
|
||||
|
||||
streaming.value = { promptId: retryId, sessionId, model };
|
||||
|
||||
regenerateMessageApi(
|
||||
rootStore.restApiContext,
|
||||
sessionId,
|
||||
|
|
@ -536,9 +550,9 @@ export const useChatStore = defineStore(CHAT_STORE, () => {
|
|||
model,
|
||||
credentials,
|
||||
},
|
||||
(chunk: EnrichedStructuredChunk) => onStreamMessage(sessionId, chunk),
|
||||
async () => await onStreamDone(sessionId),
|
||||
(e) => onStreamError(sessionId, e),
|
||||
onStreamMessage,
|
||||
onStreamDone,
|
||||
onStreamError,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -548,6 +562,7 @@ export const useChatStore = defineStore(CHAT_STORE, () => {
|
|||
if (currentMessage && currentMessage.status === 'running') {
|
||||
updateMessage(sessionId, currentMessage.id, 'cancelled');
|
||||
await stopGenerationApi(rootStore.restApiContext, sessionId, currentMessage.id);
|
||||
streaming.value = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -708,6 +723,7 @@ export const useChatStore = defineStore(CHAT_STORE, () => {
|
|||
/**
|
||||
* messaging
|
||||
*/
|
||||
streaming,
|
||||
isResponding,
|
||||
sendMessage,
|
||||
editMessage,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import {
|
|||
type ChatMessageId,
|
||||
type ChatHubSessionDto,
|
||||
type ChatHubConversationDto,
|
||||
type ChatSessionId,
|
||||
type ChatHubConversationModel,
|
||||
type EnrichedStructuredChunk,
|
||||
type ChatHubProvider,
|
||||
} from '@n8n/api-types';
|
||||
import { z } from 'zod';
|
||||
|
||||
|
|
@ -68,3 +72,16 @@ export interface ChatAgentFilter {
|
|||
provider: 'custom-agent' | 'n8n' | '';
|
||||
search: string;
|
||||
}
|
||||
|
||||
export interface ChatStreamingState extends Partial<EnrichedStructuredChunk['metadata']> {
|
||||
promptId: ChatMessageId;
|
||||
sessionId: ChatSessionId;
|
||||
model: ChatHubConversationModel;
|
||||
}
|
||||
|
||||
export interface FlattenedModel {
|
||||
provider: ChatHubProvider | null;
|
||||
model: string | null;
|
||||
workflowId: string | null;
|
||||
agentId: string | null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,16 @@ import {
|
|||
type ChatModelsResponse,
|
||||
type ChatHubSessionDto,
|
||||
type ChatModelDto,
|
||||
type ChatSessionId,
|
||||
type ChatMessageId,
|
||||
} from '@n8n/api-types';
|
||||
import type { ChatMessage, GroupedConversations, ChatAgentFilter } from './chat.types';
|
||||
import type {
|
||||
ChatMessage,
|
||||
GroupedConversations,
|
||||
ChatAgentFilter,
|
||||
ChatStreamingState,
|
||||
FlattenedModel,
|
||||
} from './chat.types';
|
||||
import { CHAT_VIEW } from './constants';
|
||||
|
||||
export function findOneFromModelsResponse(response: ChatModelsResponse): ChatModelDto | undefined {
|
||||
|
|
@ -99,9 +107,19 @@ export function getAgentRoute(model: ChatHubConversationModel) {
|
|||
};
|
||||
}
|
||||
|
||||
export function restoreConversationModelFromMessageOrSession(
|
||||
messageOrSession: ChatHubSessionDto | ChatMessage,
|
||||
): ChatHubConversationModel | null {
|
||||
export function flattenModel(model: ChatHubConversationModel): FlattenedModel {
|
||||
return {
|
||||
provider: model.provider,
|
||||
model:
|
||||
model?.provider === 'n8n' || model?.provider === 'custom-agent'
|
||||
? null
|
||||
: (model?.model ?? null),
|
||||
workflowId: model?.provider === 'n8n' ? model.workflowId : null,
|
||||
agentId: model?.provider === 'custom-agent' ? model.agentId : null,
|
||||
};
|
||||
}
|
||||
|
||||
export function unflattenModel(messageOrSession: FlattenedModel): ChatHubConversationModel | null {
|
||||
if (messageOrSession.provider === null) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -198,3 +216,34 @@ export function fromStringToModel(value: string): ChatHubConversationModel | und
|
|||
? { provider: 'custom-agent', agentId: identifier }
|
||||
: { provider: parsedProvider, model: identifier };
|
||||
}
|
||||
|
||||
export function createAiMessageFromStreamingState(
|
||||
sessionId: ChatSessionId,
|
||||
messageId: ChatMessageId,
|
||||
streaming?: Partial<ChatStreamingState>,
|
||||
): ChatMessage {
|
||||
return {
|
||||
id: messageId,
|
||||
sessionId,
|
||||
type: 'ai',
|
||||
name: 'AI',
|
||||
content: '',
|
||||
executionId: streaming?.executionId ?? null,
|
||||
status: 'running',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
previousMessageId: streaming?.previousMessageId ?? null,
|
||||
retryOfMessageId: streaming?.retryOfMessageId ?? null,
|
||||
revisionOfMessageId: null,
|
||||
responses: [],
|
||||
alternatives: [],
|
||||
...(streaming?.model
|
||||
? flattenModel(streaming.model)
|
||||
: {
|
||||
provider: null,
|
||||
model: null,
|
||||
workflowId: null,
|
||||
agentId: null,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { computed, nextTick, onBeforeMount, ref, useTemplateRef, watch } from 'v
|
|||
import VueMarkdown from 'vue-markdown-render';
|
||||
import type { ChatMessage } from '../chat.types';
|
||||
import ChatMessageActions from './ChatMessageActions.vue';
|
||||
import { restoreConversationModelFromMessageOrSession } from '@/features/ai/chatHub/chat.utils';
|
||||
import { unflattenModel } from '@/features/ai/chatHub/chat.utils';
|
||||
import { useAgent } from '@/features/ai/chatHub/composables/useAgent';
|
||||
|
||||
const { message, compact, isEditing, isStreaming, minHeight } = defineProps<{
|
||||
|
|
@ -48,7 +48,7 @@ const speech = useSpeechSynthesis(messageContent, {
|
|||
volume: 1,
|
||||
});
|
||||
|
||||
const model = computed(() => restoreConversationModelFromMessageOrSession(message));
|
||||
const model = computed(() => unflattenModel(message));
|
||||
const agent = useAgent(model);
|
||||
|
||||
async function handleCopy() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { restoreConversationModelFromMessageOrSession } from '@/features/ai/chatHub/chat.utils';
|
||||
import { unflattenModel } from '@/features/ai/chatHub/chat.utils';
|
||||
import ChatAgentAvatar from '@/features/ai/chatHub/components/ChatAgentAvatar.vue';
|
||||
import ChatSidebarLink from '@/features/ai/chatHub/components/ChatSidebarLink.vue';
|
||||
import { useAgent } from '@/features/ai/chatHub/composables/useAgent';
|
||||
|
|
@ -27,7 +27,7 @@ const editedLabel = ref('');
|
|||
|
||||
type SessionAction = 'rename' | 'delete';
|
||||
|
||||
const model = computed(() => restoreConversationModelFromMessageOrSession(session));
|
||||
const model = computed(() => unflattenModel(session));
|
||||
const agent = useAgent(model);
|
||||
|
||||
const dropdownItems = computed<Array<ActionDropdownItem<SessionAction>>>(() => [
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user