@lobehub/chat 1.52.5 → 1.52.7

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 CHANGED
@@ -2,6 +2,64 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.52.7](https://github.com/lobehub/lobe-chat/compare/v1.52.6...v1.52.7)
6
+
7
+ <sup>Released on **2025-02-09**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Rewrite to local container in docker deployment mode.
12
+
13
+ #### 💄 Styles
14
+
15
+ - **misc**: Update Cloudflare models.
16
+
17
+ <br/>
18
+
19
+ <details>
20
+ <summary><kbd>Improvements and Fixes</kbd></summary>
21
+
22
+ #### What's fixed
23
+
24
+ - **misc**: Rewrite to local container in docker deployment mode, closes [#5910](https://github.com/lobehub/lobe-chat/issues/5910) ([f399197](https://github.com/lobehub/lobe-chat/commit/f399197))
25
+
26
+ #### Styles
27
+
28
+ - **misc**: Update Cloudflare models, closes [#5899](https://github.com/lobehub/lobe-chat/issues/5899) ([b71206d](https://github.com/lobehub/lobe-chat/commit/b71206d))
29
+
30
+ </details>
31
+
32
+ <div align="right">
33
+
34
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
35
+
36
+ </div>
37
+
38
+ ### [Version 1.52.6](https://github.com/lobehub/lobe-chat/compare/v1.52.5...v1.52.6)
39
+
40
+ <sup>Released on **2025-02-08**</sup>
41
+
42
+ #### 💄 Styles
43
+
44
+ - **misc**: Update ZeroOne models.
45
+
46
+ <br/>
47
+
48
+ <details>
49
+ <summary><kbd>Improvements and Fixes</kbd></summary>
50
+
51
+ #### Styles
52
+
53
+ - **misc**: Update ZeroOne models, closes [#5904](https://github.com/lobehub/lobe-chat/issues/5904) ([6e8d1a7](https://github.com/lobehub/lobe-chat/commit/6e8d1a7))
54
+
55
+ </details>
56
+
57
+ <div align="right">
58
+
59
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
60
+
61
+ </div>
62
+
5
63
  ### [Version 1.52.5](https://github.com/lobehub/lobe-chat/compare/v1.52.4...v1.52.5)
6
64
 
7
65
  <sup>Released on **2025-02-08**</sup>
package/Dockerfile CHANGED
@@ -47,6 +47,10 @@ ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
47
47
 
48
48
  ENV NEXT_PUBLIC_BASE_PATH="${NEXT_PUBLIC_BASE_PATH}"
49
49
 
50
+ # Make the middleware rewrite through local as default
51
+ # refs: https://github.com/lobehub/lobe-chat/issues/5876
52
+ ENV MIDDLEWARE_REWRITE_THROUGH_LOCAL="1"
53
+
50
54
  # Sentry
51
55
  ENV NEXT_PUBLIC_SENTRY_DSN="${NEXT_PUBLIC_SENTRY_DSN}" \
52
56
  SENTRY_ORG="" \
@@ -49,6 +49,10 @@ ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
49
49
 
50
50
  ENV NEXT_PUBLIC_BASE_PATH="${NEXT_PUBLIC_BASE_PATH}"
51
51
 
52
+ # Make the middleware rewrite through local as default
53
+ # refs: https://github.com/lobehub/lobe-chat/issues/5876
54
+ ENV MIDDLEWARE_REWRITE_THROUGH_LOCAL="1"
55
+
52
56
  ENV NEXT_PUBLIC_SERVICE_MODE="${NEXT_PUBLIC_SERVICE_MODE:-server}" \
53
57
  NEXT_PUBLIC_ENABLE_NEXT_AUTH="${NEXT_PUBLIC_ENABLE_NEXT_AUTH:-1}" \
54
58
  APP_URL="http://app.com" \
package/changelog/v1.json CHANGED
@@ -1,4 +1,25 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Rewrite to local container in docker deployment mode."
6
+ ],
7
+ "improvements": [
8
+ "Update Cloudflare models."
9
+ ]
10
+ },
11
+ "date": "2025-02-09",
12
+ "version": "1.52.7"
13
+ },
14
+ {
15
+ "children": {
16
+ "improvements": [
17
+ "Update ZeroOne models."
18
+ ]
19
+ },
20
+ "date": "2025-02-08",
21
+ "version": "1.52.6"
22
+ },
2
23
  {
3
24
  "children": {
4
25
  "fixes": [
@@ -95,6 +95,23 @@ If you are deploying using a public network, the following assumptions apply:
95
95
 
96
96
  Then, copy the `Client ID` and `Client Secret` and save them.
97
97
 
98
+ ### Configure Webhook (Optional)
99
+
100
+ Configure the Casdoor webhook so that LobeChat can receive notifications when user information is updated.
101
+
102
+ Go to `Admin ` -> `Webhooks`, add a webhook, and fill in the following fields:
103
+
104
+ - URL: `https://lobe.example.com/api/auth/webhooks/casdoor`
105
+ - Method: `POST`
106
+ - Content Type: `application/json`
107
+ - Headers: `casdoor-secret`: `Your Webhook Secret`
108
+ > The webhook is generated by yourself, you can visit https://generate-secret.vercel.app/10 to generate a 10 bit secret.
109
+
110
+ - Event: `update-user`
111
+ - Is user extented: `true`
112
+
113
+ Save and Exit, then copy the Webhook secret and fill it in the environment variable `CASDOOR_WEBHOOK_SECRET.
114
+
98
115
  ### Configure Environment Variables
99
116
 
100
117
  Set the obtained `Client ID` and `Client Secret` as `AUTH_CASDOOR_ID` and `AUTH_CASDOOR_SECRET` in the LobeChat environment variables.
@@ -115,6 +132,7 @@ If you are deploying using a public network, the following assumptions apply:
115
132
  | `AUTH_CASDOOR_SECRET` | Required | The client secret from the Casdoor application details page. |
116
133
  | `AUTH_CASDOOR_ISSUER` | Required | The OpenID Connect issuer for the Casdoor provider. |
117
134
  | `NEXTAUTH_URL` | Required | This URL specifies the callback address for Auth.js during OAuth verification and needs to be set only if the default generated redirect address is incorrect. `https://lobe.example.com/api/auth` |
135
+ | `CASDOOR_WEBHOOK_SECRET` | Optional | A key used to verify whether the request sent by Casdoor is legal. |
118
136
 
119
137
  <Callout type={'tip'}>
120
138
  Visit [📘 Environment Variables](/docs/self-hosting/environment-variables/auth#casdoor) for
@@ -92,6 +92,23 @@ tags:
92
92
 
93
93
  随后,复制 `客户端 ID` 和 `客户端密钥`,并保存。
94
94
 
95
+ ### 配置 Webhook (可选)
96
+
97
+ 配置 Casdoor 的 Webhook 以便在用户信息更新时同步到 LobeChat 。
98
+
99
+ 前往 `管理工具` -> `Webhooks`,创建一个 Webhook,添加一个 Webhook,填写以下字段:
100
+
101
+ - 链接:`http://lobe.example.com/api/auth/webhooks/casdoor`
102
+ - 方法:`POST`
103
+ - 内容类型:`application/json`
104
+ - 协议头:`casdoor-secret`: `你的Webhook密钥`
105
+ > 密钥由你自己生成,用于验证 Casdoor 发送的请求是否合法。 可以前往 https://generate-secret.vercel.app/10 生成一个 10 位的密钥。
106
+
107
+ - 事件:`update-user`
108
+ - 拓展用户字段:`true`
109
+
110
+ 保存,并退出。 将该密钥填写到环境变量中的 `CASDOOR_WEBHOOK_SECRET`。
111
+
95
112
  ### 配置环境变量
96
113
 
97
114
  将获取到的 `客户端 ID` 和 `客户端`,设为 LobeChat 环境变量中的 `AUTH_CASDOOR_ID` 和 `AUTH_CASDOOR_SECRET`。
@@ -112,6 +129,7 @@ tags:
112
129
  | `AUTH_CASDOOR_SECRET` | 必选 | Casdoor 应用详情页的客户端密钥 |
113
130
  | `AUTH_CASDOOR_ISSUER` | 必选 | Casdoor 提供程序的 OpenID Connect 颁发者。 |
114
131
  | `NEXTAUTH_URL` | 必选 | 该 URL 用于指定 Auth.js 在执行 OAuth 验证时的回调地址,当默认生成的重定向地址发生不正确时才需要设置。`https://lobe.example.com/api/auth` |
132
+ | `CASDOOR_WEBHOOK_SECRET` | 可选 | 用于验证 Casdoor 发送的 Webhook 请求是否合法的密钥。 |
115
133
 
116
134
  <Callout type={'tip'}>
117
135
  前往 [📘 环境变量](/zh/docs/self-hosting/environment-variables/auth#casdoor) 可查阅相关变量详情。
@@ -43,6 +43,17 @@ If you are using Logto Cloud, assume its endpoint domain is `https://example.log
43
43
 
44
44
  After successful creation, save the `Client ID` and `Client Secret`.
45
45
 
46
+ ### Configure Webhook (Optional)
47
+
48
+ Configure the Logto Webhook so that LobeChat can receive notifications when user information is updated.
49
+
50
+ Go to `Webhooks`, create a Webhook, and fill in the following fields:
51
+
52
+ - Endpoint URL: `https://lobe.example.com/api/auth/webhooks/logto`
53
+ - Events: `User.Data.Updated`
54
+
55
+ After successful creation, copy the Webhook's `Signing Key` and fill it in the `LOGTO_WEBHOOK_SIGNING_KEY` environment variable.
56
+
46
57
  ### Configure Environment Variables
47
58
 
48
59
  <Image alt="Configure Environment Variables" inStep src="https://github.com/user-attachments/assets/15af6d94-af4f-4aa9-bbab-7a46e9f9e837" />
@@ -64,6 +75,7 @@ If you are using Logto Cloud, assume its endpoint domain is `https://example.log
64
75
  | `AUTH_LOGTO_SECRET` | Required | The Client Secret from the Logto App details page |
65
76
  | `AUTH_LOGTO_ISSUER` | Required | OpenID Connect issuer of the Logto provider |
66
77
  | `NEXTAUTH_URL` | Required | This URL specifies the callback address for Auth.js during OAuth verification, needed only if the default generated redirect address is incorrect. `https://lobe.example.com/api/auth` |
78
+ | `LOGTO_WEBHOOK_SIGNING_KEY` | Optional | The key used to verify the legality of Webhook requests sent by Logto. |
67
79
 
68
80
  <Callout type={'tip'}>
69
81
  Visit [📘 Environment Variables](/docs/self-hosting/environment-variables/auth#logto) for details on related variables.
@@ -40,6 +40,17 @@ tags:
40
40
 
41
41
  创建成功后, 将 `Client ID` 和 `Client Secret` 保存下来。
42
42
 
43
+ ### 配置 Webhook (可选)
44
+
45
+ 配置 Logto 的 Webhook,以便在用户信息更新时 LobeChat 可以接收到通知。
46
+
47
+ 前往 `Webhooks` ,创建一个 Webhook,填写以下字段:
48
+
49
+ - 端点 URL: `https://lobe.example.com/api/auth/webhooks/logto`
50
+ - 事件: `User.Data.Updated`
51
+
52
+ 创建成功后,复制 Webhook 的 `签名密钥`。填写到环境变量中的 `LOGTO_WEBHOOK_SIGNING_KEY`。
53
+
43
54
  ### 配置环境变量
44
55
 
45
56
  <Image alt="配置环境变量" inStep src="https://github.com/user-attachments/assets/15af6d94-af4f-4aa9-bbab-7a46e9f9e837" />
@@ -61,6 +72,7 @@ tags:
61
72
  | `AUTH_LOGTO_SECRET` | 必选 | Logto App 详情页的 Client Secret |
62
73
  | `AUTH_LOGTO_ISSUER` | 必选 | Logto 提供程序的 OpenID Connect 颁发者 |
63
74
  | `NEXTAUTH_URL` | 必选 | 该 URL 用于指定 Auth.js 在执行 OAuth 验证时的回调地址,当默认生成的重定向地址发生不正确时才需要设置。`https://lobe.example.com/api/auth` |
75
+ | `LOGTO_WEBHOOK_SIGNING_KEY` | 可选 | 用于验证 Logto 发送的 Webhook 请求是否合法的密钥。 |
64
76
 
65
77
  <Callout type={'tip'}>
66
78
  前往 [📘 环境变量](/zh/docs/self-hosting/environment-variables/auth#logto) 可查阅相关变量详情。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.52.5",
3
+ "version": "1.52.7",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -142,6 +142,7 @@
142
142
  "@trpc/server": "next",
143
143
  "@vercel/analytics": "^1.4.1",
144
144
  "@vercel/edge-config": "^1.4.0",
145
+ "@vercel/functions": "^2",
145
146
  "@vercel/speed-insights": "^1.1.0",
146
147
  "ahooks": "^3.8.4",
147
148
  "ai": "^3.4.33",
@@ -149,6 +150,7 @@
149
150
  "antd-style": "^3.7.1",
150
151
  "brotli-wasm": "^3.0.1",
151
152
  "chroma-js": "^3.1.2",
153
+ "countries-and-timezones": "^3.7.2",
152
154
  "dayjs": "^1.11.13",
153
155
  "debug": "^4.4.0",
154
156
  "dexie": "^3.2.7",
@@ -289,7 +291,7 @@
289
291
  "fs-extra": "^11.2.0",
290
292
  "glob": "^11.0.0",
291
293
  "gray-matter": "^4.0.3",
292
- "happy-dom": "^16.5.2",
294
+ "happy-dom": "^17.0.0",
293
295
  "husky": "^9.1.7",
294
296
  "just-diff": "^6.0.2",
295
297
  "lint-staged": "^15.3.0",
@@ -317,7 +319,7 @@
317
319
  "vitest": "~1.2.2",
318
320
  "vitest-canvas-mock": "^0.3.3"
319
321
  },
320
- "packageManager": "pnpm@9.15.4",
322
+ "packageManager": "pnpm@9.15.5",
321
323
  "publishConfig": {
322
324
  "access": "public",
323
325
  "registry": "https://registry.npmjs.org"
@@ -3,7 +3,6 @@ import { redirect } from 'next/navigation';
3
3
  import { metadataModule } from '@/server/metadata';
4
4
  import { translation } from '@/server/translation';
5
5
  import { DynamicLayoutProps } from '@/types/next';
6
- import { isMobileDevice } from '@/utils/server/responsive';
7
6
  import { RouteVariants } from '@/utils/server/routeVariants';
8
7
 
9
8
  import Category from './features/Category';
@@ -17,8 +16,8 @@ export const generateMetadata = async (props: DynamicLayoutProps) => {
17
16
  });
18
17
  };
19
18
 
20
- const Page = async () => {
21
- const mobile = await isMobileDevice();
19
+ const Page = async (props: DynamicLayoutProps) => {
20
+ const mobile = await RouteVariants.getIsMobile(props);
22
21
 
23
22
  if (!mobile) return redirect('/chat');
24
23
 
@@ -1,7 +1,6 @@
1
1
  import { metadataModule } from '@/server/metadata';
2
2
  import { translation } from '@/server/translation';
3
3
  import { DynamicLayoutProps } from '@/types/next';
4
- import { isMobileDevice } from '@/utils/server/responsive';
5
4
  import { RouteVariants } from '@/utils/server/routeVariants';
6
5
 
7
6
  import Page from './index';
@@ -16,8 +15,8 @@ export const generateMetadata = async (props: DynamicLayoutProps) => {
16
15
  });
17
16
  };
18
17
 
19
- export default async () => {
20
- const isMobile = await isMobileDevice();
18
+ export default async (props: DynamicLayoutProps) => {
19
+ const isMobile = await RouteVariants.getIsMobile(props);
21
20
 
22
21
  return <Page mobile={isMobile} />;
23
22
  };
@@ -4,7 +4,7 @@ import { serverFeatureFlags } from '@/config/featureFlags';
4
4
  import { metadataModule } from '@/server/metadata';
5
5
  import { translation } from '@/server/translation';
6
6
  import { DynamicLayoutProps } from '@/types/next';
7
- import { gerServerDeviceInfo, isMobileDevice } from '@/utils/server/responsive';
7
+ import { gerServerDeviceInfo } from '@/utils/server/responsive';
8
8
  import { RouteVariants } from '@/utils/server/routeVariants';
9
9
 
10
10
  import Page from './index';
@@ -18,11 +18,11 @@ export const generateMetadata = async (props: DynamicLayoutProps) => {
18
18
  url: '/settings/sync',
19
19
  });
20
20
  };
21
- export default async () => {
21
+ export default async (props: DynamicLayoutProps) => {
22
22
  const enableWebrtc = serverFeatureFlags().enableWebrtc;
23
23
  if (!enableWebrtc) return notFound();
24
24
 
25
- const isMobile = await isMobileDevice();
25
+ const isMobile = await RouteVariants.getIsMobile(props);
26
26
  const { os, browser } = await gerServerDeviceInfo();
27
27
 
28
28
  return <Page browser={browser} mobile={isMobile} os={os} />;
@@ -3,29 +3,27 @@ import { AIChatModelCard } from '@/types/aiModel';
3
3
  const cloudflareChatModels: AIChatModelCard[] = [
4
4
  {
5
5
  contextWindowTokens: 16_384,
6
- displayName: 'deepseek-coder-6.7b-instruct-awq',
6
+ displayName: 'DeepSeek R1 Distill Qwen 32B',
7
7
  enabled: true,
8
- id: '@hf/thebloke/deepseek-coder-6.7b-instruct-awq',
8
+ id: '@cf/deepseek-ai/deepseek-r1-distill-qwen-32b',
9
9
  type: 'chat',
10
10
  },
11
11
  {
12
12
  contextWindowTokens: 2048,
13
13
  displayName: 'gemma-7b-it',
14
- enabled: true,
15
14
  id: '@hf/google/gemma-7b-it',
16
15
  type: 'chat',
17
16
  },
18
17
  {
19
18
  contextWindowTokens: 4096,
20
19
  displayName: 'hermes-2-pro-mistral-7b',
21
- enabled: true,
22
20
  id: '@hf/nousresearch/hermes-2-pro-mistral-7b',
23
21
  type: 'chat',
24
22
  },
25
23
  {
26
- contextWindowTokens: 8192,
27
- displayName: 'llama-3-8b-instruct-awq',
28
- id: '@cf/meta/llama-3-8b-instruct-awq',
24
+ contextWindowTokens: 131_072,
25
+ displayName: 'llama 3.3 70b',
26
+ id: '@cf/meta/llama-3.3-70b-instruct-fp8-fast',
29
27
  type: 'chat',
30
28
  },
31
29
  {
@@ -50,7 +48,6 @@ const cloudflareChatModels: AIChatModelCard[] = [
50
48
  {
51
49
  contextWindowTokens: 32_768,
52
50
  displayName: 'openhermes-2.5-mistral-7b-awq',
53
- enabled: true,
54
51
  id: '@hf/thebloke/openhermes-2.5-mistral-7b-awq',
55
52
  type: 'chat',
56
53
  },
@@ -64,20 +61,17 @@ const cloudflareChatModels: AIChatModelCard[] = [
64
61
  {
65
62
  contextWindowTokens: 4096,
66
63
  displayName: 'starling-lm-7b-beta',
67
- enabled: true,
68
64
  id: '@hf/nexusflow/starling-lm-7b-beta',
69
65
  type: 'chat',
70
66
  },
71
67
  {
72
68
  contextWindowTokens: 32_768,
73
69
  displayName: 'zephyr-7b-beta-awq',
74
- enabled: true,
75
70
  id: '@hf/thebloke/zephyr-7b-beta-awq',
76
71
  type: 'chat',
77
72
  },
78
73
  {
79
74
  displayName: 'meta-llama-3-8b-instruct',
80
- enabled: true,
81
75
  id: '@hf/meta-llama/meta-llama-3-8b-instruct',
82
76
  type: 'chat',
83
77
  },
@@ -14,11 +14,26 @@ const zerooneChatModels: AIChatModelCard[] = [
14
14
  },
15
15
  type: 'chat',
16
16
  },
17
+ {
18
+ abilities: {
19
+ vision: true,
20
+ },
21
+ contextWindowTokens: 16_384,
22
+ description: '复杂视觉任务模型,提供基于多张图片的高性能理解、分析能力。',
23
+ displayName: 'Yi Vision V2',
24
+ enabled: true,
25
+ id: 'yi-vision-v2',
26
+ pricing: {
27
+ currency: 'CNY',
28
+ input: 6,
29
+ output: 6,
30
+ },
31
+ type: 'chat',
32
+ },
17
33
  {
18
34
  contextWindowTokens: 16_384,
19
35
  description: '小而精悍,轻量极速模型。提供强化数学运算和代码编写能力。',
20
36
  displayName: 'Yi Spark',
21
- enabled: true,
22
37
  id: 'yi-spark',
23
38
  pricing: {
24
39
  currency: 'CNY',
@@ -31,7 +46,6 @@ const zerooneChatModels: AIChatModelCard[] = [
31
46
  contextWindowTokens: 16_384,
32
47
  description: '中型尺寸模型升级微调,能力均衡,性价比高。深度优化指令遵循能力。',
33
48
  displayName: 'Yi Medium',
34
- enabled: true,
35
49
  id: 'yi-medium',
36
50
  pricing: {
37
51
  currency: 'CNY',
@@ -44,7 +58,6 @@ const zerooneChatModels: AIChatModelCard[] = [
44
58
  contextWindowTokens: 200_000,
45
59
  description: '200K 超长上下文窗口,提供长文本深度理解和生成能力。',
46
60
  displayName: 'Yi Medium 200K',
47
- enabled: true,
48
61
  id: 'yi-medium-200k',
49
62
  pricing: {
50
63
  currency: 'CNY',
@@ -57,7 +70,6 @@ const zerooneChatModels: AIChatModelCard[] = [
57
70
  contextWindowTokens: 16_384,
58
71
  description: '超高性价比、卓越性能。根据性能和推理速度、成本,进行平衡性高精度调优。',
59
72
  displayName: 'Yi Large Turbo',
60
- enabled: true,
61
73
  id: 'yi-large-turbo',
62
74
  pricing: {
63
75
  currency: 'CNY',
@@ -71,7 +83,6 @@ const zerooneChatModels: AIChatModelCard[] = [
71
83
  description:
72
84
  '基于 yi-large 超强模型的高阶服务,结合检索与生成技术提供精准答案,实时全网检索信息服务。',
73
85
  displayName: 'Yi Large RAG',
74
- enabled: true,
75
86
  id: 'yi-large-rag',
76
87
  pricing: {
77
88
  currency: 'CNY',
@@ -88,7 +99,6 @@ const zerooneChatModels: AIChatModelCard[] = [
88
99
  description:
89
100
  '在 yi-large 模型的基础上支持并强化了工具调用的能力,适用于各种需要搭建 agent 或 workflow 的业务场景。',
90
101
  displayName: 'Yi Large FC',
91
- enabled: true,
92
102
  id: 'yi-large-fc',
93
103
  pricing: {
94
104
  currency: 'CNY',
@@ -116,7 +126,6 @@ const zerooneChatModels: AIChatModelCard[] = [
116
126
  contextWindowTokens: 16_384,
117
127
  description: '复杂视觉任务模型,提供高性能图片理解、分析能力。',
118
128
  displayName: 'Yi Vision',
119
- enabled: true,
120
129
  id: 'yi-vision',
121
130
  pricing: {
122
131
  currency: 'CNY',
package/src/config/app.ts CHANGED
@@ -48,6 +48,7 @@ export const getAppConfig = () => {
48
48
 
49
49
  APP_URL: z.string().optional(),
50
50
  VERCEL_EDGE_CONFIG: z.string().optional(),
51
+ MIDDLEWARE_REWRITE_THROUGH_LOCAL: z.boolean().optional(),
51
52
 
52
53
  CDN_USE_GLOBAL: z.boolean().optional(),
53
54
  CUSTOM_FONT_FAMILY: z.string().optional(),
@@ -80,6 +81,8 @@ export const getAppConfig = () => {
80
81
  VERCEL_EDGE_CONFIG: process.env.VERCEL_EDGE_CONFIG,
81
82
 
82
83
  APP_URL,
84
+ MIDDLEWARE_REWRITE_THROUGH_LOCAL: process.env.MIDDLEWARE_REWRITE_THROUGH_LOCAL === '1',
85
+
83
86
  CUSTOM_FONT_FAMILY: process.env.CUSTOM_FONT_FAMILY,
84
87
  CUSTOM_FONT_URL: process.env.CUSTOM_FONT_URL,
85
88
  CDN_USE_GLOBAL: process.env.CDN_USE_GLOBAL === '1',
@@ -6,28 +6,25 @@ const Cloudflare: ModelProviderCard = {
6
6
  chatModels: [
7
7
  {
8
8
  contextWindowTokens: 16_384,
9
- displayName: 'deepseek-coder-6.7b-instruct-awq',
9
+ displayName: 'DeepSeek R1 Distill Qwen 32B',
10
10
  enabled: true,
11
- id: '@hf/thebloke/deepseek-coder-6.7b-instruct-awq',
11
+ id: '@cf/deepseek-ai/deepseek-r1-distill-qwen-32b',
12
12
  },
13
13
  {
14
14
  contextWindowTokens: 2048,
15
15
  displayName: 'gemma-7b-it',
16
- enabled: true,
17
16
  id: '@hf/google/gemma-7b-it',
18
17
  },
19
18
  {
20
19
  contextWindowTokens: 4096,
21
20
  displayName: 'hermes-2-pro-mistral-7b',
22
-
23
- enabled: true,
24
21
  // functionCall: true,
25
22
  id: '@hf/nousresearch/hermes-2-pro-mistral-7b',
26
23
  },
27
24
  {
28
- contextWindowTokens: 8192,
29
- displayName: 'llama-3-8b-instruct-awq',
30
- id: '@cf/meta/llama-3-8b-instruct-awq',
25
+ contextWindowTokens: 131_072,
26
+ displayName: 'llama 3.3 70b',
27
+ id: '@cf/meta/llama-3.3-70b-instruct-fp8-fast',
31
28
  },
32
29
  {
33
30
  contextWindowTokens: 4096,
@@ -37,7 +34,6 @@ const Cloudflare: ModelProviderCard = {
37
34
  {
38
35
  contextWindowTokens: 32_768,
39
36
  displayName: 'neural-chat-7b-v3-1-awq',
40
- enabled: true,
41
37
  id: '@hf/thebloke/neural-chat-7b-v3-1-awq',
42
38
  },
43
39
  {
@@ -48,7 +44,6 @@ const Cloudflare: ModelProviderCard = {
48
44
  {
49
45
  contextWindowTokens: 32_768,
50
46
  displayName: 'openhermes-2.5-mistral-7b-awq',
51
- enabled: true,
52
47
  id: '@hf/thebloke/openhermes-2.5-mistral-7b-awq',
53
48
  },
54
49
  {
@@ -60,19 +55,15 @@ const Cloudflare: ModelProviderCard = {
60
55
  {
61
56
  contextWindowTokens: 4096,
62
57
  displayName: 'starling-lm-7b-beta',
63
- enabled: true,
64
58
  id: '@hf/nexusflow/starling-lm-7b-beta',
65
59
  },
66
60
  {
67
61
  contextWindowTokens: 32_768,
68
62
  displayName: 'zephyr-7b-beta-awq',
69
- enabled: true,
70
63
  id: '@hf/thebloke/zephyr-7b-beta-awq',
71
64
  },
72
65
  {
73
66
  displayName: 'meta-llama-3-8b-instruct',
74
- enabled: true,
75
- functionCall: false,
76
67
  id: '@hf/meta-llama/meta-llama-3-8b-instruct',
77
68
  },
78
69
  ],
@@ -15,11 +15,23 @@ const ZeroOne: ModelProviderCard = {
15
15
  output: 0.99,
16
16
  },
17
17
  },
18
+ {
19
+ contextWindowTokens: 16_384,
20
+ description: '复杂视觉任务模型,提供基于多张图片的高性能理解、分析能力。',
21
+ displayName: 'Yi Vision V2',
22
+ enabled: true,
23
+ id: 'yi-vision-v2',
24
+ pricing: {
25
+ currency: 'CNY',
26
+ input: 6,
27
+ output: 6,
28
+ },
29
+ vision: true,
30
+ },
18
31
  {
19
32
  contextWindowTokens: 16_384,
20
33
  description: '小而精悍,轻量极速模型。提供强化数学运算和代码编写能力。',
21
34
  displayName: 'Yi Spark',
22
- enabled: true,
23
35
  id: 'yi-spark',
24
36
  pricing: {
25
37
  currency: 'CNY',
@@ -31,7 +43,6 @@ const ZeroOne: ModelProviderCard = {
31
43
  contextWindowTokens: 16_384,
32
44
  description: '中型尺寸模型升级微调,能力均衡,性价比高。深度优化指令遵循能力。',
33
45
  displayName: 'Yi Medium',
34
- enabled: true,
35
46
  id: 'yi-medium',
36
47
  pricing: {
37
48
  currency: 'CNY',
@@ -43,7 +54,6 @@ const ZeroOne: ModelProviderCard = {
43
54
  contextWindowTokens: 200_000,
44
55
  description: '200K 超长上下文窗口,提供长文本深度理解和生成能力。',
45
56
  displayName: 'Yi Medium 200K',
46
- enabled: true,
47
57
  id: 'yi-medium-200k',
48
58
  pricing: {
49
59
  currency: 'CNY',
@@ -55,7 +65,6 @@ const ZeroOne: ModelProviderCard = {
55
65
  contextWindowTokens: 16_384,
56
66
  description: '超高性价比、卓越性能。根据性能和推理速度、成本,进行平衡性高精度调优。',
57
67
  displayName: 'Yi Large Turbo',
58
- enabled: true,
59
68
  id: 'yi-large-turbo',
60
69
  pricing: {
61
70
  currency: 'CNY',
@@ -68,7 +77,6 @@ const ZeroOne: ModelProviderCard = {
68
77
  description:
69
78
  '基于 yi-large 超强模型的高阶服务,结合检索与生成技术提供精准答案,实时全网检索信息服务。',
70
79
  displayName: 'Yi Large RAG',
71
- enabled: true,
72
80
  id: 'yi-large-rag',
73
81
  pricing: {
74
82
  currency: 'CNY',
@@ -81,7 +89,6 @@ const ZeroOne: ModelProviderCard = {
81
89
  description:
82
90
  '在 yi-large 模型的基础上支持并强化了工具调用的能力,适用于各种需要搭建 agent 或 workflow 的业务场景。',
83
91
  displayName: 'Yi Large FC',
84
- enabled: true,
85
92
  functionCall: true,
86
93
  id: 'yi-large-fc',
87
94
  pricing: {
@@ -105,7 +112,6 @@ const ZeroOne: ModelProviderCard = {
105
112
  contextWindowTokens: 16_384,
106
113
  description: '复杂视觉任务模型,提供高性能图片理解、分析能力。',
107
114
  displayName: 'Yi Vision',
108
- enabled: true,
109
115
  id: 'yi-vision',
110
116
  pricing: {
111
117
  currency: 'CNY',
package/src/middleware.ts CHANGED
@@ -1,12 +1,15 @@
1
1
  import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
2
2
  import { NextRequest, NextResponse } from 'next/server';
3
3
  import { UAParser } from 'ua-parser-js';
4
+ import urlJoin from 'url-join';
4
5
 
6
+ import { appEnv } from '@/config/app';
5
7
  import { authEnv } from '@/config/auth';
6
8
  import { LOBE_THEME_APPEARANCE } from '@/const/theme';
7
9
  import NextAuthEdge from '@/libs/next-auth/edge';
8
10
  import { Locales } from '@/locales/resources';
9
11
  import { parseBrowserLanguage } from '@/utils/locale';
12
+ import { parseDefaultThemeFromCountry } from '@/utils/server/geo';
10
13
  import { RouteVariants } from '@/utils/server/routeVariants';
11
14
 
12
15
  import { OAUTH_AUTHORIZED } from './const/auth';
@@ -37,30 +40,17 @@ export const config = {
37
40
  ],
38
41
  };
39
42
 
40
- const parseDefaultThemeFromTime = (request: NextRequest) => {
41
- // 获取经度信息,Next.js 会自动解析 geo 信息到请求对象中
42
- const longitude = 'geo' in request && (request.geo as any)?.longitude;
43
-
44
- if (typeof longitude === 'number') {
45
- // 计算时区偏移(每15度经度对应1小时)
46
- // 东经为正,西经为负
47
- const offsetHours = Math.round(longitude / 15);
48
-
49
- // 计算当地时间
50
- const localHour = (new Date().getUTCHours() + offsetHours + 24) % 24;
51
- console.log(`[theme] localHour: ${localHour}`);
43
+ const defaultMiddleware = (request: NextRequest) => {
44
+ const url = new URL(request.url);
52
45
 
53
- // 6点到18点之间返回 light 主题
54
- return localHour >= 6 && localHour < 18 ? 'light' : 'dark';
46
+ // skip all api requests
47
+ if (['/api', '/trpc', '/webapi'].some((path) => url.pathname.startsWith(path))) {
48
+ return NextResponse.next();
55
49
  }
56
50
 
57
- return 'light';
58
- };
59
-
60
- const defaultMiddleware = (request: NextRequest) => {
61
51
  // 1. 从 cookie 中读取用户偏好
62
52
  const theme =
63
- request.cookies.get(LOBE_THEME_APPEARANCE)?.value || parseDefaultThemeFromTime(request);
53
+ request.cookies.get(LOBE_THEME_APPEARANCE)?.value || parseDefaultThemeFromCountry(request);
64
54
 
65
55
  // if it's a new user, there's no cookie
66
56
  // So we need to use the fallback language parsed by accept-language
@@ -80,11 +70,12 @@ const defaultMiddleware = (request: NextRequest) => {
80
70
  theme,
81
71
  });
82
72
 
83
- const url = new URL(request.url);
84
-
85
- // skip all api requests
86
- if (['/api', '/trpc', '/webapi'].some((path) => url.pathname.startsWith(path))) {
87
- return NextResponse.next();
73
+ // if app is in docker, rewrite to self container
74
+ // https://github.com/lobehub/lobe-chat/issues/5876
75
+ if (appEnv.MIDDLEWARE_REWRITE_THROUGH_LOCAL) {
76
+ url.protocol = 'http';
77
+ url.host = '127.0.0.1';
78
+ url.port = process.env.PORT || '3210';
88
79
  }
89
80
 
90
81
  // refs: https://github.com/lobehub/lobe-chat/pull/5866
@@ -92,7 +83,11 @@ const defaultMiddleware = (request: NextRequest) => {
92
83
  // / -> /zh-CN__0__dark
93
84
  // /discover -> /zh-CN__0__dark/discover
94
85
  const nextPathname = `/${route}` + (url.pathname === '/' ? '' : url.pathname);
95
- console.log(`[rewrite] ${url.pathname} -> ${nextPathname}`);
86
+ const nextURL = appEnv.MIDDLEWARE_REWRITE_THROUGH_LOCAL
87
+ ? urlJoin(url.origin, nextPathname)
88
+ : nextPathname;
89
+
90
+ console.log(`[rewrite] ${url.pathname} -> ${nextURL}`);
96
91
 
97
92
  url.pathname = nextPathname;
98
93
 
@@ -0,0 +1,39 @@
1
+ import { geolocation } from '@vercel/functions';
2
+ import { getCountry } from 'countries-and-timezones';
3
+ import { NextRequest } from 'next/server';
4
+
5
+ export const parseDefaultThemeFromCountry = (request: NextRequest) => {
6
+ // 1. 从请求头中获取国家代码
7
+ const geo = geolocation(request);
8
+
9
+ const countryCode =
10
+ geo?.country ||
11
+ request.headers.get('x-vercel-ip-country') || // Vercel
12
+ request.headers.get('cf-ipcountry') || // Cloudflare
13
+ request.headers.get('x-zeabur-ip-country') || // Zeabur
14
+ request.headers.get('x-country-code'); // Netlify
15
+
16
+ // 如果没有获取到国家代码,直接返回 light 主题
17
+ if (!countryCode) return 'light';
18
+
19
+ // 2. 获取国家的时区信息
20
+ const country = getCountry(countryCode);
21
+
22
+ // 如果找不到国家信息或该国家没有时区信息,返回 light 主题
23
+ if (!country?.timezones?.length) return 'light';
24
+
25
+ // 3. 获取该国家的第一个 时区下的当前时间
26
+ const localTime = new Date().toLocaleString('en-US', {
27
+ hour: 'numeric',
28
+ hour12: false,
29
+ timeZone: country.timezones[0],
30
+ });
31
+
32
+ // 4. 解析小时数并确定主题
33
+ const localHour = parseInt(localTime);
34
+ // console.log(
35
+ // `[theme] Country: ${countryCode}, Timezone: ${country.timezones[0]}, LocalHour: ${localHour}`,
36
+ // );
37
+
38
+ return localHour >= 6 && localHour < 18 ? 'light' : 'dark';
39
+ };
@@ -4,7 +4,7 @@ import { UAParser } from 'ua-parser-js';
4
4
  /**
5
5
  * check mobile device in server
6
6
  */
7
- export const isMobileDevice = async () => {
7
+ const isMobileDevice = async () => {
8
8
  if (typeof process === 'undefined') {
9
9
  throw new Error('[Server method] you are importing a server-only module outside of server');
10
10
  }