@lobehub/lobehub 2.0.13 → 2.1.1
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/changelog/v2.json +18 -0
- package/docker-compose/deploy/.env.example +35 -0
- package/docker-compose/deploy/.env.zh-CN.example +31 -0
- package/docker-compose/deploy/bucket.config.json +18 -0
- package/docker-compose/deploy/docker-compose.yml +148 -0
- package/docker-compose/deploy/searxng-settings.yml +2582 -0
- package/docker-compose/setup.sh +37 -88
- package/docs/self-hosting/advanced/auth/providers/casdoor.mdx +110 -0
- package/docs/self-hosting/advanced/auth/providers/casdoor.zh-CN.mdx +165 -0
- package/docs/self-hosting/platform/docker-compose.mdx +43 -561
- package/docs/self-hosting/platform/docker-compose.zh-CN.mdx +40 -537
- package/locales/en-US/setting.json +16 -0
- package/locales/zh-CN/setting.json +16 -0
- package/package.json +1 -1
- package/packages/const/src/url.ts +1 -1
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Cron/index.tsx +4 -2
- package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobContentEditor.tsx +39 -44
- package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobHeader.tsx +27 -20
- package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobSaveButton.tsx +4 -2
- package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobScheduleConfig.tsx +183 -145
- package/src/app/[variants]/(main)/agent/profile/features/AgentCronJobs/hooks/useAgentCronJobs.ts +3 -4
- package/src/app/[variants]/(main)/agent/profile/features/AgentCronJobs/index.tsx +4 -3
- package/src/app/[variants]/(main)/agent/profile/features/ProfileEditor/index.tsx +4 -3
- package/src/features/Conversation/Messages/AssistantGroup/components/MessageContent.tsx +6 -1
- package/src/locales/default/setting.ts +16 -0
- package/src/store/agent/slices/cron/action.ts +6 -4
|
@@ -1,17 +1,62 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Checkbox, Flexbox, FormGroup, LobeSelect as Select, Text } from '@lobehub/ui';
|
|
4
|
+
import { Divider, InputNumber, TimePicker } from 'antd';
|
|
5
|
+
import { createStaticStyles, cx } from 'antd-style';
|
|
3
6
|
import type { Dayjs } from 'dayjs';
|
|
4
7
|
import dayjs from 'dayjs';
|
|
5
|
-
import { memo
|
|
8
|
+
import { memo } from 'react';
|
|
6
9
|
import { useTranslation } from 'react-i18next';
|
|
7
10
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
import { SCHEDULE_TYPE_OPTIONS, type ScheduleType, TIMEZONE_OPTIONS } from '../CronConfig';
|
|
12
|
+
|
|
13
|
+
const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
14
|
+
label: css`
|
|
15
|
+
flex-shrink: 0;
|
|
16
|
+
width: 120px;
|
|
17
|
+
`,
|
|
18
|
+
row: css`
|
|
19
|
+
min-height: 48px;
|
|
20
|
+
padding-block: 12px;
|
|
21
|
+
padding-inline: 0;
|
|
22
|
+
`,
|
|
23
|
+
weekdayButton: css`
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
|
|
30
|
+
width: 40px;
|
|
31
|
+
height: 32px;
|
|
32
|
+
border: 1px solid ${cssVar.colorBorder};
|
|
33
|
+
border-radius: 6px;
|
|
34
|
+
|
|
35
|
+
font-size: 12px;
|
|
36
|
+
font-weight: 500;
|
|
37
|
+
color: ${cssVar.colorTextSecondary};
|
|
38
|
+
|
|
39
|
+
background: transparent;
|
|
40
|
+
|
|
41
|
+
transition: all 0.15s ease;
|
|
42
|
+
|
|
43
|
+
&:hover {
|
|
44
|
+
border-color: ${cssVar.colorPrimary};
|
|
45
|
+
color: ${cssVar.colorPrimary};
|
|
46
|
+
}
|
|
47
|
+
`,
|
|
48
|
+
weekdayButtonActive: css`
|
|
49
|
+
border-color: ${cssVar.colorPrimary};
|
|
50
|
+
color: ${cssVar.colorTextLightSolid};
|
|
51
|
+
background: ${cssVar.colorPrimary};
|
|
52
|
+
|
|
53
|
+
&:hover {
|
|
54
|
+
border-color: ${cssVar.colorPrimaryHover};
|
|
55
|
+
color: ${cssVar.colorTextLightSolid};
|
|
56
|
+
background: ${cssVar.colorPrimaryHover};
|
|
57
|
+
}
|
|
58
|
+
`,
|
|
59
|
+
}));
|
|
15
60
|
|
|
16
61
|
interface CronJobScheduleConfigProps {
|
|
17
62
|
hourlyInterval?: number;
|
|
@@ -30,6 +75,16 @@ interface CronJobScheduleConfigProps {
|
|
|
30
75
|
weekdays: number[];
|
|
31
76
|
}
|
|
32
77
|
|
|
78
|
+
const WEEKDAYS = [
|
|
79
|
+
{ key: 1, label: 'agentCronJobs.weekdays.mon' },
|
|
80
|
+
{ key: 2, label: 'agentCronJobs.weekdays.tue' },
|
|
81
|
+
{ key: 3, label: 'agentCronJobs.weekdays.wed' },
|
|
82
|
+
{ key: 4, label: 'agentCronJobs.weekdays.thu' },
|
|
83
|
+
{ key: 5, label: 'agentCronJobs.weekdays.fri' },
|
|
84
|
+
{ key: 6, label: 'agentCronJobs.weekdays.sat' },
|
|
85
|
+
{ key: 0, label: 'agentCronJobs.weekdays.sun' },
|
|
86
|
+
];
|
|
87
|
+
|
|
33
88
|
const CronJobScheduleConfig = memo<CronJobScheduleConfigProps>(
|
|
34
89
|
({
|
|
35
90
|
hourlyInterval,
|
|
@@ -42,172 +97,155 @@ const CronJobScheduleConfig = memo<CronJobScheduleConfigProps>(
|
|
|
42
97
|
}) => {
|
|
43
98
|
const { t } = useTranslation('setting');
|
|
44
99
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (scheduleTypeLabel) {
|
|
54
|
-
result.push({
|
|
55
|
-
key: 'scheduleType',
|
|
56
|
-
label: t(scheduleTypeLabel as any),
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Trigger time
|
|
61
|
-
if (scheduleType === 'hourly') {
|
|
62
|
-
const minute = triggerTime.minute();
|
|
63
|
-
result.push({
|
|
64
|
-
key: 'interval',
|
|
65
|
-
label: `Every ${hourlyInterval || 1} hour(s) at :${minute.toString().padStart(2, '0')}`,
|
|
66
|
-
});
|
|
67
|
-
} else {
|
|
68
|
-
result.push({
|
|
69
|
-
key: 'triggerTime',
|
|
70
|
-
label: triggerTime.format('HH:mm'),
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Timezone
|
|
75
|
-
result.push({
|
|
76
|
-
key: 'timezone',
|
|
77
|
-
label: timezone,
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// Weekdays for weekly schedule
|
|
81
|
-
if (scheduleType === 'weekly' && weekdays.length > 0) {
|
|
82
|
-
result.push({
|
|
83
|
-
key: 'weekdays',
|
|
84
|
-
label: weekdays.map((day) => t(WEEKDAY_LABELS[day] as any)).join(', '),
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return result;
|
|
89
|
-
}, [scheduleType, triggerTime, timezone, weekdays, hourlyInterval, t]);
|
|
100
|
+
const toggleWeekday = (day: number) => {
|
|
101
|
+
const newWeekdays = weekdays.includes(day)
|
|
102
|
+
? weekdays.filter((d) => d !== day)
|
|
103
|
+
: [...weekdays, day];
|
|
104
|
+
onScheduleChange({ weekdays: newWeekdays });
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const isUnlimited = maxExecutions === null || maxExecutions === undefined;
|
|
90
108
|
|
|
91
109
|
return (
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
{
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
options={SCHEDULE_TYPE_OPTIONS.map((opt) => ({
|
|
115
|
-
label: t(opt.label as any),
|
|
116
|
-
value: opt.value,
|
|
117
|
-
}))}
|
|
118
|
-
size="small"
|
|
119
|
-
style={{ minWidth: 120 }}
|
|
120
|
-
value={scheduleType}
|
|
121
|
-
/>
|
|
110
|
+
<FormGroup title={t('agentCronJobs.schedule')} variant="filled">
|
|
111
|
+
{/* Frequency Row */}
|
|
112
|
+
<Flexbox align="center" className={styles.row} gap={24} horizontal>
|
|
113
|
+
<Text className={styles.label}>{t('agentCronJobs.form.frequency')}</Text>
|
|
114
|
+
<Select
|
|
115
|
+
onChange={(value: ScheduleType) =>
|
|
116
|
+
onScheduleChange({
|
|
117
|
+
scheduleType: value,
|
|
118
|
+
weekdays: value === 'weekly' ? [1, 2, 3, 4, 5] : [],
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
options={SCHEDULE_TYPE_OPTIONS.map((opt) => ({
|
|
122
|
+
label: t(opt.label as any),
|
|
123
|
+
value: opt.value,
|
|
124
|
+
}))}
|
|
125
|
+
style={{ width: 140 }}
|
|
126
|
+
value={scheduleType}
|
|
127
|
+
variant="outlined"
|
|
128
|
+
/>
|
|
129
|
+
</Flexbox>
|
|
122
130
|
|
|
123
|
-
|
|
124
|
-
{scheduleType === 'weekly' && (
|
|
125
|
-
<Select
|
|
126
|
-
maxTagCount="responsive"
|
|
127
|
-
mode="multiple"
|
|
128
|
-
onChange={(values: number[]) => onScheduleChange({ weekdays: values })}
|
|
129
|
-
options={WEEKDAY_OPTIONS.map((opt) => ({
|
|
130
|
-
label: t(opt.label as any),
|
|
131
|
-
value: opt.value,
|
|
132
|
-
}))}
|
|
133
|
-
placeholder="Select days"
|
|
134
|
-
size="small"
|
|
135
|
-
style={{ minWidth: 150 }}
|
|
136
|
-
value={weekdays}
|
|
137
|
-
/>
|
|
138
|
-
)}
|
|
131
|
+
<Divider style={{ margin: 0 }} />
|
|
139
132
|
|
|
140
|
-
|
|
141
|
-
|
|
133
|
+
{/* Time Row (for daily/weekly) */}
|
|
134
|
+
{scheduleType !== 'hourly' && (
|
|
135
|
+
<>
|
|
136
|
+
<Flexbox align="center" className={styles.row} gap={24} horizontal>
|
|
137
|
+
<Text className={styles.label}>{t('agentCronJobs.form.time')}</Text>
|
|
142
138
|
<TimePicker
|
|
143
139
|
format="HH:mm"
|
|
144
|
-
minuteStep={
|
|
140
|
+
minuteStep={15}
|
|
145
141
|
onChange={(value) => {
|
|
146
142
|
if (value) onScheduleChange({ triggerTime: value });
|
|
147
143
|
}}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
value={triggerTime ?? dayjs().hour(0).minute(0)}
|
|
144
|
+
style={{ width: 120 }}
|
|
145
|
+
value={triggerTime ?? dayjs().hour(9).minute(0)}
|
|
151
146
|
/>
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
147
|
+
</Flexbox>
|
|
148
|
+
<Divider style={{ margin: 0 }} />
|
|
149
|
+
</>
|
|
150
|
+
)}
|
|
151
|
+
|
|
152
|
+
{/* Hourly Interval Row */}
|
|
153
|
+
{scheduleType === 'hourly' && (
|
|
154
|
+
<>
|
|
155
|
+
<Flexbox align="center" className={styles.row} gap={24} horizontal>
|
|
156
|
+
<Text className={styles.label}>{t('agentCronJobs.form.every')}</Text>
|
|
157
|
+
<Flexbox align="center" gap={8} horizontal>
|
|
158
158
|
<InputNumber
|
|
159
159
|
max={24}
|
|
160
160
|
min={1}
|
|
161
|
-
onChange={(value:
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
size="small"
|
|
165
|
-
style={{ width: 80 }}
|
|
161
|
+
onChange={(value) => onScheduleChange({ hourlyInterval: value ?? 1 })}
|
|
162
|
+
style={{ width: 70 }}
|
|
166
163
|
value={hourlyInterval ?? 1}
|
|
167
164
|
/>
|
|
168
|
-
<Text type="secondary">
|
|
165
|
+
<Text type="secondary">{t('agentCronJobs.form.hours')}</Text>
|
|
166
|
+
<Text type="secondary">{t('agentCronJobs.form.at')}</Text>
|
|
169
167
|
<Select
|
|
170
168
|
onChange={(value: number) =>
|
|
171
169
|
onScheduleChange({ triggerTime: dayjs().hour(0).minute(value) })
|
|
172
170
|
}
|
|
173
171
|
options={[
|
|
174
172
|
{ label: ':00', value: 0 },
|
|
173
|
+
{ label: ':15', value: 15 },
|
|
175
174
|
{ label: ':30', value: 30 },
|
|
175
|
+
{ label: ':45', value: 45 },
|
|
176
176
|
]}
|
|
177
|
-
|
|
178
|
-
style={{ width: 80 }}
|
|
177
|
+
style={{ width: '80px' }}
|
|
179
178
|
value={triggerTime?.minute() ?? 0}
|
|
179
|
+
variant="outlined"
|
|
180
180
|
/>
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
181
|
+
</Flexbox>
|
|
182
|
+
</Flexbox>
|
|
183
|
+
<Divider style={{ margin: 0 }} />
|
|
184
|
+
</>
|
|
185
|
+
)}
|
|
186
|
+
|
|
187
|
+
{/* Weekday Selector (only for weekly) */}
|
|
188
|
+
{scheduleType === 'weekly' && (
|
|
189
|
+
<>
|
|
190
|
+
<Flexbox align="center" className={styles.row} gap={24} horizontal>
|
|
191
|
+
<Text className={styles.label}>{t('agentCronJobs.weekdays')}</Text>
|
|
192
|
+
<Flexbox gap={6} horizontal>
|
|
193
|
+
{WEEKDAYS.map(({ key, label }) => (
|
|
194
|
+
<div
|
|
195
|
+
className={cx(
|
|
196
|
+
styles.weekdayButton,
|
|
197
|
+
weekdays.includes(key) && styles.weekdayButtonActive,
|
|
198
|
+
)}
|
|
199
|
+
key={key}
|
|
200
|
+
onClick={() => toggleWeekday(key)}
|
|
201
|
+
>
|
|
202
|
+
{t(label as any)}
|
|
203
|
+
</div>
|
|
204
|
+
))}
|
|
205
|
+
</Flexbox>
|
|
206
|
+
</Flexbox>
|
|
207
|
+
<Divider style={{ margin: 0 }} />
|
|
208
|
+
</>
|
|
209
|
+
)}
|
|
210
|
+
|
|
211
|
+
{/* Timezone Row */}
|
|
212
|
+
<Flexbox align="center" className={styles.row} gap={24} horizontal>
|
|
213
|
+
<Text className={styles.label}>{t('agentCronJobs.form.timezone')}</Text>
|
|
214
|
+
<Select
|
|
215
|
+
onChange={(value: string) => onScheduleChange({ timezone: value })}
|
|
216
|
+
options={TIMEZONE_OPTIONS}
|
|
217
|
+
popupMatchSelectWidth={false}
|
|
218
|
+
showSearch
|
|
219
|
+
style={{ minWidth: '200px', width: 'fit-content' }}
|
|
220
|
+
value={timezone}
|
|
221
|
+
variant="outlined"
|
|
222
|
+
/>
|
|
223
|
+
</Flexbox>
|
|
224
|
+
|
|
225
|
+
<Divider style={{ margin: 0 }} />
|
|
194
226
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
227
|
+
{/* Execution Limit Row */}
|
|
228
|
+
<Flexbox align="center" className={styles.row} gap={24} horizontal>
|
|
229
|
+
<Text className={styles.label}>{t('agentCronJobs.maxExecutions')}</Text>
|
|
230
|
+
<Flexbox align="center" gap={12} horizontal>
|
|
198
231
|
<InputNumber
|
|
232
|
+
disabled={isUnlimited}
|
|
199
233
|
min={1}
|
|
200
|
-
onChange={(value:
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
size="small"
|
|
205
|
-
style={{ width: 160 }}
|
|
206
|
-
value={maxExecutions ?? null}
|
|
234
|
+
onChange={(value) => onScheduleChange({ maxExecutions: value })}
|
|
235
|
+
placeholder="100"
|
|
236
|
+
style={{ width: 100 }}
|
|
237
|
+
value={maxExecutions ?? undefined}
|
|
207
238
|
/>
|
|
239
|
+
<Text type="secondary">{t('agentCronJobs.form.times')}</Text>
|
|
240
|
+
<Checkbox
|
|
241
|
+
checked={isUnlimited}
|
|
242
|
+
onChange={(checked) => onScheduleChange({ maxExecutions: checked ? null : 100 })}
|
|
243
|
+
>
|
|
244
|
+
{t('agentCronJobs.form.unlimited')}
|
|
245
|
+
</Checkbox>
|
|
208
246
|
</Flexbox>
|
|
209
247
|
</Flexbox>
|
|
210
|
-
</
|
|
248
|
+
</FormGroup>
|
|
211
249
|
);
|
|
212
250
|
},
|
|
213
251
|
);
|
package/src/app/[variants]/(main)/agent/profile/features/AgentCronJobs/hooks/useAgentCronJobs.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
|
|
2
1
|
import { message } from 'antd';
|
|
3
2
|
import { useCallback } from 'react';
|
|
4
3
|
import { useTranslation } from 'react-i18next';
|
|
@@ -10,7 +9,7 @@ import type {
|
|
|
10
9
|
} from '@/database/schemas/agentCronJob';
|
|
11
10
|
import { agentCronJobService } from '@/services/agentCronJob';
|
|
12
11
|
|
|
13
|
-
export const useAgentCronJobs = (agentId?: string) => {
|
|
12
|
+
export const useAgentCronJobs = (agentId?: string, enabled: boolean = true) => {
|
|
14
13
|
const { t } = useTranslation('setting');
|
|
15
14
|
|
|
16
15
|
// Fetch cron jobs for the agent
|
|
@@ -20,8 +19,8 @@ export const useAgentCronJobs = (agentId?: string) => {
|
|
|
20
19
|
isLoading: loading,
|
|
21
20
|
mutate,
|
|
22
21
|
} = useSWR(
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
enabled && agentId ? `/api/agent-cron-jobs/${agentId}` : null,
|
|
23
|
+
enabled && agentId ? () => agentCronJobService.getByAgentId(agentId) : null,
|
|
25
24
|
{
|
|
26
25
|
onError: (error) => {
|
|
27
26
|
console.error('Failed to fetch cron jobs:', error);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
|
|
4
3
|
import { Flexbox } from '@lobehub/ui';
|
|
5
4
|
import { Typography } from 'antd';
|
|
6
5
|
import { Clock } from 'lucide-react';
|
|
@@ -10,6 +9,7 @@ import urlJoin from 'url-join';
|
|
|
10
9
|
|
|
11
10
|
import { useQueryRoute } from '@/hooks/useQueryRoute';
|
|
12
11
|
import { useAgentStore } from '@/store/agent';
|
|
12
|
+
import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
13
13
|
|
|
14
14
|
import CronJobCards from './CronJobCards';
|
|
15
15
|
import { useAgentCronJobs } from './hooks/useAgentCronJobs';
|
|
@@ -20,8 +20,9 @@ const AgentCronJobs = memo(() => {
|
|
|
20
20
|
const { t } = useTranslation('setting');
|
|
21
21
|
const agentId = useAgentStore((s) => s.activeAgentId);
|
|
22
22
|
const router = useQueryRoute();
|
|
23
|
+
const enableBusinessFeatures = useServerConfigStore(serverConfigSelectors.enableBusinessFeatures);
|
|
23
24
|
|
|
24
|
-
const { cronJobs, loading, deleteCronJob } = useAgentCronJobs(agentId);
|
|
25
|
+
const { cronJobs, loading, deleteCronJob } = useAgentCronJobs(agentId, enableBusinessFeatures);
|
|
25
26
|
|
|
26
27
|
// Edit: Navigate to cron job detail page
|
|
27
28
|
const handleEdit = useCallback(
|
|
@@ -40,7 +41,7 @@ const AgentCronJobs = memo(() => {
|
|
|
40
41
|
[deleteCronJob],
|
|
41
42
|
);
|
|
42
43
|
|
|
43
|
-
if (!
|
|
44
|
+
if (!enableBusinessFeatures) return null;
|
|
44
45
|
|
|
45
46
|
if (!agentId) {
|
|
46
47
|
return null;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
|
|
4
3
|
import { Button, Flexbox } from '@lobehub/ui';
|
|
5
4
|
import { Divider } from 'antd';
|
|
6
5
|
import { useTheme } from 'antd-style';
|
|
@@ -15,6 +14,7 @@ import { useQueryRoute } from '@/hooks/useQueryRoute';
|
|
|
15
14
|
import { useAgentStore } from '@/store/agent';
|
|
16
15
|
import { agentSelectors } from '@/store/agent/selectors';
|
|
17
16
|
import { useChatStore } from '@/store/chat';
|
|
17
|
+
import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
18
18
|
|
|
19
19
|
import AgentCronJobs from '../AgentCronJobs';
|
|
20
20
|
import AgentSettings from '../AgentSettings';
|
|
@@ -31,6 +31,7 @@ const ProfileEditor = memo(() => {
|
|
|
31
31
|
const agentId = useAgentStore((s) => s.activeAgentId);
|
|
32
32
|
const switchTopic = useChatStore((s) => s.switchTopic);
|
|
33
33
|
const router = useQueryRoute();
|
|
34
|
+
const enableBusinessFeatures = useServerConfigStore(serverConfigSelectors.enableBusinessFeatures);
|
|
34
35
|
|
|
35
36
|
const handleCreateCronJob = useCallback(() => {
|
|
36
37
|
if (!agentId) return;
|
|
@@ -95,7 +96,7 @@ const ProfileEditor = memo(() => {
|
|
|
95
96
|
{t('startConversation')}
|
|
96
97
|
</Button>
|
|
97
98
|
<AgentPublishButton />
|
|
98
|
-
{
|
|
99
|
+
{enableBusinessFeatures && (
|
|
99
100
|
<Button icon={Clock} onClick={handleCreateCronJob}>
|
|
100
101
|
{t('agentCronJobs.addJob')}
|
|
101
102
|
</Button>
|
|
@@ -106,7 +107,7 @@ const ProfileEditor = memo(() => {
|
|
|
106
107
|
{/* Main Content: Prompt Editor */}
|
|
107
108
|
<EditorCanvas />
|
|
108
109
|
{/* Agent Cron Jobs Display (only show if jobs exist) */}
|
|
109
|
-
{
|
|
110
|
+
{enableBusinessFeatures && <AgentCronJobs />}
|
|
110
111
|
{/* Advanced Settings Modal */}
|
|
111
112
|
<AgentSettings />
|
|
112
113
|
</>
|
|
@@ -31,9 +31,14 @@ const MessageContent = memo<ContentBlockProps>(({ content, hasTools, id }) => {
|
|
|
31
31
|
return <ContentLoading id={id} />;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
const isSingleLine = (message || '').split('\n').length <= 2;
|
|
35
|
+
|
|
34
36
|
return (
|
|
35
37
|
content && (
|
|
36
|
-
<MarkdownMessage
|
|
38
|
+
<MarkdownMessage
|
|
39
|
+
{...markdownProps}
|
|
40
|
+
className={cx(hasTools && isSingleLine && styles.pWithTool)}
|
|
41
|
+
>
|
|
37
42
|
{message}
|
|
38
43
|
</MarkdownMessage>
|
|
39
44
|
)
|
|
@@ -37,11 +37,20 @@ export default {
|
|
|
37
37
|
'agentCronJobs.empty.description': 'Create your first scheduled task to automate your agent',
|
|
38
38
|
'agentCronJobs.empty.title': 'No scheduled tasks yet',
|
|
39
39
|
'agentCronJobs.enable': 'Enable',
|
|
40
|
+
'agentCronJobs.form.at': 'at',
|
|
40
41
|
'agentCronJobs.form.content.placeholder': 'Enter the prompt or instruction for the agent',
|
|
42
|
+
'agentCronJobs.form.every': 'Every',
|
|
43
|
+
'agentCronJobs.form.frequency': 'Frequency',
|
|
44
|
+
'agentCronJobs.form.hours': 'hour(s)',
|
|
45
|
+
'agentCronJobs.form.maxExecutions': 'Stop after',
|
|
41
46
|
'agentCronJobs.form.maxExecutions.placeholder': 'Leave empty for unlimited',
|
|
42
47
|
'agentCronJobs.form.name.placeholder': 'Enter task name',
|
|
48
|
+
'agentCronJobs.form.time': 'Time',
|
|
43
49
|
'agentCronJobs.form.timeRange.end': 'End Time',
|
|
44
50
|
'agentCronJobs.form.timeRange.start': 'Start Time',
|
|
51
|
+
'agentCronJobs.form.times': 'times',
|
|
52
|
+
'agentCronJobs.form.timezone': 'Timezone',
|
|
53
|
+
'agentCronJobs.form.unlimited': 'Run continuously',
|
|
45
54
|
'agentCronJobs.form.validation.contentRequired': 'Task content is required',
|
|
46
55
|
'agentCronJobs.form.validation.invalidTimeRange': 'Start time must be before end time',
|
|
47
56
|
'agentCronJobs.form.validation.nameRequired': 'Task name is required',
|
|
@@ -86,6 +95,13 @@ export default {
|
|
|
86
95
|
'agentCronJobs.weekday.tuesday': 'Tuesday',
|
|
87
96
|
'agentCronJobs.weekday.wednesday': 'Wednesday',
|
|
88
97
|
'agentCronJobs.weekdays': 'Weekdays',
|
|
98
|
+
'agentCronJobs.weekdays.fri': 'Fri',
|
|
99
|
+
'agentCronJobs.weekdays.mon': 'Mon',
|
|
100
|
+
'agentCronJobs.weekdays.sat': 'Sat',
|
|
101
|
+
'agentCronJobs.weekdays.sun': 'Sun',
|
|
102
|
+
'agentCronJobs.weekdays.thu': 'Thu',
|
|
103
|
+
'agentCronJobs.weekdays.tue': 'Tue',
|
|
104
|
+
'agentCronJobs.weekdays.wed': 'Wed',
|
|
89
105
|
'agentInfoDescription.basic.avatar': 'Avatar',
|
|
90
106
|
'agentInfoDescription.basic.description': 'Description',
|
|
91
107
|
'agentInfoDescription.basic.name': 'Name',
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
|
|
2
1
|
import type { SWRResponse } from 'swr';
|
|
3
2
|
import { type StateCreator } from 'zustand/vanilla';
|
|
4
3
|
|
|
@@ -33,7 +32,10 @@ export interface CronTopicGroupWithJobInfo {
|
|
|
33
32
|
export interface CronSliceAction {
|
|
34
33
|
createAgentCronJob: () => Promise<string | null>;
|
|
35
34
|
internal_refreshCronTopics: () => Promise<void>;
|
|
36
|
-
useFetchCronTopicsWithJobInfo: (
|
|
35
|
+
useFetchCronTopicsWithJobInfo: (
|
|
36
|
+
agentId?: string,
|
|
37
|
+
enabled?: boolean,
|
|
38
|
+
) => SWRResponse<CronTopicGroupWithJobInfo[]>;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
export const createCronSlice: StateCreator<
|
|
@@ -69,9 +71,9 @@ export const createCronSlice: StateCreator<
|
|
|
69
71
|
await mutate([FETCH_CRON_TOPICS_WITH_JOB_INFO_KEY, get().activeAgentId]);
|
|
70
72
|
},
|
|
71
73
|
|
|
72
|
-
useFetchCronTopicsWithJobInfo: (agentId) =>
|
|
74
|
+
useFetchCronTopicsWithJobInfo: (agentId, enabled = true) =>
|
|
73
75
|
useClientDataSWR<CronTopicGroupWithJobInfo[]>(
|
|
74
|
-
|
|
76
|
+
enabled && agentId ? [FETCH_CRON_TOPICS_WITH_JOB_INFO_KEY, agentId] : null,
|
|
75
77
|
async ([, id]: [string, string]) => {
|
|
76
78
|
const [cronJobsResult, cronTopicsGroups] = await Promise.all([
|
|
77
79
|
lambdaClient.agentCronJob.findByAgent.query({ agentId: id }),
|