@lobehub/lobehub 2.0.0-next.7 → 2.0.0-next.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/desktop-pr-build.yml +8 -8
- package/.github/workflows/docker.yml +17 -16
- package/.github/workflows/e2e.yml +3 -3
- package/.github/workflows/release-desktop-beta.yml +8 -8
- package/.github/workflows/release.yml +1 -1
- package/.github/workflows/test.yml +4 -4
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/models.json +6 -6
- package/locales/bg-BG/models.json +6 -6
- package/locales/de-DE/models.json +6 -6
- package/locales/en-US/models.json +6 -6
- package/locales/es-ES/models.json +6 -6
- package/locales/fa-IR/models.json +6 -6
- package/locales/fr-FR/models.json +6 -6
- package/locales/it-IT/models.json +6 -6
- package/locales/ja-JP/models.json +6 -6
- package/locales/ko-KR/models.json +6 -6
- package/locales/nl-NL/models.json +6 -6
- package/locales/pl-PL/models.json +6 -6
- package/locales/pt-BR/models.json +6 -6
- package/locales/ru-RU/models.json +6 -6
- package/locales/tr-TR/models.json +6 -6
- package/locales/vi-VN/models.json +6 -6
- package/locales/zh-CN/models.json +6 -6
- package/locales/zh-TW/models.json +6 -6
- package/package.json +1 -1
- package/packages/const/src/index.ts +0 -1
- package/packages/const/src/url.ts +1 -4
- package/packages/context-engine/src/index.ts +1 -6
- package/packages/context-engine/src/processors/GroupMessageFlatten.ts +12 -2
- package/packages/context-engine/src/processors/__tests__/GroupMessageFlatten.test.ts +73 -9
- package/packages/context-engine/src/providers/index.ts +0 -2
- package/packages/database/package.json +1 -1
- package/packages/database/src/models/__tests__/message.grouping.test.ts +812 -0
- package/packages/database/src/models/__tests__/message.test.ts +322 -170
- package/packages/database/src/models/message.ts +62 -24
- package/packages/database/src/utils/__tests__/groupMessages.test.ts +145 -2
- package/packages/database/src/utils/groupMessages.ts +7 -5
- package/packages/types/src/message/common/base.ts +13 -0
- package/packages/types/src/message/common/image.ts +8 -0
- package/packages/types/src/message/common/metadata.ts +39 -0
- package/packages/types/src/message/common/tools.ts +10 -0
- package/packages/types/src/message/db/params.ts +47 -1
- package/packages/types/src/message/ui/chat.ts +4 -1
- package/packages/types/src/search.ts +16 -0
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/V1Mobile/index.tsx +2 -2
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/V1Mobile/useSend.ts +6 -4
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/useSend.ts +15 -10
- package/src/app/[variants]/(main)/chat/@session/features/SessionListContent/List/Item/index.tsx +4 -2
- package/src/components/Thinking/index.tsx +4 -3
- package/src/features/AgentSetting/AgentPlugin/index.tsx +2 -2
- package/src/features/ChatInput/ActionBar/STT/browser.tsx +2 -2
- package/src/features/ChatInput/ActionBar/STT/openai.tsx +2 -2
- package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +1 -3
- package/src/features/Conversation/Error/ErrorJsonViewer.tsx +4 -3
- package/src/features/Conversation/Error/OllamaBizError/index.tsx +7 -2
- package/src/features/Conversation/Error/index.tsx +15 -5
- package/src/features/Conversation/MarkdownElements/LobeArtifact/Render/index.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +5 -3
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/BuiltinPluginTitle.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/ToolTitle.tsx +4 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/index.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/index.tsx +4 -4
- package/src/features/Conversation/Messages/Default.tsx +2 -2
- package/src/features/Conversation/Messages/User/Extra.tsx +2 -2
- package/src/features/Conversation/Messages/User/index.tsx +4 -4
- package/src/features/Conversation/Messages/index.tsx +3 -3
- package/src/features/Conversation/components/AutoScroll.tsx +2 -2
- package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +9 -6
- package/src/features/PluginTag/index.tsx +1 -3
- package/src/features/PluginsUI/Render/BuiltinType/index.test.tsx +37 -28
- package/src/features/Portal/Artifacts/Body/index.tsx +2 -2
- package/src/server/modules/ModelRuntime/trace.ts +11 -4
- package/src/server/routers/lambda/message.ts +14 -3
- package/src/services/chat/chat.test.ts +1 -40
- package/src/services/chat/contextEngineering.test.ts +0 -30
- package/src/services/chat/contextEngineering.ts +1 -12
- package/src/services/chat/index.ts +2 -7
- package/src/services/chat/types.ts +1 -1
- package/src/services/message/_deprecated.ts +1 -1
- package/src/services/message/client.ts +8 -2
- package/src/services/message/server.ts +7 -2
- package/src/services/message/type.ts +6 -1
- package/src/store/chat/helpers.test.ts +99 -0
- package/src/store/chat/helpers.ts +21 -2
- package/src/store/chat/selectors.ts +1 -1
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +3 -3
- package/src/store/chat/slices/builtinTool/actions/index.ts +1 -4
- package/src/store/chat/slices/message/action.test.ts +5 -1
- package/src/store/chat/slices/message/action.ts +102 -14
- package/src/store/chat/slices/message/reducer.test.ts +363 -5
- package/src/store/chat/slices/message/reducer.ts +87 -3
- package/src/store/chat/slices/message/{selectors.test.ts → selectors/chat.test.ts} +266 -30
- package/src/store/chat/slices/message/{selectors.ts → selectors/chat.ts} +29 -79
- package/src/store/chat/slices/message/selectors/index.ts +2 -0
- package/src/store/chat/slices/message/selectors/messageState.test.ts +36 -0
- package/src/store/chat/slices/message/selectors/messageState.ts +80 -0
- package/src/store/chat/slices/plugin/action.test.ts +34 -132
- package/src/store/chat/slices/plugin/action.ts +1 -44
- package/src/store/tool/selectors/tool.test.ts +1 -1
- package/src/store/tool/selectors/tool.ts +6 -8
- package/src/store/tool/slices/builtin/action.test.ts +83 -35
- package/src/store/tool/slices/builtin/action.ts +0 -9
- package/src/store/tool/slices/builtin/selectors.test.ts +4 -30
- package/src/store/tool/slices/builtin/selectors.ts +15 -21
- package/src/tools/index.ts +0 -6
- package/src/tools/renders.ts +0 -3
- package/src/tools/web-browsing/Portal/Search/Footer.tsx +2 -2
- package/packages/const/src/guide.ts +0 -89
- package/packages/context-engine/src/providers/InboxGuide.ts +0 -102
- package/packages/context-engine/src/providers/__tests__/InboxGuideProvider.test.ts +0 -121
- package/src/services/chat/__snapshots__/chat.test.ts.snap +0 -110
- package/src/store/chat/slices/builtinTool/actions/__tests__/dalle.test.ts +0 -121
- package/src/store/chat/slices/builtinTool/actions/dalle.ts +0 -124
- package/src/tools/dalle/Render/GalleyGrid.tsx +0 -60
- package/src/tools/dalle/Render/Item/EditMode.tsx +0 -66
- package/src/tools/dalle/Render/Item/Error.tsx +0 -49
- package/src/tools/dalle/Render/Item/Image.tsx +0 -44
- package/src/tools/dalle/Render/Item/ImageFileItem.tsx +0 -57
- package/src/tools/dalle/Render/Item/index.tsx +0 -88
- package/src/tools/dalle/Render/ToolBar.tsx +0 -56
- package/src/tools/dalle/Render/index.tsx +0 -52
- package/src/tools/dalle/index.ts +0 -92
|
@@ -1,32 +1,26 @@
|
|
|
1
1
|
import { LobeToolMeta } from '@lobechat/types';
|
|
2
2
|
|
|
3
3
|
import { shouldEnableTool } from '@/helpers/toolFilters';
|
|
4
|
-
import { DalleManifest } from '@/tools/dalle';
|
|
5
4
|
|
|
6
5
|
import type { ToolStoreState } from '../../initialState';
|
|
7
6
|
|
|
8
|
-
const metaList =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
// Filter hidden tools
|
|
14
|
-
if (item.hidden) return false;
|
|
7
|
+
const metaList = (s: ToolStoreState): LobeToolMeta[] =>
|
|
8
|
+
s.builtinTools
|
|
9
|
+
.filter((item) => {
|
|
10
|
+
// Filter hidden tools
|
|
11
|
+
if (item.hidden) return false;
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
// Filter platform-specific tools (e.g., LocalSystem desktop-only)
|
|
14
|
+
if (!shouldEnableTool(item.identifier)) return false;
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
meta: t.manifest.meta,
|
|
28
|
-
type: 'builtin',
|
|
29
|
-
}));
|
|
16
|
+
return true;
|
|
17
|
+
})
|
|
18
|
+
.map((t) => ({
|
|
19
|
+
author: 'LobeHub',
|
|
20
|
+
identifier: t.identifier,
|
|
21
|
+
meta: t.manifest.meta,
|
|
22
|
+
type: 'builtin',
|
|
23
|
+
}));
|
|
30
24
|
|
|
31
25
|
export const builtinToolSelectors = {
|
|
32
26
|
metaList,
|
package/src/tools/index.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { isDesktop } from '@/const/version';
|
|
|
4
4
|
|
|
5
5
|
import { ArtifactsManifest } from './artifacts';
|
|
6
6
|
import { CodeInterpreterManifest } from './code-interpreter';
|
|
7
|
-
import { DalleManifest } from './dalle';
|
|
8
7
|
import { LocalSystemManifest } from './local-system';
|
|
9
8
|
import { WebBrowsingManifest } from './web-browsing';
|
|
10
9
|
|
|
@@ -14,11 +13,6 @@ export const builtinTools: LobeBuiltinTool[] = [
|
|
|
14
13
|
manifest: ArtifactsManifest,
|
|
15
14
|
type: 'builtin',
|
|
16
15
|
},
|
|
17
|
-
{
|
|
18
|
-
identifier: DalleManifest.identifier,
|
|
19
|
-
manifest: DalleManifest,
|
|
20
|
-
type: 'builtin',
|
|
21
|
-
},
|
|
22
16
|
{
|
|
23
17
|
hidden: !isDesktop,
|
|
24
18
|
identifier: LocalSystemManifest.identifier,
|
package/src/tools/renders.ts
CHANGED
|
@@ -2,15 +2,12 @@ import { BuiltinRender } from '@lobechat/types';
|
|
|
2
2
|
|
|
3
3
|
import { CodeInterpreterManifest } from './code-interpreter';
|
|
4
4
|
import CodeInterpreterRender from './code-interpreter/Render';
|
|
5
|
-
import { DalleManifest } from './dalle';
|
|
6
|
-
import DalleRender from './dalle/Render';
|
|
7
5
|
import { LocalSystemManifest } from './local-system';
|
|
8
6
|
import LocalFilesRender from './local-system/Render';
|
|
9
7
|
import { WebBrowsingManifest } from './web-browsing';
|
|
10
8
|
import WebBrowsing from './web-browsing/Render';
|
|
11
9
|
|
|
12
10
|
export const BuiltinToolsRenders: Record<string, BuiltinRender> = {
|
|
13
|
-
[DalleManifest.identifier]: DalleRender as BuiltinRender,
|
|
14
11
|
[WebBrowsingManifest.identifier]: WebBrowsing as BuiltinRender,
|
|
15
12
|
[LocalSystemManifest.identifier]: LocalFilesRender as BuiltinRender,
|
|
16
13
|
[CodeInterpreterManifest.identifier]: CodeInterpreterRender as BuiltinRender,
|
|
@@ -4,12 +4,12 @@ import { useTranslation } from 'react-i18next';
|
|
|
4
4
|
import { Flexbox } from 'react-layout-kit';
|
|
5
5
|
|
|
6
6
|
import { useChatStore } from '@/store/chat';
|
|
7
|
-
import { chatPortalSelectors,
|
|
7
|
+
import { chatPortalSelectors, messageStateSelectors } from '@/store/chat/selectors';
|
|
8
8
|
|
|
9
9
|
const Footer = () => {
|
|
10
10
|
const [messageId, isAIGenerating, triggerAIMessage, saveSearchResult] = useChatStore((s) => [
|
|
11
11
|
chatPortalSelectors.toolMessageId(s),
|
|
12
|
-
|
|
12
|
+
messageStateSelectors.isAIGenerating(s),
|
|
13
13
|
s.triggerAIMessage,
|
|
14
14
|
s.saveSearchResult,
|
|
15
15
|
]);
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import urlJoin from 'url-join';
|
|
2
|
-
|
|
3
|
-
import { BRANDING_EMAIL } from './branding';
|
|
4
|
-
import {
|
|
5
|
-
BLOG,
|
|
6
|
-
DOCKER_IMAGE,
|
|
7
|
-
GITHUB,
|
|
8
|
-
OFFICIAL_PREVIEW_URL,
|
|
9
|
-
OFFICIAL_SITE,
|
|
10
|
-
OFFICIAL_URL,
|
|
11
|
-
SELF_HOSTING_DOCUMENTS,
|
|
12
|
-
USAGE_DOCUMENTS,
|
|
13
|
-
WIKI,
|
|
14
|
-
} from './url';
|
|
15
|
-
|
|
16
|
-
export const INBOX_GUIDE_SYSTEMROLE = `# Role: LobeChat Support Assistant
|
|
17
|
-
|
|
18
|
-
## About [LobeHub](${OFFICIAL_SITE})
|
|
19
|
-
|
|
20
|
-
LobeHub is an organization of design-engineers dedicated to providing advanced design components and tools for AI-generated content (AIGC).
|
|
21
|
-
It aims to create a technology-driven community platform that enables the sharing of knowledge and ideas, fostering inspiration and collaboration.
|
|
22
|
-
|
|
23
|
-
Adopting a Bootstrapping approach, LobeHub is committed to delivering an open, transparent, and user-friendly product ecosystem for both casual users and professional developers.
|
|
24
|
-
LobeHub serves as an AI Agent playground, where creativity and innovation meet.
|
|
25
|
-
|
|
26
|
-
## About [LobeChat](${OFFICIAL_URL})
|
|
27
|
-
|
|
28
|
-
LobeChat, a product of LobeHub, is an open-source ChatGPT/LLMs UI/Framework designed for modern LLMs/AI applications.
|
|
29
|
-
Supports Multi AI Providers( OpenAI / Claude 3 / Gemini / Perplexity / Bedrock / Azure / Mistral / Ollama ), Multi-Modals (Vision/TTS) and plugin system.
|
|
30
|
-
and offers a one-click FREE deployment for a private ChatGPT chat application, making it accessible and customizable for a wide range of users.
|
|
31
|
-
|
|
32
|
-
### Features
|
|
33
|
-
|
|
34
|
-
- [Multi-Model Service Provider Support](${urlJoin(USAGE_DOCUMENTS, '/features/multi-ai-providers')})
|
|
35
|
-
- [Local Large Language Model (LLM) Support](${urlJoin(USAGE_DOCUMENTS, '/features/local-llm')})
|
|
36
|
-
- [Model Visual Recognition](${urlJoin(USAGE_DOCUMENTS, '/features/vision')})
|
|
37
|
-
- [TTS & STT Voice Conversation](${urlJoin(USAGE_DOCUMENTS, '/features/tts')})
|
|
38
|
-
- [Text to Image Generation](${urlJoin(USAGE_DOCUMENTS, '/features/text-to-image')})
|
|
39
|
-
- [Plugin System (Function Calling)](${urlJoin(USAGE_DOCUMENTS, '/features/plugin-system')})
|
|
40
|
-
- [Agent Market (GPTs)](${urlJoin(USAGE_DOCUMENTS, '/features/agent-market')})
|
|
41
|
-
|
|
42
|
-
### Community Edition and Cloud Version
|
|
43
|
-
|
|
44
|
-
LobeChat is currently available as a community preview version, completely open-source and free of charge.
|
|
45
|
-
|
|
46
|
-
In the LobeChat Cloud version, we provide 500,000 free computing credits to all registered users. It is ready to use without complex configurations.
|
|
47
|
-
If you require more usage, you can subscribe to the Basic, Advanced, or Professional versions for a fee.
|
|
48
|
-
|
|
49
|
-
### Self Hosting
|
|
50
|
-
|
|
51
|
-
LobeChat provides Self-Hosted Version with [Vercel](${urlJoin(SELF_HOSTING_DOCUMENTS, '/platform/vercel')}) and [Docker Image](${DOCKER_IMAGE}).
|
|
52
|
-
This allows you to deploy your own chatbot within a few minutes without any prior knowledge.
|
|
53
|
-
|
|
54
|
-
**IMPORTANT**
|
|
55
|
-
|
|
56
|
-
When users ask about usage or deployment, DO NOT MAKE UP ANSWERS. Instead, guide them to the relevant documentation!!!
|
|
57
|
-
|
|
58
|
-
Learn more about [Build your own LobeChat](${SELF_HOSTING_DOCUMENTS}) by checking it out.
|
|
59
|
-
|
|
60
|
-
## Resources Links
|
|
61
|
-
|
|
62
|
-
In the response, please try to pick and include the relevant links below, and if a relevant answer cannot be provided, also offer the user these related links:
|
|
63
|
-
|
|
64
|
-
- Official Website: ${OFFICIAL_SITE}
|
|
65
|
-
- Cloud Version: ${OFFICIAL_URL}
|
|
66
|
-
- Community Edition: ${OFFICIAL_PREVIEW_URL}
|
|
67
|
-
- GitHub Repository: ${GITHUB}
|
|
68
|
-
- Latest News: ${BLOG}
|
|
69
|
-
- Usage Documentation: ${USAGE_DOCUMENTS}
|
|
70
|
-
- Self-Hosting Documentation: ${SELF_HOSTING_DOCUMENTS}
|
|
71
|
-
- Development Guide: ${WIKI}
|
|
72
|
-
- Email Support: ${BRANDING_EMAIL.support}
|
|
73
|
-
- Business Inquiries: ${BRANDING_EMAIL.business}
|
|
74
|
-
|
|
75
|
-
## Workflow
|
|
76
|
-
|
|
77
|
-
1. Greet users and introduce the role and purpose of LobeHub LobeChat Support Assistant.
|
|
78
|
-
2. Understand and address user inquiries related to the LobeHub ecosystem and LobeChat application.
|
|
79
|
-
3. If unable to resolve user queries, pick and guide them to appropriate resources listed above.
|
|
80
|
-
|
|
81
|
-
## Initialization
|
|
82
|
-
|
|
83
|
-
As the role <Role>, I will adhere to the following guidelines:
|
|
84
|
-
- Provide accurate and helpful information to users.
|
|
85
|
-
- Maintain a friendly and professional demeanor.
|
|
86
|
-
- Direct users to the appropriate resources when necessary.
|
|
87
|
-
- Keep the language of the response consistent with the language of the user input; if they are not consistent, then translate.
|
|
88
|
-
|
|
89
|
-
Welcome users to LobeChat, introduce myself as the <Role>, and inform them about the services and support available. Then, guide users through the <Workflow> for assistance.`;
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import debug from 'debug';
|
|
2
|
-
|
|
3
|
-
import { BaseProvider } from '../base/BaseProvider';
|
|
4
|
-
import type { PipelineContext, ProcessorOptions } from '../types';
|
|
5
|
-
|
|
6
|
-
const log = debug('context-engine:provider:InboxGuideProvider');
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Inbox Guide System Role Configuration
|
|
10
|
-
*/
|
|
11
|
-
export interface InboxGuideConfig {
|
|
12
|
-
/** Inbox guide system role content */
|
|
13
|
-
inboxGuideSystemRole: string;
|
|
14
|
-
/** Inbox session ID constant */
|
|
15
|
-
inboxSessionId: string;
|
|
16
|
-
/** Whether it's a welcome question */
|
|
17
|
-
isWelcomeQuestion?: boolean;
|
|
18
|
-
/** Session ID */
|
|
19
|
-
sessionId?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Inbox Guide Provider
|
|
24
|
-
* Responsible for injecting guide system role for welcome questions in inbox sessions
|
|
25
|
-
*/
|
|
26
|
-
export class InboxGuideProvider extends BaseProvider {
|
|
27
|
-
readonly name = 'InboxGuideProvider';
|
|
28
|
-
|
|
29
|
-
constructor(
|
|
30
|
-
private config: InboxGuideConfig,
|
|
31
|
-
options: ProcessorOptions = {},
|
|
32
|
-
) {
|
|
33
|
-
super(options);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
protected async doProcess(context: PipelineContext): Promise<PipelineContext> {
|
|
37
|
-
const clonedContext = this.cloneContext(context);
|
|
38
|
-
|
|
39
|
-
// 检查是否需要注入收件箱引导
|
|
40
|
-
const shouldInject = this.shouldInjectInboxGuide();
|
|
41
|
-
|
|
42
|
-
if (!shouldInject) {
|
|
43
|
-
log('Inbox guide injection conditions not met, skipping processing');
|
|
44
|
-
return this.markAsExecuted(clonedContext);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// 注入收件箱引导系统角色
|
|
48
|
-
this.injectInboxGuideSystemRole(clonedContext);
|
|
49
|
-
|
|
50
|
-
// 更新元数据
|
|
51
|
-
clonedContext.metadata.inboxGuide = {
|
|
52
|
-
contentLength: this.config.inboxGuideSystemRole.length,
|
|
53
|
-
injected: true,
|
|
54
|
-
isWelcomeQuestion: this.config.isWelcomeQuestion,
|
|
55
|
-
sessionId: this.config.sessionId,
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
log('Inbox guide system role injection completed');
|
|
59
|
-
return this.markAsExecuted(clonedContext);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Check if inbox guide should be injected
|
|
64
|
-
*/
|
|
65
|
-
private shouldInjectInboxGuide(): boolean {
|
|
66
|
-
return (
|
|
67
|
-
(this.config.isWelcomeQuestion &&
|
|
68
|
-
this.config.sessionId === this.config.inboxSessionId &&
|
|
69
|
-
!!this.config.inboxGuideSystemRole) ||
|
|
70
|
-
false
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Inject inbox guide system role
|
|
76
|
-
*/
|
|
77
|
-
private injectInboxGuideSystemRole(context: PipelineContext): void {
|
|
78
|
-
const existingSystemMessage = context.messages.find((msg) => msg.role === 'system');
|
|
79
|
-
|
|
80
|
-
if (existingSystemMessage) {
|
|
81
|
-
// 合并到现有系统消息
|
|
82
|
-
existingSystemMessage.content = [
|
|
83
|
-
existingSystemMessage.content,
|
|
84
|
-
this.config.inboxGuideSystemRole,
|
|
85
|
-
]
|
|
86
|
-
.filter(Boolean)
|
|
87
|
-
.join('\n\n');
|
|
88
|
-
|
|
89
|
-
log(
|
|
90
|
-
`Inbox guide merged to existing system message, final length: ${existingSystemMessage.content.length}`,
|
|
91
|
-
);
|
|
92
|
-
} else {
|
|
93
|
-
context.messages.unshift({
|
|
94
|
-
content: this.config.inboxGuideSystemRole,
|
|
95
|
-
role: 'system' as const,
|
|
96
|
-
} as any);
|
|
97
|
-
log(
|
|
98
|
-
`New inbox guide system message created, content length: ${this.config.inboxGuideSystemRole.length}`,
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import type { PipelineContext } from '../../types';
|
|
4
|
-
import { InboxGuideProvider } from '../InboxGuide';
|
|
5
|
-
|
|
6
|
-
const createContext = (messages: any[]): PipelineContext => ({
|
|
7
|
-
initialState: { messages: [] } as any,
|
|
8
|
-
messages,
|
|
9
|
-
metadata: { model: 'gpt-4', maxTokens: 4096 },
|
|
10
|
-
isAborted: false,
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
describe('InboxGuideProvider', () => {
|
|
14
|
-
const mockInboxGuideContent = '# Role: LobeChat Support Assistant\n\nWelcome to LobeChat!';
|
|
15
|
-
const INBOX_SESSION_ID = 'inbox';
|
|
16
|
-
|
|
17
|
-
it('should inject inbox guide for welcome questions in inbox session', async () => {
|
|
18
|
-
const provider = new InboxGuideProvider({
|
|
19
|
-
isWelcomeQuestion: true,
|
|
20
|
-
sessionId: INBOX_SESSION_ID,
|
|
21
|
-
inboxSessionId: INBOX_SESSION_ID,
|
|
22
|
-
inboxGuideSystemRole: mockInboxGuideContent,
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const messages = [{ id: 'u1', role: 'user', content: 'Hello, this is my first question' }];
|
|
26
|
-
|
|
27
|
-
const ctx = createContext(messages);
|
|
28
|
-
const result = await provider.process(ctx);
|
|
29
|
-
|
|
30
|
-
// Should have system message with inbox guide content
|
|
31
|
-
const systemMessage = result.messages.find((msg) => msg.role === 'system');
|
|
32
|
-
expect(systemMessage).toBeDefined();
|
|
33
|
-
expect(systemMessage!.content).toBe(mockInboxGuideContent);
|
|
34
|
-
|
|
35
|
-
// Should update metadata
|
|
36
|
-
expect(result.metadata.inboxGuide).toEqual({
|
|
37
|
-
injected: true,
|
|
38
|
-
sessionId: INBOX_SESSION_ID,
|
|
39
|
-
isWelcomeQuestion: true,
|
|
40
|
-
contentLength: mockInboxGuideContent.length,
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should merge inbox guide with existing system message', async () => {
|
|
45
|
-
const provider = new InboxGuideProvider({
|
|
46
|
-
isWelcomeQuestion: true,
|
|
47
|
-
sessionId: INBOX_SESSION_ID,
|
|
48
|
-
inboxSessionId: INBOX_SESSION_ID,
|
|
49
|
-
inboxGuideSystemRole: mockInboxGuideContent,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const existingSystemContent = 'Existing system message';
|
|
53
|
-
const messages = [
|
|
54
|
-
{ id: 's1', role: 'system', content: existingSystemContent },
|
|
55
|
-
{ id: 'u1', role: 'user', content: 'Hello' },
|
|
56
|
-
];
|
|
57
|
-
|
|
58
|
-
const ctx = createContext(messages);
|
|
59
|
-
const result = await provider.process(ctx);
|
|
60
|
-
|
|
61
|
-
const systemMessage = result.messages.find((msg) => msg.role === 'system');
|
|
62
|
-
expect(systemMessage!.content).toBe(`${existingSystemContent}\n\n${mockInboxGuideContent}`);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('should skip injection when not welcome question', async () => {
|
|
66
|
-
const provider = new InboxGuideProvider({
|
|
67
|
-
isWelcomeQuestion: false,
|
|
68
|
-
sessionId: INBOX_SESSION_ID,
|
|
69
|
-
inboxSessionId: INBOX_SESSION_ID,
|
|
70
|
-
inboxGuideSystemRole: mockInboxGuideContent,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
const messages = [{ id: 'u1', role: 'user', content: 'Regular question' }];
|
|
74
|
-
|
|
75
|
-
const ctx = createContext(messages);
|
|
76
|
-
const result = await provider.process(ctx);
|
|
77
|
-
|
|
78
|
-
// Should not have system message
|
|
79
|
-
const systemMessage = result.messages.find((msg) => msg.role === 'system');
|
|
80
|
-
expect(systemMessage).toBeUndefined();
|
|
81
|
-
|
|
82
|
-
// Should not have metadata
|
|
83
|
-
expect(result.metadata.inboxGuide).toBeUndefined();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('should skip injection when not in inbox session', async () => {
|
|
87
|
-
const provider = new InboxGuideProvider({
|
|
88
|
-
isWelcomeQuestion: true,
|
|
89
|
-
sessionId: 'other-session',
|
|
90
|
-
inboxSessionId: INBOX_SESSION_ID,
|
|
91
|
-
inboxGuideSystemRole: mockInboxGuideContent,
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
const messages = [{ id: 'u1', role: 'user', content: 'Hello' }];
|
|
95
|
-
|
|
96
|
-
const ctx = createContext(messages);
|
|
97
|
-
const result = await provider.process(ctx);
|
|
98
|
-
|
|
99
|
-
// Should not have system message
|
|
100
|
-
const systemMessage = result.messages.find((msg) => msg.role === 'system');
|
|
101
|
-
expect(systemMessage).toBeUndefined();
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('should skip injection when no inbox guide content', async () => {
|
|
105
|
-
const provider = new InboxGuideProvider({
|
|
106
|
-
isWelcomeQuestion: true,
|
|
107
|
-
sessionId: INBOX_SESSION_ID,
|
|
108
|
-
inboxSessionId: INBOX_SESSION_ID,
|
|
109
|
-
inboxGuideSystemRole: '',
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
const messages = [{ id: 'u1', role: 'user', content: 'Hello' }];
|
|
113
|
-
|
|
114
|
-
const ctx = createContext(messages);
|
|
115
|
-
const result = await provider.process(ctx);
|
|
116
|
-
|
|
117
|
-
// Should not have system message
|
|
118
|
-
const systemMessage = result.messages.find((msg) => msg.role === 'system');
|
|
119
|
-
expect(systemMessage).toBeUndefined();
|
|
120
|
-
});
|
|
121
|
-
});
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
-
|
|
3
|
-
exports[`ChatService > createAssistantMessage > with tools messages > work with dalle3 1`] = `
|
|
4
|
-
{
|
|
5
|
-
"enabledSearch": undefined,
|
|
6
|
-
"messages": [
|
|
7
|
-
{
|
|
8
|
-
"content": "<plugins description="The plugins you can use below">
|
|
9
|
-
<collection name="DALL·E 3">
|
|
10
|
-
<collection.instructions>Whenever a description of an image is given, use lobe-image-designer to create the images and then summarize the prompts used to generate the images in plain text. If the user does not ask for a specific number of images, default to creating four captions to send to lobe-image-designer that are written to be as diverse as possible.
|
|
11
|
-
|
|
12
|
-
All captions sent to lobe-image-designer must abide by the following policies:
|
|
13
|
-
|
|
14
|
-
1. If the description is not in English, then translate it.
|
|
15
|
-
2. Do not create more than 4 images, even if the user requests more.
|
|
16
|
-
3. Don't create images of politicians or other public figures. Recommend other ideas instead.
|
|
17
|
-
5. DO NOT list or refer to the descriptions before OR after generating the images. They should ONLY ever be written out ONCE, in the \`prompts\` field of the request. You do not need to ask for permission to generate, just do it!
|
|
18
|
-
6. Always mention the image type (photo, oil painting, watercolor painting, illustration, cartoon, drawing, vector, render, etc.) at the beginning of the caption. Unless the caption suggests otherwise, make at least 1--2 of the 4 images photos.
|
|
19
|
-
7. Diversify depictions of ALL images with people to include DESCENT and GENDER for EACH person using direct terms. Adjust only human descriptions.
|
|
20
|
-
- EXPLICITLY specify these attributes, not abstractly reference them. The attributes should be specified in a minimal way and should directly describe their physical form.
|
|
21
|
-
- Your choices should be grounded in reality. For example, all of a given OCCUPATION should not be the same gender or race. Additionally, focus on creating diverse, inclusive, and exploratory scenes via the properties you choose during rewrites. Make choices that may be insightful or unique sometimes.
|
|
22
|
-
- Use "various" or "diverse" ONLY IF the description refers to groups of more than 3 people. Do not change the number of people requested in the original description.
|
|
23
|
-
- Don't alter memes, fictional character origins, or unseen people. Maintain the original prompt's intent and prioritize quality.
|
|
24
|
-
- Do not create any imagery that would be offensive.
|
|
25
|
-
|
|
26
|
-
8. Silently modify descriptions that include names or hints or references of specific people or celebrities by carefully selecting a few minimal modifications to substitute references to the people with generic descriptions that don't divulge any information about their identities, except for their genders and physiques. Do this EVEN WHEN the instructions ask for the prompt to not be changed. Some special cases:
|
|
27
|
-
- Modify such prompts even if you don't know who the person is, or if their name is misspelled (e.g. "Barake Obema")
|
|
28
|
-
- If the reference to the person will only appear as TEXT out in the image, then use the reference as is and do not modify it.
|
|
29
|
-
- When making the substitutions, don't use prominent titles that could give away the person's identity. E.g., instead of saying "president", "prime minister", or "chancellor", say "politician"; instead of saying "king", "queen", "emperor", or "empress", say "public figure"; instead of saying "Pope", say "religious figure"; and so on.
|
|
30
|
-
- If any creative professional or studio is named, substitute the name with a description of their style that does not reference any specific people, or delete the reference if they are unknown. DO NOT refer to the artist or studio's style.
|
|
31
|
-
|
|
32
|
-
The prompt must intricately describe every part of the image in concrete, objective detail. THINK about what the end goal of the description is, and extrapolate that to what would make satisfying images.
|
|
33
|
-
All descriptions sent to lobe-image-designer should be a paragraph of text that is extremely descriptive and detailed. Each should be more than 3 sentences long.</collection.instructions>
|
|
34
|
-
<api identifier="lobe-image-designer____text2image____builtin">Create images from a text-only prompt.</api>
|
|
35
|
-
</collection>
|
|
36
|
-
</plugins>",
|
|
37
|
-
"role": "system",
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"content": "https://vercel.com/ 请分析 chatGPT 关键词
|
|
41
|
-
|
|
42
|
-
",
|
|
43
|
-
"role": "user",
|
|
44
|
-
},
|
|
45
|
-
],
|
|
46
|
-
"model": "gpt-3.5-turbo-1106",
|
|
47
|
-
"tools": [
|
|
48
|
-
{
|
|
49
|
-
"function": {
|
|
50
|
-
"description": "Create images from a text-only prompt.",
|
|
51
|
-
"name": "lobe-image-designer____text2image____builtin",
|
|
52
|
-
"parameters": {
|
|
53
|
-
"properties": {
|
|
54
|
-
"prompts": {
|
|
55
|
-
"description": "The user's original image description, potentially modified to abide by the lobe-image-designer policies. If the user does not suggest a number of captions to create, create four of them. If creating multiple captions, make them as diverse as possible. If the user requested modifications to previous images, the captions should not simply be longer, but rather it should be refactored to integrate the suggestions into each of the captions. Generate no more than 4 images, even if the user requests more.",
|
|
56
|
-
"items": {
|
|
57
|
-
"type": "string",
|
|
58
|
-
},
|
|
59
|
-
"maxItems": 4,
|
|
60
|
-
"minItems": 1,
|
|
61
|
-
"type": "array",
|
|
62
|
-
},
|
|
63
|
-
"quality": {
|
|
64
|
-
"default": "standard",
|
|
65
|
-
"description": "The quality of the image that will be generated. hd creates images with finer details and greater consistency across the image.",
|
|
66
|
-
"enum": [
|
|
67
|
-
"standard",
|
|
68
|
-
"hd",
|
|
69
|
-
],
|
|
70
|
-
"type": "string",
|
|
71
|
-
},
|
|
72
|
-
"seeds": {
|
|
73
|
-
"description": "A list of seeds to use for each prompt. If the user asks to modify a previous image, populate this field with the seed used to generate that image from the image lobe-image-designer metadata.",
|
|
74
|
-
"items": {
|
|
75
|
-
"type": "integer",
|
|
76
|
-
},
|
|
77
|
-
"type": "array",
|
|
78
|
-
},
|
|
79
|
-
"size": {
|
|
80
|
-
"default": "1024x1024",
|
|
81
|
-
"description": "The resolution of the requested image, which can be wide, square, or tall. Use 1024x1024 (square) as the default unless the prompt suggests a wide image, 1792x1024, or a full-body portrait, in which case 1024x1792 (tall) should be used instead. Always include this parameter in the request.",
|
|
82
|
-
"enum": [
|
|
83
|
-
"1792x1024",
|
|
84
|
-
"1024x1024",
|
|
85
|
-
"1024x1792",
|
|
86
|
-
],
|
|
87
|
-
"type": "string",
|
|
88
|
-
},
|
|
89
|
-
"style": {
|
|
90
|
-
"default": "vivid",
|
|
91
|
-
"description": "The style of the generated images. Must be one of vivid or natural. Vivid causes the model to lean towards generating hyper-real and dramatic images. Natural causes the model to produce more natural, less hyper-real looking images.",
|
|
92
|
-
"enum": [
|
|
93
|
-
"vivid",
|
|
94
|
-
"natural",
|
|
95
|
-
],
|
|
96
|
-
"type": "string",
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
"required": [
|
|
100
|
-
"prompts",
|
|
101
|
-
],
|
|
102
|
-
"type": "object",
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
"type": "function",
|
|
106
|
-
},
|
|
107
|
-
],
|
|
108
|
-
"top_p": 1,
|
|
109
|
-
}
|
|
110
|
-
`;
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { UIChatMessage } from '@lobechat/types';
|
|
2
|
-
import { act, renderHook } from '@testing-library/react';
|
|
3
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
4
|
-
|
|
5
|
-
import { messageService } from '@/services/message';
|
|
6
|
-
import { imageGenerationService } from '@/services/textToImage';
|
|
7
|
-
import { uploadService } from '@/services/upload';
|
|
8
|
-
import { useChatStore } from '@/store/chat';
|
|
9
|
-
import { chatSelectors } from '@/store/chat/selectors';
|
|
10
|
-
import { useFileStore } from '@/store/file';
|
|
11
|
-
import { DallEImageItem } from '@/types/tool/dalle';
|
|
12
|
-
|
|
13
|
-
describe('chatToolSlice - dalle', () => {
|
|
14
|
-
describe('generateImageFromPrompts', () => {
|
|
15
|
-
it('should generate images from prompts, update items, and upload images', async () => {
|
|
16
|
-
const { result } = renderHook(() => useChatStore());
|
|
17
|
-
|
|
18
|
-
const initialMessageContent = JSON.stringify([
|
|
19
|
-
{ prompt: 'test prompt', previewUrl: 'old-url', imageId: 'old-id' },
|
|
20
|
-
]);
|
|
21
|
-
|
|
22
|
-
vi.spyOn(chatSelectors, 'getMessageById').mockImplementationOnce(
|
|
23
|
-
(id) => () =>
|
|
24
|
-
({
|
|
25
|
-
id,
|
|
26
|
-
content: initialMessageContent,
|
|
27
|
-
}) as UIChatMessage,
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
const messageId = 'message-id';
|
|
31
|
-
const prompts = [
|
|
32
|
-
{ prompt: 'test prompt 1' },
|
|
33
|
-
{ prompt: 'test prompt 2' },
|
|
34
|
-
] as DallEImageItem[];
|
|
35
|
-
const mockUrl = 'https://example.com/image.png';
|
|
36
|
-
const mockId = 'image-id';
|
|
37
|
-
|
|
38
|
-
vi.spyOn(imageGenerationService, 'generateImage').mockResolvedValue(mockUrl);
|
|
39
|
-
vi.spyOn(uploadService, 'getImageFileByUrlWithCORS').mockResolvedValue(
|
|
40
|
-
new File(['1'], 'file.png', { type: 'image/png' }),
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
// Mock the new uploadWithProgress method from useFileStore
|
|
44
|
-
vi.spyOn(useFileStore, 'getState').mockReturnValue({
|
|
45
|
-
uploadWithProgress: vi.fn().mockResolvedValue({
|
|
46
|
-
id: mockId,
|
|
47
|
-
url: '',
|
|
48
|
-
dimensions: { width: 512, height: 512 },
|
|
49
|
-
filename: 'file.png',
|
|
50
|
-
}),
|
|
51
|
-
} as any);
|
|
52
|
-
|
|
53
|
-
// Mock store methods that are called in the implementation
|
|
54
|
-
vi.spyOn(result.current, 'toggleDallEImageLoading');
|
|
55
|
-
vi.spyOn(result.current, 'updatePluginState').mockResolvedValue(undefined);
|
|
56
|
-
vi.spyOn(result.current, 'internal_updateMessageContent').mockResolvedValue(undefined);
|
|
57
|
-
|
|
58
|
-
await act(async () => {
|
|
59
|
-
await result.current.generateImageFromPrompts(prompts, messageId);
|
|
60
|
-
});
|
|
61
|
-
// For each prompt, loading is toggled on and then off
|
|
62
|
-
expect(imageGenerationService.generateImage).toHaveBeenCalledTimes(prompts.length);
|
|
63
|
-
expect(useFileStore.getState().uploadWithProgress).toHaveBeenCalledTimes(prompts.length);
|
|
64
|
-
expect(result.current.toggleDallEImageLoading).toHaveBeenCalledTimes(prompts.length * 2);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
describe('updateImageItem', () => {
|
|
69
|
-
it('should update image item correctly', async () => {
|
|
70
|
-
const { result } = renderHook(() => useChatStore());
|
|
71
|
-
const messageId = 'message-id';
|
|
72
|
-
const initialMessageContent = JSON.stringify([
|
|
73
|
-
{ prompt: 'test prompt', previewUrl: 'old-url', imageId: 'old-id' },
|
|
74
|
-
]);
|
|
75
|
-
const updateFunction = (draft: any) => {
|
|
76
|
-
draft[0].previewUrl = 'new-url';
|
|
77
|
-
draft[0].imageId = 'new-id';
|
|
78
|
-
};
|
|
79
|
-
vi.spyOn(result.current, 'internal_updateMessageContent').mockResolvedValue(undefined);
|
|
80
|
-
|
|
81
|
-
// 模拟 getMessageById 返回消息内容
|
|
82
|
-
vi.spyOn(chatSelectors, 'getMessageById').mockImplementationOnce(
|
|
83
|
-
(id) => () =>
|
|
84
|
-
({
|
|
85
|
-
id,
|
|
86
|
-
content: initialMessageContent,
|
|
87
|
-
}) as UIChatMessage,
|
|
88
|
-
);
|
|
89
|
-
vi.spyOn(messageService, 'updateMessage').mockResolvedValueOnce(undefined);
|
|
90
|
-
|
|
91
|
-
await act(async () => {
|
|
92
|
-
await result.current.updateImageItem(messageId, updateFunction);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// 验证 internal_updateMessageContent 是否被正确调用以更新内容
|
|
96
|
-
expect(result.current.internal_updateMessageContent).toHaveBeenCalledWith(
|
|
97
|
-
messageId,
|
|
98
|
-
JSON.stringify([{ prompt: 'test prompt', previewUrl: 'new-url', imageId: 'new-id' }]),
|
|
99
|
-
);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
describe('text2image', () => {
|
|
104
|
-
it('should call generateImageFromPrompts with provided data', async () => {
|
|
105
|
-
const { result } = renderHook(() => useChatStore());
|
|
106
|
-
const id = 'message-id';
|
|
107
|
-
const data = [{ prompt: 'prompt 1' }, { prompt: 'prompt 2' }] as DallEImageItem[];
|
|
108
|
-
|
|
109
|
-
// Mock generateImageFromPrompts
|
|
110
|
-
const generateImageFromPromptsMock = vi
|
|
111
|
-
.spyOn(result.current, 'generateImageFromPrompts')
|
|
112
|
-
.mockResolvedValue(undefined);
|
|
113
|
-
|
|
114
|
-
await act(async () => {
|
|
115
|
-
await result.current.text2image(id, data);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
expect(generateImageFromPromptsMock).toHaveBeenCalledWith(data, id);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
});
|