@lobehub/chat 1.141.6 → 1.141.8
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/PULL_REQUEST_TEMPLATE.md +26 -0
- package/.github/workflows/e2e.yml +6 -6
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/docs/usage/features/{group-chat.mdx → agent-team.mdx} +14 -14
- package/docs/usage/features/agent-team.zh-CN.mdx +52 -0
- package/e2e/README.md +143 -0
- package/e2e/cucumber.config.js +20 -0
- package/e2e/package.json +24 -0
- package/e2e/src/features/discover/smoke.feature +11 -0
- package/e2e/src/features/routes/core-routes.feature +43 -0
- package/e2e/src/steps/common/navigation.steps.ts +36 -0
- package/e2e/src/steps/discover/smoke.steps.ts +34 -0
- package/e2e/src/steps/hooks.ts +69 -0
- package/e2e/src/steps/routes/routes.steps.ts +41 -0
- package/e2e/src/support/webServer.ts +96 -0
- package/e2e/src/support/world.ts +76 -0
- package/e2e/tsconfig.json +19 -0
- package/locales/ar/chat.json +17 -17
- package/locales/ar/setting.json +15 -19
- package/locales/ar/welcome.json +1 -1
- package/locales/bg-BG/chat.json +17 -17
- package/locales/bg-BG/setting.json +15 -19
- package/locales/de-DE/chat.json +17 -17
- package/locales/de-DE/setting.json +15 -19
- package/locales/de-DE/welcome.json +1 -1
- package/locales/en-US/chat.json +17 -17
- package/locales/en-US/setting.json +15 -19
- package/locales/en-US/welcome.json +1 -1
- package/locales/es-ES/chat.json +17 -17
- package/locales/es-ES/setting.json +15 -19
- package/locales/es-ES/welcome.json +1 -1
- package/locales/fa-IR/chat.json +17 -17
- package/locales/fa-IR/setting.json +15 -19
- package/locales/fa-IR/welcome.json +1 -1
- package/locales/fr-FR/chat.json +16 -16
- package/locales/fr-FR/setting.json +15 -19
- package/locales/fr-FR/welcome.json +1 -1
- package/locales/it-IT/chat.json +17 -17
- package/locales/it-IT/setting.json +15 -19
- package/locales/it-IT/welcome.json +1 -1
- package/locales/ja-JP/chat.json +17 -17
- package/locales/ja-JP/setting.json +15 -19
- package/locales/ja-JP/welcome.json +1 -1
- package/locales/ko-KR/chat.json +17 -17
- package/locales/ko-KR/setting.json +15 -19
- package/locales/ko-KR/welcome.json +1 -1
- package/locales/nl-NL/chat.json +17 -17
- package/locales/nl-NL/setting.json +15 -19
- package/locales/nl-NL/welcome.json +1 -1
- package/locales/pl-PL/chat.json +17 -17
- package/locales/pl-PL/setting.json +15 -19
- package/locales/pt-BR/chat.json +17 -17
- package/locales/pt-BR/setting.json +15 -19
- package/locales/pt-BR/welcome.json +1 -1
- package/locales/ru-RU/chat.json +17 -17
- package/locales/ru-RU/setting.json +15 -19
- package/locales/ru-RU/welcome.json +1 -1
- package/locales/tr-TR/chat.json +17 -17
- package/locales/tr-TR/setting.json +15 -19
- package/locales/vi-VN/chat.json +15 -15
- package/locales/vi-VN/setting.json +15 -19
- package/locales/zh-CN/chat.json +17 -17
- package/locales/zh-CN/setting.json +15 -19
- package/locales/zh-CN/welcome.json +1 -1
- package/locales/zh-TW/chat.json +17 -17
- package/locales/zh-TW/setting.json +15 -19
- package/locales/zh-TW/welcome.json +1 -1
- package/package.json +6 -3
- package/packages/const/src/layoutTokens.ts +1 -1
- package/packages/const/src/settings/systemAgent.ts +0 -1
- package/packages/database/src/models/__tests__/session.test.ts +108 -0
- package/packages/database/src/models/session.ts +41 -1
- package/packages/model-bank/src/aiModels/groq.ts +0 -17
- package/packages/model-bank/src/aiModels/novita.ts +2 -60
- package/packages/model-bank/src/aiModels/siliconcloud.ts +116 -17
- package/packages/types/src/user/settings/systemAgent.ts +0 -1
- package/pnpm-workspace.yaml +1 -0
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/OrchestratorThinking.tsx +2 -3
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/index.tsx +2 -2
- package/src/app/[variants]/(main)/chat/(workspace)/@topic/features/GroupConfig/GroupMember.tsx +34 -2
- package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Main.tsx +1 -1
- package/src/app/[variants]/(main)/chat/(workspace)/features/{GroupChatSettings → AgentTeamSettings}/index.tsx +4 -5
- package/src/app/[variants]/(main)/chat/(workspace)/features/SettingButton.tsx +2 -2
- package/src/app/[variants]/(main)/chat/@session/_layout/Desktop/SessionHeader.tsx +2 -0
- package/src/app/[variants]/(main)/chat/@session/features/SessionListContent/CollapseGroup/Actions.tsx +18 -1
- package/src/app/[variants]/(main)/discover/(list)/assistant/features/List/Item.tsx +1 -0
- package/src/app/[variants]/(main)/discover/DiscoverRouter.tsx +12 -10
- package/src/app/[variants]/(main)/discover/[[...path]]/page.tsx +7 -6
- package/src/app/[variants]/(main)/discover/features/Search.tsx +1 -0
- package/src/components/ChatGroupWizard/ChatGroupWizard.tsx +33 -5
- package/src/components/Loading/index.ts +1 -0
- package/src/components/MemberSelectionModal/MemberSelectionModal.tsx +170 -26
- package/src/features/AgentSetting/AgentModal/index.tsx +262 -35
- package/src/features/ChatInput/ActionBar/Params/Controls.tsx +261 -50
- package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +7 -2
- package/src/features/Conversation/Messages/User/Actions.tsx +8 -2
- package/src/features/GroupChatSettings/{ChatGroupSettings.tsx → AgentTeamChatSettings.tsx} +6 -5
- package/src/features/GroupChatSettings/{GroupMembers.tsx → AgentTeamMembersSettings.tsx} +64 -19
- package/src/features/GroupChatSettings/{ChatGroupMeta.tsx → AgentTeamMetaSettings.tsx} +2 -2
- package/src/features/GroupChatSettings/AgentTeamSettings.tsx +54 -0
- package/src/features/GroupChatSettings/index.ts +4 -5
- package/src/features/ModelParamsControl/FrequencyPenalty.tsx +8 -3
- package/src/features/ModelParamsControl/PresencePenalty.tsx +8 -3
- package/src/features/ModelParamsControl/Temperature.tsx +8 -5
- package/src/features/ModelParamsControl/TopP.tsx +8 -3
- package/src/locales/default/chat.ts +17 -17
- package/src/locales/default/setting.ts +15 -19
- package/src/locales/default/welcome.ts +1 -1
- package/src/services/chat/index.ts +6 -0
- package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +2 -1
- package/src/store/chatGroup/action.ts +36 -1
- package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +0 -4
- package/src/store/user/slices/settings/selectors/systemAgent.ts +0 -2
- package/docs/usage/features/group-chat.zh-CN.mdx +0 -52
- package/e2e/routes.spec.ts +0 -73
- package/playwright.config.ts +0 -35
- package/src/features/GroupChatSettings/GroupSettings.tsx +0 -30
- package/src/features/GroupChatSettings/GroupSettingsContent.tsx +0 -24
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Form, type FormItemProps, Tag } from '@lobehub/ui';
|
|
2
|
+
import { Form as AntdForm, Checkbox } from 'antd';
|
|
3
|
+
import { createStyles } from 'antd-style';
|
|
2
4
|
import isEqual from 'fast-deep-equal';
|
|
3
5
|
import { debounce } from 'lodash-es';
|
|
4
|
-
import { memo } from 'react';
|
|
6
|
+
import { memo, useCallback, useEffect, useRef } from 'react';
|
|
7
|
+
import type { ComponentType } from 'react';
|
|
5
8
|
import { useTranslation } from 'react-i18next';
|
|
6
9
|
import { Flexbox } from 'react-layout-kit';
|
|
7
10
|
|
|
@@ -20,75 +23,283 @@ interface ControlsProps {
|
|
|
20
23
|
setUpdating: (updating: boolean) => void;
|
|
21
24
|
updating: boolean;
|
|
22
25
|
}
|
|
26
|
+
|
|
27
|
+
type ParamKey = 'temperature' | 'top_p' | 'presence_penalty' | 'frequency_penalty';
|
|
28
|
+
|
|
29
|
+
type ParamLabelKey =
|
|
30
|
+
| 'settingModel.temperature.title'
|
|
31
|
+
| 'settingModel.topP.title'
|
|
32
|
+
| 'settingModel.presencePenalty.title'
|
|
33
|
+
| 'settingModel.frequencyPenalty.title';
|
|
34
|
+
|
|
35
|
+
type ParamDescKey =
|
|
36
|
+
| 'settingModel.temperature.desc'
|
|
37
|
+
| 'settingModel.topP.desc'
|
|
38
|
+
| 'settingModel.presencePenalty.desc'
|
|
39
|
+
| 'settingModel.frequencyPenalty.desc';
|
|
40
|
+
|
|
41
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
42
|
+
checkbox: css`
|
|
43
|
+
.ant-checkbox-inner {
|
|
44
|
+
border-radius: 4px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
&:hover .ant-checkbox-inner {
|
|
48
|
+
border-color: ${token.colorPrimary};
|
|
49
|
+
}
|
|
50
|
+
`,
|
|
51
|
+
label: css`
|
|
52
|
+
user-select: none;
|
|
53
|
+
`,
|
|
54
|
+
sliderWrapper: css`
|
|
55
|
+
display: flex;
|
|
56
|
+
gap: 16px;
|
|
57
|
+
align-items: center;
|
|
58
|
+
width: 100%;
|
|
59
|
+
`,
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
// Wrapper component to handle checkbox + slider
|
|
63
|
+
interface ParamControlWrapperProps {
|
|
64
|
+
Component: ComponentType<any>;
|
|
65
|
+
checked: boolean;
|
|
66
|
+
disabled: boolean;
|
|
67
|
+
onChange?: (value: number) => void;
|
|
68
|
+
onToggle: (checked: boolean) => void;
|
|
69
|
+
styles: any;
|
|
70
|
+
value?: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const ParamControlWrapper = memo<ParamControlWrapperProps>(
|
|
74
|
+
({ Component, value, onChange, disabled, checked, onToggle, styles }) => {
|
|
75
|
+
return (
|
|
76
|
+
<div className={styles.sliderWrapper}>
|
|
77
|
+
<Checkbox
|
|
78
|
+
checked={checked}
|
|
79
|
+
className={styles.checkbox}
|
|
80
|
+
onChange={(e) => {
|
|
81
|
+
e.stopPropagation();
|
|
82
|
+
onToggle(e.target.checked);
|
|
83
|
+
}}
|
|
84
|
+
/>
|
|
85
|
+
<div style={{ flex: 1 }}>
|
|
86
|
+
<Component disabled={disabled} onChange={onChange} value={value} />
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const PARAM_NAME_MAP: Record<ParamKey, (string | number)[]> = {
|
|
94
|
+
frequency_penalty: ['params', 'frequency_penalty'],
|
|
95
|
+
presence_penalty: ['params', 'presence_penalty'],
|
|
96
|
+
temperature: ['params', 'temperature'],
|
|
97
|
+
top_p: ['params', 'top_p'],
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const PARAM_DEFAULTS: Record<ParamKey, number> = {
|
|
101
|
+
frequency_penalty: 0,
|
|
102
|
+
presence_penalty: 0,
|
|
103
|
+
temperature: 0.7,
|
|
104
|
+
top_p: 1,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const PARAM_CONFIG = {
|
|
108
|
+
frequency_penalty: {
|
|
109
|
+
Component: FrequencyPenalty,
|
|
110
|
+
descKey: 'settingModel.frequencyPenalty.desc',
|
|
111
|
+
labelKey: 'settingModel.frequencyPenalty.title',
|
|
112
|
+
tag: 'frequency_penalty',
|
|
113
|
+
},
|
|
114
|
+
presence_penalty: {
|
|
115
|
+
Component: PresencePenalty,
|
|
116
|
+
descKey: 'settingModel.presencePenalty.desc',
|
|
117
|
+
labelKey: 'settingModel.presencePenalty.title',
|
|
118
|
+
tag: 'presence_penalty',
|
|
119
|
+
},
|
|
120
|
+
temperature: {
|
|
121
|
+
Component: Temperature,
|
|
122
|
+
descKey: 'settingModel.temperature.desc',
|
|
123
|
+
labelKey: 'settingModel.temperature.title',
|
|
124
|
+
tag: 'temperature',
|
|
125
|
+
},
|
|
126
|
+
top_p: {
|
|
127
|
+
Component: TopP,
|
|
128
|
+
descKey: 'settingModel.topP.desc',
|
|
129
|
+
labelKey: 'settingModel.topP.title',
|
|
130
|
+
tag: 'top_p',
|
|
131
|
+
},
|
|
132
|
+
} satisfies Record<
|
|
133
|
+
ParamKey,
|
|
134
|
+
{
|
|
135
|
+
Component: ComponentType<any>;
|
|
136
|
+
descKey: ParamDescKey;
|
|
137
|
+
labelKey: ParamLabelKey;
|
|
138
|
+
tag: string;
|
|
139
|
+
}
|
|
140
|
+
>;
|
|
141
|
+
|
|
23
142
|
const Controls = memo<ControlsProps>(({ setUpdating }) => {
|
|
24
143
|
const { t } = useTranslation('setting');
|
|
25
144
|
const mobile = useServerConfigStore((s) => s.isMobile);
|
|
26
145
|
const updateAgentConfig = useAgentStore((s) => s.updateAgentConfig);
|
|
146
|
+
const { styles } = useStyles();
|
|
27
147
|
|
|
28
148
|
const config = useAgentStore(agentSelectors.currentAgentConfig, isEqual);
|
|
149
|
+
const [form] = Form.useForm();
|
|
29
150
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
151
|
+
const { frequency_penalty, presence_penalty, temperature, top_p } = config.params ?? {};
|
|
152
|
+
|
|
153
|
+
const lastValuesRef = useRef<Record<ParamKey, number | undefined>>({
|
|
154
|
+
frequency_penalty,
|
|
155
|
+
presence_penalty,
|
|
156
|
+
temperature,
|
|
157
|
+
top_p,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
form.setFieldsValue(config);
|
|
162
|
+
|
|
163
|
+
if (typeof temperature === 'number') lastValuesRef.current.temperature = temperature;
|
|
164
|
+
if (typeof top_p === 'number') lastValuesRef.current.top_p = top_p;
|
|
165
|
+
if (typeof presence_penalty === 'number') {
|
|
166
|
+
lastValuesRef.current.presence_penalty = presence_penalty;
|
|
167
|
+
}
|
|
168
|
+
if (typeof frequency_penalty === 'number') {
|
|
169
|
+
lastValuesRef.current.frequency_penalty = frequency_penalty;
|
|
170
|
+
}
|
|
171
|
+
}, [config, form, frequency_penalty, presence_penalty, temperature, top_p]);
|
|
172
|
+
|
|
173
|
+
const temperatureValue = AntdForm.useWatch(PARAM_NAME_MAP.temperature, form);
|
|
174
|
+
const topPValue = AntdForm.useWatch(PARAM_NAME_MAP.top_p, form);
|
|
175
|
+
const presencePenaltyValue = AntdForm.useWatch(PARAM_NAME_MAP.presence_penalty, form);
|
|
176
|
+
const frequencyPenaltyValue = AntdForm.useWatch(PARAM_NAME_MAP.frequency_penalty, form);
|
|
177
|
+
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
if (typeof temperatureValue === 'number') lastValuesRef.current.temperature = temperatureValue;
|
|
180
|
+
}, [temperatureValue]);
|
|
181
|
+
|
|
182
|
+
useEffect(() => {
|
|
183
|
+
if (typeof topPValue === 'number') lastValuesRef.current.top_p = topPValue;
|
|
184
|
+
}, [topPValue]);
|
|
185
|
+
|
|
186
|
+
useEffect(() => {
|
|
187
|
+
if (typeof presencePenaltyValue === 'number') {
|
|
188
|
+
lastValuesRef.current.presence_penalty = presencePenaltyValue;
|
|
189
|
+
}
|
|
190
|
+
}, [presencePenaltyValue]);
|
|
191
|
+
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
if (typeof frequencyPenaltyValue === 'number') {
|
|
194
|
+
lastValuesRef.current.frequency_penalty = frequencyPenaltyValue;
|
|
195
|
+
}
|
|
196
|
+
}, [frequencyPenaltyValue]);
|
|
197
|
+
|
|
198
|
+
const enabledMap: Record<ParamKey, boolean> = {
|
|
199
|
+
frequency_penalty: typeof frequencyPenaltyValue === 'number',
|
|
200
|
+
presence_penalty: typeof presencePenaltyValue === 'number',
|
|
201
|
+
temperature: typeof temperatureValue === 'number',
|
|
202
|
+
top_p: typeof topPValue === 'number',
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const handleToggle = useCallback(
|
|
206
|
+
async (key: ParamKey, enabled: boolean) => {
|
|
207
|
+
const namePath = PARAM_NAME_MAP[key];
|
|
208
|
+
let newValue: number | undefined;
|
|
209
|
+
|
|
210
|
+
if (!enabled) {
|
|
211
|
+
const currentValue = form.getFieldValue(namePath);
|
|
212
|
+
if (typeof currentValue === 'number') {
|
|
213
|
+
lastValuesRef.current[key] = currentValue;
|
|
214
|
+
}
|
|
215
|
+
newValue = undefined;
|
|
216
|
+
form.setFieldValue(namePath, undefined);
|
|
217
|
+
} else {
|
|
218
|
+
const fallback = lastValuesRef.current[key];
|
|
219
|
+
const nextValue = typeof fallback === 'number' ? fallback : PARAM_DEFAULTS[key];
|
|
220
|
+
lastValuesRef.current[key] = nextValue;
|
|
221
|
+
newValue = nextValue;
|
|
222
|
+
form.setFieldValue(namePath, nextValue);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// 立即保存变更 - 手动构造配置对象确保使用最新值
|
|
226
|
+
setUpdating(true);
|
|
227
|
+
const currentValues = form.getFieldsValue();
|
|
228
|
+
const prevParams = (currentValues.params ?? {}) as Record<ParamKey, number | undefined>;
|
|
229
|
+
const currentParams: Record<ParamKey, number | undefined> = { ...prevParams };
|
|
230
|
+
|
|
231
|
+
if (newValue === undefined) {
|
|
232
|
+
// 显式删除该属性,而不是设置为 undefined
|
|
233
|
+
// 这样可以确保 Form 表单状态同步
|
|
234
|
+
delete currentParams[key];
|
|
235
|
+
// 使用 null 作为禁用标记(数据库会保留 null,前端据此判断复选框状态)
|
|
236
|
+
currentParams[key] = null as any;
|
|
237
|
+
} else {
|
|
238
|
+
currentParams[key] = newValue;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const updatedConfig = {
|
|
242
|
+
...currentValues,
|
|
243
|
+
params: currentParams,
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
await updateAgentConfig(updatedConfig);
|
|
247
|
+
setUpdating(false);
|
|
52
248
|
},
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
249
|
+
[form, setUpdating, updateAgentConfig],
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
// 使用 useMemo 确保防抖函数只创建一次
|
|
253
|
+
const handleValuesChange = useCallback(
|
|
254
|
+
debounce(async (values) => {
|
|
255
|
+
setUpdating(true);
|
|
256
|
+
await updateAgentConfig(values);
|
|
257
|
+
setUpdating(false);
|
|
258
|
+
}, 500),
|
|
259
|
+
[updateAgentConfig, setUpdating],
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
const baseItems: FormItemProps[] = (Object.keys(PARAM_CONFIG) as ParamKey[]).map((key) => {
|
|
263
|
+
const meta = PARAM_CONFIG[key];
|
|
264
|
+
const Component = meta.Component;
|
|
265
|
+
const enabled = enabledMap[key];
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
children: (
|
|
269
|
+
<ParamControlWrapper
|
|
270
|
+
Component={Component}
|
|
271
|
+
checked={enabled}
|
|
272
|
+
disabled={!enabled}
|
|
273
|
+
onToggle={(checked) => handleToggle(key, checked)}
|
|
274
|
+
styles={styles}
|
|
275
|
+
/>
|
|
60
276
|
),
|
|
61
|
-
name: ['params', 'presence_penalty'],
|
|
62
|
-
tag: 'presence_penalty',
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
children: <FrequencyPenalty />,
|
|
66
277
|
label: (
|
|
67
|
-
<Flexbox gap={8} horizontal>
|
|
68
|
-
{t(
|
|
69
|
-
<InfoTooltip title={t(
|
|
278
|
+
<Flexbox align={'center'} className={styles.label} gap={8} horizontal>
|
|
279
|
+
{t(meta.labelKey)}
|
|
280
|
+
<InfoTooltip title={t(meta.descKey)} />
|
|
70
281
|
</Flexbox>
|
|
71
282
|
),
|
|
72
|
-
name: [
|
|
73
|
-
tag:
|
|
74
|
-
}
|
|
75
|
-
|
|
283
|
+
name: PARAM_NAME_MAP[key],
|
|
284
|
+
tag: meta.tag,
|
|
285
|
+
} satisfies FormItemProps;
|
|
286
|
+
});
|
|
76
287
|
|
|
77
288
|
return (
|
|
78
289
|
<Form
|
|
290
|
+
form={form}
|
|
79
291
|
initialValues={config}
|
|
80
|
-
itemMinWidth={
|
|
292
|
+
itemMinWidth={220}
|
|
81
293
|
items={
|
|
82
294
|
mobile
|
|
83
|
-
?
|
|
84
|
-
:
|
|
295
|
+
? baseItems
|
|
296
|
+
: baseItems.map(({ tag, ...item }) => ({
|
|
297
|
+
...item,
|
|
298
|
+
desc: <Tag size={'small'}>{tag}</Tag>,
|
|
299
|
+
}))
|
|
85
300
|
}
|
|
86
301
|
itemsType={'flat'}
|
|
87
|
-
onValuesChange={
|
|
88
|
-
setUpdating(true);
|
|
89
|
-
await updateAgentConfig(values);
|
|
90
|
-
setUpdating(false);
|
|
91
|
-
}, 500)}
|
|
302
|
+
onValuesChange={handleValuesChange}
|
|
92
303
|
styles={{
|
|
93
304
|
group: {
|
|
94
305
|
background: 'transparent',
|
|
@@ -8,6 +8,8 @@ import ShareMessageModal from '@/features/Conversation/components/ShareMessageMo
|
|
|
8
8
|
import { VirtuosoContext } from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
|
|
9
9
|
import { useChatStore } from '@/store/chat';
|
|
10
10
|
import { threadSelectors } from '@/store/chat/selectors';
|
|
11
|
+
import { useSessionStore } from '@/store/session';
|
|
12
|
+
import { sessionSelectors } from '@/store/session/selectors';
|
|
11
13
|
import { ChatMessage } from '@/types/message';
|
|
12
14
|
|
|
13
15
|
import { InPortalThreadContext } from '../../../context/InPortalThreadContext';
|
|
@@ -25,6 +27,7 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
|
|
|
25
27
|
!!s.activeThreadId,
|
|
26
28
|
threadSelectors.hasThreadBySourceMsgId(id)(s),
|
|
27
29
|
]);
|
|
30
|
+
const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
|
|
28
31
|
const [showShareModal, setShareModal] = useState(false);
|
|
29
32
|
|
|
30
33
|
const {
|
|
@@ -49,8 +52,10 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
|
|
|
49
52
|
const items = useMemo(() => {
|
|
50
53
|
if (hasTools) return [delAndRegenerate, copy];
|
|
51
54
|
|
|
52
|
-
return [edit, copy, inThread ? null : branching].filter(
|
|
53
|
-
|
|
55
|
+
return [edit, copy, inThread || isGroupSession ? null : branching].filter(
|
|
56
|
+
Boolean,
|
|
57
|
+
) as ActionIconGroupItemType[];
|
|
58
|
+
}, [inThread, hasTools, isGroupSession]);
|
|
54
59
|
|
|
55
60
|
const { t } = useTranslation('common');
|
|
56
61
|
const searchParams = useSearchParams();
|
|
@@ -9,6 +9,8 @@ import { useTranslation } from 'react-i18next';
|
|
|
9
9
|
|
|
10
10
|
import { useChatStore } from '@/store/chat';
|
|
11
11
|
import { threadSelectors } from '@/store/chat/selectors';
|
|
12
|
+
import { useSessionStore } from '@/store/session';
|
|
13
|
+
import { sessionSelectors } from '@/store/session/selectors';
|
|
12
14
|
|
|
13
15
|
import { VirtuosoContext } from '../../components/VirtualizedList/VirtuosoContext';
|
|
14
16
|
import { InPortalThreadContext } from '../../context/InPortalThreadContext';
|
|
@@ -54,6 +56,8 @@ export const UserActionsBar = memo<UserActionsProps>(({ id, data, index }) => {
|
|
|
54
56
|
s.delAndResendThreadMessage,
|
|
55
57
|
]);
|
|
56
58
|
|
|
59
|
+
const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
|
|
60
|
+
|
|
57
61
|
const { regenerate, edit, copy, divider, del, branching, tts, translate } = useChatListActionsBar(
|
|
58
62
|
{ hasThread },
|
|
59
63
|
);
|
|
@@ -63,8 +67,10 @@ export const UserActionsBar = memo<UserActionsProps>(({ id, data, index }) => {
|
|
|
63
67
|
|
|
64
68
|
const items = useMemo(
|
|
65
69
|
() =>
|
|
66
|
-
[regenerate, edit, inThread ? null : branching].filter(
|
|
67
|
-
|
|
70
|
+
[regenerate, edit, inThread || isGroupSession ? null : branching].filter(
|
|
71
|
+
Boolean,
|
|
72
|
+
) as ActionIconGroupItemType[],
|
|
73
|
+
[inThread, isGroupSession],
|
|
68
74
|
);
|
|
69
75
|
|
|
70
76
|
const { message } = App.useApp();
|
|
@@ -22,9 +22,9 @@ import { selectors, useStore } from './store';
|
|
|
22
22
|
const { TextArea } = Input;
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* Chat Settings for Group Chat
|
|
25
|
+
* Chat Settings for Agent Team (Group Chat)
|
|
26
26
|
*/
|
|
27
|
-
const
|
|
27
|
+
const AgentTeamChatSettings = memo(() => {
|
|
28
28
|
const { t } = useTranslation(['setting', 'common']);
|
|
29
29
|
const [form] = Form.useForm();
|
|
30
30
|
const updateConfig = useStore((s) => s.updateGroupConfig);
|
|
@@ -171,8 +171,9 @@ const ChatGroupSettings = memo(() => {
|
|
|
171
171
|
itemsType={'group'}
|
|
172
172
|
onFinish={async ({ _modelConfig, ...rest }) => {
|
|
173
173
|
await updateConfig({
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
// Preserve existing values when _modelConfig is undefined (enableSupervisor is false)
|
|
175
|
+
orchestratorModel: _modelConfig?.model ?? config?.orchestratorModel,
|
|
176
|
+
orchestratorProvider: _modelConfig?.provider ?? config?.orchestratorProvider,
|
|
176
177
|
...rest,
|
|
177
178
|
});
|
|
178
179
|
|
|
@@ -184,4 +185,4 @@ const ChatGroupSettings = memo(() => {
|
|
|
184
185
|
);
|
|
185
186
|
});
|
|
186
187
|
|
|
187
|
-
export default
|
|
188
|
+
export default AgentTeamChatSettings;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { Grid, Tag, Text } from '@lobehub/ui';
|
|
3
|
+
import { ActionIcon, Grid, Tag, Text } from '@lobehub/ui';
|
|
4
4
|
import { createStyles } from 'antd-style';
|
|
5
|
+
import { Plus } from 'lucide-react';
|
|
5
6
|
import { memo, useMemo, useState } from 'react';
|
|
6
7
|
import { useTranslation } from 'react-i18next';
|
|
7
8
|
import { Flexbox } from 'react-layout-kit';
|
|
8
9
|
|
|
10
|
+
import { DEFAULT_AVATAR } from '@/const/meta';
|
|
9
11
|
import { useChatGroupStore } from '@/store/chatGroup';
|
|
10
12
|
import { chatGroupSelectors } from '@/store/chatGroup/selectors';
|
|
11
13
|
import { useSessionStore } from '@/store/session';
|
|
@@ -23,10 +25,11 @@ const useStyles = createStyles(({ css }) => ({
|
|
|
23
25
|
|
|
24
26
|
const HOST_MEMBER_ID = 'supervisor';
|
|
25
27
|
|
|
26
|
-
const
|
|
28
|
+
const AgentTeamMembersSettings = memo(() => {
|
|
27
29
|
const { t } = useTranslation('setting');
|
|
28
30
|
const { styles } = useStyles();
|
|
29
31
|
const [loadingAgentId, setLoadingAgentId] = useState<string | null>(null);
|
|
32
|
+
const [isCreatingMember, setIsCreatingMember] = useState(false);
|
|
30
33
|
|
|
31
34
|
const activeGroupId = useSessionStore((s) => s.activeId);
|
|
32
35
|
const currentSession = useSessionStore(sessionSelectors.currentSession) as LobeGroupSession;
|
|
@@ -36,6 +39,7 @@ const GroupMembers = memo(() => {
|
|
|
36
39
|
const removeAgentFromGroup = useChatGroupStore((s) => s.removeAgentFromGroup);
|
|
37
40
|
const updateGroupConfig = useChatGroupStore((s) => s.updateGroupConfig);
|
|
38
41
|
const refreshSessions = useSessionStore((s) => s.refreshSessions);
|
|
42
|
+
const createSession = useSessionStore((s) => s.createSession);
|
|
39
43
|
|
|
40
44
|
// Get all agent sessions
|
|
41
45
|
const agentSessions = useSessionStore((s) => {
|
|
@@ -79,8 +83,6 @@ const GroupMembers = memo(() => {
|
|
|
79
83
|
return;
|
|
80
84
|
}
|
|
81
85
|
|
|
82
|
-
console.log(`Attempting to ${action} agent:`, { action, activeGroupId, agentId });
|
|
83
|
-
|
|
84
86
|
// Check if this is the host member
|
|
85
87
|
const isHostMember = agentId === HOST_MEMBER_ID;
|
|
86
88
|
|
|
@@ -92,19 +94,15 @@ const GroupMembers = memo(() => {
|
|
|
92
94
|
if (isHostMember) {
|
|
93
95
|
// Host toggle updates supervisor flag instead of modifying members
|
|
94
96
|
await updateGroupConfig({ enableSupervisor: true });
|
|
95
|
-
console.log('Enabled supervisor');
|
|
96
97
|
} else {
|
|
97
98
|
await addAgentsToGroup(activeGroupId, [agentId]);
|
|
98
|
-
console.log(`Successfully added agent ${agentId} to group ${activeGroupId}`);
|
|
99
99
|
}
|
|
100
100
|
} else {
|
|
101
101
|
if (isHostMember) {
|
|
102
102
|
// Host toggle updates supervisor flag instead of modifying members
|
|
103
103
|
await updateGroupConfig({ enableSupervisor: false });
|
|
104
|
-
console.log('Disabled supervisor');
|
|
105
104
|
} else {
|
|
106
105
|
await removeAgentFromGroup(activeGroupId, agentId);
|
|
107
|
-
console.log(`Successfully removed agent ${agentId} from group ${activeGroupId}`);
|
|
108
106
|
}
|
|
109
107
|
}
|
|
110
108
|
|
|
@@ -124,6 +122,49 @@ const GroupMembers = memo(() => {
|
|
|
124
122
|
handleAgentAction(HOST_MEMBER_ID, checked ? 'add' : 'remove');
|
|
125
123
|
};
|
|
126
124
|
|
|
125
|
+
const handleCreateMember = async () => {
|
|
126
|
+
if (!activeGroupId || isCreatingMember) return;
|
|
127
|
+
|
|
128
|
+
setIsCreatingMember(true);
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
// Create a virtual assistant
|
|
132
|
+
const sessionId = await createSession(
|
|
133
|
+
{
|
|
134
|
+
config: {
|
|
135
|
+
virtual: true,
|
|
136
|
+
},
|
|
137
|
+
meta: {
|
|
138
|
+
avatar: DEFAULT_AVATAR,
|
|
139
|
+
description: '',
|
|
140
|
+
title: t('settingGroupMembers.defaultAgent'),
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
false, // Don't switch to the new session
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// Refresh sessions to get the latest data
|
|
147
|
+
await refreshSessions();
|
|
148
|
+
|
|
149
|
+
// Get the agent ID from the created session
|
|
150
|
+
const session = sessionSelectors.getSessionById(sessionId)(useSessionStore.getState());
|
|
151
|
+
if (session && session.type === LobeSessionType.Agent) {
|
|
152
|
+
const agentSession = session as LobeAgentSession;
|
|
153
|
+
const agentId = agentSession.config?.id;
|
|
154
|
+
|
|
155
|
+
if (agentId) {
|
|
156
|
+
// Add the agent to the current group
|
|
157
|
+
await addAgentsToGroup(activeGroupId, [agentId]);
|
|
158
|
+
await refreshSessions();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} catch (error) {
|
|
162
|
+
console.error('Failed to create virtual member:', error);
|
|
163
|
+
} finally {
|
|
164
|
+
setIsCreatingMember(false);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
127
168
|
const groupMemberCount = agentsInGroup.length + (isSupervisorEnabled ? 1 : 0);
|
|
128
169
|
const availableAgentCount = agentsNotInGroup.length + (isSupervisorEnabled ? 0 : 1);
|
|
129
170
|
|
|
@@ -131,19 +172,23 @@ const GroupMembers = memo(() => {
|
|
|
131
172
|
<Flexbox className={styles.container} gap={40}>
|
|
132
173
|
{/* Agents in Group Section */}
|
|
133
174
|
<Flexbox gap={24}>
|
|
134
|
-
<Flexbox align={'center'} gap={8} horizontal>
|
|
135
|
-
<
|
|
136
|
-
{
|
|
137
|
-
|
|
138
|
-
|
|
175
|
+
<Flexbox align={'center'} gap={8} horizontal justify="space-between">
|
|
176
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
|
177
|
+
<Text strong style={{ fontSize: 18 }}>
|
|
178
|
+
{t('settingGroupMembers.groupMembers')}
|
|
179
|
+
</Text>
|
|
180
|
+
<Tag>{groupMemberCount}</Tag>
|
|
181
|
+
</Flexbox>
|
|
182
|
+
<ActionIcon
|
|
183
|
+
icon={Plus}
|
|
184
|
+
loading={isCreatingMember}
|
|
185
|
+
onClick={handleCreateMember}
|
|
186
|
+
title={t('settingGroupMembers.createMember')}
|
|
187
|
+
/>
|
|
139
188
|
</Flexbox>
|
|
140
189
|
<Grid gap={16} rows={3}>
|
|
141
190
|
{isSupervisorEnabled && (
|
|
142
|
-
<HostMemberCard
|
|
143
|
-
checked
|
|
144
|
-
loading={hostOperationLoading}
|
|
145
|
-
onToggle={handleHostToggle}
|
|
146
|
-
/>
|
|
191
|
+
<HostMemberCard checked loading={hostOperationLoading} onToggle={handleHostToggle} />
|
|
147
192
|
)}
|
|
148
193
|
{agentsInGroup.map((agent) => (
|
|
149
194
|
<AgentCard
|
|
@@ -198,4 +243,4 @@ const GroupMembers = memo(() => {
|
|
|
198
243
|
);
|
|
199
244
|
});
|
|
200
245
|
|
|
201
|
-
export default
|
|
246
|
+
export default AgentTeamMembersSettings;
|
|
@@ -14,7 +14,7 @@ import { selectors, useStore } from './store';
|
|
|
14
14
|
|
|
15
15
|
const { TextArea } = Input;
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const AgentTeamMetaSettings = memo(() => {
|
|
18
18
|
const { t } = useTranslation(['setting', 'common']);
|
|
19
19
|
const [form] = Form.useForm();
|
|
20
20
|
|
|
@@ -100,4 +100,4 @@ const ChatGroupMeta = memo(() => {
|
|
|
100
100
|
);
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
-
export default
|
|
103
|
+
export default AgentTeamMetaSettings;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Skeleton } from 'antd';
|
|
2
|
+
import { ReactNode, Suspense, memo } from 'react';
|
|
3
|
+
|
|
4
|
+
import { GroupSettingsTabs } from '@/store/global/initialState';
|
|
5
|
+
import { useServerConfigStore } from '@/store/serverConfig';
|
|
6
|
+
|
|
7
|
+
import AgentTeamChatSettings from './AgentTeamChatSettings';
|
|
8
|
+
import AgentTeamMembersSettings from './AgentTeamMembersSettings';
|
|
9
|
+
import AgentTeamMetaSettings from './AgentTeamMetaSettings';
|
|
10
|
+
import { GroupChatSettingsProvider } from './GroupChatSettingsProvider';
|
|
11
|
+
import { StoreUpdaterProps } from './StoreUpdater';
|
|
12
|
+
|
|
13
|
+
export interface AgentTeamSettingsProps extends StoreUpdaterProps {
|
|
14
|
+
tab?: GroupSettingsTabs;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface AgentTeamSettingsContentProps {
|
|
18
|
+
loadingSkeleton?: ReactNode;
|
|
19
|
+
tab: GroupSettingsTabs;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const AgentTeamSettingsContent = memo<AgentTeamSettingsContentProps>(({ tab }) => {
|
|
23
|
+
return (
|
|
24
|
+
<>
|
|
25
|
+
{tab === GroupSettingsTabs.Settings && <AgentTeamMetaSettings />}
|
|
26
|
+
{tab === GroupSettingsTabs.Members && <AgentTeamMembersSettings />}
|
|
27
|
+
{tab === GroupSettingsTabs.Chat && <AgentTeamChatSettings />}
|
|
28
|
+
</>
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const AgentTeamSettings = memo<AgentTeamSettingsProps>(
|
|
33
|
+
({ tab = GroupSettingsTabs.Settings, ...rest }) => {
|
|
34
|
+
const isMobile = useServerConfigStore((s) => s.isMobile);
|
|
35
|
+
const loadingSkeleton = (
|
|
36
|
+
<Skeleton
|
|
37
|
+
active
|
|
38
|
+
paragraph={{ rows: 6 }}
|
|
39
|
+
style={{ padding: isMobile ? 16 : 0 }}
|
|
40
|
+
title={false}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<GroupChatSettingsProvider {...rest}>
|
|
46
|
+
<Suspense fallback={loadingSkeleton}>
|
|
47
|
+
<AgentTeamSettingsContent tab={tab} />
|
|
48
|
+
</Suspense>
|
|
49
|
+
</GroupChatSettingsProvider>
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
export default AgentTeamSettings;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
export { default as
|
|
2
|
-
export { default as
|
|
1
|
+
export { default as AgentTeamChatSettings } from './AgentTeamChatSettings';
|
|
2
|
+
export { default as GroupMembersConfig } from './AgentTeamMembersSettings';
|
|
3
|
+
export { default as ChatGroupMeta } from './AgentTeamMetaSettings';
|
|
4
|
+
export { default as AgentTeamSettings } from './AgentTeamSettings';
|
|
3
5
|
export { default as GroupCategory } from './GroupCategory';
|
|
4
6
|
export { GroupChatSettingsProvider } from './GroupChatSettingsProvider';
|
|
5
|
-
export { default as GroupMembers } from './GroupMembers';
|
|
6
|
-
export { default as GroupSettings } from './GroupSettings';
|
|
7
|
-
export { default as GroupSettingsContent } from './GroupSettingsContent';
|
|
8
7
|
|
|
9
8
|
// Hooks
|
|
10
9
|
export type { GroupChatSettingsInstance } from './hooks/useGroupChatSettings';
|