🐛 fix: 修正发送的请求不包含 systemRole 的问题

This commit is contained in:
arvinxx
2023-07-17 23:44:03 +08:00
parent 390ebfec80
commit a3653a4896
5 changed files with 193 additions and 123 deletions

View File

@@ -1,8 +1,9 @@
import { ActionIcon, Avatar, ChatHeader } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { ArchiveIcon, MoreVerticalIcon, Share2Icon } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { ArchiveIcon, LucideEdit, MoreVerticalIcon, Share2Icon } from 'lucide-react';
import Router from 'next/router';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';
import { shallow } from 'zustand/shallow';
@@ -20,9 +21,9 @@ const useStyles = createStyles(({ css, token }) => ({
}));
const Header = memo(() => {
const { t } = useTranslation('common');
const meta = useSessionStore((s) => {
const [meta, id] = useSessionStore((s) => {
const chat = sessionSelectors.currentSession(s);
return chat?.meta;
return [chat?.meta, s.activeId];
}, shallow);
const [
@@ -50,23 +51,33 @@ const Header = memo(() => {
</>
}
right={
<>
<ActionIcon
icon={Share2Icon}
onClick={() => {
// genShareUrl();
}}
size={{ fontSize: 24 }}
title={t('share')}
/>
<ActionIcon icon={ArchiveIcon} size={{ fontSize: 24 }} title={t('archive')} />
<ActionIcon
icon={MoreVerticalIcon}
onClick={() => toggleConfig()}
size={{ fontSize: 24 }}
title={t('sessionSetting')}
/>
</>
id && (
<>
<ActionIcon
icon={Share2Icon}
onClick={() => {
// genShareUrl();
}}
size={{ fontSize: 24 }}
title={t('share')}
/>
<ActionIcon icon={ArchiveIcon} size={{ fontSize: 24 }} title={t('archive')} />
<ActionIcon
icon={LucideEdit}
onClick={() => {
Router.push(`/chat/${id}/edit`);
}}
size={{ blockSize: 32, fontSize: 20 }}
title={t('edit')}
/>
<ActionIcon
icon={MoreVerticalIcon}
onClick={() => toggleConfig()}
size={{ fontSize: 24 }}
title={t('sessionSetting')}
/>
</>
)
}
/>
);

View File

@@ -1,4 +1,3 @@
import { TextArea } from '@lobehub/ui';
import { Collapse, InputNumber, Segmented, Slider } from 'antd';
import isEqual from 'fast-deep-equal';
import { useTranslation } from 'react-i18next';
@@ -9,6 +8,7 @@ import { agentSelectors, useSessionStore } from '@/store/session';
import { LanguageModel } from '@/types/llm';
import { FormItem } from './FormItem';
import Prompt from './Prompt';
import { useStyles } from './style';
const AgentConfig = () => {
@@ -48,19 +48,7 @@ const AgentConfig = () => {
value={config.model}
/>
</FormItem>
<FormItem label={t('agentPrompt')}>
<Flexbox gap={16}>
<TextArea
onChange={(e) => {
updateAgentConfig({ systemRole: e.target.value });
}}
placeholder={t('agentPromptPlaceholder')}
style={{ minHeight: 160 }}
type={'block'}
value={config.systemRole}
/>
</Flexbox>
</FormItem>
<Prompt />
<Collapse
activeKey={['advanceSettings']}
bordered={false}

View File

@@ -0,0 +1,68 @@
import { EditableMessage } from '@lobehub/ui';
import { Button } from 'antd';
import { createStyles } from 'antd-style';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';
import { shallow } from 'zustand/shallow';
import { agentSelectors, useSessionStore } from '@/store/session';
import { FormItem } from './FormItem';
export const useStyles = createStyles(({ css, token }) => ({
input: css`
padding: 12px;
background: ${token.colorFillTertiary};
border: 1px solid ${token.colorPrimaryBorder};
border-radius: 8px;
`,
markdown: css`
padding: 12px;
background: ${token.colorFillTertiary};
`,
}));
const Prompt = () => {
const { t } = useTranslation('common');
const [editing, setEditing] = useState(false);
const { styles } = useStyles();
const systemRole = useSessionStore((s) => {
const config = agentSelectors.currentAgentConfigSafe(s);
return config.systemRole;
}, shallow);
const [updateAgentConfig] = useSessionStore((s) => [s.updateAgentConfig], shallow);
return (
<FormItem label={t('agentPrompt')}>
<Flexbox gap={16}>
<EditableMessage
classNames={styles}
editing={editing}
onChange={(e) => {
updateAgentConfig({ systemRole: e });
}}
onEditingChange={setEditing}
value={systemRole}
/>
{!editing && (
<Flexbox direction={'horizontal-reverse'}>
<Button
onClick={() => {
setEditing(true);
}}
type={'primary'}
>
{t('edit')}
</Button>
</Flexbox>
)}
</Flexbox>
</FormItem>
);
};
export default Prompt;

View File

@@ -18,6 +18,10 @@ export const useStyles = createStyles(({ css, token }) => ({
font-weight: bold;
color: ${token.colorText};
`,
prompt: css`
padding: 12px;
background: ${token.colorFillTertiary};
`,
title: css`
font-size: 16px;
font-weight: 500;

View File

@@ -10,35 +10,55 @@ import { MessageDispatch, messagesReducer } from './messageReducer';
const LOADING_FLAT = '...';
/**
* 聊天操作
*/
export interface ChatAction {
clearMessage: () => void;
createOrSendMsg: (text: string) => Promise<void>;
deleteMessage: (id: string) => void;
/**
* @title 派发消息
* @param payload - 消息分发
* @returns void
* 清除消息
*/
clearMessage: () => void;
/**
* 创建或发送消息
* @param text - 消息文本
*/
createOrSendMsg: (text: string) => Promise<void>;
/**
* 删除消息
* @param id - 消息 ID
*/
deleteMessage: (id: string) => void;
/**
* 分发消息
* @param payload - 消息分发参数
*/
dispatchMessage: (payload: MessageDispatch) => void;
generateMessage: (messages: ChatMessage[], options: FetchSSEOptions) => Promise<void>;
/**
* @title 处理消息编辑
* @param index - 消息索引或空
* @returns void
* 生成消息
* @param messages - 聊天消息数组
* @param options - 获取 SSE 选项
*/
generateMessage: (messages: ChatMessage[], options: FetchSSEOptions) => Promise<void>;
/**
* 处理消息编辑
* @param messageId - 消息 ID可选
*/
handleMessageEditing: (messageId: string | undefined) => void;
/**
* @title 重发消息
* @param index - 消息索引
* @returns Promise<void>
* 实际获取 AI 响应
*
* @param messages - 聊天消息数组
* @param parentId - 父消息 ID可选
*/
realFetchAIResponse: (messages: ChatMessage[], parentId?: string) => Promise<void>;
/**
* 重新发送消息
* @param id - 消息 ID
*/
resendMessage: (id: string) => Promise<void>;
/**
* @title 发送消息
* @returns Promise<void>
* 发送消息
* @param text - 消息文本
*/
sendMessage: (text: string) => Promise<void>;
}
@@ -56,7 +76,6 @@ export const createChatSlice: StateCreator<
createOrSendMsg: async (message) => {
if (!message) return;
console.log(message);
const { sendMessage, createSession } = get();
const session = sessionSelectors.currentSession(get());
@@ -96,81 +115,20 @@ export const createChatSlice: StateCreator<
set({ editingMessageId: messageId });
},
resendMessage: async (messageId) => {
const session = sessionSelectors.currentSession(get());
realFetchAIResponse: async (messages: ChatMessage[], parentId?: string) => {
const { dispatchMessage, generateMessage } = 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();
// 添加 systemRole
const { systemRole } = agentSelectors.currentAgentConfigSafe(get());
if (systemRole) {
messages.unshift({ content: systemRole, role: 'system' } as ChatMessage);
}
// 再添加一个空的信息用于放置 ai 响应,注意顺序不能反
// 因为如果顺序反了messages 中将包含新增的 ai message
const assistantId = nanoid();
const latestMsg = histories.filter((s) => s.role === 'user').at(-1);
const userId = parentId ?? nanoid();
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) => {
const { dispatchMessage, generateMessage, autocompleteSessionAgentMeta } = get();
const session = sessionSelectors.currentSession(get());
if (!session || !message) return;
const userId = nanoid();
const assistantId = nanoid();
dispatchMessage({ id: userId, message, role: 'user', type: 'addMessage' });
// 先拿到当前的 messages
const messages = chatSelectors.currentChats(get());
// 再添加一个空的信息用于放置 ai 响应,注意顺序不能反
// 因为如果顺序反了messages 中将包含新增的 ai message
dispatchMessage({
id: assistantId,
message: LOADING_FLAT,
@@ -202,6 +160,47 @@ export const createChatSlice: StateCreator<
item.scrollIntoView({ behavior: 'smooth' });
},
});
},
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 { realFetchAIResponse } = get();
const latestMsg = histories.filter((s) => s.role === 'user').at(-1);
if (!latestMsg) return;
await realFetchAIResponse(histories, latestMsg.id);
},
sendMessage: async (message) => {
const { dispatchMessage, realFetchAIResponse, autocompleteSessionAgentMeta } = get();
const session = sessionSelectors.currentSession(get());
if (!session || !message) return;
const userId = nanoid();
dispatchMessage({ id: userId, message, role: 'user', type: 'addMessage' });
// 先拿到当前的 messages
const messages = chatSelectors.currentChats(get());
await realFetchAIResponse(messages);
const chats = chatSelectors.currentChats(get());
if (chats.length >= 4) {