@lobehub/chat 1.76.0 → 1.77.0

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.
Files changed (135) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/Dockerfile +3 -2
  3. package/Dockerfile.database +3 -1
  4. package/Dockerfile.pglite +3 -1
  5. package/changelog/v1.json +18 -0
  6. package/locales/ar/common.json +12 -1
  7. package/locales/ar/error.json +10 -0
  8. package/locales/ar/models.json +12 -6
  9. package/locales/ar/setting.json +28 -0
  10. package/locales/bg-BG/common.json +12 -1
  11. package/locales/bg-BG/error.json +10 -0
  12. package/locales/bg-BG/models.json +12 -6
  13. package/locales/bg-BG/setting.json +28 -0
  14. package/locales/de-DE/common.json +12 -1
  15. package/locales/de-DE/error.json +10 -0
  16. package/locales/de-DE/models.json +12 -6
  17. package/locales/de-DE/setting.json +28 -0
  18. package/locales/en-US/common.json +12 -1
  19. package/locales/en-US/error.json +10 -0
  20. package/locales/en-US/models.json +12 -6
  21. package/locales/en-US/setting.json +28 -0
  22. package/locales/es-ES/common.json +12 -1
  23. package/locales/es-ES/error.json +10 -0
  24. package/locales/es-ES/models.json +12 -6
  25. package/locales/es-ES/setting.json +28 -0
  26. package/locales/fa-IR/common.json +12 -1
  27. package/locales/fa-IR/error.json +10 -0
  28. package/locales/fa-IR/models.json +12 -6
  29. package/locales/fa-IR/setting.json +28 -0
  30. package/locales/fr-FR/common.json +12 -1
  31. package/locales/fr-FR/error.json +10 -0
  32. package/locales/fr-FR/models.json +12 -6
  33. package/locales/fr-FR/setting.json +28 -0
  34. package/locales/it-IT/common.json +12 -1
  35. package/locales/it-IT/error.json +10 -0
  36. package/locales/it-IT/models.json +12 -6
  37. package/locales/it-IT/setting.json +28 -0
  38. package/locales/ja-JP/common.json +12 -1
  39. package/locales/ja-JP/error.json +10 -0
  40. package/locales/ja-JP/models.json +12 -6
  41. package/locales/ja-JP/setting.json +28 -0
  42. package/locales/ko-KR/common.json +12 -1
  43. package/locales/ko-KR/error.json +10 -0
  44. package/locales/ko-KR/models.json +12 -6
  45. package/locales/ko-KR/setting.json +28 -0
  46. package/locales/nl-NL/common.json +12 -1
  47. package/locales/nl-NL/error.json +10 -0
  48. package/locales/nl-NL/models.json +12 -6
  49. package/locales/nl-NL/setting.json +28 -0
  50. package/locales/pl-PL/common.json +12 -1
  51. package/locales/pl-PL/error.json +10 -0
  52. package/locales/pl-PL/models.json +12 -6
  53. package/locales/pl-PL/setting.json +28 -0
  54. package/locales/pt-BR/common.json +12 -1
  55. package/locales/pt-BR/error.json +10 -0
  56. package/locales/pt-BR/models.json +12 -6
  57. package/locales/pt-BR/setting.json +28 -0
  58. package/locales/ru-RU/common.json +12 -1
  59. package/locales/ru-RU/error.json +10 -0
  60. package/locales/ru-RU/models.json +12 -6
  61. package/locales/ru-RU/setting.json +28 -0
  62. package/locales/tr-TR/common.json +12 -1
  63. package/locales/tr-TR/error.json +10 -0
  64. package/locales/tr-TR/models.json +12 -6
  65. package/locales/tr-TR/setting.json +28 -0
  66. package/locales/vi-VN/common.json +12 -1
  67. package/locales/vi-VN/error.json +10 -0
  68. package/locales/vi-VN/models.json +12 -6
  69. package/locales/vi-VN/setting.json +28 -0
  70. package/locales/zh-CN/common.json +12 -1
  71. package/locales/zh-CN/error.json +10 -0
  72. package/locales/zh-CN/models.json +12 -6
  73. package/locales/zh-CN/setting.json +28 -0
  74. package/locales/zh-TW/common.json +12 -1
  75. package/locales/zh-TW/error.json +10 -0
  76. package/locales/zh-TW/models.json +12 -6
  77. package/locales/zh-TW/setting.json +28 -0
  78. package/package.json +1 -1
  79. package/src/app/[variants]/(main)/(mobile)/me/data/features/Category.tsx +1 -1
  80. package/src/app/[variants]/(main)/chat/features/Migration/UpgradeButton.tsx +2 -1
  81. package/src/app/[variants]/(main)/settings/common/features/Common.tsx +0 -44
  82. package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +40 -14
  83. package/src/app/[variants]/(main)/settings/storage/Advanced.tsx +133 -0
  84. package/src/app/[variants]/(main)/settings/storage/IndexedDBStorage.tsx +55 -0
  85. package/src/app/[variants]/(main)/settings/storage/page.tsx +17 -0
  86. package/src/app/[variants]/(main)/settings/tts/features/const.tsx +4 -0
  87. package/src/components/GroupIcon/index.tsx +25 -0
  88. package/src/components/IndexCard/index.tsx +143 -0
  89. package/src/components/ProgressItem/index.tsx +75 -0
  90. package/src/config/aiModels/openai.ts +10 -0
  91. package/src/database/repositories/dataExporter/index.test.ts +330 -0
  92. package/src/database/repositories/dataExporter/index.ts +216 -0
  93. package/src/database/repositories/dataImporter/__tests__/fixtures/agents.json +65 -0
  94. package/src/database/repositories/dataImporter/__tests__/fixtures/agentsToSessions.json +541 -0
  95. package/src/database/repositories/dataImporter/__tests__/fixtures/topic.json +269 -0
  96. package/src/database/repositories/dataImporter/__tests__/fixtures/userSettings.json +18 -0
  97. package/src/database/repositories/dataImporter/__tests__/fixtures/with-client-id.json +778 -0
  98. package/src/database/repositories/dataImporter/__tests__/index.test.ts +120 -880
  99. package/src/database/repositories/dataImporter/deprecated/__tests__/index.test.ts +940 -0
  100. package/src/database/repositories/dataImporter/deprecated/index.ts +326 -0
  101. package/src/database/repositories/dataImporter/index.ts +684 -289
  102. package/src/features/DataImporter/ImportDetail.tsx +203 -0
  103. package/src/features/DataImporter/SuccessResult.tsx +22 -6
  104. package/src/features/DataImporter/_deprecated.ts +43 -0
  105. package/src/features/DataImporter/config.ts +21 -0
  106. package/src/features/DataImporter/index.tsx +112 -31
  107. package/src/features/DevPanel/PostgresViewer/DataTable/index.tsx +6 -0
  108. package/src/features/User/UserPanel/useMenu.tsx +0 -35
  109. package/src/features/User/__tests__/useMenu.test.tsx +0 -2
  110. package/src/locales/default/common.ts +11 -0
  111. package/src/locales/default/error.ts +10 -0
  112. package/src/locales/default/setting.ts +28 -0
  113. package/src/server/routers/lambda/exporter.ts +25 -0
  114. package/src/server/routers/lambda/importer.ts +19 -3
  115. package/src/server/routers/lambda/index.ts +2 -0
  116. package/src/services/config.ts +80 -135
  117. package/src/services/export/_deprecated.ts +155 -0
  118. package/src/services/export/client.ts +15 -0
  119. package/src/services/export/index.ts +6 -0
  120. package/src/services/export/server.ts +9 -0
  121. package/src/services/export/type.ts +5 -0
  122. package/src/services/import/_deprecated.ts +42 -1
  123. package/src/services/import/client.test.ts +1 -1
  124. package/src/services/import/client.ts +30 -1
  125. package/src/services/import/server.ts +70 -2
  126. package/src/services/import/type.ts +10 -0
  127. package/src/store/global/initialState.ts +1 -0
  128. package/src/types/export.ts +11 -0
  129. package/src/types/exportConfig.ts +2 -0
  130. package/src/types/importer.ts +15 -0
  131. package/src/types/user/settings/tts.ts +1 -1
  132. package/src/utils/client/exportFile.ts +21 -0
  133. package/vitest.config.ts +1 -1
  134. package/src/utils/config.ts +0 -109
  135. /package/src/database/repositories/dataImporter/{__tests__ → deprecated/__tests__}/fixtures/messages.json +0 -0
@@ -0,0 +1,133 @@
1
+ 'use client';
2
+
3
+ import { Form, Icon, type ItemGroup } from '@lobehub/ui';
4
+ import { App, Button, Dropdown, Space } from 'antd';
5
+ import isEqual from 'fast-deep-equal';
6
+ import { ChevronDown, HardDriveDownload, HardDriveUpload } from 'lucide-react';
7
+ import { useCallback } from 'react';
8
+ import { useTranslation } from 'react-i18next';
9
+
10
+ import { FORM_STYLE } from '@/const/layoutTokens';
11
+ import DataImporter from '@/features/DataImporter';
12
+ import { configService } from '@/services/config';
13
+ import { useChatStore } from '@/store/chat';
14
+ import { useFileStore } from '@/store/file';
15
+ import { useSessionStore } from '@/store/session';
16
+ import { useToolStore } from '@/store/tool';
17
+ import { useUserStore } from '@/store/user';
18
+ import { settingsSelectors } from '@/store/user/selectors';
19
+
20
+ const AdvancedActions = () => {
21
+ const { t } = useTranslation('setting');
22
+ const [form] = Form.useForm();
23
+ const { message, modal } = App.useApp();
24
+ const [clearSessions, clearSessionGroups] = useSessionStore((s) => [
25
+ s.clearSessions,
26
+ s.clearSessionGroups,
27
+ ]);
28
+ const [clearTopics, clearAllMessages] = useChatStore((s) => [
29
+ s.removeAllTopics,
30
+ s.clearAllMessages,
31
+ ]);
32
+ const [removeAllFiles] = useFileStore((s) => [s.removeAllFiles]);
33
+ const removeAllPlugins = useToolStore((s) => s.removeAllPlugins);
34
+ const settings = useUserStore(settingsSelectors.currentSettings, isEqual);
35
+
36
+ const handleClear = useCallback(() => {
37
+ modal.confirm({
38
+ centered: true,
39
+ okButtonProps: {
40
+ danger: true,
41
+ },
42
+ onOk: async () => {
43
+ await clearSessions();
44
+ await removeAllPlugins();
45
+ await clearTopics();
46
+ await removeAllFiles();
47
+ await clearAllMessages();
48
+ await clearSessionGroups();
49
+
50
+ message.success(t('danger.clear.success'));
51
+ },
52
+ title: t('danger.clear.confirm'),
53
+ });
54
+ }, []);
55
+
56
+ const system: ItemGroup = {
57
+ children: [
58
+ {
59
+ children: (
60
+ <DataImporter>
61
+ <Button icon={<Icon icon={HardDriveDownload} />}>
62
+ {t('storage.actions.import.button')}
63
+ </Button>
64
+ </DataImporter>
65
+ ),
66
+ label: t('storage.actions.import.title'),
67
+ minWidth: undefined,
68
+ },
69
+ {
70
+ children: (
71
+ <Space.Compact>
72
+ <Button
73
+ icon={<Icon icon={HardDriveUpload} />}
74
+ onClick={() => {
75
+ configService.exportAll();
76
+ }}
77
+ >
78
+ {t('storage.actions.export.button')}
79
+ </Button>
80
+ <Dropdown
81
+ menu={{
82
+ items: [
83
+ {
84
+ key: 'allAgent',
85
+ label: t('storage.actions.export.exportType.allAgent'),
86
+ onClick: configService.exportAgents,
87
+ },
88
+ {
89
+ key: 'allAgentWithMessage',
90
+ label: t('storage.actions.export.exportType.allAgentWithMessage'),
91
+ onClick: configService.exportSessions,
92
+ },
93
+ {
94
+ key: 'globalSetting',
95
+ label: t('storage.actions.export.exportType.globalSetting'),
96
+ onClick: configService.exportSettings,
97
+ },
98
+ ],
99
+ }}
100
+ >
101
+ <Button icon={<Icon icon={ChevronDown} />} />
102
+ </Dropdown>
103
+ </Space.Compact>
104
+ ),
105
+ label: t('storage.actions.export.title'),
106
+ minWidth: undefined,
107
+ },
108
+ {
109
+ children: (
110
+ <Button danger onClick={handleClear} type="primary">
111
+ {t('danger.clear.action')}
112
+ </Button>
113
+ ),
114
+ desc: t('danger.clear.desc'),
115
+ label: t('danger.clear.title'),
116
+ minWidth: undefined,
117
+ },
118
+ ],
119
+ title: t('storage.actions.title'),
120
+ };
121
+ return (
122
+ <Form
123
+ form={form}
124
+ initialValues={settings}
125
+ items={[system]}
126
+ itemsType={'group'}
127
+ variant={'pure'}
128
+ {...FORM_STYLE}
129
+ />
130
+ );
131
+ };
132
+
133
+ export default AdvancedActions;
@@ -0,0 +1,55 @@
1
+ 'use client';
2
+
3
+ import { Skeleton } from 'antd';
4
+ import { DatabaseIcon } from 'lucide-react';
5
+ import { memo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { Flexbox } from 'react-layout-kit';
8
+ import useSWR from 'swr';
9
+
10
+ import GroupIcon from '@/components/GroupIcon';
11
+ import IndexCard from '@/components/IndexCard';
12
+ import ProgressItem from '@/components/ProgressItem';
13
+ import { formatSize } from '@/utils/format';
14
+
15
+ const IndexedDBStorage = memo(() => {
16
+ const { t } = useTranslation('setting');
17
+ const { data, isLoading } = useSWR('fetch-client-usage', async () => {
18
+ const estimate = await navigator.storage.estimate();
19
+ const quota = estimate.quota || 0;
20
+ const usage = estimate.usage || 0;
21
+
22
+ const percent = (usage / quota) * 100;
23
+
24
+ return { percent: percent < 1 ? 1 : percent, total: quota, used: usage };
25
+ });
26
+
27
+ return (
28
+ <IndexCard
29
+ desc={t('storage.desc', { day: 15 })}
30
+ icon={<GroupIcon icon={DatabaseIcon} />}
31
+ padding={0}
32
+ title={t('storage.title')}
33
+ >
34
+ {isLoading ? (
35
+ <Flexbox padding={16}>
36
+ <Skeleton active paragraph={{ rows: 1, width: '100%' }} title={false} />
37
+ <Skeleton.Button active style={{ height: 48, width: '100%' }} />
38
+ </Flexbox>
39
+ ) : (
40
+ <Flexbox gap={16} paddingBlock={16}>
41
+ <ProgressItem
42
+ percent={data?.percent || 0}
43
+ title={t('storage.used')}
44
+ usage={{
45
+ total: data ? formatSize(data.total) : '-',
46
+ used: data ? formatSize(data.used) : '-',
47
+ }}
48
+ />
49
+ </Flexbox>
50
+ )}
51
+ </IndexCard>
52
+ );
53
+ });
54
+
55
+ export default IndexedDBStorage;
@@ -0,0 +1,17 @@
1
+ 'use client';
2
+
3
+ import { isServerMode } from '@/const/version';
4
+
5
+ import Advanced from './Advanced';
6
+ import IndexedDBStorage from './IndexedDBStorage';
7
+
8
+ const StorageEstimate = () => {
9
+ return (
10
+ <>
11
+ {!isServerMode && <IndexedDBStorage />}
12
+ <Advanced />
13
+ </>
14
+ );
15
+ };
16
+
17
+ export default StorageEstimate;
@@ -4,6 +4,10 @@ import { SelectProps } from 'antd';
4
4
  import { LabelRenderer } from '@/components/ModelSelect';
5
5
 
6
6
  export const opeanaiTTSOptions: SelectProps['options'] = [
7
+ {
8
+ label: <LabelRenderer Icon={OpenAI.Avatar} label={'gpt-4o-mini-tts'} />,
9
+ value: 'gpt-4o-mini-tts',
10
+ },
7
11
  {
8
12
  label: <LabelRenderer Icon={OpenAI.Avatar} label={'tts-1'} />,
9
13
  value: 'tts-1',
@@ -0,0 +1,25 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import { LucideIcon } from 'lucide-react';
4
+ import { memo } from 'react';
5
+ import { Center } from 'react-layout-kit';
6
+
7
+ const useStyles = createStyles(({ css, token }) => ({
8
+ icon: css`
9
+ border: 1px solid ${token.colorBorderSecondary};
10
+ border-radius: 8px;
11
+ background: ${token.colorBgElevated};
12
+ `,
13
+ }));
14
+
15
+ const GroupIcon = memo<{ icon: LucideIcon }>(({ icon }) => {
16
+ const { styles } = useStyles();
17
+
18
+ return (
19
+ <Center className={styles.icon} flex={'none'} height={40} width={40}>
20
+ <Icon icon={icon} size={{ fontSize: 24 }} />
21
+ </Center>
22
+ );
23
+ });
24
+
25
+ export default GroupIcon;
@@ -0,0 +1,143 @@
1
+ import { ActionIcon } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import { ChevronDown, ChevronRight } from 'lucide-react';
4
+ import { ReactNode, memo } from 'react';
5
+ import { Center, Flexbox, FlexboxProps } from 'react-layout-kit';
6
+
7
+ const useStyles = createStyles(({ css, token, responsive }) => ({
8
+ card: css`
9
+ position: relative;
10
+
11
+ overflow: hidden;
12
+
13
+ border: 1px solid ${token.colorBorderSecondary};
14
+ border-radius: ${token.borderRadiusLG}px;
15
+
16
+ background: ${token.colorBgContainer};
17
+ `,
18
+ desc: css`
19
+ font-size: 14px;
20
+ line-height: 1.4;
21
+ color: ${token.colorTextDescription};
22
+ ${responsive.mobile} {
23
+ font-size: 12px;
24
+ }
25
+ `,
26
+ expend: css`
27
+ position: absolute;
28
+ inset-block-end: -12px;
29
+ inset-inline-start: 50%;
30
+ transform: translateX(-50%);
31
+
32
+ border: 1px solid ${token.colorBorderSecondary};
33
+ border-radius: 50%;
34
+
35
+ background: ${token.colorBgContainer};
36
+ `,
37
+ header: css`
38
+ border-block-end: 1px solid ${token.colorBorderSecondary};
39
+ background: ${token.colorFillQuaternary};
40
+ `,
41
+ more: css`
42
+ border: 1px solid ${token.colorBorderSecondary};
43
+ `,
44
+ title: css`
45
+ font-size: 16px;
46
+ font-weight: bold;
47
+ line-height: 1.4;
48
+ ${responsive.mobile} {
49
+ font-size: 14px;
50
+ }
51
+ `,
52
+ }));
53
+
54
+ interface IndexCardProps extends Omit<FlexboxProps, 'title'> {
55
+ desc?: ReactNode;
56
+ expand?: boolean;
57
+ extra?: ReactNode;
58
+ icon?: ReactNode;
59
+ moreTooltip?: string;
60
+ onExpand?: () => void;
61
+ onMoreClick?: () => void;
62
+ title?: ReactNode;
63
+ }
64
+
65
+ const IndexCard = memo<IndexCardProps>(
66
+ ({
67
+ expand = true,
68
+ onExpand,
69
+ icon,
70
+ className,
71
+ onMoreClick,
72
+ title,
73
+ extra,
74
+ moreTooltip,
75
+ desc,
76
+ children,
77
+ ...rest
78
+ }) => {
79
+ const { styles } = useStyles();
80
+ return (
81
+ <Flexbox
82
+ style={{
83
+ marginBottom: !expand ? 12 : undefined,
84
+ position: 'relative',
85
+ }}
86
+ >
87
+ <Flexbox
88
+ className={styles.card}
89
+ style={{
90
+ paddingBottom: !expand ? 12 : undefined,
91
+ }}
92
+ >
93
+ {title && (
94
+ <Flexbox
95
+ align={'center'}
96
+ className={styles.header}
97
+ gap={16}
98
+ horizontal
99
+ justify={'space-between'}
100
+ padding={16}
101
+ >
102
+ <Flexbox align={'center'} gap={12} horizontal>
103
+ {icon}
104
+ <Flexbox>
105
+ <div className={styles.title}>{title}</div>
106
+ {desc && <div className={styles.desc}>{desc}</div>}
107
+ </Flexbox>
108
+ </Flexbox>
109
+ <Flexbox align={'center'} gap={8} horizontal>
110
+ {extra}
111
+ {onMoreClick && (
112
+ <ActionIcon
113
+ className={styles.more}
114
+ icon={ChevronRight}
115
+ onClick={onMoreClick}
116
+ size={{ blockSize: 32, borderRadius: '50%', fontSize: 16 }}
117
+ title={moreTooltip}
118
+ />
119
+ )}
120
+ </Flexbox>
121
+ </Flexbox>
122
+ )}
123
+ <Flexbox className={className} gap={16} padding={16} width={'100%'} {...rest}>
124
+ {children}
125
+ </Flexbox>
126
+ </Flexbox>
127
+ {!expand && (
128
+ <Center className={styles.expend} height={24} width={24}>
129
+ <ActionIcon
130
+ icon={ChevronDown}
131
+ onClick={onExpand}
132
+ size={{ blockSize: 24, borderRadius: '50%', fontSize: 16 }}
133
+ />
134
+ </Center>
135
+ )}
136
+ </Flexbox>
137
+ );
138
+ },
139
+ );
140
+
141
+ IndexCard.displayName = 'IndexCard';
142
+
143
+ export default IndexCard;
@@ -0,0 +1,75 @@
1
+ import { Progress } from 'antd';
2
+ import { createStyles, useResponsive } from 'antd-style';
3
+ import { CSSProperties, memo } from 'react';
4
+ import { Flexbox } from 'react-layout-kit';
5
+
6
+ const useStyles = createStyles(({ css, token }) => ({
7
+ desc: css`
8
+ height: 20px;
9
+ font-size: 12px;
10
+ line-height: 20px;
11
+ color: ${token.colorTextTertiary};
12
+ `,
13
+ title: css`
14
+ font-size: 15px;
15
+ font-weight: bold;
16
+ color: ${token.colorTextSecondary};
17
+ `,
18
+ }));
19
+
20
+ interface ProgressItemProps {
21
+ className?: string;
22
+ desc?: string;
23
+ legend?: string;
24
+ padding?: number;
25
+ percent: number;
26
+ style?: CSSProperties;
27
+ title: string;
28
+ usage: {
29
+ total: string | number;
30
+ used: string | number;
31
+ };
32
+ }
33
+
34
+ const ProgressItem = memo<ProgressItemProps>(
35
+ ({ legend, title, desc, usage, percent, style, className }) => {
36
+ const { mobile } = useResponsive();
37
+ const { styles, theme } = useStyles();
38
+
39
+ return (
40
+ <Flexbox className={className} paddingInline={16} style={style} width={'100%'}>
41
+ <Flexbox align={'center'} horizontal justify={'space-between'} width={'100%'}>
42
+ <Flexbox align={'center'} gap={8} horizontal>
43
+ {legend && (
44
+ <Flexbox
45
+ height={8}
46
+ style={{
47
+ background: theme.geekblue,
48
+ borderRadius: '50%',
49
+ flex: 'none',
50
+ }}
51
+ width={8}
52
+ />
53
+ )}
54
+ <Flexbox align={'baseline'} gap={mobile ? 0 : 8} horizontal={!mobile}>
55
+ <div className={styles.title}>{title}</div>
56
+ {desc && <div className={styles.desc}>{desc}</div>}
57
+ </Flexbox>
58
+ </Flexbox>
59
+ <div>
60
+ <span style={{ fontWeight: 'bold' }}>{usage.used}</span>
61
+ {['', '/', usage.total].join(' ')}
62
+ </div>
63
+ </Flexbox>
64
+ <Progress
65
+ percent={percent}
66
+ showInfo={false}
67
+ size={'small'}
68
+ strokeColor={theme.colorPrimary}
69
+ />
70
+ </Flexbox>
71
+ );
72
+ },
73
+ );
74
+
75
+ export default ProgressItem;
@@ -480,6 +480,16 @@ export const openaiTTSModels: AITTSModelCard[] = [
480
480
  },
481
481
  type: 'tts',
482
482
  },
483
+ {
484
+ description:
485
+ 'GPT-4o mini TTS 是一个基于 GPT-4o mini 构建的文本转语音模型,这是一种快速且强大的语言模型。使用它可以将文本转换为自然听起来的语音文本。最大输入标记数为 2000。',
486
+ displayName: 'GPT-4o Mini TTS',
487
+ id: 'gpt-4o-mini-tts',
488
+ pricing: {
489
+ input: 10,
490
+ },
491
+ type: 'tts',
492
+ },
483
493
  ];
484
494
 
485
495
  // 语音识别模型