mirror of
https://github.com/lobehub/lobehub
synced 2026-04-21 17:47:27 +00:00
✨ feat: Add agent share
This commit is contained in:
parent
c45526a811
commit
953d7c73b3
34 changed files with 534 additions and 105 deletions
|
|
@ -1,23 +1,23 @@
|
|||
{
|
||||
"agentDefaultMessage": "Hello, I'm **{{name}}**. You can start chatting with me right away or go to [Assistant Settings](/chat/settings#session={{id}}) to improve my information.",
|
||||
"agentDefaultMessage": "Hello, I'm **{{name}}**. You can start chatting with me right away or go to [Agent Settings](/chat/settings#session={{id}}) to improve my information.",
|
||||
"agentDefaultMessageWithSystemRole": "Hello, I'm **{{name}}**, {{systemRole}}. Let's start the conversation!",
|
||||
"backToBottom": "Go to Latest Messages",
|
||||
"clearCurrentMessages": "Clear Current Session Messages",
|
||||
"confirmClearCurrentMessages": "You are about to clear the current session messages. Once cleared, they cannot be recovered. Please confirm your operation.",
|
||||
"confirmRemoveSessionItemAlert": "You are about to delete this assistant. Once deleted, it cannot be recovered. Please confirm your operation.",
|
||||
"defaultAgent": "Custom Assistant",
|
||||
"defaultSession": "Custom Assistant",
|
||||
"confirmRemoveSessionItemAlert": "You are about to delete this agent. Once deleted, it cannot be recovered. Please confirm your operation.",
|
||||
"defaultAgent": "Custom Agent",
|
||||
"defaultSession": "Custom Agent",
|
||||
"historyRange": "History Range",
|
||||
"inbox": {
|
||||
"defaultMessage": "Hello, I'm your intelligent assistant. You can ask me any questions, and I will do my best to answer you. If you need a more professional or customized assistant, you can click on `+` to create a custom assistant.",
|
||||
"desc": "Activate brain clusters and spark thinking. Your intelligent assistant is here to communicate with you about everything.",
|
||||
"defaultMessage": "Hello, I'm your intelligent agent. You can ask me any questions, and I will do my best to answer you. If you need a more professional or customized agent, you can click on `+` to create a custom agent.",
|
||||
"desc": "Activate brain clusters and spark thinking. Your intelligent agent is here to communicate with you about everything.",
|
||||
"title": "Chat Randomly"
|
||||
},
|
||||
"newAgent": "Create New Assistant",
|
||||
"newAgent": "Create New Agent",
|
||||
"noDescription": "No description available",
|
||||
"regenerate": "Regenerate",
|
||||
"roleAndArchive": "Roles and Archives",
|
||||
"searchAgentPlaceholder": "Search assistants and conversations...",
|
||||
"searchAgentPlaceholder": "Search agents and conversations...",
|
||||
"send": "Send",
|
||||
"sendPlaceholder": "Enter chat content...",
|
||||
"shareModal": {
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
"withBackground": "Include Background Image",
|
||||
"withFooter": "Include Footer",
|
||||
"withPluginInfo": "Include Plugin Information",
|
||||
"withSystemRole": "Include Assistant Role Setting"
|
||||
"withSystemRole": "Include Agent Role Setting"
|
||||
},
|
||||
"stop": "Stop",
|
||||
"temp": "Temporary",
|
||||
|
|
@ -50,6 +50,6 @@
|
|||
"clear": "Clear Translation"
|
||||
},
|
||||
"translateTo": "Translate",
|
||||
"updateAgent": "Update Assistant Information",
|
||||
"updateAgent": "Update Agent Information",
|
||||
"warp": "Line Break"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -235,6 +235,13 @@
|
|||
},
|
||||
"title": "Theme Settings"
|
||||
},
|
||||
"submitAgentModal": {
|
||||
"tooltips": "Share to Assistant Market",
|
||||
"button": "Submit Assistant",
|
||||
"identifier": "Identifier",
|
||||
"metaMiss": "Please complete the assistant information before submitting. It should include name, description, and tags.",
|
||||
"placeholder": "Please enter a unique identifier for the assistant, such as web-development."
|
||||
},
|
||||
"tab": {
|
||||
"agent": "Default Agent",
|
||||
"common": "Common Settings",
|
||||
|
|
|
|||
|
|
@ -222,6 +222,13 @@
|
|||
},
|
||||
"title": "テーマ設定"
|
||||
},
|
||||
"submitAgentModal": {
|
||||
"tooltips": "アシスタントマーケットに共有する",
|
||||
"button": "助手を提出する",
|
||||
"identifier": "識別子 エージェントの識別子",
|
||||
"metaMiss": "エージェント情報を入力してから提出してください。名前、説明、およびタグが必要です",
|
||||
"placeholder": "エージェントの識別子を入力してください。一意である必要があります。例:web-development"
|
||||
},
|
||||
"tab": {
|
||||
"agent": "デフォルトのアシスタント",
|
||||
"common": "一般設定",
|
||||
|
|
|
|||
|
|
@ -222,6 +222,13 @@
|
|||
},
|
||||
"title": "테마 설정"
|
||||
},
|
||||
"submitAgentModal": {
|
||||
"tooltips": "도우미 마켓에 공유",
|
||||
"button": "도우미 제출",
|
||||
"identifier": "식별자 도우미 식별자",
|
||||
"metaMiss": "도우미 정보를 입력한 후 제출하세요. 이름, 설명 및 태그를 포함해야 합니다.",
|
||||
"placeholder": "도우미의 식별자를 입력하세요. 고유해야 하며, 예를 들어 web-development과 같은 형식이어야 합니다."
|
||||
},
|
||||
"tab": {
|
||||
"agent": "기본 도우미",
|
||||
"common": "일반 설정",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"about": "Наш Github",
|
||||
"advanceSettings": "Дополнительные настройки",
|
||||
"agentDefaultMessage": "Здравствуйте, я **{{name}}**. Вы можете начать общаться со мной прямо сейчас или перейти на [Assistant Settings](/chat/settings#session={{id}}) для моей настройки.",
|
||||
"agentDefaultMessage": "Здравствуйте, я **{{name}}**. Вы можете начать общаться со мной прямо сейчас или перейти на [Agent Settings](/chat/settings#session={{id}}) для моей настройки.",
|
||||
"agentMaxToken": "Максимальная длина сессии",
|
||||
"agentModel": "Модель",
|
||||
"agentProfile": "Информация о помощнике",
|
||||
"appInitializing": "LobeChat запускается, пожалуйста, подождите...",
|
||||
|
|
|
|||
|
|
@ -235,6 +235,13 @@
|
|||
},
|
||||
"title": "Настройки темы"
|
||||
},
|
||||
"submitAgentModal": {
|
||||
"tooltips": "Поделиться на рынке помощников",
|
||||
"button": "Отправить агента",
|
||||
"identifier": "Идентификатор агента",
|
||||
"metaMiss": "Пожалуйста, заполните информацию об агенте перед отправкой. Необходимо указать имя, описание и метки",
|
||||
"placeholder": "Введите идентификатор агента, который должен быть уникальным, например, web-development"
|
||||
},
|
||||
"tab": {
|
||||
"agent": "Помощник по умолчанию",
|
||||
"common": "Общие настройки",
|
||||
|
|
|
|||
|
|
@ -222,6 +222,13 @@
|
|||
},
|
||||
"title": "主题设置"
|
||||
},
|
||||
"submitAgentModal": {
|
||||
"button": "提交助手",
|
||||
"identifier": "identifier 助手标识符",
|
||||
"metaMiss": "请补全助手信息后提交,需要包含名称、描述和标签",
|
||||
"placeholder": "请输入助手的标识符,需要是唯一的,比如 web-development",
|
||||
"tooltips": "分享到助手市场"
|
||||
},
|
||||
"tab": {
|
||||
"agent": "默认助手",
|
||||
"common": "通用设置",
|
||||
|
|
|
|||
|
|
@ -235,6 +235,13 @@
|
|||
},
|
||||
"title": "主題設定"
|
||||
},
|
||||
"submitAgentModal": {
|
||||
"tooltips": "分享到助手市場",
|
||||
"button": "提交助手",
|
||||
"identifier": "助手識別符",
|
||||
"metaMiss": "請補全助手資訊後提交,需要包含名稱、描述和標籤",
|
||||
"placeholder": "請輸入助手的識別符,需要是唯一的,例如 web-development"
|
||||
},
|
||||
"tab": {
|
||||
"agent": "預設助理",
|
||||
"common": "通用設定",
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@
|
|||
"@emoji-mart/data": "^1",
|
||||
"@emoji-mart/react": "^1",
|
||||
"@icons-pack/react-simple-icons": "^9",
|
||||
"@lobehub/chat-plugin-sdk": "^1.17.8",
|
||||
"@lobehub/chat-plugin-sdk": "latest",
|
||||
"@lobehub/chat-plugins-gateway": "latest",
|
||||
"@lobehub/ui": "latest",
|
||||
"@vercel/analytics": "^1",
|
||||
|
|
@ -79,7 +79,7 @@
|
|||
"antd-style": "^3.5",
|
||||
"brotli-wasm": "^1",
|
||||
"chroma-js": "^2",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"copy-to-clipboard": "^3",
|
||||
"dayjs": "^1",
|
||||
"emoji-mart": "^5",
|
||||
"fast-deep-equal": "^3",
|
||||
|
|
@ -98,6 +98,7 @@
|
|||
"openai": "^4.10.0",
|
||||
"polished": "^4",
|
||||
"posthog-js": "^1",
|
||||
"query-string": "^8",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react-hotkeys-hook": "^4",
|
||||
|
|
@ -146,7 +147,7 @@
|
|||
"eslint": "^8",
|
||||
"husky": "^8",
|
||||
"jsdom": "^22",
|
||||
"lint-staged": "^15.0.0",
|
||||
"lint-staged": "^15",
|
||||
"lodash": "^4",
|
||||
"next-pwa": "^5",
|
||||
"node-fetch": "^3",
|
||||
|
|
|
|||
|
|
@ -13,9 +13,12 @@ export const readJSON = (filePath: string) => {
|
|||
return JSON.parse(data);
|
||||
};
|
||||
|
||||
export const replaceAssistantToAgent = (text: string) =>
|
||||
text.replaceAll('assistant', 'agent').replaceAll('Assistant', 'Agent');
|
||||
|
||||
export const writeJSON = (filePath: string, data: any) => {
|
||||
const jsonStr = JSON.stringify(data, null, 2);
|
||||
writeFileSync(filePath, jsonStr, 'utf8');
|
||||
writeFileSync(filePath, replaceAssistantToAgent(jsonStr), 'utf8');
|
||||
};
|
||||
|
||||
export const genResourcesContent = (locales: string[]) => {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { memo, useState } from 'react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import AgentInfo from '@/features/AgentInfo';
|
||||
import { useSessionChatInit, useSessionStore } from '@/store/session';
|
||||
import { agentSelectors } from '@/store/session/selectors';
|
||||
|
||||
|
|
@ -15,8 +16,9 @@ const SystemRole = memo(() => {
|
|||
const [openModal, setOpenModal] = useState(false);
|
||||
const [editing, setEditing] = useState(false);
|
||||
const { styles } = useStyles();
|
||||
const [systemRole, updateAgentConfig] = useSessionStore((s) => [
|
||||
const [systemRole, meta, updateAgentConfig] = useSessionStore((s) => [
|
||||
agentSelectors.currentAgentSystemRole(s),
|
||||
agentSelectors.currentAgentMeta(s),
|
||||
s.updateAgentConfig,
|
||||
]);
|
||||
|
||||
|
|
@ -57,6 +59,7 @@ const SystemRole = memo(() => {
|
|||
<EditableMessage
|
||||
classNames={{ markdown: styles.prompt }}
|
||||
editing={editing}
|
||||
model={{ extra: <AgentInfo meta={meta} style={{ marginBottom: 16 }} /> }}
|
||||
onChange={(e) => {
|
||||
updateAgentConfig({ systemRole: e });
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
import { memo } from 'react';
|
||||
|
||||
import { HeaderContent } from '@/app/chat/settings/components/Header';
|
||||
|
||||
import Layout from './Desktop';
|
||||
|
||||
const Header = memo(() => (
|
||||
<Layout>
|
||||
<HeaderContent />
|
||||
</Layout>
|
||||
));
|
||||
|
||||
export default Header;
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
import { ChatHeader, ChatHeaderTitle } from '@lobehub/ui';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { ReactNode, memo } from 'react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import HeaderContent from '@/app/chat/settings/features/HeaderContent';
|
||||
import { pathString } from '@/utils/url';
|
||||
|
||||
const Header = memo<{ children: ReactNode }>(({ children }) => {
|
||||
const Header = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const router = useRouter();
|
||||
|
||||
|
|
@ -13,7 +14,7 @@ const Header = memo<{ children: ReactNode }>(({ children }) => {
|
|||
<ChatHeader
|
||||
left={<ChatHeaderTitle title={t('header.session')} />}
|
||||
onBackClick={() => router.push(pathString('/chat', { hash: location.hash }))}
|
||||
right={children}
|
||||
right={<HeaderContent />}
|
||||
showBackButton
|
||||
/>
|
||||
);
|
||||
|
|
@ -5,7 +5,7 @@ import SafeSpacing from '@/components/SafeSpacing';
|
|||
import { HEADER_HEIGHT } from '@/const/layoutTokens';
|
||||
|
||||
import Layout from '../../(desktop)/layout.desktop';
|
||||
import Header from './Header';
|
||||
import Header from './features/Header';
|
||||
|
||||
export default memo(({ children }: PropsWithChildren) => (
|
||||
<Layout>
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
import { memo } from 'react';
|
||||
|
||||
import { HeaderContent } from '@/app/chat/settings/components/Header';
|
||||
import Layout from '@/app/chat/settings/components/Header/Mobile';
|
||||
|
||||
const Header = memo(() => (
|
||||
<Layout>
|
||||
<HeaderContent />
|
||||
</Layout>
|
||||
));
|
||||
|
||||
export default Header;
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
import { MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { type ReactNode, memo } from 'react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import HeaderContent from '@/app/chat/settings/features/HeaderContent';
|
||||
import { pathString } from '@/utils/url';
|
||||
|
||||
const Header = memo<{ children: ReactNode }>(({ children }) => {
|
||||
const Header = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const router = useRouter();
|
||||
|
||||
|
|
@ -13,7 +14,7 @@ const Header = memo<{ children: ReactNode }>(({ children }) => {
|
|||
<MobileNavBar
|
||||
center={<MobileNavBarTitle title={t('header.session')} />}
|
||||
onBackClick={() => router.push(pathString('/chat/mobile', { hash: location.hash }))}
|
||||
right={children}
|
||||
right={<HeaderContent />}
|
||||
showBackButton
|
||||
/>
|
||||
);
|
||||
|
|
@ -2,7 +2,7 @@ import { PropsWithChildren, memo } from 'react';
|
|||
|
||||
import AppLayoutMobile from '@/layout/AppLayout.mobile';
|
||||
|
||||
import Header from './Header';
|
||||
import Header from './features/Header';
|
||||
|
||||
export default memo(({ children }: PropsWithChildren) => (
|
||||
<AppLayoutMobile navBar={<Header />}>{children}</AppLayoutMobile>
|
||||
|
|
|
|||
|
|
@ -9,9 +9,12 @@ import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|||
import { exportSingleAgent, exportSingleSession } from '@/helpers/export';
|
||||
import { useSessionStore } from '@/store/session';
|
||||
|
||||
import SubmitAgentButton from './SubmitAgentButton';
|
||||
|
||||
export const HeaderContent = memo<{ mobile?: boolean }>(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const id = useSessionStore((s) => s.activeId);
|
||||
|
||||
const { mobile } = useResponsive();
|
||||
|
||||
const items = useMemo<MenuProps['items']>(
|
||||
|
|
@ -41,8 +44,13 @@ export const HeaderContent = memo<{ mobile?: boolean }>(() => {
|
|||
const size = mobile ? MOBILE_HEADER_ICON_SIZE : { fontSize: 24 };
|
||||
|
||||
return (
|
||||
<Dropdown arrow={false} menu={{ items }} trigger={['click']}>
|
||||
<ActionIcon icon={HardDriveDownload} size={size} title={t('export', { ns: 'common' })} />
|
||||
</Dropdown>
|
||||
<>
|
||||
<SubmitAgentButton />
|
||||
<Dropdown arrow={false} menu={{ items }} trigger={['click']}>
|
||||
<ActionIcon icon={HardDriveDownload} size={size} title={t('export', { ns: 'common' })} />
|
||||
</Dropdown>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default HeaderContent;
|
||||
86
src/app/chat/settings/features/SubmitAgentButton/Inner.tsx
Normal file
86
src/app/chat/settings/features/SubmitAgentButton/Inner.tsx
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import { Alert, Button, Divider, Input } from 'antd';
|
||||
import { useTheme } from 'antd-style';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { kebabCase } from 'lodash-es';
|
||||
import qs from 'query-string';
|
||||
import { memo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import MobilePadding from '@/components/MobilePadding';
|
||||
import { AGENTS_INDEX_GITHUB_ISSUE } from '@/const/url';
|
||||
import AgentInfo from '@/features/AgentInfo';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { useSessionStore } from '@/store/session';
|
||||
import { agentSelectors } from '@/store/session/slices/agentConfig';
|
||||
|
||||
const Inner = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const [identifier, setIdentifier] = useState('');
|
||||
const systemRole = useSessionStore(agentSelectors.currentAgentSystemRole);
|
||||
const theme = useTheme();
|
||||
const meta = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
|
||||
const language = useGlobalStore((s) => s.settings.language);
|
||||
|
||||
const isMetaPass = Boolean(
|
||||
meta && meta.title && meta.description && (meta.tags as string[])?.length > 0 && meta.avatar,
|
||||
);
|
||||
|
||||
const handleSubmit = () => {
|
||||
const body = [
|
||||
'### systemRole',
|
||||
systemRole,
|
||||
'### identifier',
|
||||
identifier,
|
||||
'### avatar',
|
||||
meta.avatar,
|
||||
'### title',
|
||||
meta.title,
|
||||
'### description',
|
||||
meta.description,
|
||||
'### tags',
|
||||
(meta.tags as string[]).join(', '),
|
||||
'### locale',
|
||||
language === 'auto' ? navigator.language : language,
|
||||
].join('\n\n');
|
||||
|
||||
const url = qs.stringifyUrl({
|
||||
query: { body, labels: '🤖 Agent PR', title: `[Agent] ${meta.title}` },
|
||||
url: AGENTS_INDEX_GITHUB_ISSUE,
|
||||
});
|
||||
|
||||
window.open(url, '_blank');
|
||||
};
|
||||
|
||||
return (
|
||||
<Flexbox gap={16}>
|
||||
<MobilePadding>
|
||||
{!isMetaPass && (
|
||||
<Alert message={t('submitAgentModal.metaMiss')} showIcon type={'warning'} />
|
||||
)}
|
||||
<AgentInfo meta={meta} systemRole={systemRole} />
|
||||
<Divider style={{ margin: '8px 0' }} />
|
||||
<strong>
|
||||
<span style={{ color: theme.colorError, marginRight: 4 }}>*</span>
|
||||
{t('submitAgentModal.identifier')}
|
||||
</strong>
|
||||
<Input
|
||||
onChange={(e) => setIdentifier(kebabCase(e.target.value))}
|
||||
placeholder={t('submitAgentModal.placeholder')}
|
||||
value={identifier}
|
||||
/>
|
||||
<Button
|
||||
block
|
||||
disabled={!isMetaPass || !identifier}
|
||||
onClick={handleSubmit}
|
||||
size={'large'}
|
||||
type={'primary'}
|
||||
>
|
||||
{t('submitAgentModal.button')}
|
||||
</Button>
|
||||
</MobilePadding>
|
||||
</Flexbox>
|
||||
);
|
||||
});
|
||||
|
||||
export default Inner;
|
||||
37
src/app/chat/settings/features/SubmitAgentButton/index.tsx
Normal file
37
src/app/chat/settings/features/SubmitAgentButton/index.tsx
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { ActionIcon, Modal } from '@lobehub/ui';
|
||||
import { useResponsive } from 'antd-style';
|
||||
import { Share2 } from 'lucide-react';
|
||||
import { memo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
||||
|
||||
import Inner from './Inner';
|
||||
|
||||
const SubmitAgentButton = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const { mobile } = useResponsive();
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const size = mobile ? MOBILE_HEADER_ICON_SIZE : { fontSize: 24 };
|
||||
return (
|
||||
<>
|
||||
<ActionIcon
|
||||
icon={Share2}
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
size={size}
|
||||
title={t('submitAgentModal.tooltips')}
|
||||
/>
|
||||
<Modal
|
||||
centered={false}
|
||||
footer={null}
|
||||
onCancel={() => setIsModalOpen(false)}
|
||||
open={isModalOpen}
|
||||
title={t('submitAgentModal.tooltips')}
|
||||
>
|
||||
<Inner />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default SubmitAgentButton;
|
||||
46
src/app/chat/settings/features/SubmitAgentButton/style.ts
Normal file
46
src/app/chat/settings/features/SubmitAgentButton/style.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css, token, prefixCls }) => ({
|
||||
author: css`
|
||||
font-size: 12px;
|
||||
`,
|
||||
|
||||
avatar: css`
|
||||
flex: none;
|
||||
`,
|
||||
container: css`
|
||||
position: relative;
|
||||
padding: 16px 16px 24px;
|
||||
border-bottom: 1px solid ${token.colorBorderSecondary};
|
||||
`,
|
||||
date: css`
|
||||
font-size: 12px;
|
||||
color: ${token.colorTextDescription};
|
||||
`,
|
||||
desc: css`
|
||||
color: ${token.colorTextDescription};
|
||||
text-align: center;
|
||||
`,
|
||||
loading: css`
|
||||
.${prefixCls}-skeleton-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
`,
|
||||
nav: css`
|
||||
padding-top: 4px;
|
||||
|
||||
.${prefixCls}-tabs-tab {
|
||||
margin: 4px !important;
|
||||
|
||||
+ .${prefixCls}-tabs-tab {
|
||||
margin: 4px !important;
|
||||
}
|
||||
}
|
||||
`,
|
||||
title: css`
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
`,
|
||||
}));
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { SearchBar } from '@lobehub/ui';
|
||||
import { useResponsive } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
import { memo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useMarketStore } from '@/store/market';
|
||||
|
|
@ -8,17 +8,20 @@ import { useMarketStore } from '@/store/market';
|
|||
const AgentSearchBar = memo(() => {
|
||||
const { t } = useTranslation('market');
|
||||
const keywords = useMarketStore((s) => s.searchKeywords);
|
||||
const [value, setValue] = useState(keywords);
|
||||
const { mobile } = useResponsive();
|
||||
return (
|
||||
<SearchBar
|
||||
allowClear
|
||||
enableShortKey={!mobile}
|
||||
onChange={(e) => useMarketStore.setState({ searchKeywords: e.target.value })}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
onPressEnter={() => useMarketStore.setState({ searchKeywords: value })}
|
||||
onSubmit={() => useMarketStore.setState({ searchKeywords: value })}
|
||||
placeholder={t('search.placeholder')}
|
||||
shortKey={'k'}
|
||||
spotlight={!mobile}
|
||||
type={mobile ? 'block' : 'ghost'}
|
||||
value={keywords}
|
||||
value={value}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,29 +4,24 @@ import { OpenAIChatStreamPayload } from '@/types/openai/chat';
|
|||
export const promptSummaryAgentName = (content: string): Partial<OpenAIChatStreamPayload> => ({
|
||||
messages: [
|
||||
{
|
||||
content: `你是一名擅长起名的起名大师,你需要将用户的描述总结为 20 个字以内的角色,格式要求如下:
|
||||
输入: {文本作为JSON引用字符串}
|
||||
输出: {角色名}
|
||||
`,
|
||||
content: `你是一名擅长起名的起名大师,名字需要有文学内涵,注重精炼和赋子意境,你需要将用户的描述总结为 20 个字以内的角色, 格式要求如下:\n输入: {文本作为JSON引用字符串}\n输出: {角色名}`,
|
||||
role: 'system',
|
||||
},
|
||||
{
|
||||
content: `输入: {你是一名专业的前端开发者,擅长结合 vitest 和\`testing-library/react\` 书写单元测试。接下来用户会输入一串 ts 代码,你需要给出完善的单元测试。\n你需要注意,单元测试代码中,不应该使用 jest 。如果需要使用 \`jest.fn\`,请使用 \`vi.fn\` 替换}`,
|
||||
content: `输入: {你是一名文案大师,帮我为一些设计 / 艺术作品起名,名字需要有文学内涵,注重精炼和赋子意境,表达作品的情景氛国,使名称既简洁又富有诗意。}`,
|
||||
role: 'user',
|
||||
},
|
||||
{ content: '前端 vitest 测试专家', role: 'assistant' },
|
||||
{ content: '创意命名师', role: 'assistant' },
|
||||
{
|
||||
content: `输入: {你是一名前端专家,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
|
||||
content: `输入: {你是一名前端代码专家,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
|
||||
role: 'user',
|
||||
},
|
||||
{ content: 'js 转 ts 专家', role: 'assistant' },
|
||||
{ content: 'ts转换魔术师', role: 'assistant' },
|
||||
{
|
||||
content: `输入:{你是一名擅长比喻和隐喻的UX Writter。用户会输入文案,你需要给出3个优化后的结果,使用 markdown格式的文本。下面是一个例子:
|
||||
输入:页面加载中
|
||||
输出:页面似乎在思考,一会儿才能准备好}`,
|
||||
content: `输入: {你是一名创业计划撰写专家,可以提供包括创意名称、简短的标语、目标用户画像、用户痛点、主要价值主张、销售/营销渠道、收入流、成本结构等计划生成。}`,
|
||||
role: 'user',
|
||||
},
|
||||
{ content: '文案比喻优化专家', role: 'assistant' },
|
||||
{ content: '创业咨询专家', role: 'assistant' },
|
||||
{ content: `输入: {${content}}`, role: 'user' },
|
||||
],
|
||||
});
|
||||
|
|
@ -35,42 +30,76 @@ export const promptSummaryAgentName = (content: string): Partial<OpenAIChatStrea
|
|||
export const promptPickEmoji = (content: string): Partial<OpenAIChatStreamPayload> => ({
|
||||
messages: [
|
||||
{
|
||||
content: '你是一名非常懂设计与时尚的设计师,你需要从用户的描述中匹配一个合适的 emoji。',
|
||||
content:
|
||||
'你是一名擅长进行概念抽象的设计师与 Emoji 专家,你需要根据角色能力的描述抽象出一个表达物理实体的概念 Emoji 作为角色头像, 格式要求如下:\n输入: {文本作为JSON引用字符串}\n输出: {一个Emoji}',
|
||||
role: 'system',
|
||||
},
|
||||
{
|
||||
content: `输入:你是一名精通体验设计的设计系统设计师,设计系统存在诸多类别的 token,比如品牌色、成功色等,你需要为各个类别的 token 提供说明文案。`,
|
||||
content: `输入: {你是一名文案大师,帮我为一些设计 / 艺术作品起名,名字需要有文学内涵,注重精炼和赋子意境,表达作品的情景氛国,使名称既简洁又富有诗意。}`,
|
||||
role: 'user',
|
||||
},
|
||||
{ content: '✒️', role: 'assistant' },
|
||||
{
|
||||
content: `💅`,
|
||||
role: 'assistant',
|
||||
},
|
||||
{
|
||||
content: `输入:用户会输入一串 ts 代码,为了确保所有功能和分支的 100% 的覆盖率,你需要给出需要考虑哪些数据场景。`,
|
||||
content: `输入: {你是一名代码巫师,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
|
||||
role: 'user',
|
||||
},
|
||||
{ content: '🧙♂️', role: 'assistant' },
|
||||
{
|
||||
content: `🧪`,
|
||||
role: 'assistant',
|
||||
},
|
||||
{
|
||||
content: `输入:${content}`,
|
||||
content: `输入: {你是一名创业计划撰写专家,可以提供包括创意名称、简短的标语、目标用户画像、用户痛点、主要价值主张、销售/营销渠道、收入流、成本结构等计划生成。}`,
|
||||
role: 'user',
|
||||
},
|
||||
{ content: '🚀', role: 'assistant' },
|
||||
{ content: `输入: {${content}}`, role: 'user' },
|
||||
],
|
||||
});
|
||||
|
||||
export const promptSummaryDescription = (content: string): Partial<OpenAIChatStreamPayload> => ({
|
||||
messages: [
|
||||
{
|
||||
content:
|
||||
'你是一名擅长会话的助理,你需要将用户的输入的内容总结为一个专家的简介,不超过 20 个字',
|
||||
content: `你是一名擅长技能总结的助理,你需要将用户的输入的内容总结为一个角色技能简介,确保信息清晰、逻辑清晰,并有效地传达角色的技能和经验,不超过 20 个字, 格式要求如下:\n输入: {文本作为JSON引用字符串}\n输出: {角色技能简介}`,
|
||||
role: 'system',
|
||||
},
|
||||
{
|
||||
content: `${content}`,
|
||||
content: `输入: {你是一名文案大师,帮我为一些设计 / 艺术作品起名,名字需要有文学内涵,注重精炼和赋子意境,表达作品的情景氛国,使名称既简洁又富有诗意。}`,
|
||||
role: 'user',
|
||||
},
|
||||
{ content: '擅长文创艺术作品起名', role: 'assistant' },
|
||||
{
|
||||
content: `输入: {你是一名前端代码专家,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
|
||||
role: 'user',
|
||||
},
|
||||
{ content: '擅长 ts 转换和补充类型声明', role: 'assistant' },
|
||||
{
|
||||
content: `输入: {你是一名创业计划撰写专家,可以提供包括创意名称、简短的标语、目标用户画像、用户痛点、主要价值主张、销售/营销渠道、收入流、成本结构等计划生成。}`,
|
||||
role: 'user',
|
||||
},
|
||||
{ content: '擅长创业计划撰写与咨询', role: 'assistant' },
|
||||
{ content: `输入: {${content}}`, role: 'user' },
|
||||
],
|
||||
});
|
||||
|
||||
export const promptSummaryTags = (content: string): Partial<OpenAIChatStreamPayload> => ({
|
||||
messages: [
|
||||
{
|
||||
content:
|
||||
'你是一名擅长会话标签总结的助理,你需要将用户的输入的内容提炼出分类标签,使用`,`分隔,不超过 5 个标签, 格式要求如下:\n输入: {文本作为JSON引用字符串}\n输出: {角色名}',
|
||||
role: 'system',
|
||||
},
|
||||
{
|
||||
content: `输入: {你是一名文案大师,帮我为一些设计 / 艺术作品起名,名字需要有文学内涵,注重精炼和赋子意境,表达作品的情景氛国,使名称既简洁又富有诗意。}`,
|
||||
role: 'user',
|
||||
},
|
||||
{ content: '起名,写作,创意', role: 'assistant' },
|
||||
{
|
||||
content: `输入: {你是一名前端专家,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
|
||||
role: 'user',
|
||||
},
|
||||
{ content: '代码,软件开发,效率', role: 'assistant' },
|
||||
{
|
||||
content: `输入: {你是一名创业计划撰写专家,可以提供包括创意名称、简短的标语、目标用户画像、用户痛点、主要价值主张、销售/营销渠道、收入流、成本结构等计划生成。}`,
|
||||
role: 'user',
|
||||
},
|
||||
{ content: '创业,计划,咨询', role: 'assistant' },
|
||||
{ content: `输入: {${content}}`, role: 'user' },
|
||||
],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -47,9 +47,9 @@ const HotKeys = memo<HotKeysProps>(({ keys, desc }) => {
|
|||
|
||||
if (!desc) return content;
|
||||
return (
|
||||
<Flexbox align={'center'} gap={8} horizontal>
|
||||
{content}
|
||||
<Flexbox align={'center'} style={{ textAlign: 'center' }}>
|
||||
{desc}
|
||||
{content}
|
||||
</Flexbox>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,10 +14,12 @@ const ResponsiveIndex = ({ children, Mobile }: ResponsiveIndexProps) => {
|
|||
const { t } = useTranslation();
|
||||
const mobile = useIsMobile();
|
||||
|
||||
return (
|
||||
return mobile ? (
|
||||
<Suspense fallback={<FullscreenLoading title={t('layoutInitializing', { ns: 'common' })} />}>
|
||||
{mobile ? <Mobile /> : children}
|
||||
<Mobile />
|
||||
</Suspense>
|
||||
) : (
|
||||
children
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export const getAgentJSON = (
|
|||
};
|
||||
|
||||
export const AGENTS_INDEX_GITHUB = 'https://github.com/lobehub/lobe-chat-agents';
|
||||
export const AGENTS_INDEX_GITHUB_ISSUE = urlJoin(AGENTS_INDEX_GITHUB, 'issues/new');
|
||||
|
||||
export const SESSION_CHAT_URL = (id: string = INBOX_SESSION_ID, mobile?: boolean) =>
|
||||
mobile ? `/chat/mobile#session=${id}` : `/chat#session=${id}`;
|
||||
|
|
|
|||
69
src/features/AgentInfo/index.tsx
Normal file
69
src/features/AgentInfo/index.tsx
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { Avatar, Markdown, Tag } from '@lobehub/ui';
|
||||
import { Divider } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { startCase } from 'lodash-es';
|
||||
import { CSSProperties, memo } from 'react';
|
||||
import { Center } from 'react-layout-kit';
|
||||
|
||||
import { MetaData } from '@/types/meta';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
avatar: css`
|
||||
flex: none;
|
||||
`,
|
||||
desc: css`
|
||||
color: ${token.colorTextDescription};
|
||||
text-align: center;
|
||||
`,
|
||||
|
||||
title: css`
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
`,
|
||||
}));
|
||||
|
||||
export interface AgentInfoProps {
|
||||
meta?: MetaData;
|
||||
style?: CSSProperties;
|
||||
systemRole?: string;
|
||||
}
|
||||
|
||||
const AgentInfo = memo<AgentInfoProps>(({ systemRole, style, meta }) => {
|
||||
const { styles, theme } = useStyles();
|
||||
|
||||
if (!meta) return;
|
||||
|
||||
return (
|
||||
<Center gap={16} style={style}>
|
||||
{meta.avatar && (
|
||||
<Avatar
|
||||
animation
|
||||
avatar={meta.avatar}
|
||||
background={meta.backgroundColor || theme.colorFillTertiary}
|
||||
className={styles.avatar}
|
||||
size={100}
|
||||
/>
|
||||
)}
|
||||
{meta.title && <div className={styles.title}>{meta.title}</div>}
|
||||
{(meta?.tags as string[])?.length > 0 && (
|
||||
<Center gap={6} horizontal style={{ flexWrap: 'wrap' }}>
|
||||
{(meta.tags as string[]).map((tag: string, index) => (
|
||||
<Tag key={index} style={{ margin: 0 }}>
|
||||
{startCase(tag).trim()}
|
||||
</Tag>
|
||||
))}
|
||||
</Center>
|
||||
)}
|
||||
{meta.description && <div className={styles.desc}>{meta.description}</div>}
|
||||
{systemRole && (
|
||||
<>
|
||||
<Divider style={{ margin: '8px 0' }} />
|
||||
<Markdown>{systemRole}</Markdown>
|
||||
</>
|
||||
)}
|
||||
</Center>
|
||||
);
|
||||
});
|
||||
|
||||
export default AgentInfo;
|
||||
48
src/features/AgentSetting/AgentMeta/AutoGenerateSelect.tsx
Normal file
48
src/features/AgentSetting/AgentMeta/AutoGenerateSelect.tsx
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import { ActionIcon } from '@lobehub/ui';
|
||||
import { Select, SelectProps } from 'antd';
|
||||
import { useTheme } from 'antd-style';
|
||||
import { isString } from 'lodash-es';
|
||||
import { Wand2 } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export interface AutoGenerateInputProps extends SelectProps {
|
||||
loading?: boolean;
|
||||
onGenerate?: () => void;
|
||||
}
|
||||
|
||||
const AutoGenerateSelect = memo<AutoGenerateInputProps>(
|
||||
({ loading, onGenerate, value, ...props }) => {
|
||||
const { t } = useTranslation('common');
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Select
|
||||
mode="tags"
|
||||
open={false}
|
||||
style={{ width: '100%' }}
|
||||
suffixIcon={
|
||||
onGenerate && (
|
||||
<ActionIcon
|
||||
active
|
||||
icon={Wand2}
|
||||
loading={loading}
|
||||
onClick={onGenerate}
|
||||
size={'small'}
|
||||
style={{
|
||||
color: theme.colorInfo,
|
||||
marginRight: -4,
|
||||
}}
|
||||
title={t('autoGenerate')}
|
||||
/>
|
||||
)
|
||||
}
|
||||
tokenSeparators={[',', ',', ' ']}
|
||||
value={isString(value) ? value.split(',') : value}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default AutoGenerateSelect;
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { Form, type FormItemProps, Icon, type ItemGroup, Tooltip } from '@lobehub/ui';
|
||||
import { Button } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { isString } from 'lodash-es';
|
||||
import { UserCircle, Wand2 } from 'lucide-react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { memo, useMemo } from 'react';
|
||||
|
|
@ -11,6 +12,7 @@ import { FORM_STYLE } from '@/const/layoutTokens';
|
|||
import { useStore } from '../store';
|
||||
import { SessionLoadingState } from '../store/initialState';
|
||||
import AutoGenerateInput from './AutoGenerateInput';
|
||||
import AutoGenerateSelect from './AutoGenerateSelect';
|
||||
import BackgroundSwatches from './BackgroundSwatches';
|
||||
|
||||
const EmojiPicker = dynamic(() => import('@/components/EmojiPicker'), { ssr: false });
|
||||
|
|
@ -28,26 +30,35 @@ const AgentMeta = memo(() => {
|
|||
|
||||
const basic = [
|
||||
{
|
||||
Render: AutoGenerateInput,
|
||||
key: 'title',
|
||||
label: t('settingAgent.name.title'),
|
||||
onChange: (e: any) => updateMeta({ title: e.target.value }),
|
||||
placeholder: t('settingAgent.name.placeholder'),
|
||||
},
|
||||
{
|
||||
Render: AutoGenerateInput,
|
||||
key: 'description',
|
||||
label: t('settingAgent.description.title'),
|
||||
onChange: (e: any) => updateMeta({ description: e.target.value }),
|
||||
placeholder: t('settingAgent.description.placeholder'),
|
||||
},
|
||||
// { key: 'tag', label: t('agentTag'), placeholder: t('agentTagPlaceholder') },
|
||||
{
|
||||
Render: AutoGenerateSelect,
|
||||
key: 'tags',
|
||||
label: t('settingAgent.tag.title'),
|
||||
onChange: (e: any) => updateMeta({ tags: isString(e) ? e.split(',') : e }),
|
||||
placeholder: t('settingAgent.tag.placeholder'),
|
||||
},
|
||||
];
|
||||
|
||||
const autocompleteItems: FormItemProps[] = basic.map((item) => {
|
||||
const AutoGenerate = item.Render;
|
||||
return {
|
||||
children: (
|
||||
<AutoGenerateInput
|
||||
<AutoGenerate
|
||||
loading={loading[item.key as keyof SessionLoadingState]}
|
||||
onChange={(e) => {
|
||||
updateMeta({ [item.key]: e.target.value });
|
||||
}}
|
||||
onChange={item.onChange}
|
||||
onGenerate={() => {
|
||||
autocompleteMeta(item.key as keyof typeof meta);
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
import { StateCreator } from 'zustand/vanilla';
|
||||
|
||||
import { promptPickEmoji, promptSummaryAgentName, promptSummaryDescription } from '@/chains/agent';
|
||||
import {
|
||||
promptPickEmoji,
|
||||
promptSummaryAgentName,
|
||||
promptSummaryDescription,
|
||||
promptSummaryTags,
|
||||
} from '@/chains/agent';
|
||||
import { MetaData } from '@/types/meta';
|
||||
import { LobeAgentConfig } from '@/types/session';
|
||||
import { fetchPresetTaskResult } from '@/utils/fetch';
|
||||
|
|
@ -26,6 +31,7 @@ export interface Action {
|
|||
* @returns 一个 Promise,用于异步操作完成后的处理
|
||||
*/
|
||||
autocompleteAgentDescription: () => Promise<void>;
|
||||
autocompleteAgentTags: () => Promise<void>;
|
||||
/**
|
||||
* 自动完成代理标题
|
||||
* @param id - 代理的 ID
|
||||
|
|
@ -47,7 +53,8 @@ export interface Action {
|
|||
setAgentConfig: (config: Partial<LobeAgentConfig>) => void;
|
||||
|
||||
setAgentMeta: (meta: Partial<MetaData>) => void;
|
||||
streamUpdateMeta: (key: keyof MetaData) => any;
|
||||
streamUpdateMetaArray: (key: keyof MetaData) => any;
|
||||
streamUpdateMetaString: (key: keyof MetaData) => any;
|
||||
toggleAgentPlugin: (pluginId: string, state?: boolean) => void;
|
||||
/**
|
||||
* 更新加载状态
|
||||
|
|
@ -65,7 +72,7 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|||
...initialState,
|
||||
|
||||
autoPickEmoji: async () => {
|
||||
const { config, dispatchMeta } = get();
|
||||
const { config, meta, dispatchMeta } = get();
|
||||
|
||||
const systemRole = config.systemRole;
|
||||
|
||||
|
|
@ -73,7 +80,7 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|||
onLoadingChange: (loading) => {
|
||||
get().updateLoadingState('avatar', loading);
|
||||
},
|
||||
params: promptPickEmoji(systemRole),
|
||||
params: promptPickEmoji([meta.title, meta.description, systemRole].filter(Boolean).join(',')),
|
||||
});
|
||||
|
||||
if (emoji) {
|
||||
|
|
@ -81,7 +88,7 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|||
}
|
||||
},
|
||||
autocompleteAgentDescription: async () => {
|
||||
const { dispatchMeta, config, meta, updateLoadingState, streamUpdateMeta } = get();
|
||||
const { dispatchMeta, config, meta, updateLoadingState, streamUpdateMetaString } = get();
|
||||
|
||||
const systemRole = config.systemRole;
|
||||
|
||||
|
|
@ -99,12 +106,37 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|||
onLoadingChange: (loading) => {
|
||||
updateLoadingState('description', loading);
|
||||
},
|
||||
onMessageHandle: streamUpdateMeta('description'),
|
||||
onMessageHandle: streamUpdateMetaString('description'),
|
||||
params: promptSummaryDescription(systemRole),
|
||||
});
|
||||
},
|
||||
autocompleteAgentTags: async () => {
|
||||
const { dispatchMeta, config, meta, updateLoadingState, streamUpdateMetaArray } = get();
|
||||
|
||||
const systemRole = config.systemRole;
|
||||
|
||||
if (!systemRole) return;
|
||||
|
||||
const preValue = meta.tags;
|
||||
|
||||
// 替换为 ...
|
||||
dispatchMeta({ type: 'update', value: { tags: ['...'] } });
|
||||
|
||||
fetchPresetTaskResult({
|
||||
onError: () => {
|
||||
dispatchMeta({ type: 'update', value: { tags: preValue } });
|
||||
},
|
||||
onLoadingChange: (loading) => {
|
||||
updateLoadingState('tags', loading);
|
||||
},
|
||||
onMessageHandle: streamUpdateMetaArray('tags'),
|
||||
params: promptSummaryTags(
|
||||
[meta.title, meta.description, systemRole].filter(Boolean).join(','),
|
||||
),
|
||||
});
|
||||
},
|
||||
autocompleteAgentTitle: async () => {
|
||||
const { dispatchMeta, config, meta, updateLoadingState, streamUpdateMeta } = get();
|
||||
const { dispatchMeta, config, meta, updateLoadingState, streamUpdateMetaString } = get();
|
||||
|
||||
const systemRole = config.systemRole;
|
||||
|
||||
|
|
@ -122,8 +154,8 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|||
onLoadingChange: (loading) => {
|
||||
updateLoadingState('title', loading);
|
||||
},
|
||||
onMessageHandle: streamUpdateMeta('title'),
|
||||
params: promptSummaryAgentName(systemRole),
|
||||
onMessageHandle: streamUpdateMetaString('title'),
|
||||
params: promptSummaryAgentName([meta.description, systemRole].filter(Boolean).join(',')),
|
||||
});
|
||||
},
|
||||
autocompleteAllMeta: (replace) => {
|
||||
|
|
@ -140,9 +172,18 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|||
if (!meta.avatar || replace) {
|
||||
get().autoPickEmoji();
|
||||
}
|
||||
|
||||
if (!meta.tags || replace) {
|
||||
get().autocompleteAgentTags();
|
||||
}
|
||||
},
|
||||
autocompleteMeta: (key) => {
|
||||
const { autoPickEmoji, autocompleteAgentTitle, autocompleteAgentDescription } = get();
|
||||
const {
|
||||
autoPickEmoji,
|
||||
autocompleteAgentTitle,
|
||||
autocompleteAgentDescription,
|
||||
autocompleteAgentTags,
|
||||
} = get();
|
||||
|
||||
switch (key) {
|
||||
case 'avatar': {
|
||||
|
|
@ -157,6 +198,12 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|||
|
||||
case 'title': {
|
||||
autocompleteAgentTitle();
|
||||
return;
|
||||
}
|
||||
|
||||
case 'tags': {
|
||||
autocompleteAgentTags();
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -187,10 +234,19 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|||
get().dispatchConfig({ config, type: 'update' });
|
||||
},
|
||||
setAgentMeta: (meta) => {
|
||||
console.log(meta);
|
||||
get().dispatchMeta({ type: 'update', value: meta });
|
||||
},
|
||||
|
||||
streamUpdateMeta: (key: keyof MetaData) => {
|
||||
streamUpdateMetaArray: (key: keyof MetaData) => {
|
||||
let value = '';
|
||||
return (text: string) => {
|
||||
value += text;
|
||||
get().dispatchMeta({ type: 'update', value: { [key]: value.split(',') } });
|
||||
};
|
||||
},
|
||||
|
||||
streamUpdateMetaString: (key: keyof MetaData) => {
|
||||
let value = '';
|
||||
return (text: string) => {
|
||||
value += text;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ export default {
|
|||
withSystemRole: '包含助手角色设定',
|
||||
},
|
||||
stop: '停止',
|
||||
|
||||
temp: '临时',
|
||||
tokenDetail: '角色设定: {{systemRoleToken}} · 历史消息: {{chatsToken}}',
|
||||
tokenTag: {
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ export default {
|
|||
},
|
||||
title: '助手信息',
|
||||
},
|
||||
|
||||
settingChat: {
|
||||
chatStyleType: {
|
||||
title: '聊天窗口样式',
|
||||
|
|
@ -222,6 +223,13 @@ export default {
|
|||
},
|
||||
title: '主题设置',
|
||||
},
|
||||
submitAgentModal: {
|
||||
button: '提交助手',
|
||||
identifier: 'identifier 助手标识符',
|
||||
metaMiss: '请补全助手信息后提交,需要包含名称、描述和标签',
|
||||
placeholder: '请输入助手的标识符,需要是唯一的,比如 web-development',
|
||||
tooltips: '分享到助手市场',
|
||||
},
|
||||
tab: {
|
||||
agent: '默认助手',
|
||||
common: '通用设置',
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ const resources = {
|
|||
'zh-CN': zh_CN,
|
||||
'zh-TW': zh_TW,
|
||||
} as const;
|
||||
|
||||
export default resources;
|
||||
export const defaultResources = zh_CN;
|
||||
export type Resources = typeof resources;
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ export const sessionsReducer = (state: LobeSessions, payload: SessionDispatch):
|
|||
|
||||
const { key, value } = payload;
|
||||
|
||||
const validKeys = ['avatar', 'backgroundColor', 'description', 'tag', 'title'];
|
||||
const validKeys = ['avatar', 'backgroundColor', 'description', 'tags', 'title'];
|
||||
|
||||
if (validKeys.includes(key)) chat.meta[key] = value;
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue