@lobehub/chat 0.118.3 → 0.118.5
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/CHANGELOG.md +50 -0
- package/README.md +8 -8
- package/README.zh-CN.md +8 -8
- package/docs/Development/Chat-API.zh-CN.md +90 -52
- package/package.json +1 -1
- package/src/features/Conversation/ChatList/Plugins/Render/BuiltinType/index.test.tsx +53 -0
- package/src/hooks/useIsPWA.ts +3 -4
- package/src/services/__tests__/__snapshots__/chat.test.ts.snap +116 -0
- package/src/services/__tests__/chat.test.ts +8 -95
- package/src/store/chat/store.ts +20 -6
- package/src/store/middleware/createHyperStorage/index.ts +8 -6
- package/src/store/middleware/createHyperStorage/keyMapper.ts +4 -4
- package/src/store/middleware/createHyperStorage/type.ts +6 -2
- package/src/store/session/store.ts +24 -9
- package/src/tools/dalle/index.ts +29 -18
- package/src/tools/index.ts +1 -1
- package/src/tools/renders.ts +6 -0
- package/src/utils/matchMedia.ts +10 -0
- package/src/utils/screen.ts +14 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.118.5](https://github.com/lobehub/lobe-chat/compare/v0.118.4...v0.118.5)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2024-01-02**</sup>
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **misc**: Mobile device return to the previous page error.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's fixed
|
|
19
|
+
|
|
20
|
+
- **misc**: Mobile device return to the previous page error, closes [#886](https://github.com/lobehub/lobe-chat/issues/886) ([99cfc0f](https://github.com/lobehub/lobe-chat/commit/99cfc0f))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
### [Version 0.118.4](https://github.com/lobehub/lobe-chat/compare/v0.118.3...v0.118.4)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2024-01-02**</sup>
|
|
33
|
+
|
|
34
|
+
#### 🐛 Bug Fixes
|
|
35
|
+
|
|
36
|
+
- **misc**: Update dalle identifier to fix unstable dalle function call.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### What's fixed
|
|
44
|
+
|
|
45
|
+
- **misc**: Update dalle identifier to fix unstable dalle function call, closes [#896](https://github.com/lobehub/lobe-chat/issues/896) ([9d9ac32](https://github.com/lobehub/lobe-chat/commit/9d9ac32))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
### [Version 0.118.3](https://github.com/lobehub/lobe-chat/compare/v0.118.2...v0.118.3)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2024-01-01**</sup>
|
package/README.md
CHANGED
|
@@ -208,14 +208,14 @@ Our marketplace is not just a showcase platform but also a collaborative space.
|
|
|
208
208
|
|
|
209
209
|
<!-- AGENT LIST -->
|
|
210
210
|
|
|
211
|
-
| Recent Submits
|
|
212
|
-
|
|
|
213
|
-
| [
|
|
214
|
-
| [
|
|
215
|
-
| [
|
|
216
|
-
| [
|
|
217
|
-
|
|
218
|
-
> 📊 Total agents: [<kbd>**
|
|
211
|
+
| Recent Submits | Description |
|
|
212
|
+
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
213
|
+
| [Amazon Title Assistant](https://chat-preview.lobehub.com/market?agent=amazon)<br/><sup>By **[cm2457618290](https://github.com/cm2457618290)** on **2024-01-02**</sup> | Provide product keywords or product links to automatically write titles and product introductions<br/>`assistant` |
|
|
214
|
+
| [Exam Assistant](https://chat-preview.lobehub.com/market?agent=generador-examenes)<br/><sup>By **[aitorroma](https://github.com/aitorroma)** on **2024-01-02**</sup> | I am a skills summary assistant and cannot conduct interactive exams. However, I can help you summarize your skills and knowledge in a clear and concise format.<br/>`exam` `learning` `statistics` |
|
|
215
|
+
| [Teaching Mentor](https://chat-preview.lobehub.com/market?agent=ljrwwjl-development)<br/><sup>By **[ljr1314](https://github.com/ljr1314)** on **2024-01-02**</sup> | A friendly and helpful mentor who customizes explanations and examples based on the user's learning level and interests to ensure clarity. Ask 4 questions, then provide explanations, examples, and analogies, and ensure understanding through questioning. Finally, have the user explain the topic in their own words and provide an example. End on a positive note and encourage further learning.<br/>`mentor` `education` `explanation` `communication` `learning` |
|
|
216
|
+
| [MidjourneyGPT](https://chat-preview.lobehub.com/market?agent=prompt-composition)<br/><sup>By **[richards199999](https://github.com/richards199999)** on **2023-12-30**</sup> | Write perfect and beautiful prompts for Midjourney. (Including V6!)<br/>`midjourney` `prompt` `ai` |
|
|
217
|
+
|
|
218
|
+
> 📊 Total agents: [<kbd>**87**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
|
|
219
219
|
|
|
220
220
|
<!-- AGENT LIST -->
|
|
221
221
|
|
package/README.zh-CN.md
CHANGED
|
@@ -184,14 +184,14 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
|
|
184
184
|
|
|
185
185
|
<!-- AGENT LIST -->
|
|
186
186
|
|
|
187
|
-
| 最近新增
|
|
188
|
-
|
|
|
189
|
-
| [
|
|
190
|
-
| [
|
|
191
|
-
| [
|
|
192
|
-
| [
|
|
193
|
-
|
|
194
|
-
> 📊 Total agents: [<kbd>**
|
|
187
|
+
| 最近新增 | 助手说明 |
|
|
188
|
+
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
189
|
+
| [Amazon Title Assistant](https://chat-preview.lobehub.com/market?agent=amazon)<br/><sup>By **[cm2457618290](https://github.com/cm2457618290)** on **2024-01-02**</sup> | Provide product keywords or product links to automatically write titles and product introductions<br/>`assistant` |
|
|
190
|
+
| [考试助手](https://chat-preview.lobehub.com/market?agent=generador-examenes)<br/><sup>By **[aitorroma](https://github.com/aitorroma)** on **2024-01-02**</sup> | 我是一个技能总结助手,无法进行互动式考试。但我可以帮助你以清晰简洁的格式总结你的技能和知识。<br/>`考试` `学习` `统计` |
|
|
191
|
+
| [教学导师](https://chat-preview.lobehub.com/market?agent=ljrwwjl-development)<br/><sup>By **[ljr1314](https://github.com/ljr1314)** on **2024-01-02**</sup> | 友好乐于助人的导师,根据用户的学习水平和兴趣定制解释和示例,确保简洁明了。问 4 个问题,然后提供解释、例子和类比,并通过提问确保用户理解。最后让用户用自己的语言解释主题,并给出一个例子。以积极正面的方式结束,并鼓励用户深入学习。<br/>`导师` `教育` `解释` `交流` `学习` |
|
|
192
|
+
| [MidjourneyGPT](https://chat-preview.lobehub.com/market?agent=prompt-composition)<br/><sup>By **[richards199999](https://github.com/richards199999)** on **2023-12-30**</sup> | 为 Midjourney 编写完美而美丽的提示。(包括 V6!)<br/>`midjourney` `prompt` `ai` |
|
|
193
|
+
|
|
194
|
+
> 📊 Total agents: [<kbd>**87**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
|
|
195
195
|
|
|
196
196
|
<!-- AGENT LIST -->
|
|
197
197
|
|
|
@@ -17,46 +17,70 @@ LobeChat 的大模型 AI 实现主要依赖于 OpenAI 的 API,包括后端的
|
|
|
17
17
|
|
|
18
18
|
### 核心会话 API
|
|
19
19
|
|
|
20
|
-
在 `src/app/api/openai/chat/
|
|
20
|
+
在 `src/app/api/openai/chat/route.ts` 中,定义了一个处理 POST 请求的方法,主要负责从请求体中提取 `OpenAIChatStreamPayload` 类型的 payload,并使用 `createBizOpenAI` 函数根据请求和模型信息创建 OpenAI 实例。随后,该方法调用 `createChatCompletion` 来处理实际的会话,并返回响应结果。如果创建 OpenAI 实例过程中出现错误,则直接返回错误响应。
|
|
21
21
|
|
|
22
22
|
```ts
|
|
23
23
|
export const POST = async (req: Request) => {
|
|
24
|
-
const payload = await req.json();
|
|
24
|
+
const payload = (await req.json()) as OpenAIChatStreamPayload;
|
|
25
25
|
|
|
26
|
-
const
|
|
26
|
+
const openaiOrErrResponse = createBizOpenAI(req, payload.model);
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
// if resOrOpenAI is a Response, it means there is an error,just return it
|
|
29
|
+
if (openaiOrErrResponse instanceof Response) return openaiOrErrResponse;
|
|
29
30
|
|
|
30
|
-
return createChatCompletion({ openai, payload });
|
|
31
|
+
return createChatCompletion({ openai: openaiOrErrResponse, payload });
|
|
31
32
|
};
|
|
32
33
|
```
|
|
33
34
|
|
|
34
35
|
### 会话结果处理
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
而在 `src/app/api/openai/chat/createChatCompletion.ts` 文件中,`createChatCompletion` 方法主要负责与 OpenAI API 进行交互,处理会话请求。它首先对 payload 中的消息进行预处理,然后通过 `openai.chat.completions.create` 方法发送 API 请求,并使用 `OpenAIStream` 将返回的响应转换为流式格式。如果在 API 调用过程中出现错误,方法将生成并处理相应的错误响应。
|
|
37
38
|
|
|
38
39
|
```ts
|
|
39
40
|
import { OpenAIStream, StreamingTextResponse } from 'ai';
|
|
40
41
|
|
|
41
42
|
export const createChatCompletion = async ({ payload, openai }: CreateChatCompletionOptions) => {
|
|
43
|
+
// 预处理消息
|
|
42
44
|
const { messages, ...params } = payload;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
45
|
+
// 发送 API 请求
|
|
46
|
+
try {
|
|
47
|
+
const response = await openai.chat.completions.create(
|
|
48
|
+
{
|
|
49
|
+
messages,
|
|
50
|
+
...params,
|
|
51
|
+
stream: true,
|
|
52
|
+
} as unknown as OpenAI.ChatCompletionCreateParamsStreaming,
|
|
53
|
+
{ headers: { Accept: '*/*' } },
|
|
54
|
+
);
|
|
55
|
+
const stream = OpenAIStream(response);
|
|
56
|
+
return new StreamingTextResponse(stream);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
// 检查错误是否为 OpenAI APIError
|
|
59
|
+
if (error instanceof OpenAI.APIError) {
|
|
60
|
+
let errorResult: any;
|
|
61
|
+
// 如果错误是 OpenAI APIError,那么会有一个 error 对象
|
|
62
|
+
if (error.error) {
|
|
63
|
+
errorResult = error.error;
|
|
64
|
+
} else if (error.cause) {
|
|
65
|
+
errorResult = error.cause;
|
|
66
|
+
}
|
|
67
|
+
// 如果没有其他请求错误,错误对象是一个类似 Response 的对象
|
|
68
|
+
else {
|
|
69
|
+
errorResult = { headers: error.headers, stack: error.stack, status: error.status };
|
|
70
|
+
}
|
|
71
|
+
console.error(errorResult);
|
|
72
|
+
// 返回错误响应
|
|
73
|
+
return createErrorResponse(ChatErrorType.OpenAIBizError, {
|
|
74
|
+
endpoint: openai.baseURL,
|
|
75
|
+
error: errorResult,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
console.error(error);
|
|
79
|
+
return createErrorResponse(ChatErrorType.InternalServerError, {
|
|
80
|
+
endpoint: openai.baseURL,
|
|
81
|
+
error: JSON.stringify(error),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
60
84
|
};
|
|
61
85
|
```
|
|
62
86
|
|
|
@@ -64,35 +88,45 @@ export const createChatCompletion = async ({ payload, openai }: CreateChatComple
|
|
|
64
88
|
|
|
65
89
|
### 前端集成
|
|
66
90
|
|
|
67
|
-
在 `src/services/
|
|
91
|
+
在 `src/services/chat.ts` 文件中,我们定义了 `ChatService` 类。这个类提供了一些方法来处理与 OpenAI 聊天 API 的交互。
|
|
92
|
+
|
|
93
|
+
`createAssistantMessage` 方法用于创建一个新的助手消息。它接收一个包含插件、消息和其他参数的对象,以及一个可选的 `FetchOptions` 对象。这个方法会合并默认的代理配置和传入的参数,预处理消息和工具,然后调用 `getChatCompletion` 方法获取聊天完成任务。
|
|
94
|
+
|
|
95
|
+
`getChatCompletion` 方法用于获取聊天完成任务。它接收一个 `OpenAIChatStreamPayload` 对象和一个可选的 `FetchOptions` 对象。这个方法会合并默认的代理配置和传入的参数,然后发送 POST 请求到 OpenAI 的聊天 API。
|
|
96
|
+
|
|
97
|
+
`runPluginApi` 方法用于运行插件 API 并获取结果。它接收一个 `PluginRequestPayload` 对象和一个可选的 `FetchOptions` 对象。这个方法会从工具存储中获取状态,通过插件标识符获取插件设置和清单,然后发送 POST 请求到插件的网关 URL。
|
|
98
|
+
|
|
99
|
+
`fetchPresetTaskResult` 方法用于获取预设任务的结果。它使用 `fetchAIFactory` 工厂函数创建一个新的函数,这个函数接收一个聊天完成任务的参数,并返回一个 Promise。当 Promise 解析时,返回的结果是聊天完成任务的结果。
|
|
100
|
+
|
|
101
|
+
`processMessages` 方法用于处理聊天消息。它接收一个聊天消息数组,一个可选的模型名称,和一个可选的工具数组。这个方法会处理消息内容,将输入的 `messages` 数组映射为 `OpenAIChatMessage` 类型的数组,如果存在启用的工具,将工具的系统角色添加到系统消息中。
|
|
68
102
|
|
|
69
103
|
```ts
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
}
|
|
104
|
+
class ChatService {
|
|
105
|
+
// 创建一个新的助手消息
|
|
106
|
+
createAssistantMessage(params: object, fetchOptions?: FetchOptions) {
|
|
107
|
+
// 实现细节...
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 获取聊天完成任务
|
|
111
|
+
getChatCompletion(payload: OpenAIChatStreamPayload, fetchOptions?: FetchOptions) {
|
|
112
|
+
// 实现细节...
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 运行插件 API 并获取结果
|
|
116
|
+
runPluginApi(payload: PluginRequestPayload, fetchOptions?: FetchOptions) {
|
|
117
|
+
// 实现细节...
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 获取预设任务的结果
|
|
121
|
+
fetchPresetTaskResult() {
|
|
122
|
+
// 实现细节...
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 处理聊天消息
|
|
126
|
+
processMessages(messages: ChatMessage[], modelName?: string, tools?: Tool[]) {
|
|
127
|
+
// 实现细节...
|
|
128
|
+
}
|
|
129
|
+
}
|
|
96
130
|
```
|
|
97
131
|
|
|
98
132
|
### 使用流式获取结果
|
|
@@ -103,6 +137,7 @@ export const fetchChatModel = (
|
|
|
103
137
|
export const fetchSSE = async (fetchFn: () => Promise<Response>, options: FetchSSEOptions = {}) => {
|
|
104
138
|
const response = await fetchFn();
|
|
105
139
|
|
|
140
|
+
// 如果不 ok 说明有请求错误
|
|
106
141
|
if (!response.ok) {
|
|
107
142
|
const chatMessageError = await getMessageError(response);
|
|
108
143
|
|
|
@@ -115,7 +150,7 @@ export const fetchSSE = async (fetchFn: () => Promise<Response>, options: FetchS
|
|
|
115
150
|
const data = response.body;
|
|
116
151
|
|
|
117
152
|
if (!data) return;
|
|
118
|
-
|
|
153
|
+
let output = '';
|
|
119
154
|
const reader = data.getReader();
|
|
120
155
|
const decoder = new TextDecoder();
|
|
121
156
|
|
|
@@ -124,11 +159,14 @@ export const fetchSSE = async (fetchFn: () => Promise<Response>, options: FetchS
|
|
|
124
159
|
while (!done) {
|
|
125
160
|
const { value, done: doneReading } = await reader.read();
|
|
126
161
|
done = doneReading;
|
|
127
|
-
const chunkValue = decoder.decode(value);
|
|
162
|
+
const chunkValue = decoder.decode(value, { stream: true });
|
|
128
163
|
|
|
164
|
+
output += chunkValue;
|
|
129
165
|
options.onMessageHandle?.(chunkValue);
|
|
130
166
|
}
|
|
131
167
|
|
|
168
|
+
await options?.onFinish?.(output);
|
|
169
|
+
|
|
132
170
|
return returnRes;
|
|
133
171
|
};
|
|
134
172
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.118.
|
|
3
|
+
"version": "0.118.5",
|
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { DalleManifest } from '@/tools/dalle';
|
|
5
|
+
import { BuiltinToolsRenders } from '@/tools/renders';
|
|
6
|
+
|
|
7
|
+
import BuiltinType from './index';
|
|
8
|
+
|
|
9
|
+
// Mock Render component and useParseContent hook
|
|
10
|
+
vi.mock('@/tools/renders', () => ({
|
|
11
|
+
BuiltinToolsRenders: {
|
|
12
|
+
dalle3: vi.fn(() => <div>Test Renderer</div>),
|
|
13
|
+
[DalleManifest.identifier]: vi.fn(() => <div>{DalleManifest.identifier}</div>),
|
|
14
|
+
},
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
// Mock Loading component
|
|
18
|
+
vi.mock('../Loading', () => ({
|
|
19
|
+
default: vi.fn(() => <div>Loading...</div>),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
describe('BuiltinType', () => {
|
|
23
|
+
it('should render loading state if not JSON and loading is true', () => {
|
|
24
|
+
render(<BuiltinType content="..." id="123" loading={true} />);
|
|
25
|
+
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should not render anything if not JSON and loading is false', () => {
|
|
29
|
+
const { container } = render(<BuiltinType content="..." id="123" loading={false} />);
|
|
30
|
+
expect(container).toBeEmptyDOMElement();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should not render anything if identifier is unknown', () => {
|
|
34
|
+
const { container } = render(<BuiltinType content="{}" id="123" identifier="unknown" />);
|
|
35
|
+
expect(container).toBeEmptyDOMElement();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('DALL·E', () => {
|
|
39
|
+
it('should render the correct renderer if identifier is dalle3', () => {
|
|
40
|
+
render(<BuiltinType content='{"some":"data"}' id="123" identifier="dalle3" />);
|
|
41
|
+
expect(BuiltinToolsRenders.dalle3).toHaveBeenCalled();
|
|
42
|
+
expect(screen.getByText('Test Renderer')).toBeInTheDocument();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should render the correct renderer if is DALL·E ', () => {
|
|
46
|
+
render(
|
|
47
|
+
<BuiltinType content='{"some":"data"}' id="123" identifier={DalleManifest.identifier} />,
|
|
48
|
+
);
|
|
49
|
+
expect(BuiltinToolsRenders.dalle3).toHaveBeenCalled();
|
|
50
|
+
expect(screen.getByText(DalleManifest.identifier)).toBeInTheDocument();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
});
|
package/src/hooks/useIsPWA.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
|
+
import { isInStandaloneMode } from '@/utils/matchMedia';
|
|
4
|
+
|
|
3
5
|
export const useIsPWA = () => {
|
|
4
6
|
const [isPWA, setIsPWA] = useState(false);
|
|
5
7
|
|
|
6
8
|
useEffect(() => {
|
|
7
|
-
|
|
8
|
-
if (isInStandaloneMode()) {
|
|
9
|
-
setIsPWA(true);
|
|
10
|
-
}
|
|
9
|
+
setIsPWA(isInStandaloneMode());
|
|
11
10
|
}, []);
|
|
12
11
|
|
|
13
12
|
return isPWA;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`ChatService > createAssistantMessage > with tools messages > work with dalle3 1`] = `
|
|
4
|
+
{
|
|
5
|
+
"messages": [
|
|
6
|
+
{
|
|
7
|
+
"content": "## Tools
|
|
8
|
+
|
|
9
|
+
You can use these tools below:
|
|
10
|
+
|
|
11
|
+
### DALL·E 3
|
|
12
|
+
|
|
13
|
+
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.
|
|
14
|
+
|
|
15
|
+
All captions sent to lobe-image-designer must abide by the following policies:
|
|
16
|
+
|
|
17
|
+
1. If the description is not in English, then translate it.
|
|
18
|
+
2. Do not create more than 4 images, even if the user requests more.
|
|
19
|
+
3. Don't create images of politicians or other public figures. Recommend other ideas instead.
|
|
20
|
+
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!
|
|
21
|
+
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.
|
|
22
|
+
7. Diversify depictions of ALL images with people to include DESCENT and GENDER for EACH person using direct terms. Adjust only human descriptions.
|
|
23
|
+
- EXPLICITLY specify these attributes, not abstractly reference them. The attributes should be specified in a minimal way and should directly describe their physical form.
|
|
24
|
+
- 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.
|
|
25
|
+
- 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.
|
|
26
|
+
- Don't alter memes, fictional character origins, or unseen people. Maintain the original prompt's intent and prioritize quality.
|
|
27
|
+
- Do not create any imagery that would be offensive.
|
|
28
|
+
|
|
29
|
+
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:
|
|
30
|
+
- Modify such prompts even if you don't know who the person is, or if their name is misspelled (e.g. "Barake Obema")
|
|
31
|
+
- 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.
|
|
32
|
+
- 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" or "Dalai Lama", say "religious figure"; and so on.
|
|
33
|
+
- 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.
|
|
34
|
+
|
|
35
|
+
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.
|
|
36
|
+
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.
|
|
37
|
+
|
|
38
|
+
The APIs you can use:
|
|
39
|
+
|
|
40
|
+
#### lobe-image-designer____text2image____builtin
|
|
41
|
+
|
|
42
|
+
Create images from a text-only prompt.",
|
|
43
|
+
"role": "system",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"content": "https://vercel.com/ 请分析 chatGPT 关键词
|
|
47
|
+
|
|
48
|
+
",
|
|
49
|
+
"role": "user",
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
"model": "gpt-3.5-turbo-1106",
|
|
53
|
+
"tools": [
|
|
54
|
+
{
|
|
55
|
+
"function": {
|
|
56
|
+
"description": "Create images from a text-only prompt.",
|
|
57
|
+
"name": "lobe-image-designer____text2image____builtin",
|
|
58
|
+
"parameters": {
|
|
59
|
+
"properties": {
|
|
60
|
+
"prompts": {
|
|
61
|
+
"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.",
|
|
62
|
+
"items": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
},
|
|
65
|
+
"maxItems": 4,
|
|
66
|
+
"minItems": 1,
|
|
67
|
+
"type": "array",
|
|
68
|
+
},
|
|
69
|
+
"quality": {
|
|
70
|
+
"default": "standard",
|
|
71
|
+
"description": "The quality of the image that will be generated. hd creates images with finer details and greater consistency across the image.",
|
|
72
|
+
"enum": [
|
|
73
|
+
"standard",
|
|
74
|
+
"hd",
|
|
75
|
+
],
|
|
76
|
+
"type": "string",
|
|
77
|
+
},
|
|
78
|
+
"seeds": {
|
|
79
|
+
"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.",
|
|
80
|
+
"items": {
|
|
81
|
+
"type": "integer",
|
|
82
|
+
},
|
|
83
|
+
"type": "array",
|
|
84
|
+
},
|
|
85
|
+
"size": {
|
|
86
|
+
"default": "1024x1024",
|
|
87
|
+
"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.",
|
|
88
|
+
"enum": [
|
|
89
|
+
"1792x1024",
|
|
90
|
+
"1024x1024",
|
|
91
|
+
"1024x1792",
|
|
92
|
+
],
|
|
93
|
+
"type": "string",
|
|
94
|
+
},
|
|
95
|
+
"style": {
|
|
96
|
+
"default": "vivid",
|
|
97
|
+
"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.",
|
|
98
|
+
"enum": [
|
|
99
|
+
"vivid",
|
|
100
|
+
"natural",
|
|
101
|
+
],
|
|
102
|
+
"type": "string",
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
"required": [
|
|
106
|
+
"prompts",
|
|
107
|
+
],
|
|
108
|
+
"type": "object",
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
"type": "function",
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
"top_p": 1,
|
|
115
|
+
}
|
|
116
|
+
`;
|
|
@@ -5,6 +5,7 @@ import { describe, expect, it, vi } from 'vitest';
|
|
|
5
5
|
import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
|
|
6
6
|
import { useFileStore } from '@/store/file';
|
|
7
7
|
import { useToolStore } from '@/store/tool';
|
|
8
|
+
import { DalleManifest } from '@/tools/dalle';
|
|
8
9
|
import { ChatMessage } from '@/types/message';
|
|
9
10
|
import { OpenAIChatStreamPayload } from '@/types/openai/chat';
|
|
10
11
|
import { LobeTool } from '@/types/tool';
|
|
@@ -514,104 +515,16 @@ Get data from users`,
|
|
|
514
515
|
messages,
|
|
515
516
|
model: 'gpt-3.5-turbo-1106',
|
|
516
517
|
top_p: 1,
|
|
517
|
-
plugins: [
|
|
518
|
+
plugins: [DalleManifest.identifier],
|
|
518
519
|
});
|
|
519
520
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
model: 'gpt-3.5-turbo-1106',
|
|
523
|
-
top_p: 1,
|
|
524
|
-
tools: [
|
|
525
|
-
{
|
|
526
|
-
type: 'function',
|
|
527
|
-
function: {
|
|
528
|
-
description: 'Create images from a text-only prompt.',
|
|
529
|
-
name: 'dalle3____text2image____builtin',
|
|
530
|
-
parameters: {
|
|
531
|
-
properties: {
|
|
532
|
-
prompts: {
|
|
533
|
-
description:
|
|
534
|
-
"The user's original image description, potentially modified to abide by the dalle 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.",
|
|
535
|
-
items: { type: 'string' },
|
|
536
|
-
maxItems: 4,
|
|
537
|
-
minItems: 1,
|
|
538
|
-
type: 'array',
|
|
539
|
-
},
|
|
540
|
-
quality: {
|
|
541
|
-
default: 'standard',
|
|
542
|
-
description:
|
|
543
|
-
'The quality of the image that will be generated. hd creates images with finer details and greater consistency across the image.',
|
|
544
|
-
enum: ['standard', 'hd'],
|
|
545
|
-
type: 'string',
|
|
546
|
-
},
|
|
547
|
-
seeds: {
|
|
548
|
-
description:
|
|
549
|
-
'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 dalle metadata.',
|
|
550
|
-
items: { type: 'integer' },
|
|
551
|
-
type: 'array',
|
|
552
|
-
},
|
|
553
|
-
size: {
|
|
554
|
-
default: '1024x1024',
|
|
555
|
-
description:
|
|
556
|
-
'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.',
|
|
557
|
-
enum: ['1792x1024', '1024x1024', '1024x1792'],
|
|
558
|
-
type: 'string',
|
|
559
|
-
},
|
|
560
|
-
style: {
|
|
561
|
-
default: 'vivid',
|
|
562
|
-
description:
|
|
563
|
-
'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.',
|
|
564
|
-
enum: ['vivid', 'natural'],
|
|
565
|
-
type: 'string',
|
|
566
|
-
},
|
|
567
|
-
},
|
|
568
|
-
required: ['prompts'],
|
|
569
|
-
type: 'object',
|
|
570
|
-
},
|
|
571
|
-
},
|
|
572
|
-
},
|
|
573
|
-
],
|
|
574
|
-
messages: [
|
|
575
|
-
{
|
|
576
|
-
content: `## Tools
|
|
577
|
-
|
|
578
|
-
You can use these tools below:
|
|
579
|
-
|
|
580
|
-
### Dall·E 3
|
|
581
|
-
|
|
582
|
-
Whenever a description of an image is given, use dalle 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 dalle that are written to be as diverse as possible. All captions sent to dalle must abide by the following policies:
|
|
583
|
-
1. If the description is not in English, then translate it.
|
|
584
|
-
2. Do not create more than 4 images, even if the user requests more.
|
|
585
|
-
3. Don't create images of politicians or other public figures. Recommend other ideas instead.
|
|
586
|
-
4. Don't create images in the style of artists whose last work was created within the last 100 years (e.g. Picasso, Kahlo). Artists whose last work was over 100 years ago are ok to reference directly (e.g. Van Gogh, Klimt). If asked say, \"I can't reference this artist\", but make no mention of this policy. Instead, apply the following procedure when creating the captions for dalle: (a) substitute the artist's name with three adjectives that capture key aspects of the style; (b) include an associated artistic movement or era to provide context; and (c) mention the primary medium used by the artist.
|
|
587
|
-
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!
|
|
588
|
-
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.
|
|
589
|
-
7. Diversify depictions of ALL images with people to include DESCENT and GENDER for EACH person using direct terms. Adjust only human descriptions.
|
|
590
|
-
- EXPLICITLY specify these attributes, not abstractly reference them. The attributes should be specified in a minimal way and should directly describe their physical form.
|
|
591
|
-
- 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.
|
|
592
|
-
- 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.
|
|
593
|
-
- Don't alter memes, fictional character origins, or unseen people. Maintain the original prompt's intent and prioritize quality.
|
|
594
|
-
- Do not create any imagery that would be offensive.
|
|
595
|
-
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:
|
|
596
|
-
- Modify such prompts even if you don't know who the person is, or if their name is misspelled (e.g. \"Barake Obema\")
|
|
597
|
-
- 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.
|
|
598
|
-
- 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\" or \"Dalai Lama\", say \"religious figure\"; and so on.
|
|
599
|
-
- 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.
|
|
600
|
-
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.
|
|
601
|
-
All descriptions sent to dalle should be a paragraph of text that is extremely descriptive and detailed. Each should be more than 3 sentences long.
|
|
602
|
-
|
|
603
|
-
The APIs you can use:
|
|
604
|
-
|
|
605
|
-
#### dalle3____text2image____builtin
|
|
521
|
+
// Assert that getChatCompletionSpy was called with the expected arguments
|
|
522
|
+
expect(getChatCompletionSpy).toHaveBeenCalled();
|
|
606
523
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
],
|
|
612
|
-
},
|
|
613
|
-
undefined,
|
|
614
|
-
);
|
|
524
|
+
const calls = getChatCompletionSpy.mock.lastCall;
|
|
525
|
+
// Take a snapshot of the first call's first argument
|
|
526
|
+
expect(calls![0]).toMatchSnapshot();
|
|
527
|
+
expect(calls![1]).toBeUndefined();
|
|
615
528
|
});
|
|
616
529
|
});
|
|
617
530
|
});
|
package/src/store/chat/store.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { StateCreator } from 'zustand/vanilla';
|
|
|
5
5
|
|
|
6
6
|
import { createHyperStorage } from '@/store/middleware/createHyperStorage';
|
|
7
7
|
import { isDev } from '@/utils/env';
|
|
8
|
+
import { isInStandaloneMode } from '@/utils/matchMedia';
|
|
9
|
+
import { isMobileScreen } from '@/utils/screen';
|
|
8
10
|
|
|
9
11
|
import { ChatStoreState, initialState } from './initialState';
|
|
10
12
|
import { ChatEnhanceAction, chatEnhance } from './slices/enchance/action';
|
|
@@ -40,12 +42,24 @@ const persistOptions: PersistOptions<ChatStore> = {
|
|
|
40
42
|
// 手动控制 Hydration ,避免 ssr 报错
|
|
41
43
|
skipHydration: true,
|
|
42
44
|
|
|
43
|
-
storage: createHyperStorage({
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
storage: createHyperStorage(() => {
|
|
46
|
+
const isMobileAndPWA = isMobileScreen() && isInStandaloneMode();
|
|
47
|
+
const selectors = [{ activeTopicId: 'topic' }];
|
|
48
|
+
|
|
49
|
+
if (isMobileAndPWA)
|
|
50
|
+
return {
|
|
51
|
+
localStorage: {
|
|
52
|
+
selectors,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
localStorage: false,
|
|
58
|
+
url: {
|
|
59
|
+
mode: 'hash',
|
|
60
|
+
selectors,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
49
63
|
}),
|
|
50
64
|
version: 0,
|
|
51
65
|
};
|
|
@@ -10,26 +10,28 @@ import { creatUrlStorage } from './urlStorage';
|
|
|
10
10
|
export const createHyperStorage = <T extends object>(
|
|
11
11
|
options: HyperStorageOptions,
|
|
12
12
|
): PersistStorage<T> => {
|
|
13
|
+
const optionsObj = typeof options === 'function' ? options() : options;
|
|
14
|
+
|
|
13
15
|
const getLocalStorageConfig = () => {
|
|
14
|
-
if (
|
|
16
|
+
if (optionsObj.localStorage === false) {
|
|
15
17
|
return { dbName: '', skipLocalStorage: true, useIndexedDB: false };
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
const useIndexedDB =
|
|
19
|
-
const dbName =
|
|
20
|
+
const useIndexedDB = optionsObj.localStorage?.mode === 'indexedDB';
|
|
21
|
+
const dbName = optionsObj.localStorage?.dbName || 'indexedDB';
|
|
20
22
|
|
|
21
23
|
return { dbName, skipLocalStorage: false, useIndexedDB };
|
|
22
24
|
};
|
|
23
25
|
|
|
24
|
-
const hasUrl = !!
|
|
26
|
+
const hasUrl = !!optionsObj.url;
|
|
25
27
|
|
|
26
28
|
const { skipLocalStorage, useIndexedDB, dbName } = getLocalStorageConfig();
|
|
27
29
|
|
|
28
|
-
const { mapStateKeyToStorageKey, getStateKeyFromStorageKey } = createKeyMapper(
|
|
30
|
+
const { mapStateKeyToStorageKey, getStateKeyFromStorageKey } = createKeyMapper(optionsObj);
|
|
29
31
|
|
|
30
32
|
const indexedDB = createIndexedDB(dbName);
|
|
31
33
|
const localStorage = createLocalStorage();
|
|
32
|
-
const urlStorage = creatUrlStorage(
|
|
34
|
+
const urlStorage = creatUrlStorage(optionsObj.url?.mode);
|
|
33
35
|
return {
|
|
34
36
|
getItem: async (name): Promise<StorageValue<T>> => {
|
|
35
37
|
const state: any = {};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HyperStorageOptionsObj } from './type';
|
|
2
2
|
|
|
3
|
-
export const createKeyMapper = (options:
|
|
3
|
+
export const createKeyMapper = (options: HyperStorageOptionsObj) => {
|
|
4
4
|
const mapStateKeyToStorageKey = (
|
|
5
5
|
key: string,
|
|
6
|
-
mode: keyof
|
|
6
|
+
mode: keyof HyperStorageOptionsObj = 'localStorage',
|
|
7
7
|
) => {
|
|
8
8
|
const media = options[mode];
|
|
9
9
|
if (media === false) return key;
|
|
@@ -26,7 +26,7 @@ export const createKeyMapper = (options: HyperStorageOptions) => {
|
|
|
26
26
|
|
|
27
27
|
const getStateKeyFromStorageKey = (
|
|
28
28
|
key: string,
|
|
29
|
-
mode: keyof
|
|
29
|
+
mode: keyof HyperStorageOptionsObj = 'localStorage',
|
|
30
30
|
) => {
|
|
31
31
|
const media = options[mode];
|
|
32
32
|
if (media === false) return key;
|
|
@@ -9,7 +9,7 @@ export interface LocalStorageOptions {
|
|
|
9
9
|
selectors: StorageSelector[];
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export
|
|
12
|
+
export type HyperStorageOptionsObj = {
|
|
13
13
|
localStorage?: LocalStorageOptions | false;
|
|
14
14
|
url?: {
|
|
15
15
|
/**
|
|
@@ -18,4 +18,8 @@ export interface HyperStorageOptions {
|
|
|
18
18
|
mode?: 'hash' | 'search';
|
|
19
19
|
selectors: StorageSelector[];
|
|
20
20
|
};
|
|
21
|
-
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type HyperStorageOptionsFn = () => HyperStorageOptionsObj;
|
|
24
|
+
|
|
25
|
+
export type HyperStorageOptions = HyperStorageOptionsObj | HyperStorageOptionsFn;
|
|
@@ -4,6 +4,8 @@ import { createWithEqualityFn } from 'zustand/traditional';
|
|
|
4
4
|
import { StateCreator } from 'zustand/vanilla';
|
|
5
5
|
|
|
6
6
|
import { isDev } from '@/utils/env';
|
|
7
|
+
import { isInStandaloneMode } from '@/utils/matchMedia';
|
|
8
|
+
import { isMobileScreen } from '@/utils/screen';
|
|
7
9
|
|
|
8
10
|
import { createHyperStorage } from '../middleware/createHyperStorage';
|
|
9
11
|
import { SessionStoreState, initialState } from './initialState';
|
|
@@ -27,15 +29,28 @@ const persistOptions: PersistOptions<SessionStore> = {
|
|
|
27
29
|
// 手动控制 Hydration ,避免 ssr 报错
|
|
28
30
|
skipHydration: true,
|
|
29
31
|
|
|
30
|
-
storage: createHyperStorage({
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
storage: createHyperStorage(() => {
|
|
33
|
+
const isMobileAndPWA = isMobileScreen() && isInStandaloneMode();
|
|
34
|
+
|
|
35
|
+
const selectors = [
|
|
36
|
+
// map state key to storage key
|
|
37
|
+
{ activeId: 'session' },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
if (isMobileAndPWA)
|
|
41
|
+
return {
|
|
42
|
+
localStorage: {
|
|
43
|
+
selectors,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
localStorage: false,
|
|
49
|
+
url: {
|
|
50
|
+
mode: 'hash',
|
|
51
|
+
selectors,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
39
54
|
}),
|
|
40
55
|
|
|
41
56
|
version: 2,
|
package/src/tools/dalle/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { BuiltinToolManifest } from '@/types/tool';
|
|
2
2
|
|
|
3
|
+
// import {SiOpenai} from "@icons-pack/react-simple-icons";
|
|
4
|
+
|
|
3
5
|
export const DalleManifest: BuiltinToolManifest = {
|
|
4
6
|
api: [
|
|
5
7
|
{
|
|
@@ -9,7 +11,7 @@ export const DalleManifest: BuiltinToolManifest = {
|
|
|
9
11
|
properties: {
|
|
10
12
|
prompts: {
|
|
11
13
|
description:
|
|
12
|
-
"The user's original image description, potentially modified to abide by the
|
|
14
|
+
"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.",
|
|
13
15
|
items: {
|
|
14
16
|
type: 'string',
|
|
15
17
|
},
|
|
@@ -26,7 +28,7 @@ export const DalleManifest: BuiltinToolManifest = {
|
|
|
26
28
|
},
|
|
27
29
|
seeds: {
|
|
28
30
|
description:
|
|
29
|
-
'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
|
|
31
|
+
'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.',
|
|
30
32
|
items: {
|
|
31
33
|
type: 'integer',
|
|
32
34
|
},
|
|
@@ -52,30 +54,39 @@ export const DalleManifest: BuiltinToolManifest = {
|
|
|
52
54
|
},
|
|
53
55
|
},
|
|
54
56
|
],
|
|
55
|
-
|
|
57
|
+
// due to system prompt is for training Dalle3 as a built-in tool by OpenAI,
|
|
58
|
+
// there are occasional instances where the function call contains the name "dalle," leading to subsequent failures.
|
|
59
|
+
// so we need a different unique identifier to avoid failure.refs:
|
|
60
|
+
// https://github.com/lobehub/lobe-chat/issues/783
|
|
61
|
+
// https://github.com/lobehub/lobe-chat/issues/870
|
|
62
|
+
identifier: 'lobe-image-designer',
|
|
56
63
|
meta: {
|
|
57
|
-
avatar:
|
|
58
|
-
title: '
|
|
64
|
+
avatar: `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NiIgaGVpZ2h0PSI5NiIgZmlsbD0ibm9uZSIgc3R5bGU9ImNvbG9yOiNmZmY7ZmlsbDojMDAwIj4KICAgIDxwYXRoIGQ9Ik0wIDBoOTZ2OTZIMHoiLz4KICAgIDxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIKICAgICAgICAgIGQ9Ik03OC44MyA0MS40NjJBMTcuOTQgMTcuOTQgMCAwIDAgNzcuMjkgMjYuNzNhMTguMTQgMTguMTQgMCAwIDAtMTkuNTM4LTguNzA0IDE3LjkzNyAxNy45MzcgMCAwIDAtMTMuNTI5LTYuMDMxIDE4LjE0MyAxOC4xNDMgMCAwIDAtMTcuMzA1IDEyLjU2IDE3Ljk0NiAxNy45NDYgMCAwIDAtMTEuOTk0IDguNzAxIDE4LjE0MyAxOC4xNDMgMCAwIDAgMi4yMzMgMjEuMjcxIDE3LjkzOCAxNy45MzggMCAwIDAgMS41NCAxNC43MzIgMTguMTQxIDE4LjE0MSAwIDAgMCAxOS41MzggOC43MDQgMTcuOTMgMTcuOTMgMCAwIDAgMTMuNTI4IDYuMDMxQTE4LjE0MiAxOC4xNDIgMCAwIDAgNjkuMDggNzEuNDI3YTE3LjkzOCAxNy45MzggMCAwIDAgMTEuOTk0LTguNzAxIDE4LjE0MSAxOC4xNDEgMCAwIDAtMi4yNDMtMjEuMjY0Wk01MS45MSA3OS4yODdjLTMuNTgzIDAtNi4zNTYtMS4xLTguNzgtMy4xMjIuMTEtLjA2LjMwMi0uMTY1LjQyNy0uMjQybDE0LjMzNi04LjI4YTIuMzI3IDIuMzI3IDAgMCAwIDEuMTc4LTIuMDRWNDUuMzkybDYuMDYgMy40OTlhLjIxNy4yMTcgMCAwIDEgLjExOC4xNjZWNjUuNzljLS4wMDEgNy41OTMtNi4zMjIgMTMuNDk3LTEzLjM0IDEzLjQ5N1pNMjIuNzc4IDY2LjkwNmExMy40NDcgMTMuNDQ3IDAgMCAxLTEuNjEtOS4wNDJjLjEwNy4wNjQuMjkzLjE3OC40MjYuMjU0TDM1LjkzIDY2LjRhMi4zMyAyLjMzIDAgMCAwIDIuMzU1IDBsMTcuNTAzLTEwLjEwNnY2Ljk5OGEuMjE3LjIxNyAwIDAgMS0uMDg3LjE4Nkw0MS4yMSA3MS44NDRhMTMuNTA3IDEzLjUwNyAwIDAgMS0xOC40MzEtNC45MzhabS0zLjc3Ny0zMS4yOTdhMTMuNDQ5IDEzLjQ0OSAwIDAgMSA3LjAyMy01LjkxNlY0Ni43NWEyLjMyNCAyLjMyNCAwIDAgMCAxLjE3NyAyLjAzOGwxNy41MDEgMTAuMTA1LTYuMDYgMy40OTlhLjIxOC4yMTggMCAwIDEtLjIwNC4wMThsLTE0LjQ5My04LjM3NEExMy41MDcgMTMuNTA3IDAgMCAxIDE5IDM1LjYwOVptNDkuNzkgMTEuNTg3TDUxLjI4OCAzNy4wOWw2LjA2LTMuNDk4YS4yMi4yMiAwIDAgMSAuMjA0LS4wMThsMTQuNDk0IDguMzdhMTMuNDk1IDEzLjQ5NSAwIDAgMS0yLjA4OCAyNC4zNDZWNDkuMjMzYTIuMzI4IDIuMzI4IDAgMCAwLTEuMTY3LTIuMDM3Wm02LjAzLTkuMDg0YTIwLjA2NCAyMC4wNjQgMCAwIDAtLjQyNS0uMjU0TDYwLjA2IDI5LjU3N2EyLjMzNyAyLjMzNyAwIDAgMC0yLjM1NSAwTDQwLjIwMyAzOS42ODN2LTYuOTk4YS4yMTguMjE4IDAgMCAxIC4wODYtLjE4NmwxNC40OTItOC4zNmExMy40OTUgMTMuNDk1IDAgMCAxIDIwLjA0IDEzLjk3M1pNMzYuOTA5IDUwLjU5bC02LjA2LTMuNDk5YS4yMS4yMSAwIDAgMS0uMTE4LS4xNjZWMzAuMTg3YTEzLjQ5NCAxMy40OTQgMCAwIDEgMjIuMTI4LTEwLjM2MWMtLjExLjA2LS4zLjE2NC0uNDI2LjI0bC0xNC4zMzYgOC4yODJhMi4zMzIgMi4zMzIgMCAwIDAtMS4xNzggMi4wMzhsLS4wMSAyMC4yMDRabTMuMjkyLTcuMDk3IDcuNzk1LTQuNTAzIDcuNzk2IDQuNXY5LjAwMmwtNy43OTYgNC40OTgtNy43OTUtNC41di04Ljk5N1oiLz4KPC9zdmc+Cg==`,
|
|
65
|
+
title: 'DALL·E 3',
|
|
59
66
|
},
|
|
60
|
-
systemRole: `Whenever a description of an image is given, use
|
|
67
|
+
systemRole: `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.
|
|
68
|
+
|
|
69
|
+
All captions sent to lobe-image-designer must abide by the following policies:
|
|
70
|
+
|
|
61
71
|
1. If the description is not in English, then translate it.
|
|
62
72
|
2. Do not create more than 4 images, even if the user requests more.
|
|
63
73
|
3. Don't create images of politicians or other public figures. Recommend other ideas instead.
|
|
64
|
-
|
|
65
|
-
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!
|
|
74
|
+
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!
|
|
66
75
|
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.
|
|
67
76
|
7. Diversify depictions of ALL images with people to include DESCENT and GENDER for EACH person using direct terms. Adjust only human descriptions.
|
|
68
|
-
- EXPLICITLY specify these attributes, not abstractly reference them. The attributes should be specified in a minimal way and should directly describe their physical form.
|
|
69
|
-
- 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.
|
|
70
|
-
- 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.
|
|
71
|
-
- Don't alter memes, fictional character origins, or unseen people. Maintain the original prompt's intent and prioritize quality.
|
|
72
|
-
- Do not create any imagery that would be offensive.
|
|
77
|
+
- EXPLICITLY specify these attributes, not abstractly reference them. The attributes should be specified in a minimal way and should directly describe their physical form.
|
|
78
|
+
- 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.
|
|
79
|
+
- 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.
|
|
80
|
+
- Don't alter memes, fictional character origins, or unseen people. Maintain the original prompt's intent and prioritize quality.
|
|
81
|
+
- Do not create any imagery that would be offensive.
|
|
82
|
+
|
|
73
83
|
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:
|
|
74
|
-
- Modify such prompts even if you don't know who the person is, or if their name is misspelled (e.g. "Barake Obema")
|
|
75
|
-
- 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.
|
|
76
|
-
- 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" or "Dalai Lama", say "religious figure"; and so on.
|
|
77
|
-
- 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.
|
|
84
|
+
- Modify such prompts even if you don't know who the person is, or if their name is misspelled (e.g. "Barake Obema")
|
|
85
|
+
- 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.
|
|
86
|
+
- 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" or "Dalai Lama", say "religious figure"; and so on.
|
|
87
|
+
- 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.
|
|
88
|
+
|
|
78
89
|
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.
|
|
79
|
-
All descriptions sent to
|
|
90
|
+
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.`,
|
|
80
91
|
type: 'builtin',
|
|
81
92
|
};
|
package/src/tools/index.ts
CHANGED
package/src/tools/renders.ts
CHANGED
|
@@ -5,4 +5,10 @@ import DalleRender from './dalle/Render';
|
|
|
5
5
|
|
|
6
6
|
export const BuiltinToolsRenders: Record<string, BuiltinRender> = {
|
|
7
7
|
[DalleManifest.identifier]: DalleRender as BuiltinRender,
|
|
8
|
+
/**
|
|
9
|
+
* 兼容旧版本 dalle3 的 identifier
|
|
10
|
+
* TODO: 后续数据库版本迁移时记得迁移 dalle3 对应的 identifier
|
|
11
|
+
* @deprecated
|
|
12
|
+
*/
|
|
13
|
+
dalle3: DalleRender as BuiltinRender,
|
|
8
14
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// mobile 设备宽度
|
|
2
|
+
// https://github.com/ant-design/ant-design/blob/master/components/theme/util/alias.ts#L28
|
|
3
|
+
export const screenSM = 576;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* check mobile device in browser
|
|
7
|
+
*/
|
|
8
|
+
export const isMobileScreen = () => {
|
|
9
|
+
if (typeof window === 'undefined') {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return window.innerWidth <= screenSM;
|
|
14
|
+
};
|