@lobehub/chat 1.79.8 → 1.79.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/.eslintrc.js +1 -0
  2. package/CHANGELOG.md +33 -0
  3. package/changelog/v1.json +9 -0
  4. package/locales/ar/oauth.json +7 -6
  5. package/locales/bg-BG/oauth.json +7 -6
  6. package/locales/de-DE/oauth.json +7 -6
  7. package/locales/en-US/oauth.json +7 -6
  8. package/locales/es-ES/oauth.json +7 -6
  9. package/locales/fa-IR/oauth.json +7 -6
  10. package/locales/fr-FR/oauth.json +7 -6
  11. package/locales/it-IT/oauth.json +7 -6
  12. package/locales/ja-JP/oauth.json +7 -6
  13. package/locales/ko-KR/oauth.json +7 -6
  14. package/locales/nl-NL/oauth.json +7 -6
  15. package/locales/pl-PL/oauth.json +7 -6
  16. package/locales/pt-BR/oauth.json +7 -6
  17. package/locales/ru-RU/oauth.json +7 -6
  18. package/locales/tr-TR/oauth.json +7 -6
  19. package/locales/vi-VN/oauth.json +7 -6
  20. package/locales/zh-CN/oauth.json +7 -6
  21. package/locales/zh-TW/oauth.json +7 -6
  22. package/package.json +1 -1
  23. package/src/app/(backend)/oidc/[...oidc]/route.ts +27 -201
  24. package/src/app/(backend)/oidc/consent/route.ts +58 -24
  25. package/src/app/(backend)/trpc/async/[trpc]/route.ts +1 -1
  26. package/src/app/(backend)/trpc/edge/[trpc]/route.ts +2 -2
  27. package/src/app/(backend)/trpc/lambda/[trpc]/route.ts +2 -2
  28. package/src/app/(backend)/trpc/tools/[trpc]/route.ts +2 -2
  29. package/src/app/[variants]/(main)/files/[id]/page.tsx +1 -1
  30. package/src/app/[variants]/oauth/consent/[uid]/Client.tsx +184 -57
  31. package/src/app/[variants]/oauth/consent/[uid]/ClientError.tsx +46 -0
  32. package/src/app/[variants]/oauth/consent/[uid]/page.tsx +19 -21
  33. package/src/components/Branding/ProductLogo/index.tsx +6 -1
  34. package/src/config/aiModels/openai.ts +63 -41
  35. package/src/database/server/models/__tests__/adapter.test.ts +1 -5
  36. package/src/libs/oidc-provider/adapter.ts +47 -0
  37. package/src/libs/oidc-provider/config.ts +4 -5
  38. package/src/libs/oidc-provider/http-adapter.ts +60 -28
  39. package/src/libs/oidc-provider/provider.ts +41 -13
  40. package/src/libs/trpc/async/init.ts +1 -1
  41. package/src/{server → libs/trpc/edge}/context.ts +2 -2
  42. package/src/libs/trpc/{index.ts → edge/index.ts} +8 -8
  43. package/src/libs/trpc/{init.ts → edge/init.ts} +2 -2
  44. package/src/libs/trpc/{middleware → edge/middleware}/jwtPayload.test.ts +3 -3
  45. package/src/libs/trpc/{middleware → edge/middleware}/jwtPayload.ts +3 -2
  46. package/src/libs/trpc/lambda/context.ts +70 -0
  47. package/src/libs/trpc/lambda/index.ts +39 -1
  48. package/src/libs/trpc/lambda/init.ts +26 -0
  49. package/src/libs/trpc/lambda/middleware/index.ts +2 -0
  50. package/src/libs/trpc/{middleware → lambda/middleware}/keyVaults.ts +2 -1
  51. package/src/libs/trpc/lambda/{serverDatabase.ts → middleware/serverDatabase.ts} +2 -1
  52. package/src/libs/trpc/middleware/userAuth.test.ts +3 -3
  53. package/src/libs/trpc/middleware/userAuth.ts +1 -1
  54. package/src/libs/trpc/mock.ts +7 -0
  55. package/src/locales/default/oauth.ts +8 -6
  56. package/src/server/routers/edge/appStatus.ts +1 -1
  57. package/src/server/routers/edge/config/index.test.ts +2 -3
  58. package/src/server/routers/edge/config/index.ts +1 -1
  59. package/src/server/routers/edge/index.ts +1 -1
  60. package/src/server/routers/edge/upload.ts +1 -1
  61. package/src/server/routers/lambda/_template.ts +2 -2
  62. package/src/server/routers/lambda/agent.ts +2 -2
  63. package/src/server/routers/lambda/aiModel.ts +2 -2
  64. package/src/server/routers/lambda/aiProvider.ts +2 -2
  65. package/src/server/routers/lambda/chunk.ts +2 -3
  66. package/src/server/routers/lambda/exporter.ts +2 -2
  67. package/src/server/routers/lambda/file.ts +2 -2
  68. package/src/server/routers/lambda/importer.ts +2 -2
  69. package/src/server/routers/lambda/index.ts +1 -1
  70. package/src/server/routers/lambda/knowledgeBase.ts +2 -2
  71. package/src/server/routers/lambda/message.ts +2 -2
  72. package/src/server/routers/lambda/plugin.ts +2 -2
  73. package/src/server/routers/lambda/ragEval.ts +2 -3
  74. package/src/server/routers/lambda/session.ts +2 -2
  75. package/src/server/routers/lambda/sessionGroup.ts +2 -2
  76. package/src/server/routers/lambda/thread.ts +2 -2
  77. package/src/server/routers/lambda/topic.ts +2 -2
  78. package/src/server/routers/lambda/user.ts +2 -2
  79. package/src/server/routers/tools/__tests__/search.test.ts +2 -2
  80. package/src/server/routers/tools/index.ts +1 -1
  81. package/src/server/routers/tools/search.ts +2 -1
  82. package/src/server/services/oidc/index.ts +36 -1
  83. package/src/server/services/oidc/oidcProvider.ts +1 -3
  84. package/src/server/mock.ts +0 -8
  85. /package/src/{server/asyncContext.ts → libs/trpc/async/context.ts} +0 -0
