diff --git a/locales/en_US/chat.json b/locales/en_US/chat.json
index 3e41ef2bec..3bbdf11123 100644
--- a/locales/en_US/chat.json
+++ b/locales/en_US/chat.json
@@ -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"
}
diff --git a/locales/en_US/setting.json b/locales/en_US/setting.json
index 7c71e16cc4..04c340ba60 100644
--- a/locales/en_US/setting.json
+++ b/locales/en_US/setting.json
@@ -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",
diff --git a/locales/ja_JP/setting.json b/locales/ja_JP/setting.json
index 98fe40d35d..ceca7990ab 100644
--- a/locales/ja_JP/setting.json
+++ b/locales/ja_JP/setting.json
@@ -222,6 +222,13 @@
},
"title": "テーマ設定"
},
+ "submitAgentModal": {
+ "tooltips": "アシスタントマーケットに共有する",
+ "button": "助手を提出する",
+ "identifier": "識別子 エージェントの識別子",
+ "metaMiss": "エージェント情報を入力してから提出してください。名前、説明、およびタグが必要です",
+ "placeholder": "エージェントの識別子を入力してください。一意である必要があります。例:web-development"
+ },
"tab": {
"agent": "デフォルトのアシスタント",
"common": "一般設定",
diff --git a/locales/ko_KR/setting.json b/locales/ko_KR/setting.json
index 9e1e145155..1e217133db 100644
--- a/locales/ko_KR/setting.json
+++ b/locales/ko_KR/setting.json
@@ -222,6 +222,13 @@
},
"title": "테마 설정"
},
+ "submitAgentModal": {
+ "tooltips": "도우미 마켓에 공유",
+ "button": "도우미 제출",
+ "identifier": "식별자 도우미 식별자",
+ "metaMiss": "도우미 정보를 입력한 후 제출하세요. 이름, 설명 및 태그를 포함해야 합니다.",
+ "placeholder": "도우미의 식별자를 입력하세요. 고유해야 하며, 예를 들어 web-development과 같은 형식이어야 합니다."
+ },
"tab": {
"agent": "기본 도우미",
"common": "일반 설정",
diff --git a/locales/ru_RU/common.json b/locales/ru_RU/common.json
index c843f5ee2a..d3f3a40b6e 100644
--- a/locales/ru_RU/common.json
+++ b/locales/ru_RU/common.json
@@ -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 запускается, пожалуйста, подождите...",
diff --git a/locales/ru_RU/setting.json b/locales/ru_RU/setting.json
index e73d8f9bdc..2d9dc2add1 100644
--- a/locales/ru_RU/setting.json
+++ b/locales/ru_RU/setting.json
@@ -235,6 +235,13 @@
},
"title": "Настройки темы"
},
+ "submitAgentModal": {
+ "tooltips": "Поделиться на рынке помощников",
+ "button": "Отправить агента",
+ "identifier": "Идентификатор агента",
+ "metaMiss": "Пожалуйста, заполните информацию об агенте перед отправкой. Необходимо указать имя, описание и метки",
+ "placeholder": "Введите идентификатор агента, который должен быть уникальным, например, web-development"
+ },
"tab": {
"agent": "Помощник по умолчанию",
"common": "Общие настройки",
diff --git a/locales/zh_CN/setting.json b/locales/zh_CN/setting.json
index 5a09e490bb..ff1d68155c 100644
--- a/locales/zh_CN/setting.json
+++ b/locales/zh_CN/setting.json
@@ -222,6 +222,13 @@
},
"title": "主题设置"
},
+ "submitAgentModal": {
+ "button": "提交助手",
+ "identifier": "identifier 助手标识符",
+ "metaMiss": "请补全助手信息后提交,需要包含名称、描述和标签",
+ "placeholder": "请输入助手的标识符,需要是唯一的,比如 web-development",
+ "tooltips": "分享到助手市场"
+ },
"tab": {
"agent": "默认助手",
"common": "通用设置",
diff --git a/locales/zh_TW/setting.json b/locales/zh_TW/setting.json
index e50c404757..fe30093188 100644
--- a/locales/zh_TW/setting.json
+++ b/locales/zh_TW/setting.json
@@ -235,6 +235,13 @@
},
"title": "主題設定"
},
+ "submitAgentModal": {
+ "tooltips": "分享到助手市場",
+ "button": "提交助手",
+ "identifier": "助手識別符",
+ "metaMiss": "請補全助手資訊後提交,需要包含名稱、描述和標籤",
+ "placeholder": "請輸入助手的識別符,需要是唯一的,例如 web-development"
+ },
"tab": {
"agent": "預設助理",
"common": "通用設定",
diff --git a/package.json b/package.json
index 2cca6f4660..c56ef17c00 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/scripts/i18nWorkflow/utils.ts b/scripts/i18nWorkflow/utils.ts
index feeca468a4..763c35de77 100644
--- a/scripts/i18nWorkflow/utils.ts
+++ b/scripts/i18nWorkflow/utils.ts
@@ -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[]) => {
diff --git a/src/app/chat/features/TopicListContent/SystemRole/index.tsx b/src/app/chat/features/TopicListContent/SystemRole/index.tsx
index b96d89ef65..5f78df6011 100644
--- a/src/app/chat/features/TopicListContent/SystemRole/index.tsx
+++ b/src/app/chat/features/TopicListContent/SystemRole/index.tsx
@@ -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(() => {
}}
onChange={(e) => {
updateAgentConfig({ systemRole: e });
}}
diff --git a/src/app/chat/settings/(desktop)/Header/index.tsx b/src/app/chat/settings/(desktop)/Header/index.tsx
deleted file mode 100644
index 0aad6bba41..0000000000
--- a/src/app/chat/settings/(desktop)/Header/index.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { memo } from 'react';
-
-import { HeaderContent } from '@/app/chat/settings/components/Header';
-
-import Layout from './Desktop';
-
-const Header = memo(() => (
-
-
-
-));
-
-export default Header;
diff --git a/src/app/chat/settings/(desktop)/Header/Desktop.tsx b/src/app/chat/settings/(desktop)/features/Header.tsx
similarity index 75%
rename from src/app/chat/settings/(desktop)/Header/Desktop.tsx
rename to src/app/chat/settings/(desktop)/features/Header.tsx
index 8a725f8a8b..836ce80976 100644
--- a/src/app/chat/settings/(desktop)/Header/Desktop.tsx
+++ b/src/app/chat/settings/(desktop)/features/Header.tsx
@@ -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 }) => {
}
onBackClick={() => router.push(pathString('/chat', { hash: location.hash }))}
- right={children}
+ right={}
showBackButton
/>
);
diff --git a/src/app/chat/settings/(desktop)/layout.desktop.tsx b/src/app/chat/settings/(desktop)/layout.desktop.tsx
index e46f0847c7..146b885d66 100644
--- a/src/app/chat/settings/(desktop)/layout.desktop.tsx
+++ b/src/app/chat/settings/(desktop)/layout.desktop.tsx
@@ -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) => (
diff --git a/src/app/chat/settings/(mobile)/Header.tsx b/src/app/chat/settings/(mobile)/Header.tsx
deleted file mode 100644
index 941847c030..0000000000
--- a/src/app/chat/settings/(mobile)/Header.tsx
+++ /dev/null
@@ -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(() => (
-
-
-
-));
-
-export default Header;
diff --git a/src/app/chat/settings/components/Header/Mobile.tsx b/src/app/chat/settings/(mobile)/features/Header.tsx
similarity index 76%
rename from src/app/chat/settings/components/Header/Mobile.tsx
rename to src/app/chat/settings/(mobile)/features/Header.tsx
index b5cfd6e632..7a4db0fb1a 100644
--- a/src/app/chat/settings/components/Header/Mobile.tsx
+++ b/src/app/chat/settings/(mobile)/features/Header.tsx
@@ -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 }) => {
}
onBackClick={() => router.push(pathString('/chat/mobile', { hash: location.hash }))}
- right={children}
+ right={}
showBackButton
/>
);
diff --git a/src/app/chat/settings/(mobile)/layout.mobile.tsx b/src/app/chat/settings/(mobile)/layout.mobile.tsx
index 58f9b15613..86923563c7 100644
--- a/src/app/chat/settings/(mobile)/layout.mobile.tsx
+++ b/src/app/chat/settings/(mobile)/layout.mobile.tsx
@@ -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) => (
}>{children}
diff --git a/src/app/chat/settings/components/Header/index.tsx b/src/app/chat/settings/features/HeaderContent.tsx
similarity index 80%
rename from src/app/chat/settings/components/Header/index.tsx
rename to src/app/chat/settings/features/HeaderContent.tsx
index a2564b6c81..bc7f9bce11 100644
--- a/src/app/chat/settings/components/Header/index.tsx
+++ b/src/app/chat/settings/features/HeaderContent.tsx
@@ -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(
@@ -41,8 +44,13 @@ export const HeaderContent = memo<{ mobile?: boolean }>(() => {
const size = mobile ? MOBILE_HEADER_ICON_SIZE : { fontSize: 24 };
return (
-
-
-
+ <>
+
+
+
+
+ >
);
});
+
+export default HeaderContent;
diff --git a/src/app/chat/settings/features/SubmitAgentButton/Inner.tsx b/src/app/chat/settings/features/SubmitAgentButton/Inner.tsx
new file mode 100644
index 0000000000..c5770a015e
--- /dev/null
+++ b/src/app/chat/settings/features/SubmitAgentButton/Inner.tsx
@@ -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 (
+
+
+ {!isMetaPass && (
+
+ )}
+
+
+
+ *
+ {t('submitAgentModal.identifier')}
+
+ setIdentifier(kebabCase(e.target.value))}
+ placeholder={t('submitAgentModal.placeholder')}
+ value={identifier}
+ />
+
+
+
+ );
+});
+
+export default Inner;
diff --git a/src/app/chat/settings/features/SubmitAgentButton/index.tsx b/src/app/chat/settings/features/SubmitAgentButton/index.tsx
new file mode 100644
index 0000000000..7c0e4e49d1
--- /dev/null
+++ b/src/app/chat/settings/features/SubmitAgentButton/index.tsx
@@ -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 (
+ <>
+ setIsModalOpen(true)}
+ size={size}
+ title={t('submitAgentModal.tooltips')}
+ />
+ setIsModalOpen(false)}
+ open={isModalOpen}
+ title={t('submitAgentModal.tooltips')}
+ >
+
+
+ >
+ );
+});
+
+export default SubmitAgentButton;
diff --git a/src/app/chat/settings/features/SubmitAgentButton/style.ts b/src/app/chat/settings/features/SubmitAgentButton/style.ts
new file mode 100644
index 0000000000..7264d5f6bf
--- /dev/null
+++ b/src/app/chat/settings/features/SubmitAgentButton/style.ts
@@ -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;
+ `,
+}));
diff --git a/src/app/market/features/AgentSearchBar/index.tsx b/src/app/market/features/AgentSearchBar/index.tsx
index 6c5199fe6c..a025ff7a40 100644
--- a/src/app/market/features/AgentSearchBar/index.tsx
+++ b/src/app/market/features/AgentSearchBar/index.tsx
@@ -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 (
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}
/>
);
});
diff --git a/src/chains/agent.ts b/src/chains/agent.ts
index 1acd33aa38..1cf3e06318 100644
--- a/src/chains/agent.ts
+++ b/src/chains/agent.ts
@@ -4,29 +4,24 @@ import { OpenAIChatStreamPayload } from '@/types/openai/chat';
export const promptSummaryAgentName = (content: string): Partial => ({
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 => ({
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 => ({
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 => ({
+ 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' },
],
});
diff --git a/src/components/HotKeys/index.tsx b/src/components/HotKeys/index.tsx
index d44523cb1d..e3aeebd95d 100644
--- a/src/components/HotKeys/index.tsx
+++ b/src/components/HotKeys/index.tsx
@@ -47,9 +47,9 @@ const HotKeys = memo(({ keys, desc }) => {
if (!desc) return content;
return (
-
- {content}
+
{desc}
+ {content}
);
});
diff --git a/src/components/ResponsiveIndex/index.tsx b/src/components/ResponsiveIndex/index.tsx
index 544655b761..e425409a7b 100644
--- a/src/components/ResponsiveIndex/index.tsx
+++ b/src/components/ResponsiveIndex/index.tsx
@@ -14,10 +14,12 @@ const ResponsiveIndex = ({ children, Mobile }: ResponsiveIndexProps) => {
const { t } = useTranslation();
const mobile = useIsMobile();
- return (
+ return mobile ? (
}>
- {mobile ? : children}
+
+ ) : (
+ children
);
};
diff --git a/src/const/url.ts b/src/const/url.ts
index 550fa2d375..4ea092e0f4 100644
--- a/src/const/url.ts
+++ b/src/const/url.ts
@@ -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}`;
diff --git a/src/features/AgentInfo/index.tsx b/src/features/AgentInfo/index.tsx
new file mode 100644
index 0000000000..ef35b9be0c
--- /dev/null
+++ b/src/features/AgentInfo/index.tsx
@@ -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(({ systemRole, style, meta }) => {
+ const { styles, theme } = useStyles();
+
+ if (!meta) return;
+
+ return (
+
+ {meta.avatar && (
+
+ )}
+ {meta.title && {meta.title}
}
+ {(meta?.tags as string[])?.length > 0 && (
+
+ {(meta.tags as string[]).map((tag: string, index) => (
+
+ {startCase(tag).trim()}
+
+ ))}
+
+ )}
+ {meta.description && {meta.description}
}
+ {systemRole && (
+ <>
+
+ {systemRole}
+ >
+ )}
+
+ );
+});
+
+export default AgentInfo;
diff --git a/src/features/AgentSetting/AgentMeta/AutoGenerateSelect.tsx b/src/features/AgentSetting/AgentMeta/AutoGenerateSelect.tsx
new file mode 100644
index 0000000000..980ffe9baf
--- /dev/null
+++ b/src/features/AgentSetting/AgentMeta/AutoGenerateSelect.tsx
@@ -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(
+ ({ loading, onGenerate, value, ...props }) => {
+ const { t } = useTranslation('common');
+ const theme = useTheme();
+
+ return (
+
+ )
+ }
+ tokenSeparators={[',', ',', ' ']}
+ value={isString(value) ? value.split(',') : value}
+ {...props}
+ />
+ );
+ },
+);
+
+export default AutoGenerateSelect;
diff --git a/src/features/AgentSetting/AgentMeta/index.tsx b/src/features/AgentSetting/AgentMeta/index.tsx
index d09a113321..fdba417837 100644
--- a/src/features/AgentSetting/AgentMeta/index.tsx
+++ b/src/features/AgentSetting/AgentMeta/index.tsx
@@ -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: (
- {
- updateMeta({ [item.key]: e.target.value });
- }}
+ onChange={item.onChange}
onGenerate={() => {
autocompleteMeta(item.key as keyof typeof meta);
}}
diff --git a/src/features/AgentSetting/store/action.ts b/src/features/AgentSetting/store/action.ts
index d0c3e6fcc2..1897ab43f4 100644
--- a/src/features/AgentSetting/store/action.ts
+++ b/src/features/AgentSetting/store/action.ts
@@ -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;
+ autocompleteAgentTags: () => Promise;
/**
* 自动完成代理标题
* @param id - 代理的 ID
@@ -47,7 +53,8 @@ export interface Action {
setAgentConfig: (config: Partial) => void;
setAgentMeta: (meta: Partial) => 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 = (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 = (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 = (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 = (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 = (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 = (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 = (set, g
case 'title': {
autocompleteAgentTitle();
+ return;
+ }
+
+ case 'tags': {
+ autocompleteAgentTags();
+ return;
}
}
},
@@ -187,10 +234,19 @@ export const store: StateCreator = (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;
diff --git a/src/locales/default/chat.ts b/src/locales/default/chat.ts
index a98d233666..8460412094 100644
--- a/src/locales/default/chat.ts
+++ b/src/locales/default/chat.ts
@@ -36,7 +36,6 @@ export default {
withSystemRole: '包含助手角色设定',
},
stop: '停止',
-
temp: '临时',
tokenDetail: '角色设定: {{systemRoleToken}} · 历史消息: {{chatsToken}}',
tokenTag: {
diff --git a/src/locales/default/setting.ts b/src/locales/default/setting.ts
index 88a195916f..229e6a8c68 100644
--- a/src/locales/default/setting.ts
+++ b/src/locales/default/setting.ts
@@ -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: '通用设置',
diff --git a/src/locales/resources/index.ts b/src/locales/resources/index.ts
index 8c088c89ad..c1d961d42e 100644
--- a/src/locales/resources/index.ts
+++ b/src/locales/resources/index.ts
@@ -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;
diff --git a/src/store/session/slices/session/reducers/session.ts b/src/store/session/slices/session/reducers/session.ts
index 68f64dfb53..a1483b3a32 100644
--- a/src/store/session/slices/session/reducers/session.ts
+++ b/src/store/session/slices/session/reducers/session.ts
@@ -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;
});