mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
🐛 fix: 修正发送的请求不包含 systemRole 的问题
This commit is contained in:
@@ -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')}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -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}
|
||||
|
||||
68
src/pages/chat/[id]/edit/Prompt.tsx
Normal file
68
src/pages/chat/[id]/edit/Prompt.tsx
Normal 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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user