@@ -1,36 +1,114 @@
1
1
  'use client';
2
2
 
3
- import { Button, Card, Typography } from 'antd';
3
+ import { Icon } from '@lobehub/ui';
4
+ import { Button, Card, Divider, Typography } from 'antd';
4
5
  import { createStyles } from 'antd-style';
6
+ import { Link2Icon, ServerIcon } from 'lucide-react';
7
+ import Image from 'next/image';
5
8
  import React, { memo } from 'react';
6
9
  import { useTranslation } from 'react-i18next';
7
10
  import { Center, Flexbox } from 'react-layout-kit';
8
11
 
9
- type ClientProps = {
12
+ import { ProductLogo } from '@/components/Branding';
13
+
14
+ interface ClientProps {
10
15
  clientId: string;
11
- error?: {
12
- message: string;
13
- title: string;
16
+ clientMetadata: {
17
+ clientName?: string;
18
+ logo?: string;
14
19
  };
20
+
21
+ redirectUri?: string;
15
22
  scopes: string[];
16
23
  uid: string;
17
- };
24
+ }
18
25
 
19
26
  const { Title, Text, Paragraph } = Typography;
20
27
 
21
28
  const useStyles = createStyles(({ css, token }) => ({
22
- error: css`
23
- text-align: center;
29
+ authButton: css`
30
+ width: 100%;
31
+ height: 40px;
32
+ border-radius: ${token.borderRadius}px;
33
+ font-weight: 500;
34
+ `,
35
+ cancelButton: css`
36
+ width: 100%;
37
+ height: 40px;
38
+ border-color: ${token.colorBorderSecondary};
39
+ border-radius: ${token.borderRadius}px;
40
+
41
+ font-weight: 500;
42
+ color: ${token.colorTextBase};
43
+
44
+ background-color: transparent;
45
+ `,
46
+ card: css`
47
+ width: 100%;
48
+ max-width: 500px;
49
+ border-color: ${token.colorBorderSecondary};
50
+ border-radius: 12px;
51
+
52
+ background-color: ${token.colorBgContainer};
53
+ `,
54
+ connector: css`
55
+ width: 40px;
56
+ height: 40px;
57
+ `,
58
+ connectorLine: css`
59
+ width: 32px;
60
+ height: 1px;
61
+ background-color: ${token.colorBorderSecondary};
62
+ `,
63
+ container: css`
64
+ width: 100%;
65
+ min-height: 100vh;
66
+ color: ${token.colorTextBase};
67
+ background-color: ${token.colorBgLayout};
68
+ `,
69
+ icon: css`
70
+ overflow: hidden;
71
+ display: flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+
75
+ width: 64px;
76
+ height: 64px;
77
+ border: 1px solid ${token.colorBorderSecondary};
78
+ border-radius: 16px;
79
+
80
+ background-color: ${token.colorBgElevated};
81
+ `,
82
+ iconContainer: css`
83
+ display: flex;
84
+ align-items: center;
85
+ justify-content: center;
86
+ `,
87
+ lobeIcon: css`
88
+ overflow: hidden;
89
+ display: flex;
90
+ align-items: center;
91
+ justify-content: center;
92
+
93
+ width: 64px;
94
+ height: 64px;
95
+ border-radius: 50%;
96
+
97
+ background-color: ${token.colorBgElevated};
24
98
  `,
25
99
  scope: css`
26
100
  margin-block: 8px;
27
101
  padding: 12px;
28
- border-radius: 4px;
29
- background: ${token.colorFillTertiary};
102
+ border-radius: 6px;
103
+ background: ${token.colorFillQuaternary};
30
104
  `,
31
105
  scopes: css`
32
106
  width: 100%;
33
- margin-block: 16px;
107
+ `,
108
+ title: css`
109
+ margin-block-end: ${token.marginLG}px;
110
+ color: ${token.colorTextBase};
111
+ text-align: center;
34
112
  `,
35
113
  }));
36
114
 
@@ -38,60 +116,109 @@ const useStyles = createStyles(({ css, token }) => ({
38
116
  * 获取 Scope 的描述
39
117
  */
40
118
  function getScopeDescription(scope: string, t: any): string {
41
- return t(`consent.scope.${scope}`, scope);
119
+ return t(`consent.scope.${scope.replace(':', '-')}`, scope);
42
120
  }
43
121
 
44
- const ConsentClient = memo(({ uid, clientId, scopes, error }: ClientProps) => {
45
- const { styles } = useStyles();
46
- const { t } = useTranslation('oauth');
122
+ const ConsentClient = memo<ClientProps>(
123
+ ({ uid, clientId, scopes, clientMetadata, redirectUri }) => {
124
+ const { styles, theme } = useStyles();
125
+ const { t } = useTranslation('oauth');
47
126
 
48
- // 如果有错误,显示错误信息
49
- if (error) {
127
+ const clientDisplayName = clientMetadata?.clientName || clientId;
50
128
  return (
51
- <Center height="100vh">
52
- <div className={styles.error}>
53
- <Title level={2}>{error.title}</Title>
54
- <Paragraph>{error.message}</Paragraph>
55
- </div>
56
- </Center>
57
- );
58
- }
59
-
60
- return (
61
- <Center height="100vh">
62
- <Card style={{ maxWidth: 500, width: '100%' }}>
63
- <Flexbox gap={24}>
64
- <Title level={3} style={{ margin: 0 }}>
65
- {t('consent.title')}
129
+ <Center className={styles.container} gap={16}>
130
+ <Flexbox gap={40}>
131
+ <Flexbox align={'center'} gap={12} horizontal justify={'center'}>
132
+ <div className={styles.icon}>
133
+ {clientMetadata?.logo ? (
134
+ <Image
135
+ alt={clientDisplayName}
136
+ height={56}
137
+ src={clientMetadata?.logo}
138
+ unoptimized
139
+ width={56}
140
+ />
141
+ ) : (
142
+ <Icon icon={ServerIcon} />
143
+ )}
144
+ </div>
145
+ <div className={styles.connectorLine} />
146
+ <Center className={styles.connector}>
147
+ <Icon icon={Link2Icon} style={{ color: theme.colorTextSecondary, fontSize: 20 }} />
148
+ </Center>
149
+ <div className={styles.connectorLine} />
150
+ <div className={styles.lobeIcon}>
151
+ <ProductLogo height={48} style={{ objectFit: 'cover' }} width={48} />
152
+ </div>
153
+ </Flexbox>
154
+
155
+ <Title className={styles.title} level={3}>
156
+ {t('consent.title', { clientName: clientDisplayName })}
66
157
  </Title>
67
- <Paragraph>{t('consent.description', { clientId })}</Paragraph>
158
+ </Flexbox>
159
+ <Card className={styles.card}>
160
+ <Flexbox gap={8}>
161
+ <Flexbox gap={12}>
162
+ <Paragraph>{t('consent.description', { clientName: clientDisplayName })}</Paragraph>
68
163
 
69
- <div className={styles.scopes}>
70
- <Paragraph>{t('consent.permissionsTitle')}</Paragraph>
71
- {scopes.map((scope) => (
72
- <div className={styles.scope} key={scope}>
73
- <Text>{getScopeDescription(scope, t)}</Text>
164
+ <div className={styles.scopes}>
165
+ <Paragraph type={'secondary'}>{t('consent.permissionsTitle')}</Paragraph>
166
+ {scopes.map((scope) => (
167
+ <div className={styles.scope} key={scope}>
168
+ <Text>{getScopeDescription(scope, t)}</Text>
169
+ </div>
170
+ ))}
74
171
  </div>
75
- ))}
76
- </div>
77
-
78
- <form action="/oidc/consent" method="post">
79
- <input name="uid" type="hidden" value={uid} />
80
- <Flexbox gap={12} horizontal justify="flex-end">
81
- <Button htmlType="submit" name="consent" value="deny">
82
- {t('consent.buttons.deny')}
83
- </Button>
84
- <Button htmlType="submit" name="consent" type="primary" value="accept">
85
- {t('consent.buttons.accept')}
86
- </Button>
172
+
173
+ <Divider dashed />
174
+ <Flexbox gap={16}>
175
+ <form action="/oidc/consent" method="post" style={{ width: '100%' }}>
176
+ <input name="uid" type="hidden" value={uid} />
177
+ <Flexbox gap={12} horizontal>
178
+ <Button
179
+ className={styles.cancelButton}
180
+ htmlType="submit"
181
+ name="consent"
182
+ value="deny"
183
+ >
184
+ {t('consent.buttons.deny')}
185
+ </Button>
186
+ <Button
187
+ className={styles.authButton}
188
+ htmlType="submit"
189
+ name="consent"
190
+ type="primary"
191
+ value="accept"
192
+ >
193
+ {t('consent.buttons.accept')}
194
+ </Button>
195
+ </Flexbox>
196
+ </form>
197
+ <Center>
198
+ <div style={{ color: theme.colorTextTertiary, fontSize: 12, height: '18px' }}>
199
+ {t('consent.redirectUri')}
200
+ </div>
201
+ <div>
202
+ <div
203
+ style={{
204
+ color: theme.colorTextSecondary,
205
+ fontSize: 12,
206
+ height: '18px',
207
+ }}
208
+ >
209
+ {redirectUri}
210
+ </div>
211
+ </div>
212
+ </Center>
213
+ </Flexbox>
87
214
  </Flexbox>
88
- </form>
89
- </Flexbox>
90
- </Card>
91
- </Center>
92
- );
93
- });
215
+ </Flexbox>
216
+ </Card>
217
+ </Center>
218
+ );
219
+ },
220
+ );
94
221
 
95
222
  ConsentClient.displayName = 'ConsentClient';
96
223
 
97
- export { ConsentClient };
224
+ export default ConsentClient;
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+
3
+ import { Typography } from 'antd';
4
+ import { createStyles } from 'antd-style';
5
+ import React, { memo } from 'react';
6
+ import { Center } from 'react-layout-kit';
7
+
8
+ interface ClientProps {
9
+ error: {
10
+ message: string;
11
+ title: string;
12
+ };
13
+ }
14
+
15
+ const { Title, Paragraph } = Typography;
16
+
17
+ const useStyles = createStyles(({ css, token }) => ({
18
+ container: css`
19
+ width: 100%;
20
+ min-height: 100vh;
21
+ color: ${token.colorTextBase};
22
+ background-color: ${token.colorBgLayout};
23
+ `,
24
+ error: css`
25
+ text-align: center;
26
+ `,
27
+ }));
28
+
29
+ const ConsentClientError = memo<ClientProps>(({ error }) => {
30
+ const { styles } = useStyles();
31
+
32
+ return (
33
+ <Center className={styles.container}>
34
+ <div className={styles.error}>
35
+ <Title level={2} style={{ color: 'inherit' }}>
36
+ {error.title}
37
+ </Title>
38
+ <Paragraph style={{ color: 'inherit' }}>{error.message}</Paragraph>
39
+ </div>
40
+ </Center>
41
+ );
42
+ });
43
+
44
+ ConsentClientError.displayName = 'ConsentClientError';
45
+
46
+ export default ConsentClientError;
@@ -3,11 +3,9 @@ import { notFound } from 'next/navigation';
3
3
  import { oidcEnv } from '@/envs/oidc';
4
4
  import { OIDCService } from '@/server/services/oidc';
5
5
 
6
- import { ConsentClient } from './Client';
6
+ import ConsentClient from './Client';
7
+ import ConsentClientError from './ClientError';
7
8
 
8
- /**
9
- * Consent 授权页面
10
- */
11
9
  const InteractionPage = async (props: { params: Promise<{ uid: string }> }) => {
12
10
  if (!oidcEnv.ENABLE_OIDC) return notFound();
13
11
 
@@ -23,14 +21,11 @@ const InteractionPage = async (props: { params: Promise<{ uid: string }> }) => {
23
21
  // 支持 login 和 consent 类型的交互
24
22
  if (details.prompt.name !== 'consent' && details.prompt.name !== 'login') {
25
23
  return (
26
- <ConsentClient
27
- clientId=""
24
+ <ConsentClientError
28
25
  error={{
29
26
  message: `不支持的交互类型: ${details.prompt.name}`,
30
27
  title: '不支持的交互类型',
31
28
  }}
32
- scopes={[]}
33
- uid={params.uid}
34
29
  />
35
30
  );
36
31
  }
@@ -39,8 +34,21 @@ const InteractionPage = async (props: { params: Promise<{ uid: string }> }) => {
39
34
  const clientId = (details.params.client_id as string) || 'unknown';
40
35
  const scopes = (details.params.scope as string)?.split(' ') || [];
41
36
 
37
+ const clientDetail = await oidcService.getClientMetadata(clientId);
38
+
42
39
  // 渲染客户端组件,无论是 login 还是 consent 类型
43
- return <ConsentClient clientId={clientId} scopes={scopes} uid={params.uid} />;
40
+ return (
41
+ <ConsentClient
42
+ clientId={clientId}
43
+ clientMetadata={{
44
+ clientName: clientDetail?.client_name,
45
+ logo: clientDetail?.logo_uri,
46
+ }}
47
+ redirectUri={details.params.redirect_uri as string}
48
+ scopes={scopes}
49
+ uid={params.uid}
50
+ />
51
+ );
44
52
  } catch (error) {
45
53
  console.error('Error handling OIDC interaction:', error);
46
54
  // 确保错误处理能正确显示
@@ -48,23 +56,13 @@ const InteractionPage = async (props: { params: Promise<{ uid: string }> }) => {
48
56
  // 检查是否是 'interaction session not found' 错误,可以给用户更友好的提示
49
57
  if (errorMessage.includes('interaction session not found')) {
50
58
  return (
51
- <ConsentClient
52
- clientId=""
59
+ <ConsentClientError
53
60
  error={{ message: '授权会话已过期或无效,请重新发起授权流程。', title: '授权会话无效' }}
54
- scopes={[]}
55
- uid={params.uid} // uid 可能已失效,但仍传递给 Client
56
61
  />
57
62
  );
58
63
  }
59
64
 
60
- return (
61
- <ConsentClient
62
- clientId=""
63
- error={{ message: errorMessage, title: '发生错误' }}
64
- scopes={[]}
65
- uid={params.uid}
66
- />
67
- );
65
+ return <ConsentClientError error={{ message: errorMessage, title: '发生错误' }} />;
68
66
  }
69
67
  };
70
68
 
@@ -5,7 +5,12 @@ import { isCustomBranding } from '@/const/version';
5
5
 
6
6
  import CustomLogo from './Custom';
7
7
 
8
- export const ProductLogo = memo<LobeHubProps>((props) => {
8
+ interface ProductLogoProps extends LobeHubProps {
9
+ height?: number;
10
+ width?: number;
11
+ }
12
+
13
+ export const ProductLogo = memo<ProductLogoProps>((props) => {
9
14
  if (isCustomBranding) {
10
15
  return <CustomLogo {...props} />;
11
16
  }
@@ -8,6 +8,66 @@ import {
8
8
  } from '@/types/aiModel';
9
9
 
10
10
  export const openaiChatModels: AIChatModelCard[] = [
11
+ {
12
+ abilities: {
13
+ functionCall: true,
14
+ vision: true,
15
+ },
16
+ contextWindowTokens: 1_047_576,
17
+ description:
18
+ 'GPT-4.1 是我们用于复杂任务的旗舰模型。它非常适合跨领域解决问题。',
19
+ displayName: 'GPT-4.1',
20
+ enabled: true,
21
+ id: 'gpt-4.1',
22
+ maxOutput: 32_768,
23
+ pricing: {
24
+ cachedInput: 0.5,
25
+ input: 2,
26
+ output: 8,
27
+ },
28
+ releasedAt: '2025-04-14',
29
+ type: 'chat',
30
+ },
31
+ {
32
+ abilities: {
33
+ functionCall: true,
34
+ vision: true,
35
+ },
36
+ contextWindowTokens: 1_047_576,
37
+ description:
38
+ 'GPT-4.1 mini 提供了智能、速度和成本之间的平衡,使其成为许多用例中有吸引力的模型。',
39
+ displayName: 'GPT-4.1 mini',
40
+ enabled: true,
41
+ id: 'gpt-4.1-mini',
42
+ maxOutput: 32_768,
43
+ pricing: {
44
+ cachedInput: 0.1,
45
+ input: 0.4,
46
+ output: 1.6,
47
+ },
48
+ releasedAt: '2025-04-14',
49
+ type: 'chat',
50
+ },
51
+ {
52
+ abilities: {
53
+ functionCall: true,
54
+ vision: true,
55
+ },
56
+ contextWindowTokens: 1_047_576,
57
+ description:
58
+ 'GPT-4.1 mini 提供了智能、速度和成本之间的平衡,使其成为许多用例中有吸引力的模型。',
59
+ displayName: 'GPT-4.1 nano',
60
+ enabled: true,
61
+ id: 'gpt-4.1-nano',
62
+ maxOutput: 32_768,
63
+ pricing: {
64
+ cachedInput: 0.025,
65
+ input: 0.1,
66
+ output: 0.4,
67
+ },
68
+ releasedAt: '2025-04-14',
69
+ type: 'chat',
70
+ },
11
71
  {
12
72
  abilities: {
13
73
  functionCall: true,
@@ -36,7 +96,6 @@ export const openaiChatModels: AIChatModelCard[] = [
36
96
  description:
37
97
  'o1-mini是一款针对编程、数学和科学应用场景而设计的快速、经济高效的推理模型。该模型具有128K上下文和2023年10月的知识截止日期。',
38
98
  displayName: 'OpenAI o1-mini',
39
- enabled: true,
40
99
  id: 'o1-mini',
41
100
  maxOutput: 65_536,
42
101
  pricing: {
@@ -75,7 +134,6 @@ export const openaiChatModels: AIChatModelCard[] = [
75
134
  description:
76
135
  'o1是OpenAI新的推理模型,适用于需要广泛通用知识的复杂任务。该模型具有128K上下文和2023年10月的知识截止日期。',
77
136
  displayName: 'OpenAI o1-preview',
78
- enabled: true,
79
137
  id: 'o1-preview',
80
138
  maxOutput: 32_768,
81
139
  pricing: {
@@ -94,8 +152,7 @@ export const openaiChatModels: AIChatModelCard[] = [
94
152
  description:
95
153
  'GPT-4.5 的研究预览版,它是我们迄今为止最大、最强大的 GPT 模型。它拥有广泛的世界知识,并能更好地理解用户意图,使其在创造性任务和自主规划方面表现出色。GPT-4.5 可接受文本和图像输入,并生成文本输出(包括结构化输出)。支持关键的开发者功能,如函数调用、批量 API 和流式输出。在需要创造性、开放式思考和对话的任务(如写作、学习或探索新想法)中,GPT-4.5 表现尤为出色。知识截止日期为 2023 年 10 月。',
96
154
  displayName: 'GPT-4.5 Preview',
97
- enabled: true,
98
- id: 'gpt-4.5-preview',
155
+ id: 'gpt-4.5-preview', // deprecated on 2025-07-14
99
156
  maxOutput: 16_384,
100
157
  pricing: {
101
158
  cachedInput: 37.5,
@@ -114,7 +171,6 @@ export const openaiChatModels: AIChatModelCard[] = [
114
171
  description:
115
172
  'GPT-4o mini是OpenAI在GPT-4 Omni之后推出的最新模型,支持图文输入并输出文本。作为他们最先进的小型模型,它比其他近期的前沿模型便宜很多,并且比GPT-3.5 Turbo便宜超过60%。它保持了最先进的智能,同时具有显著的性价比。GPT-4o mini在MMLU测试中获得了 82% 的得分,目前在聊天偏好上排名高于 GPT-4。',
116
173
  displayName: 'GPT-4o mini',
117
- enabled: true,
118
174
  id: 'gpt-4o-mini',
119
175
  maxOutput: 16_384,
120
176
  pricing: {
@@ -136,6 +192,7 @@ export const openaiChatModels: AIChatModelCard[] = [
136
192
  displayName: 'GPT-4o 1120',
137
193
  id: 'gpt-4o-2024-11-20',
138
194
  pricing: {
195
+ cachedInput: 1.25,
139
196
  input: 2.5,
140
197
  output: 10,
141
198
  },
@@ -151,7 +208,6 @@ export const openaiChatModels: AIChatModelCard[] = [
151
208
  description:
152
209
  'ChatGPT-4o 是一款动态模型,实时更新以保持当前最新版本。它结合了强大的语言理解与生成能力,适合于大规模应用场景,包括客户服务、教育和技术支持。',
153
210
  displayName: 'GPT-4o',
154
- enabled: true,
155
211
  id: 'gpt-4o',
156
212
  pricing: {
157
213
  cachedInput: 1.25,
@@ -161,23 +217,6 @@ export const openaiChatModels: AIChatModelCard[] = [
161
217
  releasedAt: '2024-05-13',
162
218
  type: 'chat',
163
219
  },
164
- {
165
- abilities: {
166
- functionCall: true,
167
- vision: true,
168
- },
169
- contextWindowTokens: 128_000,
170
- description:
171
- 'ChatGPT-4o 是一款动态模型,实时更新以保持当前最新版本。它结合了强大的语言理解与生成能力,适合于大规模应用场景,包括客户服务、教育和技术支持。',
172
- displayName: 'GPT-4o 0806',
173
- id: 'gpt-4o-2024-08-06',
174
- pricing: {
175
- input: 2.5,
176
- output: 10,
177
- },
178
- releasedAt: '2024-08-06',
179
- type: 'chat',
180
- },
181
220
  {
182
221
  abilities: {
183
222
  functionCall: true,
@@ -345,8 +384,7 @@ export const openaiChatModels: AIChatModelCard[] = [
345
384
  description:
346
385
  'GPT-4 提供了一个更大的上下文窗口,能够处理更长的文本输入,适用于需要广泛信息整合和数据分析的场景。',
347
386
  displayName: 'GPT-4 32K',
348
- id: 'gpt-4-32k',
349
- // Will be discontinued on June 6, 2025
387
+ id: 'gpt-4-32k', // deprecated on 2025-06-06
350
388
  legacy: true,
351
389
  pricing: {
352
390
  input: 60,
@@ -354,22 +392,6 @@ export const openaiChatModels: AIChatModelCard[] = [
354
392
  },
355
393
  type: 'chat',
356
394
  },
357
- {
358
- abilities: {
359
- functionCall: true,
360
- },
361
- contextWindowTokens: 32_768,
362
- description:
363
- 'GPT-4 提供了一个更大的上下文窗口,能够处理更长的文本输入,适用于需要广泛信息整合和数据分析的场景。',
364
- displayName: 'GPT-4 32K 0613',
365
- id: 'gpt-4-32k-0613',
366
- pricing: {
367
- input: 60,
368
- output: 120,
369
- },
370
- releasedAt: '2023-06-13',
371
- type: 'chat',
372
- },
373
395
  {
374
396
  abilities: {
375
397
  functionCall: true,
@@ -1,20 +1,16 @@
1
- import type { AdapterUser } from '@auth/core/adapters';
2
1
  import { eq } from 'drizzle-orm/expressions';
3
- import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
2
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
4
3
 
5
4
  import { getTestDBInstance } from '@/database/core/dbForTest';
6
5
  import { users } from '@/database/schemas';
7
6
  import {
8
7
  oidcAccessTokens,
9
- oidcAuthorizationCodes,
10
8
  oidcClients,
11
9
  oidcDeviceCodes,
12
- oidcGrants,
13
10
  oidcInteractions,
14
11
  oidcRefreshTokens,
15
12
  oidcSessions,
16
13
  } from '@/database/schemas/oidc';
17
- import { LobeChatDatabase } from '@/database/type';
18
14
  import { DrizzleAdapter } from '@/libs/oidc-provider/adapter';
19
15
 
20
16
  let serverDB = await getTestDBInstance();
@@ -1,4 +1,5 @@
1
1
  import debug from 'debug';
2
+ import { sql } from 'drizzle-orm';
2
3
  import { eq } from 'drizzle-orm/expressions';
3
4
 
4
5
  import {
@@ -161,6 +162,24 @@ class OIDCAdapter {
161
162
  if (payload.accountId) {
162
163
  record.userId = payload.accountId;
163
164
  log('[%s] Setting userId: %s', this.name, payload.accountId);
165
+ } else {
166
+ try {
167
+ const { getUserAuth } = await import('@/utils/server/auth');
168
+ try {
169
+ const { userId } = await getUserAuth();
170
+ if (userId) {
171
+ payload.accountId = userId;
172
+ record.userId = userId;
173
+ log('[%s] Setting userId from auth context: %s', this.name, userId);
174
+ }
175
+ } catch (authError) {
176
+ log('[%s] Error getting userId from auth context: %O', this.name, authError);
177
+ // 如果获取 userId 失败,继续处理而不抛出错误
178
+ }
179
+ } catch (importError) {
180
+ log('[%s] Error importing auth module: %O', this.name, importError);
181
+ // 如果导入模块失败,继续处理而不抛出错误
182
+ }
164
183
  }
165
184
 
166
185
  if (payload.clientId) {
@@ -180,6 +199,7 @@ class OIDCAdapter {
180
199
 
181
200
  try {
182
201
  log('[%s] Executing upsert DB operation', this.name);
202
+
183
203
  await this.db
184
204
  .insert(table)
185
205
  .values(record as any)
@@ -335,7 +355,34 @@ class OIDCAdapter {
335
355
  */
336
356
  async findByUid(uid: string): Promise<any> {
337
357
  log('[Interaction] findByUid called - uid: %s', uid);
358
+ const table = this.getTable();
359
+ if (this.name === 'Session') {
360
+ try {
361
+ const jsonbUidEq = sql`${(table as any).data}->>'uid' = ${uid}`;
362
+ // @ts-ignore
363
+ const results = await this.db.select().from(table).where(jsonbUidEq).limit(1);
364
+ log('[Session] Find by data.uid query results: %O', results);
338
365
 
366
+ if (!results || results.length === 0) {
367
+ log('[Session] No record found by data.uid: %s', uid);
368
+ return undefined;
369
+ }
370
+
371
+ const model = results[0] as any;
372
+ // 检查过期
373
+ if (model.expiresAt && model.expiresAt < new Date()) {
374
+ log('[Session] Record found by data.uid but expired: %s', uid);
375
+ await this.destroy(model.id); // 仍然使用主键 id 删除
376
+ return undefined;
377
+ }
378
+
379
+ log('[Session] Successfully found by data.uid and returning record data for uid %s', uid);
380
+ return model.data;
381
+ } catch (error) {
382
+ log('[Session] ERROR during findSessionByUid operation for %s: %O', uid, error);
383
+ console.error(`[OIDC Adapter] Error finding Session by uid:`, error);
384
+ }
385
+ }
339
386
  // 复用 find 方法实现
340
387
  log('[Interaction] Delegating to find() method');
341
388
  return this.find(uid);