Skip to content

Commit

Permalink
✨ feat: 实现优化重发请求功能
Browse files Browse the repository at this point in the history
  • Loading branch information
arvinxx committed Jul 16, 2023
1 parent 47b316c commit d7195d9
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 90 deletions.
1 change: 1 addition & 0 deletions src/pages/chat/[id]/Conversation/ChatList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const List = () => {
shallow,
);

console.log(data);
return (
<ChatList
data={data}
Expand Down
11 changes: 11 additions & 0 deletions src/store/session/slices/agentConfig/initialState.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { LanguageModel } from '@/types/llm';
import { LobeAgentConfig } from '@/types/session';

export interface SessionLoadingState {
pickingEmojiAvatar: boolean;
summarizingDescription: boolean;
Expand All @@ -9,6 +12,14 @@ export interface AgentConfigState {
showAgentSettings: boolean;
}

export const initialLobeAgentConfig: LobeAgentConfig = {
model: LanguageModel.GPT3_5,
params: { temperature: 0.6 },
systemRole: '',
};

export const defaultAvatar = 'https://npm.elemecdn.com/@lobehub/assets-logo/assets/logo-3d.webp';

export const initialAgentConfigState: AgentConfigState = {
// // loading 中间态
loading: {
Expand Down
8 changes: 7 additions & 1 deletion src/store/session/slices/agentConfig/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { SessionStore } from '@/store/session';
import { LanguageModel } from '@/types/llm';
import { LobeAgentConfig } from '@/types/session';

import { sessionSelectors } from '../session';
import { defaultAvatar, initialLobeAgentConfig } from './initialState';

const currentAgentTitle = (s: SessionStore) => {
const session = sessionSelectors.currentSession(s);
Expand All @@ -12,7 +14,6 @@ const currentAgentTitle = (s: SessionStore) => {
const currentAgentAvatar = (s: SessionStore) => {
const session = sessionSelectors.currentSession(s);

const defaultAvatar = 'https://npm.elemecdn.com/@lobehub/assets-logo/assets/logo-3d.webp';
if (!session) return defaultAvatar;

return session.meta.avatar || defaultAvatar;
Expand All @@ -24,6 +25,10 @@ const currentAgentConfig = (s: SessionStore) => {
return session?.config;
};

const currentAgentConfigSafe = (s: SessionStore): LobeAgentConfig => {
return currentAgentConfig(s) || initialLobeAgentConfig;
};

const currentAgentModel = (s: SessionStore): LanguageModel => {
const config = currentAgentConfig(s);

Expand All @@ -33,6 +38,7 @@ const currentAgentModel = (s: SessionStore): LanguageModel => {
export const agentSelectors = {
currentAgentAvatar,
currentAgentConfig,
currentAgentConfigSafe,
currentAgentModel,
currentAgentTitle,
};
132 changes: 65 additions & 67 deletions src/store/session/slices/chat/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,72 +95,65 @@ export const createChatSlice: StateCreator<
set({ editingMessageId: messageId });
},

resendMessage: async () => {
// const {
// sendMessage,
// dispatchMessage,
// // generateMessage
// } = get();
//
// const session = sessionSelectors.currentSession(get());
//
// if (!session) return;
//
// const index = session.chats.findIndex((s) => s.id === id);
// if (index < 0) return;
//
// const message = session.chats[index];
//
// // 用户通过手动删除,造成了他的问题是最后一条消息
// // 这种情况下,相当于用户重新发送消息
// if (session.chats.length === index && message.role === 'user') {
// // 发送消息的时候会把传入的消息 message 新建一条,因此在发送前先把这条消息在记录中删除
// dispatchMessage({ id: message.id, type: 'deleteMessage' });
// await sendMessage(message.content);
// return;
// }
//
// // 上下文消息就是当前消息之前的消息
// const contextMessages = session.chats.slice(0, index);
//
// // 上下文消息中最后一条消息
// const userMessage = contextMessages.at(-1)?.content;
// if (!userMessage) return;
//
// const targetMessage = session.chats[index];
//
// // 如果不是 assistant 的消息,那么需要额外插入一条消息
// if (targetMessage.role === 'assistant') {
// // 保存之前的消息为历史消息
// // dispatchMessage({ type: 'updateMessage', message: botPrevMsg, index });
// // dispatchMessage({ type: 'updateMessage', message: LOADING_FLAT, index });
// } else {
// // dispatchMessage({
// // type: 'insertMessage',
// // index,
// // message: { role: 'assistant', content: LOADING_FLAT },
// // });
// }
//
// // 重置错误信息
// dispatchMessage({
// id: targetMessage.id,
// key: 'error',
// type: 'updateMessage',
// value: undefined,
// });
//
// // 开始更新消息
//
// // await generateMessage(userMessage, contextMessages, {
// // onMessageHandle: (text) => {
// // currentResponse = [...currentResponse, text];
// // dispatchMessage({ type: 'updateMessage', message: currentResponse.join(''), index });
// // },
// // onErrorHandle: (error) => {
// // dispatchMessage({ type: 'updateMessage' });
// // },
// // });
resendMessage: async (messageId) => {
const session = sessionSelectors.currentSession(get());

if (!session) return;

// 1. 构造所有相关的历史记录
const chats = chatSelectors.currentChats(get());

const currentIndex = chats.findIndex((c) => c.id === messageId);

const histories = chats
.slice(0, currentIndex + 1)
// 如果点击重新发送的 message 其 role 是 assistant,那么需要移除
// 如果点击重新发送的 message 其 role 是 user,则不需要移除
.filter((c) => !(c.role === 'assistant' && c.id === messageId));

if (histories.length <= 0) return;

const { generateMessage, dispatchMessage } = get();

// 再添加一个空的信息用于放置 ai 响应,注意顺序不能反
// 因为如果顺序反了,messages 中将包含新增的 ai message
const assistantId = nanoid();
const latestMsg = histories.filter((s) => s.role === 'user').at(-1);

if (!latestMsg) return;

dispatchMessage({
id: assistantId,
message: LOADING_FLAT,
parentId: latestMsg.id,
role: 'assistant',
type: 'addMessage',
});

let output = '';

// 生成 ai message
await generateMessage(histories, {
onErrorHandle: (error) => {
dispatchMessage({ id: assistantId, key: 'error', type: 'updateMessage', value: error });
},
onMessageHandle: (text) => {
output += text;

dispatchMessage({
id: assistantId,
key: 'content',
type: 'updateMessage',
value: output,
});

// 滚动到最后一条消息
const item = document.querySelector('#for-loading');
if (!item) return;

item.scrollIntoView({ behavior: 'smooth' });
},
});
},

sendMessage: async (message) => {
Expand Down Expand Up @@ -194,7 +187,12 @@ export const createChatSlice: StateCreator<
onMessageHandle: (text) => {
output += text;

dispatchMessage({ id: assistantId, key: 'content', type: 'updateMessage', value: output });
dispatchMessage({
id: assistantId,
key: 'content',
type: 'updateMessage',
value: output,
});

// 滚动到最后一条消息
const item = document.querySelector('#for-loading');
Expand Down
59 changes: 47 additions & 12 deletions src/store/session/slices/chat/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,66 @@
import { encode } from 'gpt-tokenizer';

import { agentSelectors } from '@/store/session';
import { ChatMessage } from '@/types/chatMessage';

import type { SessionStore } from '../../store';
import { sessionSelectors } from '../session';

const currentChatsSel = (s: SessionStore): ChatMessage[] => {
const chat = sessionSelectors.currentSession(s);
if (!chat) return [];
const chatArr = Object.values<ChatMessage>(chat.chats)
// 展示在聊天框中的消息
const currentChats = (s: SessionStore): ChatMessage[] => {
const session = sessionSelectors.currentSession(s);
if (!session) return [];

const basic = Object.values<ChatMessage>(session.chats)
// 首先按照时间顺序排序,越早的在越前面
.sort((pre, next) => pre.createAt - next.createAt)
// 过滤掉已归档的消息,归档消息不应该出现在聊天框中
.filter((m) => !m.archive);
.filter((m) => !m.archive)
// 映射头像关系
.map((m) => {
return {
...m,
meta:
m.role === 'assistant'
? {
avatar: agentSelectors.currentAgentAvatar(s),
title: session.meta.title,
}
: m.meta,
};
});

const finalList: ChatMessage[] = [];

const addItem = (item: ChatMessage) => {
const isExist = finalList.findIndex((i) => item.id === i.id) > -1;
if (!isExist) {
finalList.push(item);
}
};

for (const item of basic) {
// 先判存在与否,不存在就加入
addItem(item);

for (const another of basic) {
if (another.parentId === item.id) {
addItem(another);
}
}
}

return chatArr;
return finalList;
};

const systemRoleSel = (s: SessionStore): string | undefined => {
const systemRoleMessage = currentChatsSel(s);
const systemRoleSel = (s: SessionStore): string => {
const config = agentSelectors.currentAgentConfigSafe(s);

return systemRoleMessage.find((s) => s.role === 'system')?.content;
return config.systemRole;
};

const totalTokens = (s: SessionStore): number[] => {
const chats = currentChatsSel(s);
const chats = currentChats(s);
return encode(chats.map((m) => m.content).join(''));
};

Expand All @@ -39,8 +75,7 @@ const totalTokenCount = (s: SessionStore) => totalTokens(s).length;
const systemRoleTokenCount = (s: SessionStore) => systemRoleTokens(s).length;

export const chatSelectors = {
currentChats: currentChatsSel,
systemRole: systemRoleSel,
currentChats,
systemRoleTokenCount,
systemRoleTokens,
totalTokenCount,
Expand Down
11 changes: 3 additions & 8 deletions src/store/session/slices/session/initialState.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { LanguageModel } from '@/types/llm';
import { LobeAgentConfig, LobeAgentSession, LobeSessionType } from '@/types/session';
import { LobeAgentSession, LobeSessionType } from '@/types/session';

import { initialLobeAgentConfig } from '../agentConfig';

export interface SessionState {
/**
Expand All @@ -12,12 +13,6 @@ export interface SessionState {
sessions: Record<string, LobeAgentSession>;
}

export const initialLobeAgentConfig: LobeAgentConfig = {
model: LanguageModel.GPT3_5,
params: { temperature: 0.6 },
systemRole: '',
};

export const initLobeSession: LobeAgentSession = {
chats: {},
config: initialLobeAgentConfig,
Expand Down
4 changes: 2 additions & 2 deletions src/store/session/slices/session/selectors/chat.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defaultAvatar } from '@/store/session/slices/agentConfig';
import { MetaData } from '@/types/meta';

export const getAgentAvatar = (s: MetaData) =>
s.avatar || 'https://npm.elemecdn.com/@lobehub/assets-logo/assets/logo-3d.webp';
export const getAgentAvatar = (s: MetaData) => s.avatar || defaultAvatar;

0 comments on commit d7195d9

Please sign in to comment.