mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
✨ feat: Add new features and improve user experience
The changes include renaming keys, adding new keys, and modifying key-value pairs in JSON files. Language translations, new components and settings, modifications to existing components, and bug fixes are included. The changes also involve importing and using different components and modules, as well as adding new functionality and features. Some changes involve implementing different input components and their functionalities. New files are added and existing files are deleted. Overall, the changes aim to improve the chat application's settings, customization options, and user experience.
This commit is contained in:
@@ -1,41 +1,33 @@
|
||||
{
|
||||
"DefaultSession": "Default Session",
|
||||
"advanceSettings": "Advanced Settings",
|
||||
"agentAvatar": "Avatar",
|
||||
"agentDescription": "Description",
|
||||
"agentDescriptionPlaceholder": "Please enter a description",
|
||||
"agentMaxToken": "Max Token Length",
|
||||
"agentModel": "Model",
|
||||
"agentName": "Name",
|
||||
"agentNamePlaceholder": "Please enter a name",
|
||||
"agentProfile": "Agent Profile",
|
||||
"agentPrompt": "AI Prompt",
|
||||
"agentPromptPlaceholder": "Please enter AI prompt",
|
||||
"agentTag": "Tag",
|
||||
"agentTagPlaceholder": "Please enter a tag",
|
||||
"archive": "Archive",
|
||||
"archiveCurrentMessages": "Archive Current Session",
|
||||
"autoGenerate": "Auto Generate",
|
||||
"autoGenerateTooltip": "Description of the autocomplete assistant based on prompts",
|
||||
"autoGenerateTooltip": "Auto generate assistant description based on prompts",
|
||||
"cancel": "Cancel",
|
||||
"clearCurrentMessages": "Clear Current Session Messages",
|
||||
"close": "Close",
|
||||
"confirmRemoveSessionItemAlert": "You are about to remove this agent. Once removed, it cannot be recovered. Please confirm your action.",
|
||||
"defaultAgent": "Default Agent",
|
||||
"confirmClearCurrentMessages": "You are about to clear the current session messages. Once cleared, they cannot be recovered. Please confirm your action.",
|
||||
"confirmRemoveSessionItemAlert": "You are about to delete this assistant. Once deleted, it cannot be recovered. Please confirm your action.",
|
||||
"defaultAgent": "Default Assistant",
|
||||
"defaultSession": "Default Session",
|
||||
"edit": "Edit",
|
||||
"editAgentProfile": "Edit Agent Profile",
|
||||
"export": "Export",
|
||||
"gpt-3.5-turbo": "GPT 3.5 Turbo",
|
||||
"gpt-3.5-turbo-16k": "GPT 3.5 Turbo (16K)",
|
||||
"gpt-4": "GPT 4",
|
||||
"gpt-4-32k": "GPT 4 (32K)",
|
||||
"modelConfig": "Model Configuration",
|
||||
"modelTemperature": "Temperature",
|
||||
"newAgent": "New Agent",
|
||||
"noDescription": "No description",
|
||||
"newAgent": "New Assistant",
|
||||
"noDescription": "No description available",
|
||||
"ok": "OK",
|
||||
"profile": "Profile",
|
||||
"reset": "Reset",
|
||||
"searchAgentPlaceholder": "Search agents and conversations...",
|
||||
"sessionSetting": "Session Setting",
|
||||
"setting": "Setting",
|
||||
"searchAgentPlaceholder": "Search assistants and sessions...",
|
||||
"send": "Send",
|
||||
"sendPlaceholder": "Enter chat content...",
|
||||
"sessionSetting": "Session Settings",
|
||||
"setting": "Settings",
|
||||
"share": "Share",
|
||||
"updateAgent": "Update Agent",
|
||||
"updatePrompt": "Update Prompt"
|
||||
"tokenDetail": "System Role Token: {{systemRoleToken}} Chats Token: {{chatsToken}}",
|
||||
"updateAgent": "Update Assistant Profile",
|
||||
"updatePrompt": "Update Prompts"
|
||||
}
|
||||
|
||||
10
locales/en_US/plugin.json
Normal file
10
locales/en_US/plugin.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"pluginList": "Plugin List",
|
||||
"pluginLoading": "Plugin Loading...",
|
||||
"plugins": {
|
||||
"realtimeWeather": "Real-time Weather Forecast",
|
||||
"searchEngine": "Search Engine",
|
||||
"undefined": "Plugin Detection...",
|
||||
"websiteCrawler": "Website Content Extraction"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"agentHeader": "Edit Assistant Information",
|
||||
"danger": {
|
||||
"clear": {
|
||||
"action": "Clear Now",
|
||||
@@ -15,6 +16,31 @@
|
||||
}
|
||||
},
|
||||
"header": "Settings",
|
||||
"settingAgent": {
|
||||
"avatar": {
|
||||
"title": "Avatar"
|
||||
},
|
||||
"description": {
|
||||
"placeholder": "Please enter assistant description",
|
||||
"title": "Assistant Description"
|
||||
},
|
||||
"name": {
|
||||
"placeholder": "Please enter assistant name",
|
||||
"title": "Name"
|
||||
},
|
||||
"prompt": {
|
||||
"placeholder": "Please enter AI prompt",
|
||||
"title": "Assistant Role"
|
||||
},
|
||||
"tag": {
|
||||
"placeholder": "Please enter tags",
|
||||
"title": "Tags"
|
||||
},
|
||||
"title": "Assistant Information",
|
||||
"backgroundColor": {
|
||||
"title": "Background Color"
|
||||
}
|
||||
},
|
||||
"settingChat": {
|
||||
"compressThreshold": {
|
||||
"desc": "When the uncompressed chat history exceeds this value, it will be compressed",
|
||||
@@ -27,12 +53,13 @@
|
||||
"title": "Enable History Message Count Limit"
|
||||
},
|
||||
"historyCount": {
|
||||
"desc": "Number of history messages to include in each request",
|
||||
"desc": "Number of history messages carried in each request",
|
||||
"title": "History Message Count"
|
||||
},
|
||||
"inputTemplate": {
|
||||
"desc": "The latest user message will be filled into this template",
|
||||
"title": "User Input Preprocessing"
|
||||
"title": "User Input Preprocessing",
|
||||
"placeholder": "Enter pre-processing template, {text} will be replaced with real-time input information"
|
||||
},
|
||||
"title": "Chat Settings"
|
||||
},
|
||||
@@ -50,6 +77,12 @@
|
||||
},
|
||||
"model": {
|
||||
"desc": "ChatGPT Model",
|
||||
"list": {
|
||||
"gpt-3.5-turbo": "GPT 3.5",
|
||||
"gpt-3.5-turbo-16k": "GPT 3.5 (16K)",
|
||||
"gpt-4": "GPT 4",
|
||||
"gpt-4-32k": "GPT 4 (32K)"
|
||||
},
|
||||
"title": "Model"
|
||||
},
|
||||
"presencePenalty": {
|
||||
@@ -63,7 +96,7 @@
|
||||
"title": "Model Settings",
|
||||
"topP": {
|
||||
"desc": "Similar to randomness, but do not change together with randomness",
|
||||
"title": "Top-p Sampling"
|
||||
"title": "Nucleus Sampling"
|
||||
}
|
||||
},
|
||||
"settingOpenAI": {
|
||||
@@ -79,10 +112,13 @@
|
||||
"title": "API Key"
|
||||
}
|
||||
},
|
||||
"settingPlugin": {
|
||||
"title": "Plugin List"
|
||||
},
|
||||
"settingSystem": {
|
||||
"accessCode": {
|
||||
"desc": "Encryption access is enabled by the administrator",
|
||||
"placeholder": "Please enter the access password",
|
||||
"desc": "Encrypted access has been enabled by the administrator",
|
||||
"placeholder": "Please enter access password",
|
||||
"title": "Access Password"
|
||||
},
|
||||
"title": "System Settings"
|
||||
@@ -99,7 +135,7 @@
|
||||
"title": "Language Settings"
|
||||
},
|
||||
"neutralColor": {
|
||||
"desc": "Custom grayscale for different color biases",
|
||||
"desc": "Custom grayscale for different color tendencies",
|
||||
"title": "Neutral Color"
|
||||
},
|
||||
"primaryColor": {
|
||||
|
||||
@@ -1,41 +1,32 @@
|
||||
{
|
||||
"advanceSettings": "高级设置",
|
||||
"agentAvatar": "头像",
|
||||
"agentDescription": "描述",
|
||||
"agentDescriptionPlaceholder": "请输入描述",
|
||||
"agentMaxToken": "会话最大长度",
|
||||
"agentModel": "模型",
|
||||
"agentName": "名称",
|
||||
"agentNamePlaceholder": "请输入名称",
|
||||
"agentProfile": "助手信息",
|
||||
"agentPrompt": "提示词",
|
||||
"agentPromptPlaceholder": "请输入 AI 提示词",
|
||||
"agentTag": "标签",
|
||||
"agentTagPlaceholder": "请输入标签",
|
||||
"archive": "归档",
|
||||
"archiveCurrentMessages": "归档当前会话",
|
||||
"autoGenerate": "自动补全",
|
||||
"autoGenerateTooltip": "基于提示词自动补全助手描述",
|
||||
"cancel": "取消",
|
||||
"clearCurrentMessages": "清空当前会话消息",
|
||||
"close": "关闭",
|
||||
"confirmClearCurrentMessages": "即将清空当前会话消息,清空后将无法找回,请确认你的操作",
|
||||
"confirmRemoveSessionItemAlert": "即将删除该助手,删除后该将无法找回,请确认你的操作",
|
||||
"defaultAgent": "默认助手",
|
||||
"defaultSession": "默认对话",
|
||||
"edit": "编辑",
|
||||
"editAgentProfile": "编辑助手信息",
|
||||
"export": "导出",
|
||||
"gpt-3.5-turbo": "GPT 3.5",
|
||||
"gpt-3.5-turbo-16k": "GPT 3.5 (16K)",
|
||||
"gpt-4": "GPT 4",
|
||||
"gpt-4-32k": "GPT 4 (32K)",
|
||||
"modelConfig": "模型配置",
|
||||
"modelTemperature": "发散度",
|
||||
"newAgent": "新建助手",
|
||||
"noDescription": "暂无描述",
|
||||
"ok": "确定",
|
||||
"profile": "助手身份",
|
||||
"reset": "重置",
|
||||
"searchAgentPlaceholder": "搜索助手和对话...",
|
||||
"send": "发送",
|
||||
"sendPlaceholder": "输入聊天内容...",
|
||||
"sessionSetting": "会话设置",
|
||||
"setting": "设置",
|
||||
"share": "分享",
|
||||
"tokenDetail": "系统设定: {{systemRoleToken}} 历史消息: {{chatsToken}}",
|
||||
"updateAgent": "更新助理信息",
|
||||
"updatePrompt": "更新提示词"
|
||||
}
|
||||
|
||||
10
locales/zh_CN/plugin.json
Normal file
10
locales/zh_CN/plugin.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"pluginList": "插件列表",
|
||||
"pluginLoading": "插件运行中...",
|
||||
"plugins": {
|
||||
"realtimeWeather": "实时天气预报",
|
||||
"searchEngine": "搜索引擎",
|
||||
"undefined": "插件检测中...",
|
||||
"websiteCrawler": "网页内容提取"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"agentHeader": "编辑助手信息",
|
||||
"danger": {
|
||||
"clear": {
|
||||
"action": "立即清除",
|
||||
@@ -15,6 +16,31 @@
|
||||
}
|
||||
},
|
||||
"header": "设置",
|
||||
"settingAgent": {
|
||||
"avatar": {
|
||||
"title": "头像"
|
||||
},
|
||||
"backgroundColor": {
|
||||
"title": "背景色"
|
||||
},
|
||||
"description": {
|
||||
"placeholder": "请输入助手描述",
|
||||
"title": "助手描述"
|
||||
},
|
||||
"name": {
|
||||
"placeholder": "请输入助手名称",
|
||||
"title": "名称"
|
||||
},
|
||||
"prompt": {
|
||||
"placeholder": "请输入 AI 提示词",
|
||||
"title": "助手角色"
|
||||
},
|
||||
"tag": {
|
||||
"placeholder": "请输入标签",
|
||||
"title": "标签"
|
||||
},
|
||||
"title": "助手信息"
|
||||
},
|
||||
"settingChat": {
|
||||
"compressThreshold": {
|
||||
"desc": "当未压缩的历史消息超过该值时,将进行压缩",
|
||||
@@ -32,6 +58,7 @@
|
||||
},
|
||||
"inputTemplate": {
|
||||
"desc": "用户最新的一条消息会填充到此模板",
|
||||
"placeholder": "输入预处理模版,{text} 将替换为实时输入信息",
|
||||
"title": "用户输入预处理"
|
||||
},
|
||||
"title": "聊天设置"
|
||||
@@ -50,6 +77,12 @@
|
||||
},
|
||||
"model": {
|
||||
"desc": "ChatGPT 模型",
|
||||
"list": {
|
||||
"gpt-3.5-turbo": "GPT 3.5",
|
||||
"gpt-3.5-turbo-16k": "GPT 3.5 (16K)",
|
||||
"gpt-4": "GPT 4",
|
||||
"gpt-4-32k": "GPT 4 (32K)"
|
||||
},
|
||||
"title": "模型"
|
||||
},
|
||||
"presencePenalty": {
|
||||
@@ -79,6 +112,9 @@
|
||||
"title": "API Key"
|
||||
}
|
||||
},
|
||||
"settingPlugin": {
|
||||
"title": "插件列表"
|
||||
},
|
||||
"settingSystem": {
|
||||
"accessCode": {
|
||||
"desc": "管理员已开启加密访问",
|
||||
|
||||
@@ -2,6 +2,8 @@ import { memo } from 'react';
|
||||
|
||||
import { HEADER_HEIGHT } from '@/const/layoutTokens';
|
||||
|
||||
const HeaderSpacing = memo(() => <div style={{ flex: 'none', height: HEADER_HEIGHT }} />);
|
||||
const HeaderSpacing = memo<{ height?: number }>(({ height = HEADER_HEIGHT }) => (
|
||||
<div style={{ flex: 'none', height }} />
|
||||
));
|
||||
|
||||
export default HeaderSpacing;
|
||||
|
||||
42
src/components/HeaderTitle/index.tsx
Normal file
42
src/components/HeaderTitle/index.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
import { ReactNode, memo } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
desc: css`
|
||||
font-size: 12px;
|
||||
color: ${token.colorTextTertiary};
|
||||
`,
|
||||
title: css`
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
`,
|
||||
titleWithDesc: css`
|
||||
font-weight: bold;
|
||||
`,
|
||||
}));
|
||||
|
||||
export interface HeaderTitleProps {
|
||||
desc?: string | ReactNode;
|
||||
tag?: ReactNode;
|
||||
title: string | ReactNode;
|
||||
}
|
||||
|
||||
const HeaderTitle = memo<HeaderTitleProps>(({ title, desc, tag }) => {
|
||||
const { styles } = useStyles();
|
||||
if (desc)
|
||||
return (
|
||||
<Flexbox>
|
||||
<Flexbox align={'center'} className={styles.titleWithDesc} gap={8} horizontal>
|
||||
{title}
|
||||
{tag}
|
||||
</Flexbox>
|
||||
<Flexbox align={'center'} className={styles.desc} horizontal>
|
||||
{desc}
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
);
|
||||
return <div className={styles.title}>{title}</div>;
|
||||
});
|
||||
|
||||
export default HeaderTitle;
|
||||
@@ -1,5 +1,10 @@
|
||||
import type { FormProps } from '@lobehub/ui';
|
||||
|
||||
export const HEADER_HEIGHT = 64;
|
||||
export const CHAT_TEXTAREA_HEIGHT = 200;
|
||||
export const CHAT_SIDEBAR_WIDTH = 280;
|
||||
|
||||
export const FOLDER_WIDTH = 256;
|
||||
export const FORM_STYLE: FormProps = {
|
||||
itemMinWidth: 'max(30%,240px)',
|
||||
style: { maxWidth: 1024, width: '100%' },
|
||||
};
|
||||
|
||||
@@ -1,53 +1,33 @@
|
||||
export default {
|
||||
'DefaultSession': '默认对话',
|
||||
'advanceSettings': '高级设置',
|
||||
'agentAvatar': '头像',
|
||||
'agentDescription': '描述',
|
||||
'agentDescriptionPlaceholder': '请输入描述',
|
||||
'agentMaxToken': '会话最大长度',
|
||||
'agentModel': '模型',
|
||||
'agentName': '名称',
|
||||
'agentNamePlaceholder': '请输入名称',
|
||||
'agentProfile': '助手信息',
|
||||
'agentPrompt': '提示词',
|
||||
'agentPromptPlaceholder': '请输入 AI 提示词',
|
||||
'agentTag': '标签',
|
||||
'agentTagPlaceholder': '请输入标签',
|
||||
'archive': '归档',
|
||||
'archiveCurrentMessages': '归档当前会话',
|
||||
'autoGenerate': '自动补全',
|
||||
'autoGenerateTooltip': '基于提示词自动补全助手描述',
|
||||
'cancel': '取消',
|
||||
'clearCurrentMessages': '清空当前会话消息',
|
||||
'close': '关闭',
|
||||
'confirmClearCurrentMessages': '即将清空当前会话消息,清空后将无法找回,请确认你的操作',
|
||||
'confirmRemoveSessionItemAlert': '即将删除该助手,删除后该将无法找回,请确认你的操作',
|
||||
'defaultAgent': '默认助手',
|
||||
'edit': '编辑',
|
||||
'editAgentProfile': '编辑助手信息',
|
||||
'export': '导出',
|
||||
'gpt-3.5-turbo': 'GPT 3.5',
|
||||
'gpt-3.5-turbo-16k': 'GPT 3.5 (16K)',
|
||||
'gpt-4': 'GPT 4',
|
||||
'gpt-4-32k': 'GPT 4 (32K)',
|
||||
'modelConfig': '模型配置',
|
||||
'modelTemperature': '发散度',
|
||||
'newAgent': '新建助手',
|
||||
'noDescription': '暂无描述',
|
||||
'ok': '确定',
|
||||
'plugin-realtimeWeather': '实时天气预报',
|
||||
'plugin-searchEngine': '搜索引擎',
|
||||
'plugin-undefined': '插件检测中...',
|
||||
'plugin-websiteCrawler': '网页内容提取',
|
||||
'pluginList': '插件列表',
|
||||
'pluginLoading': '插件运行中...',
|
||||
'profile': '助手身份',
|
||||
'reset': '重置',
|
||||
'searchAgentPlaceholder': '搜索助手和对话...',
|
||||
'sessionSetting': '会话设置',
|
||||
'setting': '设置',
|
||||
'share': '分享',
|
||||
'tokenDetail': '系统设定: {{systemRoleToken}} 历史消息: {{chatsToken}}',
|
||||
'updateAgent': '更新助理信息',
|
||||
'updatePrompt': '更新提示词',
|
||||
advanceSettings: '高级设置',
|
||||
agentMaxToken: '会话最大长度',
|
||||
agentModel: '模型',
|
||||
agentProfile: '助手信息',
|
||||
archive: '归档',
|
||||
archiveCurrentMessages: '归档当前会话',
|
||||
archiveSearchPlaceholder: '搜索归档对话...',
|
||||
autoGenerate: '自动补全',
|
||||
autoGenerateTooltip: '基于提示词自动补全助手描述',
|
||||
cancel: '取消',
|
||||
clearCurrentMessages: '清空当前会话消息',
|
||||
close: '关闭',
|
||||
confirmClearCurrentMessages: '即将清空当前会话消息,清空后将无法找回,请确认你的操作',
|
||||
confirmRemoveSessionItemAlert: '即将删除该助手,删除后该将无法找回,请确认你的操作',
|
||||
defaultAgent: '默认助手',
|
||||
defaultSession: '默认对话',
|
||||
edit: '编辑',
|
||||
export: '导出',
|
||||
newAgent: '新建助手',
|
||||
noDescription: '暂无描述',
|
||||
ok: '确定',
|
||||
reset: '重置',
|
||||
roleAndArchive: '角色与记录',
|
||||
searchAgentPlaceholder: '搜索助手和对话...',
|
||||
send: '发送',
|
||||
sendPlaceholder: '输入聊天内容...',
|
||||
setting: '设置',
|
||||
share: '分享',
|
||||
tokenDetail: '系统设定: {{systemRoleToken}} 历史消息: {{chatsToken}}',
|
||||
updateAgent: '更新助理信息',
|
||||
updatePrompt: '更新提示词',
|
||||
};
|
||||
|
||||
10
src/locales/default/plugin.ts
Normal file
10
src/locales/default/plugin.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export default {
|
||||
pluginList: '插件列表',
|
||||
pluginLoading: '插件运行中...',
|
||||
plugins: {
|
||||
realtimeWeather: '实时天气预报',
|
||||
searchEngine: '搜索引擎',
|
||||
undefined: '插件检测中...',
|
||||
websiteCrawler: '网页内容提取',
|
||||
},
|
||||
};
|
||||
@@ -14,8 +14,43 @@ export default {
|
||||
title: '重置所有设置',
|
||||
},
|
||||
},
|
||||
header: '设置',
|
||||
header: {
|
||||
global: '全局设置',
|
||||
session: '会话设置',
|
||||
},
|
||||
settingAgent: {
|
||||
avatar: {
|
||||
title: '头像',
|
||||
},
|
||||
backgroundColor: {
|
||||
title: '背景色',
|
||||
},
|
||||
description: {
|
||||
placeholder: '请输入助手描述',
|
||||
title: '助手描述',
|
||||
},
|
||||
name: {
|
||||
placeholder: '请输入助手名称',
|
||||
title: '名称',
|
||||
},
|
||||
prompt: {
|
||||
placeholder: '请输入 AI 提示词',
|
||||
title: '助手角色',
|
||||
},
|
||||
tag: {
|
||||
placeholder: '请输入标签',
|
||||
title: '标签',
|
||||
},
|
||||
title: '助手信息',
|
||||
},
|
||||
settingChat: {
|
||||
chatStyleType: {
|
||||
title: '聊天窗口样式',
|
||||
type: {
|
||||
bubble: '气泡模式',
|
||||
docs: '文档模式',
|
||||
},
|
||||
},
|
||||
compressThreshold: {
|
||||
desc: '当未压缩的历史消息超过该值时,将进行压缩',
|
||||
title: '历史消息长度压缩阈值',
|
||||
@@ -32,6 +67,7 @@ export default {
|
||||
},
|
||||
inputTemplate: {
|
||||
desc: '用户最新的一条消息会填充到此模板',
|
||||
placeholder: '输入预处理模版,{text} 将替换为实时输入信息',
|
||||
title: '用户输入预处理',
|
||||
},
|
||||
title: '聊天设置',
|
||||
@@ -50,6 +86,12 @@ export default {
|
||||
},
|
||||
model: {
|
||||
desc: 'ChatGPT 模型',
|
||||
list: {
|
||||
'gpt-3.5-turbo': 'GPT 3.5',
|
||||
'gpt-3.5-turbo-16k': 'GPT 3.5 (16K)',
|
||||
'gpt-4': 'GPT 4',
|
||||
'gpt-4-32k': 'GPT 4 (32K)',
|
||||
},
|
||||
title: '模型',
|
||||
},
|
||||
presencePenalty: {
|
||||
@@ -79,6 +121,9 @@ export default {
|
||||
title: 'API Key',
|
||||
},
|
||||
},
|
||||
settingPlugin: {
|
||||
title: '插件列表',
|
||||
},
|
||||
settingSystem: {
|
||||
accessCode: {
|
||||
desc: '管理员已开启加密访问',
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import common from '../../../locales/en_US/common.json';
|
||||
import plugin from '../../../locales/en_US/plugin.json';
|
||||
import setting from '../../../locales/en_US/setting.json';
|
||||
|
||||
const resources = {
|
||||
common,
|
||||
plugin,
|
||||
setting,
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import common from '../default/common';
|
||||
import plugin from '../default/plugin';
|
||||
import setting from '../default/setting';
|
||||
|
||||
const resources = {
|
||||
common,
|
||||
plugin,
|
||||
setting,
|
||||
} as const;
|
||||
|
||||
|
||||
26
src/pages/chat/[id]/Config/Header.tsx
Normal file
26
src/pages/chat/[id]/Config/Header.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { type ReactNode, memo } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
interface HeaderProps {
|
||||
actions?: ReactNode;
|
||||
title: string;
|
||||
}
|
||||
|
||||
const Header = memo<HeaderProps>(({ title, actions }) => {
|
||||
return (
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
distribution={'space-between'}
|
||||
horizontal
|
||||
padding={12}
|
||||
paddingInline={16}
|
||||
>
|
||||
<Flexbox>{title}</Flexbox>
|
||||
<Flexbox gap={4} horizontal>
|
||||
{actions}
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
);
|
||||
});
|
||||
|
||||
export default Header;
|
||||
@@ -1,64 +0,0 @@
|
||||
import { Avatar } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { LucideBrain, LucideThermometer, WholeWord } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Center, Flexbox } from 'react-layout-kit';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { agentSelectors, sessionSelectors, useSessionStore } from '@/store/session';
|
||||
import { DEFAULT_TITLE } from '@/store/session/slices/agentConfig';
|
||||
|
||||
import { ConfigCell, ConfigCellGroup } from './ConfigCell';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
desc: css`
|
||||
color: ${token.colorText};
|
||||
`,
|
||||
model: css`
|
||||
color: ${token.colorTextTertiary};
|
||||
`,
|
||||
title: css`
|
||||
font-size: ${token.fontSizeHeading4}px;
|
||||
font-weight: bold;
|
||||
`,
|
||||
}));
|
||||
|
||||
const ReadMode = memo(() => {
|
||||
const { styles } = useStyles();
|
||||
const session = useSessionStore(sessionSelectors.currentSessionSafe, isEqual);
|
||||
const avatar = useSessionStore(agentSelectors.currentAgentAvatar, shallow);
|
||||
const title = useSessionStore(agentSelectors.currentAgentTitle, shallow);
|
||||
const model = useSessionStore(agentSelectors.currentAgentModel, shallow);
|
||||
|
||||
const { t } = useTranslation('common');
|
||||
return (
|
||||
<Center gap={12} paddingBlock={16} style={{ marginTop: 8 }}>
|
||||
<Avatar avatar={avatar} size={100} />
|
||||
<Flexbox className={styles.title}>{title || DEFAULT_TITLE}</Flexbox>
|
||||
<Flexbox className={styles.model}>{model}</Flexbox>
|
||||
<Flexbox className={styles.desc}>{session.meta.description}</Flexbox>
|
||||
|
||||
<Flexbox flex={1} gap={12} width={'100%'}>
|
||||
<ConfigCell icon={LucideBrain} label={t('agentPrompt')} />
|
||||
<ConfigCellGroup
|
||||
items={[
|
||||
{
|
||||
icon: LucideThermometer,
|
||||
label: t('modelTemperature'),
|
||||
value: session.config.params.temperature,
|
||||
},
|
||||
{
|
||||
icon: WholeWord,
|
||||
label: t('agentMaxToken'),
|
||||
value: session.config.params.max_tokens,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Flexbox>
|
||||
</Center>
|
||||
);
|
||||
});
|
||||
|
||||
export default ReadMode;
|
||||
86
src/pages/chat/[id]/Config/SideBar.tsx
Normal file
86
src/pages/chat/[id]/Config/SideBar.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import { ActionIcon, DraggablePanelBody, EditableMessage, SearchBar } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
import { memo, 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 Header from './Header';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
desc: css`
|
||||
color: ${token.colorText};
|
||||
`,
|
||||
model: css`
|
||||
color: ${token.colorTextTertiary};
|
||||
`,
|
||||
prompt: css`
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
height: 200px;
|
||||
padding: 0 16px 16px;
|
||||
|
||||
opacity: 0.75;
|
||||
border-bottom: 1px solid ${token.colorBorder};
|
||||
|
||||
transition: 200ms ${token.motionEaseOut};
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
`,
|
||||
title: css`
|
||||
font-size: ${token.fontSizeHeading4}px;
|
||||
font-weight: bold;
|
||||
`,
|
||||
}));
|
||||
|
||||
const SideBar = memo(() => {
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const { styles } = useStyles();
|
||||
const [updateAgentConfig] = useSessionStore((s) => [s.updateAgentConfig], shallow);
|
||||
const systemRole = useSessionStore(agentSelectors.currentAgentSystemRole, shallow);
|
||||
|
||||
const { t } = useTranslation('common');
|
||||
return (
|
||||
<DraggablePanelBody style={{ padding: 0 }}>
|
||||
<Header
|
||||
actions={
|
||||
<ActionIcon
|
||||
icon={ChevronRight}
|
||||
onClick={() => setOpenModal(true)}
|
||||
size="small"
|
||||
title={t('edit')}
|
||||
/>
|
||||
}
|
||||
title={t('settingAgent.prompt.title', { ns: 'setting' })}
|
||||
/>
|
||||
<EditableMessage
|
||||
classNames={{ markdown: styles.prompt }}
|
||||
onChange={(e) => {
|
||||
updateAgentConfig({ systemRole: e });
|
||||
}}
|
||||
onOpenChange={setOpenModal}
|
||||
openModal={openModal}
|
||||
placeholder={`${t('settingAgent.prompt.placeholder', { ns: 'setting' })}...`}
|
||||
styles={{ markdown: systemRole ? {} : { opacity: 0.5 } }}
|
||||
text={{
|
||||
cancel: t('cancel'),
|
||||
confirm: t('ok'),
|
||||
edit: t('edit'),
|
||||
title: t('settingAgent.prompt.title', { ns: 'setting' }),
|
||||
}}
|
||||
value={systemRole}
|
||||
/>
|
||||
<Flexbox style={{ padding: 16 }}>
|
||||
<SearchBar placeholder={t('archiveSearchPlaceholder')} />
|
||||
</Flexbox>
|
||||
</DraggablePanelBody>
|
||||
);
|
||||
});
|
||||
|
||||
export default SideBar;
|
||||
@@ -1,22 +1,13 @@
|
||||
import {
|
||||
ActionIcon,
|
||||
DraggablePanel,
|
||||
DraggablePanelBody,
|
||||
DraggablePanelContainer,
|
||||
} from '@lobehub/ui';
|
||||
import { DraggablePanel, DraggablePanelContainer } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { LucideEdit, LucideX } from 'lucide-react';
|
||||
import Router from 'next/router';
|
||||
import { rgba } from 'polished';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import HeaderSpacing from '@/components/HeaderSpacing';
|
||||
import { CHAT_SIDEBAR_WIDTH } from '@/const/layoutTokens';
|
||||
import { useSessionStore } from '@/store/session';
|
||||
|
||||
import ReadMode from './ReadMode';
|
||||
import SideBar from './SideBar';
|
||||
|
||||
const useStyles = createStyles(({ cx, css, token, stylish }) => ({
|
||||
drawer: cx(
|
||||
@@ -31,10 +22,9 @@ const useStyles = createStyles(({ cx, css, token, stylish }) => ({
|
||||
}));
|
||||
|
||||
const Config = () => {
|
||||
const { t } = useTranslation('common');
|
||||
const { styles } = useStyles();
|
||||
const [showAgentSettings, toggleConfig, id] = useSessionStore(
|
||||
(s) => [s.showAgentSettings, s.toggleConfig, s.activeId],
|
||||
const [showAgentSettings, toggleConfig] = useSessionStore(
|
||||
(s) => [s.showAgentSettings, s.toggleConfig],
|
||||
shallow,
|
||||
);
|
||||
|
||||
@@ -50,37 +40,7 @@ const Config = () => {
|
||||
>
|
||||
<HeaderSpacing />
|
||||
<DraggablePanelContainer style={{ flex: 'none', minWidth: CHAT_SIDEBAR_WIDTH }}>
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
className={styles.header}
|
||||
distribution={'space-between'}
|
||||
horizontal
|
||||
padding={12}
|
||||
paddingInline={16}
|
||||
>
|
||||
<Flexbox>{t('agentProfile')}</Flexbox>
|
||||
<Flexbox gap={4} horizontal>
|
||||
<ActionIcon
|
||||
icon={LucideEdit}
|
||||
onClick={() => {
|
||||
Router.push(`/chat/${id}/edit`);
|
||||
}}
|
||||
size={{ blockSize: 32, fontSize: 20 }}
|
||||
title={t('edit')}
|
||||
/>
|
||||
<ActionIcon
|
||||
icon={LucideX}
|
||||
onClick={() => {
|
||||
toggleConfig(false);
|
||||
}}
|
||||
size={{ blockSize: 32, fontSize: 20 }}
|
||||
title={t('close')}
|
||||
/>
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
<DraggablePanelBody>
|
||||
<ReadMode />
|
||||
</DraggablePanelBody>
|
||||
<SideBar />
|
||||
</DraggablePanelContainer>
|
||||
</DraggablePanel>
|
||||
);
|
||||
|
||||
@@ -16,7 +16,7 @@ const useStyles = createStyles(({ css, token }) => ({
|
||||
`,
|
||||
}));
|
||||
const FunctionMessage = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation('plugin');
|
||||
const { styles } = useStyles();
|
||||
return (
|
||||
<Flexbox className={styles.container} gap={8} horizontal>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ActionIcon } from '@lobehub/ui';
|
||||
import { Popconfirm } from 'antd';
|
||||
import { Eraser, Languages } from 'lucide-react';
|
||||
import { BrainCog, Eraser, Thermometer, Timer } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
@@ -8,14 +8,22 @@ import { shallow } from 'zustand/shallow';
|
||||
import { useSessionStore } from '@/store/session';
|
||||
|
||||
const InputActions = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation('setting');
|
||||
const [clearMessage] = useSessionStore((s) => [s.clearMessage], shallow);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ActionIcon icon={Languages} />
|
||||
<Popconfirm onConfirm={() => clearMessage()} title={t('confirmClearCurrentMessages')}>
|
||||
<ActionIcon icon={Eraser} title={t('clearCurrentMessages')} />
|
||||
<ActionIcon icon={BrainCog} title={t('settingModel.model.title')} />
|
||||
<ActionIcon icon={Thermometer} title={t('settingModel.temperature.title')} />
|
||||
<ActionIcon icon={Timer} title={t('settingChat.historyCount.title')} />
|
||||
<Popconfirm
|
||||
cancelText={t('cancel', { ns: 'common' })}
|
||||
okButtonProps={{ danger: true }}
|
||||
okText={t('ok', { ns: 'common' })}
|
||||
onConfirm={() => clearMessage()}
|
||||
title={t('confirmClearCurrentMessages', { ns: 'common' })}
|
||||
>
|
||||
<ActionIcon icon={Eraser} title={t('clearCurrentMessages', { ns: 'common' })} />
|
||||
</Popconfirm>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChatInputArea, DraggablePanel, Icon } from '@lobehub/ui';
|
||||
import { ChatInputArea, DraggablePanel, Icon, Tooltip } from '@lobehub/ui';
|
||||
import { Button } from 'antd';
|
||||
import { Archive } from 'lucide-react';
|
||||
import { memo, useMemo, useState } from 'react';
|
||||
@@ -13,7 +13,7 @@ import InputActions from './Action';
|
||||
import Token from './Token';
|
||||
|
||||
const ChatInput = () => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation('common');
|
||||
const [expand, setExpand] = useState<boolean>(false);
|
||||
const [text, setText] = useState('');
|
||||
|
||||
@@ -21,7 +21,11 @@ const ChatInput = () => {
|
||||
const [sendMessage] = useSessionStore((s) => [s.createOrSendMsg], shallow);
|
||||
|
||||
const footer = useMemo(
|
||||
() => <Button icon={<Icon icon={Archive} title={t('archiveCurrentMessages')} />} />,
|
||||
() => (
|
||||
<Tooltip title={t('archiveCurrentMessages')}>
|
||||
<Button icon={<Icon icon={Archive} />} />
|
||||
</Tooltip>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
@@ -54,6 +58,10 @@ const ChatInput = () => {
|
||||
onExpandChange={setExpand}
|
||||
onInputChange={setText}
|
||||
onSend={sendMessage}
|
||||
placeholder={t('sendPlaceholder')}
|
||||
text={{
|
||||
send: t('send'),
|
||||
}}
|
||||
/>
|
||||
</DraggablePanel>
|
||||
);
|
||||
|
||||
@@ -1,61 +1,45 @@
|
||||
import { ActionIcon, Avatar, ChatHeader } from '@lobehub/ui';
|
||||
import { Tag } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { ArchiveIcon, MoreVerticalIcon, Share2 } from 'lucide-react';
|
||||
import { PanelRightClose, PanelRightOpen, Settings, Share2 } 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';
|
||||
|
||||
import { agentSelectors, sessionSelectors, useSessionStore } from '@/store/session';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
desc: css`
|
||||
font-size: 12px;
|
||||
color: ${token.colorTextTertiary};
|
||||
`,
|
||||
title: css`
|
||||
font-weight: bold;
|
||||
color: ${token.colorText};
|
||||
`,
|
||||
}));
|
||||
import HeaderTitle from '@/components/HeaderTitle';
|
||||
import { agentSelectors, useSessionStore } from '@/store/session';
|
||||
|
||||
const Header = memo(() => {
|
||||
const { t } = useTranslation('common');
|
||||
const [avatar, model] = useSessionStore(
|
||||
(s) => [agentSelectors.currentAgentAvatar(s), agentSelectors.currentAgentModel(s)],
|
||||
|
||||
const [meta, id, modle] = useSessionStore(
|
||||
(s) => [agentSelectors.currentAgentMeta(s), s.activeId, agentSelectors.currentAgentModel(s)],
|
||||
shallow,
|
||||
);
|
||||
const [meta, id] = useSessionStore((s) => {
|
||||
const chat = sessionSelectors.currentSession(s);
|
||||
return [chat?.meta, s.activeId];
|
||||
}, shallow);
|
||||
|
||||
const [
|
||||
// genShareUrl,
|
||||
|
||||
toggleConfig,
|
||||
] = useSessionStore(
|
||||
(s) => [
|
||||
// s.genShareUrl,
|
||||
s.toggleConfig,
|
||||
],
|
||||
const [showAgentSettings, toggleConfig] = useSessionStore(
|
||||
(s) => [s.showAgentSettings, s.toggleConfig],
|
||||
shallow,
|
||||
);
|
||||
|
||||
const { styles } = useStyles();
|
||||
return (
|
||||
<ChatHeader
|
||||
left={
|
||||
<>
|
||||
<Avatar avatar={avatar} size={40} title={meta?.title} />
|
||||
<Flexbox>
|
||||
<Flexbox align={'center'} className={styles.title} gap={8} horizontal>
|
||||
{meta?.title || t('defaultAgent')}
|
||||
<Tag bordered={false}>{model}</Tag>
|
||||
</Flexbox>
|
||||
<Flexbox className={styles.desc}>{meta?.description || t('noDescription')}</Flexbox>
|
||||
</Flexbox>
|
||||
<Avatar
|
||||
avatar={meta?.avatar}
|
||||
background={meta?.backgroundColor}
|
||||
onClick={() => {
|
||||
Router.push(`/chat/${id}/edit`);
|
||||
}}
|
||||
size={40}
|
||||
style={{ cursor: 'pointer' }}
|
||||
title={meta?.title}
|
||||
/>
|
||||
<HeaderTitle
|
||||
desc={meta?.description || t('noDescription')}
|
||||
tag={<Tag>{modle}</Tag>}
|
||||
title={meta?.title || t('defaultAgent')}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
right={
|
||||
@@ -69,12 +53,19 @@ const Header = memo(() => {
|
||||
size={{ fontSize: 24 }}
|
||||
title={t('share')}
|
||||
/>
|
||||
<ActionIcon icon={ArchiveIcon} size={{ fontSize: 24 }} title={t('archive')} />
|
||||
<ActionIcon
|
||||
icon={MoreVerticalIcon}
|
||||
icon={showAgentSettings ? PanelRightClose : PanelRightOpen}
|
||||
onClick={() => toggleConfig()}
|
||||
size={{ fontSize: 24 }}
|
||||
title={t('sessionSetting')}
|
||||
title={t('roleAndArchive')}
|
||||
/>
|
||||
<ActionIcon
|
||||
icon={Settings}
|
||||
onClick={() => {
|
||||
Router.push(`/chat/${id}/edit`);
|
||||
}}
|
||||
size={{ fontSize: 24 }}
|
||||
title={t('header.session', { ns: 'setting' })}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import { Avatar } from '@lobehub/ui';
|
||||
import { List, Switch, Tag } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import pluginList from '@/plugins';
|
||||
import { agentSelectors, useSessionStore } from '@/store/session';
|
||||
|
||||
const PluginList = () => {
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
const config = useSessionStore(agentSelectors.currentAgentConfigSafe, isEqual);
|
||||
|
||||
const [toggleAgentPlugin] = useSessionStore((s) => [s.toggleAgentPlugin], shallow);
|
||||
|
||||
return (
|
||||
<List
|
||||
bordered
|
||||
dataSource={pluginList}
|
||||
renderItem={(item) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
avatar={<Avatar avatar={item.avatar} />}
|
||||
description={item.schema.description}
|
||||
title={
|
||||
<Flexbox align={'center'} gap={8} horizontal>
|
||||
{t(`plugin-${item.name}` as any)} <Tag>{item.name}</Tag>
|
||||
</Flexbox>
|
||||
}
|
||||
/>
|
||||
<Switch
|
||||
checked={!config.plugins ? false : config.plugins.includes(item.name)}
|
||||
onChange={() => {
|
||||
toggleAgentPlugin(item.name);
|
||||
}}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
size={'large'}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default PluginList;
|
||||
@@ -1,69 +0,0 @@
|
||||
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}
|
||||
showEditWhenEmpty
|
||||
value={systemRole}
|
||||
/>
|
||||
{!editing && !!systemRole && (
|
||||
<Flexbox direction={'horizontal-reverse'}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setEditing(true);
|
||||
}}
|
||||
type={'primary'}
|
||||
>
|
||||
{t('edit')}
|
||||
</Button>
|
||||
</Flexbox>
|
||||
)}
|
||||
</Flexbox>
|
||||
</FormItem>
|
||||
);
|
||||
};
|
||||
|
||||
export default Prompt;
|
||||
@@ -1,113 +1,172 @@
|
||||
import { Collapse, ConfigProvider, InputNumber, Segmented, Slider } from 'antd';
|
||||
import { Form, ItemGroup } from '@lobehub/ui';
|
||||
import { Input, Segmented, Select, Switch } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { BrainCog, MessagesSquare } from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { FORM_STYLE } from '@/const/layoutTokens';
|
||||
import SliderWithInput from '@/features/SliderWithInput';
|
||||
import { agentSelectors, useSessionStore } from '@/store/session';
|
||||
import { LanguageModel } from '@/types/llm';
|
||||
import type { LobeAgentConfig } from '@/types/session';
|
||||
|
||||
import { FormItem } from '../FormItem';
|
||||
import { useStyles } from '../style';
|
||||
import Plugin from './Plugin';
|
||||
import Prompt from './Prompt';
|
||||
type SettingItemGroup = ItemGroup & {
|
||||
children: {
|
||||
name?: keyof LobeAgentConfig;
|
||||
}[];
|
||||
};
|
||||
|
||||
const AgentConfig = () => {
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
const { styles, theme } = useStyles();
|
||||
const { t } = useTranslation('setting');
|
||||
|
||||
const config = useSessionStore(agentSelectors.currentAgentConfigSafe, isEqual);
|
||||
|
||||
const [updateAgentConfig] = useSessionStore((s) => [s.updateAgentConfig], shallow);
|
||||
|
||||
return (
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
components: {
|
||||
Segmented: {
|
||||
colorBgLayout: theme.isDarkMode ? '#111' : '#f1f1f1',
|
||||
},
|
||||
// TODO: setting 结构嵌套,现在是扁平的 params: { temperature: 0.6 }
|
||||
// @ts-ignore
|
||||
const chat: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
children: (
|
||||
<Segmented
|
||||
options={[
|
||||
{ label: t('settingChat.chatStyleType.type.bubble'), value: 'chat' },
|
||||
{ label: t('settingChat.chatStyleType.type.docs'), value: 'docs' },
|
||||
]}
|
||||
/>
|
||||
),
|
||||
label: t('settingChat.chatStyleType.title'),
|
||||
minWidth: undefined,
|
||||
name: 'chatStyleType',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
distribution={'space-between'}
|
||||
horizontal
|
||||
paddingBlock={12}
|
||||
style={{
|
||||
borderBottom: `1px solid ${theme.colorBorder}`,
|
||||
}}
|
||||
>
|
||||
<Flexbox className={styles.profile}> {t('modelConfig')}</Flexbox>
|
||||
</Flexbox>
|
||||
<Flexbox gap={24}>
|
||||
<FormItem label={t('agentModel')}>
|
||||
<Segmented
|
||||
block
|
||||
onChange={(value) => {
|
||||
updateAgentConfig({ model: value as LanguageModel });
|
||||
}}
|
||||
options={Object.values(LanguageModel).map((value) => ({
|
||||
label: t(value),
|
||||
value,
|
||||
}))}
|
||||
size={'large'}
|
||||
value={config.model}
|
||||
/>
|
||||
</FormItem>
|
||||
<Prompt />
|
||||
<Collapse
|
||||
className={styles.title}
|
||||
expandIconPosition={'end'}
|
||||
items={[
|
||||
{
|
||||
children: (
|
||||
<Flexbox paddingBlock={16}>
|
||||
<FormItem label={t('modelTemperature')}>
|
||||
<Flexbox gap={16} horizontal>
|
||||
<Slider
|
||||
max={1}
|
||||
min={0}
|
||||
onChange={(value) => {
|
||||
updateAgentConfig({ params: { temperature: value } });
|
||||
}}
|
||||
step={0.1}
|
||||
style={{ flex: 1 }}
|
||||
value={Number(config.params.temperature)}
|
||||
/>
|
||||
<InputNumber
|
||||
max={1}
|
||||
min={0}
|
||||
onChange={(value) => {
|
||||
if (value) updateAgentConfig({ params: { temperature: value } });
|
||||
}}
|
||||
value={config.params.temperature}
|
||||
/>
|
||||
</Flexbox>
|
||||
</FormItem>
|
||||
</Flexbox>
|
||||
),
|
||||
key: 'advanceSettings',
|
||||
label: t('advanceSettings'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
distribution={'space-between'}
|
||||
horizontal
|
||||
paddingBlock={12}
|
||||
style={{
|
||||
borderBottom: `1px solid ${theme.colorBorder}`,
|
||||
}}
|
||||
>
|
||||
<Flexbox className={styles.profile}> {t('pluginList')}</Flexbox>
|
||||
</Flexbox>
|
||||
<Plugin />
|
||||
</Flexbox>
|
||||
</ConfigProvider>
|
||||
{
|
||||
children: <Input placeholder={t('settingChat.inputTemplate.placeholder')} />,
|
||||
desc: t('settingChat.inputTemplate.desc'),
|
||||
label: t('settingChat.inputTemplate.title'),
|
||||
name: 'enableHistoryCount',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
label: t('settingChat.enableHistoryCount.title'),
|
||||
minWidth: undefined,
|
||||
name: 'enableHistoryCount',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <SliderWithInput max={32} min={0} />,
|
||||
desc: t('settingChat.historyCount.desc'),
|
||||
// @ts-ignore
|
||||
hidden: !config.params.enableHistoryCount,
|
||||
label: t('settingChat.historyCount.title'),
|
||||
name: 'historyCount',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
label: t('settingChat.enableCompressThreshold.title'),
|
||||
minWidth: undefined,
|
||||
name: 'enableCompressThreshold',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <SliderWithInput max={32} min={0} />,
|
||||
desc: t('settingChat.compressThreshold.desc'),
|
||||
// @ts-ignore
|
||||
hidden: !config.params.enableCompressThreshold,
|
||||
label: t('settingChat.compressThreshold.title'),
|
||||
name: 'compressThreshold',
|
||||
},
|
||||
],
|
||||
icon: MessagesSquare,
|
||||
title: t('settingChat.title'),
|
||||
}),
|
||||
[config],
|
||||
);
|
||||
|
||||
// TODO: setting 结构嵌套,现在是扁平的 params: { temperature: 0.6 }
|
||||
// @ts-ignore
|
||||
const model: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
children: (
|
||||
<Select
|
||||
options={Object.values(LanguageModel).map((value) => ({
|
||||
label: value,
|
||||
value,
|
||||
}))}
|
||||
/>
|
||||
),
|
||||
desc: t('settingModel.model.desc'),
|
||||
label: t('settingModel.model.title'),
|
||||
name: 'model',
|
||||
tag: 'model',
|
||||
},
|
||||
{
|
||||
children: <SliderWithInput max={1} min={0} step={0.1} />,
|
||||
desc: t('settingModel.temperature.desc'),
|
||||
label: t('settingModel.temperature.title'),
|
||||
name: 'temperature',
|
||||
tag: 'temperature',
|
||||
},
|
||||
{
|
||||
children: <SliderWithInput max={1} min={0} step={0.1} />,
|
||||
desc: t('settingModel.topP.desc'),
|
||||
label: t('settingModel.topP.title'),
|
||||
name: 'topP',
|
||||
tag: 'top_p',
|
||||
},
|
||||
{
|
||||
children: <SliderWithInput max={2} min={-2} step={0.1} />,
|
||||
desc: t('settingModel.presencePenalty.desc'),
|
||||
label: t('settingModel.presencePenalty.title'),
|
||||
name: 'presencePenalty',
|
||||
tag: 'presence_penalty',
|
||||
},
|
||||
{
|
||||
children: <SliderWithInput max={2} min={-2} step={0.1} />,
|
||||
desc: t('settingModel.frequencyPenalty.desc'),
|
||||
label: t('settingModel.frequencyPenalty.title'),
|
||||
name: 'frequencyPenalty',
|
||||
tag: 'frequency_penalty',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
label: t('settingModel.enableMaxTokens.title'),
|
||||
minWidth: undefined,
|
||||
name: 'enableMaxTokens',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <SliderWithInput max={32_000} min={0} step={100} />,
|
||||
desc: t('settingModel.maxTokens.desc'),
|
||||
// TODO
|
||||
// @ts-ignore
|
||||
hidden: !config?.params?.enableMaxTokens,
|
||||
label: t('settingModel.maxTokens.title'),
|
||||
name: 'params.maxTokens',
|
||||
tag: 'max_tokens',
|
||||
},
|
||||
],
|
||||
icon: BrainCog,
|
||||
title: t('settingModel.title'),
|
||||
}),
|
||||
[config],
|
||||
);
|
||||
|
||||
const items = useMemo(() => [chat, model], [config]);
|
||||
|
||||
return (
|
||||
<Form
|
||||
initialValues={config}
|
||||
items={items}
|
||||
onValuesChange={debounce(updateAgentConfig, 100)}
|
||||
{...FORM_STYLE}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
39
src/pages/chat/[id]/edit/AgentMeta/AutoGenerateInput.tsx
Normal file
39
src/pages/chat/[id]/edit/AgentMeta/AutoGenerateInput.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { ActionIcon } from '@lobehub/ui';
|
||||
import { Input, InputProps } from 'antd';
|
||||
import { useTheme } from 'antd-style';
|
||||
import { Wand2 } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export interface AutoGenerateInputProps extends InputProps {
|
||||
loading: boolean;
|
||||
onGenerate: () => void;
|
||||
}
|
||||
|
||||
const AutoGenerateInput = memo<AutoGenerateInputProps>(({ loading, onGenerate, ...props }) => {
|
||||
const { t } = useTranslation('common');
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Input
|
||||
suffix={
|
||||
<ActionIcon
|
||||
active
|
||||
icon={Wand2}
|
||||
loading={loading}
|
||||
onClick={onGenerate}
|
||||
size={'small'}
|
||||
style={{
|
||||
color: theme.colorInfo,
|
||||
marginRight: -4,
|
||||
}}
|
||||
title={t('autoGenerate')}
|
||||
/>
|
||||
}
|
||||
type={'block'}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default AutoGenerateInput;
|
||||
31
src/pages/chat/[id]/edit/AgentMeta/BackgroundSwatches.tsx
Normal file
31
src/pages/chat/[id]/edit/AgentMeta/BackgroundSwatches.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Swatches, primaryColorsSwatches } from '@lobehub/ui';
|
||||
import { memo } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { agentSelectors, useSessionStore } from '@/store/session';
|
||||
import { DEFAULT_BACKGROUND_COLOR } from '@/store/session/slices/agentConfig';
|
||||
|
||||
const BackgroundSwatches = memo(() => {
|
||||
const [backgroundColor, updateAgentMeta] = useSessionStore(
|
||||
(s) => [agentSelectors.currentAgentBackgroundColor(s), s.updateAgentMeta],
|
||||
shallow,
|
||||
);
|
||||
|
||||
const handleSelect = (v: any) => {
|
||||
if (v) {
|
||||
updateAgentMeta({ backgroundColor: v });
|
||||
} else {
|
||||
updateAgentMeta({ backgroundColor: DEFAULT_BACKGROUND_COLOR });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Swatches
|
||||
activeColor={backgroundColor}
|
||||
colors={primaryColorsSwatches}
|
||||
onSelect={handleSelect}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default BackgroundSwatches;
|
||||
@@ -39,8 +39,12 @@ const useStyles = createStyles(({ css, token, prefixCls }) => ({
|
||||
|
||||
const EmojiPicker = () => {
|
||||
const { styles } = useStyles();
|
||||
const [avatar, updateAgentMeta] = useSessionStore(
|
||||
(s) => [agentSelectors.currentAgentAvatar(s), s.updateAgentMeta],
|
||||
const [avatar, backgroundColor, updateAgentMeta] = useSessionStore(
|
||||
(s) => [
|
||||
agentSelectors.currentAgentAvatar(s),
|
||||
agentSelectors.currentAgentBackgroundColor(s),
|
||||
s.updateAgentMeta,
|
||||
],
|
||||
shallow,
|
||||
);
|
||||
|
||||
@@ -65,7 +69,7 @@ const EmojiPicker = () => {
|
||||
trigger={'click'}
|
||||
>
|
||||
<div className={styles.avatar} style={{ width: 'fit-content' }}>
|
||||
<Avatar avatar={avatar} size={200} />
|
||||
<Avatar avatar={avatar} background={backgroundColor} size={44} />
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import { ActionIcon, Input, Tooltip } from '@lobehub/ui';
|
||||
import { Button, Collapse } from 'antd';
|
||||
import { Form, Icon, type ItemGroup, Tooltip } from '@lobehub/ui';
|
||||
import { Button } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { LucideSparkles } from 'lucide-react';
|
||||
import { UserCircle, Wand2 } from 'lucide-react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { FORM_STYLE } from '@/const/layoutTokens';
|
||||
import { agentSelectors, useSessionStore } from '@/store/session';
|
||||
|
||||
import { FormItem } from '../FormItem';
|
||||
import { useStyles } from '../style';
|
||||
import AutoGenerateInput from './AutoGenerateInput';
|
||||
import BackgroundSwatches from './BackgroundSwatches';
|
||||
import EmojiPicker from './EmojiPicker';
|
||||
|
||||
const AgentMeta = () => {
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
const { styles, theme } = useStyles();
|
||||
const AgentMeta = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
|
||||
const metaData = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
|
||||
|
||||
@@ -39,80 +38,74 @@ const AgentMeta = () => {
|
||||
);
|
||||
|
||||
const basic = [
|
||||
{ key: 'title', label: t('agentName'), placeholder: t('agentNamePlaceholder') },
|
||||
{
|
||||
key: 'title',
|
||||
label: t('settingAgent.name.title'),
|
||||
placeholder: t('settingAgent.name.placeholder'),
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
label: t('agentDescription'),
|
||||
placeholder: t('agentDescriptionPlaceholder'),
|
||||
label: t('settingAgent.description.title'),
|
||||
placeholder: t('settingAgent.description.placeholder'),
|
||||
},
|
||||
// { key: 'tag', label: t('agentTag'), placeholder: t('agentTagPlaceholder') },
|
||||
];
|
||||
|
||||
return (
|
||||
<Collapse
|
||||
defaultActiveKey={hasSystemRole ? ['meta'] : []}
|
||||
items={[
|
||||
const meta: ItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
children: (
|
||||
<Flexbox gap={80} horizontal style={{ marginTop: 16 }}>
|
||||
<Flexbox flex={1} gap={24}>
|
||||
{basic.map((item) => (
|
||||
<FormItem key={item.key} label={item.label}>
|
||||
<Input
|
||||
onChange={(e) => {
|
||||
updateAgentMeta({ [item.key]: e.target.value });
|
||||
}}
|
||||
placeholder={item.placeholder}
|
||||
suffix={
|
||||
<ActionIcon
|
||||
icon={LucideSparkles}
|
||||
loading={loading[item.key as keyof typeof loading]}
|
||||
onClick={() => {
|
||||
autocompleteMeta(item.key as keyof typeof metaData);
|
||||
}}
|
||||
size={'small'}
|
||||
style={{
|
||||
color: theme.purple,
|
||||
}}
|
||||
title={t('autoGenerate')}
|
||||
/>
|
||||
}
|
||||
type={'block'}
|
||||
value={metaData[item.key as keyof typeof metaData]}
|
||||
/>
|
||||
</FormItem>
|
||||
))}
|
||||
</Flexbox>
|
||||
<FormItem label={t('agentAvatar')}>
|
||||
<EmojiPicker />
|
||||
</FormItem>
|
||||
</Flexbox>
|
||||
),
|
||||
className: styles.collapseHeader,
|
||||
extra: (
|
||||
<Tooltip title={t('autoGenerateTooltip')}>
|
||||
<Button
|
||||
disabled={!hasSystemRole}
|
||||
loading={Object.values(loading).some((i) => !!i)}
|
||||
onClick={(e: any) => {
|
||||
e.stopPropagation();
|
||||
console.log(id);
|
||||
if (!id) return;
|
||||
|
||||
autocompleteSessionAgentMeta(id, true);
|
||||
}}
|
||||
size={'large'}
|
||||
>
|
||||
{t('autoGenerate')}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
),
|
||||
key: 'meta',
|
||||
label: <Flexbox className={styles.profile}>{t('profile')}</Flexbox>,
|
||||
children: <EmojiPicker />,
|
||||
label: t('settingAgent.avatar.title'),
|
||||
minWidth: undefined,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{
|
||||
children: <BackgroundSwatches />,
|
||||
label: t('settingAgent.backgroundColor.title'),
|
||||
minWidth: undefined,
|
||||
},
|
||||
...basic.map((item) => ({
|
||||
children: (
|
||||
<AutoGenerateInput
|
||||
loading={loading[item.key as keyof typeof loading]}
|
||||
onChange={(e) => {
|
||||
updateAgentMeta({ [item.key]: e.target.value });
|
||||
}}
|
||||
onGenerate={() => {
|
||||
autocompleteMeta(item.key as keyof typeof metaData);
|
||||
}}
|
||||
placeholder={item.placeholder}
|
||||
value={metaData[item.key as keyof typeof metaData]}
|
||||
/>
|
||||
),
|
||||
label: item.label,
|
||||
})),
|
||||
],
|
||||
extra: (
|
||||
<Tooltip title={t('autoGenerateTooltip', { ns: 'common' })}>
|
||||
<Button
|
||||
disabled={!hasSystemRole}
|
||||
icon={<Icon icon={Wand2} />}
|
||||
loading={Object.values(loading).some((i) => !!i)}
|
||||
onClick={(e: any) => {
|
||||
e.stopPropagation();
|
||||
console.log(id);
|
||||
if (!id) return;
|
||||
autocompleteSessionAgentMeta(id, true);
|
||||
}}
|
||||
size={'small'}
|
||||
>
|
||||
{t('autoGenerate', { ns: 'common' })}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
),
|
||||
icon: UserCircle,
|
||||
title: t('settingAgent.title'),
|
||||
}),
|
||||
[basic, metaData],
|
||||
);
|
||||
};
|
||||
|
||||
return <Form items={[meta]} {...FORM_STYLE} />;
|
||||
});
|
||||
|
||||
export default AgentMeta;
|
||||
|
||||
44
src/pages/chat/[id]/edit/AgentPlugin/index.tsx
Normal file
44
src/pages/chat/[id]/edit/AgentPlugin/index.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Avatar, Form, ItemGroup } from '@lobehub/ui';
|
||||
import { Switch } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { ToyBrick } from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { FORM_STYLE } from '@/const/layoutTokens';
|
||||
import pluginList from '@/plugins';
|
||||
import { agentSelectors, useSessionStore } from '@/store/session';
|
||||
|
||||
const PluginList = () => {
|
||||
const { t } = useTranslation('setting');
|
||||
|
||||
const config = useSessionStore(agentSelectors.currentAgentConfigSafe, isEqual);
|
||||
|
||||
const toggleAgentPlugin = useSessionStore((s) => s.toggleAgentPlugin, shallow);
|
||||
|
||||
const plugin: ItemGroup = useMemo(
|
||||
() => ({
|
||||
children: pluginList.map((item) => ({
|
||||
avatar: <Avatar avatar={item.avatar} />,
|
||||
children: (
|
||||
<Switch
|
||||
checked={!config.plugins ? false : config.plugins.includes(item.name)}
|
||||
onChange={() => toggleAgentPlugin(item.name)}
|
||||
/>
|
||||
),
|
||||
desc: item.schema.description,
|
||||
label: t(`plugins.${item.name}` as any, { ns: 'plugin' }),
|
||||
minWidth: undefined,
|
||||
tag: item.name,
|
||||
})),
|
||||
icon: ToyBrick,
|
||||
title: t('settingPlugin.title'),
|
||||
}),
|
||||
[config],
|
||||
);
|
||||
|
||||
return <Form items={[plugin]} {...FORM_STYLE} />;
|
||||
};
|
||||
|
||||
export default PluginList;
|
||||
48
src/pages/chat/[id]/edit/AgentPrompt/index.tsx
Normal file
48
src/pages/chat/[id]/edit/AgentPrompt/index.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { CodeEditor, FormGroup } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { Bot } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { FORM_STYLE } from '@/const/layoutTokens';
|
||||
import { agentSelectors, useSessionStore } from '@/store/session';
|
||||
|
||||
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 AgentPrompt = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
|
||||
const systemRole = useSessionStore(agentSelectors.currentAgentSystemRole, shallow);
|
||||
|
||||
const [updateAgentConfig] = useSessionStore((s) => [s.updateAgentConfig], shallow);
|
||||
|
||||
return (
|
||||
<FormGroup icon={Bot} style={FORM_STYLE.style} title={t('settingAgent.prompt.title')}>
|
||||
<CodeEditor
|
||||
language={'md'}
|
||||
onValueChange={(e) => {
|
||||
updateAgentConfig({ systemRole: e });
|
||||
}}
|
||||
placeholder={t('settingAgent.name.placeholder')}
|
||||
resize={false}
|
||||
style={{ marginBottom: 16, marginTop: 16 }}
|
||||
type={'pure'}
|
||||
value={systemRole}
|
||||
/>
|
||||
</FormGroup>
|
||||
);
|
||||
});
|
||||
|
||||
export default AgentPrompt;
|
||||
31
src/pages/chat/[id]/edit/Header.tsx
Normal file
31
src/pages/chat/[id]/edit/Header.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { ActionIcon, ChatHeader } from '@lobehub/ui';
|
||||
import { Download, Share2 } from 'lucide-react';
|
||||
import Router from 'next/router';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import HeaderTitle from '@/components/HeaderTitle';
|
||||
|
||||
const Header = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
|
||||
return (
|
||||
<ChatHeader
|
||||
left={<HeaderTitle title={t('header.session')} />}
|
||||
onBackClick={() => Router.back()}
|
||||
right={
|
||||
<>
|
||||
<ActionIcon icon={Share2} size={{ fontSize: 24 }} title={t('share', { ns: 'common' })} />
|
||||
<ActionIcon
|
||||
icon={Download}
|
||||
size={{ fontSize: 24 }}
|
||||
title={t('export', { ns: 'common' })}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
showBackButton
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default Header;
|
||||
@@ -1,43 +1,23 @@
|
||||
import { ActionIcon, ChatHeader } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { Download, Share2 } from 'lucide-react';
|
||||
import Head from 'next/head';
|
||||
import Router from 'next/router';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import HeaderSpacing from '@/components/HeaderSpacing';
|
||||
import { HEADER_HEIGHT } from '@/const/layoutTokens';
|
||||
import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
|
||||
|
||||
import ChatLayout from '../../layout';
|
||||
import AgentConfig from './AgentConfig';
|
||||
import AgentMeta from './AgentMeta';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
footer: css`
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
border-top: 1px solid ${token.colorBorder};
|
||||
`,
|
||||
form: css`
|
||||
overflow-y: auto;
|
||||
`,
|
||||
header: css`
|
||||
background: ${token.colorBgContainer};
|
||||
border-bottom: 1px solid ${token.colorSplit};
|
||||
`,
|
||||
title: css`
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
`,
|
||||
}));
|
||||
import AgentPlugin from './AgentPlugin';
|
||||
import AgentPrompt from './AgentPrompt';
|
||||
import Header from './Header';
|
||||
|
||||
const EditPage = memo(() => {
|
||||
const { t } = useTranslation('common');
|
||||
const { t } = useTranslation('setting');
|
||||
|
||||
const { styles } = useStyles();
|
||||
|
||||
const pageTitle = t('editAgentProfile');
|
||||
const pageTitle = genSiteHeadTitle(t('header.session'));
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -45,21 +25,13 @@ const EditPage = memo(() => {
|
||||
<title>{pageTitle}</title>
|
||||
</Head>
|
||||
<ChatLayout>
|
||||
<ChatHeader
|
||||
left={<div className={styles.title}>{t('editAgentProfile')}</div>}
|
||||
onBackClick={() => Router.back()}
|
||||
right={
|
||||
<>
|
||||
<ActionIcon icon={Share2} size={{ fontSize: 24 }} title={t('share')} />
|
||||
<ActionIcon icon={Download} size={{ fontSize: 24 }} title={t('export')} />
|
||||
</>
|
||||
}
|
||||
showBackButton
|
||||
/>
|
||||
<Flexbox className={styles.form} flex={1} gap={10} padding={24}>
|
||||
<HeaderSpacing />
|
||||
<Header />
|
||||
<Flexbox align={'center'} flex={1} gap={16} padding={24} style={{ overflow: 'auto' }}>
|
||||
<HeaderSpacing height={HEADER_HEIGHT - 16} />
|
||||
<AgentPrompt />
|
||||
<AgentMeta />
|
||||
<AgentConfig />
|
||||
<AgentPlugin />
|
||||
</Flexbox>
|
||||
</ChatLayout>
|
||||
</>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { memo } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import { sessionSelectors, useSessionStore } from '@/store/session';
|
||||
import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
|
||||
|
||||
import Layout from '../layout';
|
||||
import Config from './Config';
|
||||
@@ -16,7 +17,7 @@ const Chat = memo(() => {
|
||||
return [context?.meta.title];
|
||||
}, isEqual);
|
||||
|
||||
const pageTitle = title ? `${title} - LobeChat` : 'LobeChat';
|
||||
const pageTitle = genSiteHeadTitle(title);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
import { ChatHeader } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Router from 'next/router';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import HeaderTitle from '@/components/HeaderTitle';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
title: css`
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: ${token.colorText};
|
||||
`,
|
||||
}));
|
||||
const Header = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
|
||||
const { styles } = useStyles();
|
||||
return (
|
||||
<ChatHeader
|
||||
left={<div className={styles.title}>{t('header')}</div>}
|
||||
left={<HeaderTitle title={t('header.global')} />}
|
||||
onBackClick={() => Router.back()}
|
||||
showBackButton
|
||||
/>
|
||||
|
||||
@@ -2,11 +2,12 @@ import { Form, type ItemGroup, ThemeSwitch } from '@lobehub/ui';
|
||||
import { Form as AntForm, Button, Input, Popconfirm, Select, Switch } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { AppWindow, Bot, MessagesSquare, Palette, Webhook } from 'lucide-react';
|
||||
import { AppWindow, BrainCog, MessagesSquare, Palette, Webhook } from 'lucide-react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { FORM_STYLE } from '@/const/layoutTokens';
|
||||
import AvatarWithUpload from '@/features/AvatarWithUpload';
|
||||
import SliderWithInput from '@/features/SliderWithInput';
|
||||
import { options } from '@/locales/options';
|
||||
@@ -99,7 +100,7 @@ const SettingForm = memo(() => {
|
||||
[settings],
|
||||
);
|
||||
|
||||
const OpenAI: SettingItemGroup = useMemo(
|
||||
const openAI: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
@@ -123,7 +124,7 @@ const SettingForm = memo(() => {
|
||||
[settings],
|
||||
);
|
||||
|
||||
const Chat: SettingItemGroup = useMemo(
|
||||
const chat: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
@@ -161,7 +162,7 @@ const SettingForm = memo(() => {
|
||||
[settings],
|
||||
);
|
||||
|
||||
const Model: SettingItemGroup = useMemo(
|
||||
const model: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
@@ -222,13 +223,13 @@ const SettingForm = memo(() => {
|
||||
tag: 'max_tokens',
|
||||
},
|
||||
],
|
||||
icon: Bot,
|
||||
icon: BrainCog,
|
||||
title: t('settingModel.title'),
|
||||
}),
|
||||
[settings],
|
||||
);
|
||||
|
||||
const System: SettingItemGroup = useMemo(
|
||||
const system: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
@@ -282,16 +283,15 @@ const SettingForm = memo(() => {
|
||||
[settings],
|
||||
);
|
||||
|
||||
const items = useMemo(() => [theme, OpenAI, Chat, Model, System], [settings]);
|
||||
const items = useMemo(() => [theme, openAI, chat, model, system], [settings]);
|
||||
|
||||
return (
|
||||
<Form
|
||||
form={form}
|
||||
initialValues={settings}
|
||||
itemMinWidth="max(30%,240px)"
|
||||
items={items}
|
||||
onValuesChange={debounce(setSettings, 100)}
|
||||
style={{ maxWidth: 1024, width: '100%' }}
|
||||
{...FORM_STYLE}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Flexbox } from 'react-layout-kit';
|
||||
import HeaderSpacing from '@/components/HeaderSpacing';
|
||||
import { createI18nNext } from '@/locales/create';
|
||||
import ChatLayout from '@/pages/chat/layout';
|
||||
import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
|
||||
|
||||
import Header from './Header';
|
||||
import SettingForm from './SettingForm';
|
||||
@@ -14,7 +15,7 @@ const initI18n = createI18nNext('setting');
|
||||
|
||||
const SettingLayout = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const pageTitle = `${t('header')} - LobeChat`;
|
||||
const pageTitle = genSiteHeadTitle(t('header.global'));
|
||||
|
||||
useEffect(() => {
|
||||
initI18n.finally();
|
||||
|
||||
@@ -16,9 +16,11 @@ export const initialLobeAgentConfig: LobeAgentConfig = {
|
||||
systemRole: '',
|
||||
};
|
||||
|
||||
export const DEFAULT_AVATAR = 'https://npm.elemecdn.com/@lobehub/assets-logo/assets/logo-3d.webp';
|
||||
export const DEFAULT_AVATAR = '🤖';
|
||||
|
||||
export const DEFAULT_TITLE = 'DefaultSession';
|
||||
export const DEFAULT_BACKGROUND_COLOR = 'rgba(0,0,0,0)';
|
||||
|
||||
export const DEFAULT_TITLE = 'defaultSession';
|
||||
|
||||
export const initialAgentConfigState: AgentConfigState = {
|
||||
// // loading 中间态
|
||||
@@ -30,5 +32,5 @@ export const initialAgentConfigState: AgentConfigState = {
|
||||
title: false,
|
||||
},
|
||||
|
||||
showAgentSettings: false,
|
||||
showAgentSettings: true,
|
||||
};
|
||||
|
||||
@@ -4,27 +4,30 @@ import { MetaData } from '@/types/meta';
|
||||
import { LobeAgentConfig } from '@/types/session';
|
||||
|
||||
import { sessionSelectors } from '../session';
|
||||
import { DEFAULT_AVATAR, initialLobeAgentConfig } from './initialState';
|
||||
import { DEFAULT_AVATAR, DEFAULT_BACKGROUND_COLOR, initialLobeAgentConfig } from './initialState';
|
||||
|
||||
const currentAgentMeta = (s: SessionStore): MetaData => {
|
||||
const session = sessionSelectors.currentSession(s);
|
||||
|
||||
return session?.meta || {};
|
||||
return { avatar: DEFAULT_AVATAR, backgroundColor: DEFAULT_BACKGROUND_COLOR, ...session?.meta };
|
||||
};
|
||||
|
||||
const currentAgentTitle = (s: SessionStore) => currentAgentMeta(s)?.title;
|
||||
|
||||
const currentAgentBackgroundColor = (s: SessionStore) => {
|
||||
const session = sessionSelectors.currentSession(s);
|
||||
if (!session) return DEFAULT_BACKGROUND_COLOR;
|
||||
return session.meta.backgroundColor || DEFAULT_BACKGROUND_COLOR;
|
||||
};
|
||||
|
||||
const currentAgentAvatar = (s: SessionStore) => {
|
||||
const session = sessionSelectors.currentSession(s);
|
||||
|
||||
if (!session) return DEFAULT_AVATAR;
|
||||
|
||||
return session.meta.avatar || DEFAULT_AVATAR;
|
||||
};
|
||||
|
||||
const currentAgentConfig = (s: SessionStore) => {
|
||||
const session = sessionSelectors.currentSession(s);
|
||||
|
||||
return session?.config;
|
||||
};
|
||||
|
||||
@@ -32,6 +35,10 @@ const currentAgentConfigSafe = (s: SessionStore): LobeAgentConfig => {
|
||||
return currentAgentConfig(s) || initialLobeAgentConfig;
|
||||
};
|
||||
|
||||
const currentAgentSystemRole = (s: SessionStore) => {
|
||||
return currentAgentConfigSafe(s).systemRole;
|
||||
};
|
||||
|
||||
const currentAgentModel = (s: SessionStore): LanguageModel => {
|
||||
const config = currentAgentConfig(s);
|
||||
|
||||
@@ -45,10 +52,12 @@ const hasSystemRole = (s: SessionStore) => {
|
||||
};
|
||||
export const agentSelectors = {
|
||||
currentAgentAvatar,
|
||||
currentAgentBackgroundColor,
|
||||
currentAgentConfig,
|
||||
currentAgentConfigSafe,
|
||||
currentAgentMeta,
|
||||
currentAgentModel,
|
||||
currentAgentSystemRole,
|
||||
currentAgentTitle,
|
||||
hasSystemRole,
|
||||
};
|
||||
|
||||
1
src/utils/genSiteHeadTitle.ts
Normal file
1
src/utils/genSiteHeadTitle.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const genSiteHeadTitle = (title?: string) => (title ? `${title} - LobeChat` : 'LobeChat');
|
||||
Reference in New Issue
Block a user