@lobehub/chat 1.116.3 → 1.117.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.
- package/.github/PULL_REQUEST_TEMPLATE.md +1 -0
- package/.github/workflows/release.yml +2 -0
- package/.i18nrc.js +1 -1
- package/CHANGELOG.md +117 -0
- package/changelog/v1.json +21 -0
- package/locales/ar/components.json +12 -0
- package/locales/ar/models.json +3 -0
- package/locales/bg-BG/components.json +12 -0
- package/locales/bg-BG/models.json +3 -0
- package/locales/de-DE/components.json +12 -0
- package/locales/de-DE/models.json +3 -0
- package/locales/en-US/components.json +12 -0
- package/locales/en-US/models.json +3 -0
- package/locales/es-ES/components.json +12 -0
- package/locales/es-ES/models.json +3 -0
- package/locales/fa-IR/components.json +12 -0
- package/locales/fa-IR/models.json +3 -0
- package/locales/fr-FR/components.json +12 -0
- package/locales/fr-FR/models.json +3 -0
- package/locales/it-IT/components.json +12 -0
- package/locales/it-IT/models.json +3 -0
- package/locales/ja-JP/components.json +12 -0
- package/locales/ja-JP/models.json +3 -0
- package/locales/ko-KR/components.json +12 -0
- package/locales/ko-KR/models.json +3 -0
- package/locales/nl-NL/components.json +12 -0
- package/locales/nl-NL/models.json +3 -0
- package/locales/pl-PL/components.json +12 -0
- package/locales/pl-PL/models.json +3 -0
- package/locales/pt-BR/components.json +12 -0
- package/locales/pt-BR/models.json +3 -0
- package/locales/ru-RU/components.json +12 -0
- package/locales/ru-RU/models.json +3 -0
- package/locales/tr-TR/components.json +12 -0
- package/locales/tr-TR/models.json +3 -0
- package/locales/vi-VN/components.json +12 -0
- package/locales/vi-VN/models.json +3 -0
- package/locales/zh-CN/components.json +12 -0
- package/locales/zh-CN/models.json +3 -0
- package/locales/zh-TW/components.json +12 -0
- package/locales/zh-TW/models.json +3 -0
- package/package.json +5 -5
- package/packages/const/src/image.ts +9 -0
- package/packages/const/src/index.ts +2 -1
- package/packages/const/src/meta.ts +3 -2
- package/packages/const/src/settings/agent.ts +9 -4
- package/packages/const/src/settings/systemAgent.ts +0 -3
- package/packages/database/vitest.config.mts +1 -0
- package/packages/database/vitest.config.server.mts +1 -0
- package/packages/file-loaders/package.json +1 -1
- package/packages/file-loaders/vitest.config.mts +3 -7
- package/packages/model-runtime/src/RouterRuntime/createRuntime.ts +11 -9
- package/packages/model-runtime/src/google/createImage.test.ts +657 -0
- package/packages/model-runtime/src/google/createImage.ts +152 -0
- package/packages/model-runtime/src/google/index.test.ts +0 -328
- package/packages/model-runtime/src/google/index.ts +3 -40
- package/packages/model-runtime/src/utils/modelParse.ts +2 -1
- package/packages/model-runtime/src/utils/openaiCompatibleFactory/createImage.ts +239 -0
- package/packages/model-runtime/src/utils/openaiCompatibleFactory/index.test.ts +22 -22
- package/packages/model-runtime/src/utils/openaiCompatibleFactory/index.ts +9 -116
- package/packages/model-runtime/src/utils/postProcessModelList.ts +55 -0
- package/packages/model-runtime/src/utils/streams/google-ai.test.ts +7 -7
- package/packages/model-runtime/src/utils/streams/google-ai.ts +15 -2
- package/packages/model-runtime/src/utils/streams/openai/openai.test.ts +41 -0
- package/packages/model-runtime/src/utils/streams/openai/openai.ts +38 -2
- package/packages/model-runtime/src/utils/streams/protocol.test.ts +32 -0
- package/packages/model-runtime/src/utils/streams/protocol.ts +7 -3
- package/packages/model-runtime/src/utils/usageConverter.test.ts +58 -0
- package/packages/model-runtime/src/utils/usageConverter.ts +5 -1
- package/packages/model-runtime/vitest.config.mts +3 -0
- package/packages/prompts/package.json +0 -1
- package/packages/prompts/src/chains/__tests__/abstractChunk.test.ts +52 -0
- package/packages/prompts/src/chains/__tests__/answerWithContext.test.ts +100 -0
- package/packages/prompts/src/chains/__tests__/rewriteQuery.test.ts +88 -0
- package/packages/prompts/src/chains/__tests__/summaryGenerationTitle.test.ts +107 -0
- package/packages/prompts/src/chains/abstractChunk.ts +0 -2
- package/packages/prompts/src/chains/rewriteQuery.ts +3 -1
- package/packages/prompts/src/index.test.ts +41 -0
- package/packages/prompts/src/prompts/systemRole/index.test.ts +136 -0
- package/packages/prompts/vitest.config.mts +3 -0
- package/packages/types/src/index.ts +2 -0
- package/packages/utils/package.json +5 -1
- package/packages/utils/src/client/index.ts +2 -0
- package/packages/utils/src/server/index.ts +5 -0
- package/packages/utils/vitest.config.mts +4 -0
- package/src/app/(backend)/middleware/auth/index.test.ts +2 -2
- package/src/app/(backend)/middleware/auth/index.ts +1 -1
- package/src/app/(backend)/oidc/consent/route.ts +1 -2
- package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +2 -2
- package/src/app/(backend)/webapi/plugin/gateway/route.ts +1 -1
- package/src/app/[variants]/(main)/files/[id]/page.tsx +1 -1
- package/src/app/[variants]/(main)/settings/sync/page.tsx +1 -1
- package/src/app/[variants]/(main)/settings/system-agent/index.tsx +2 -1
- package/src/components/HtmlPreview/HtmlPreviewAction.tsx +32 -0
- package/src/components/HtmlPreview/PreviewDrawer.tsx +133 -0
- package/src/components/HtmlPreview/index.ts +2 -0
- package/src/config/aiModels/google.ts +42 -22
- package/src/config/aiModels/openrouter.ts +33 -0
- package/src/config/aiModels/vertexai.ts +4 -4
- package/src/features/Conversation/Extras/Usage/UsageDetail/index.tsx +6 -0
- package/src/features/Conversation/Extras/Usage/UsageDetail/tokens.test.ts +38 -0
- package/src/features/Conversation/Extras/Usage/UsageDetail/tokens.ts +13 -1
- package/src/features/Conversation/components/ChatItem/ShareMessageModal/ShareText/index.tsx +1 -1
- package/src/features/Conversation/components/ChatItem/index.tsx +23 -0
- package/src/features/ShareModal/ShareJSON/index.tsx +2 -2
- package/src/features/ShareModal/ShareText/index.tsx +1 -1
- package/src/libs/oidc-provider/adapter.ts +1 -1
- package/src/libs/trpc/edge/middleware/jwtPayload.test.ts +1 -1
- package/src/libs/trpc/edge/middleware/jwtPayload.ts +1 -2
- package/src/libs/trpc/lambda/middleware/keyVaults.ts +1 -2
- package/src/locales/default/chat.ts +1 -0
- package/src/locales/default/components.ts +12 -0
- package/src/middleware.ts +3 -3
- package/src/server/routers/tools/search.test.ts +1 -1
- package/src/services/config.ts +2 -4
- package/src/utils/client/switchLang.ts +1 -1
- package/{packages/utils/src → src/utils}/server/pageProps.ts +2 -1
- package/tsconfig.json +1 -1
- package/vitest.config.mts +1 -0
- package/packages/model-runtime/src/UniformRuntime/index.ts +0 -117
- /package/{packages/const/src → src/const}/locale.ts +0 -0
- /package/{packages/utils/src → src/utils}/locale.test.ts +0 -0
- /package/{packages/utils/src → src/utils}/locale.ts +0 -0
- /package/{packages/utils/src → src/utils}/server/routeVariants.ts +0 -0
@@ -11,6 +11,39 @@ const openrouterChatModels: AIChatModelCard[] = [
|
|
11
11
|
id: 'openrouter/auto',
|
12
12
|
type: 'chat',
|
13
13
|
},
|
14
|
+
{
|
15
|
+
abilities: {
|
16
|
+
imageOutput: true,
|
17
|
+
vision: true,
|
18
|
+
},
|
19
|
+
contextWindowTokens: 32_768 + 8192,
|
20
|
+
description: 'Gemini 2.5 Flash 实验模型,支持图像生成',
|
21
|
+
displayName: 'Gemini 2.5 Flash Image Preview',
|
22
|
+
id: 'google/gemini-2.5-flash-image-preview',
|
23
|
+
maxOutput: 8192,
|
24
|
+
pricing: {
|
25
|
+
units: [
|
26
|
+
{ name: 'imageOutput', rate: 30, strategy: 'fixed', unit: 'millionTokens' },
|
27
|
+
{ name: 'textInput', rate: 0.3, strategy: 'fixed', unit: 'millionTokens' },
|
28
|
+
{ name: 'textOutput', rate: 2.5, strategy: 'fixed', unit: 'millionTokens' },
|
29
|
+
],
|
30
|
+
},
|
31
|
+
releasedAt: '2025-08-26',
|
32
|
+
type: 'chat',
|
33
|
+
},
|
34
|
+
{
|
35
|
+
abilities: {
|
36
|
+
imageOutput: true,
|
37
|
+
vision: true,
|
38
|
+
},
|
39
|
+
contextWindowTokens: 32_768 + 8192,
|
40
|
+
description: 'Gemini 2.5 Flash 实验模型,支持图像生成',
|
41
|
+
displayName: 'Gemini 2.5 Flash Image Preview (free)',
|
42
|
+
id: 'google/gemini-2.5-flash-image-preview:free',
|
43
|
+
maxOutput: 8192,
|
44
|
+
releasedAt: '2025-08-26',
|
45
|
+
type: 'chat',
|
46
|
+
},
|
14
47
|
{
|
15
48
|
abilities: {
|
16
49
|
reasoning: true,
|
@@ -126,21 +126,21 @@ const vertexaiChatModels: AIChatModelCard[] = [
|
|
126
126
|
imageOutput: true,
|
127
127
|
vision: true,
|
128
128
|
},
|
129
|
-
contextWindowTokens: 32_768 +
|
129
|
+
contextWindowTokens: 32_768 + 8192,
|
130
130
|
description:
|
131
131
|
'Gemini 2.5 Flash Image Preview 是 Google 最新、最快、最高效的原生多模态模型,它允许您通过对话生成和编辑图像。',
|
132
132
|
displayName: 'Gemini 2.5 Flash Image Preview',
|
133
133
|
enabled: true,
|
134
134
|
id: 'gemini-2.5-flash-image-preview',
|
135
|
-
maxOutput:
|
135
|
+
maxOutput: 8192,
|
136
136
|
pricing: {
|
137
137
|
units: [
|
138
138
|
{ name: 'textInput', rate: 0.3, strategy: 'fixed', unit: 'millionTokens' },
|
139
139
|
{ name: 'textOutput', rate: 2.5, strategy: 'fixed', unit: 'millionTokens' },
|
140
|
-
{ name: 'imageOutput', rate:
|
140
|
+
{ name: 'imageOutput', rate: 30, strategy: 'fixed', unit: 'millionTokens' },
|
141
141
|
],
|
142
142
|
},
|
143
|
-
releasedAt: '2025-08-
|
143
|
+
releasedAt: '2025-08-26',
|
144
144
|
type: 'chat',
|
145
145
|
},
|
146
146
|
{
|
@@ -61,6 +61,12 @@ const TokenDetail = memo<TokenDetailProps>(({ meta, model, provider }) => {
|
|
61
61
|
? detailTokens.outputReasoning.credit
|
62
62
|
: detailTokens.outputReasoning.token,
|
63
63
|
},
|
64
|
+
!!detailTokens.outputImage && {
|
65
|
+
color: theme.purple,
|
66
|
+
id: 'outputImage',
|
67
|
+
title: t('messages.tokenDetails.outputImage'),
|
68
|
+
value: isShowCredit ? detailTokens.outputImage.credit : detailTokens.outputImage.token,
|
69
|
+
},
|
64
70
|
!!detailTokens.outputAudio && {
|
65
71
|
color: theme.cyan9,
|
66
72
|
id: 'outputAudio',
|
@@ -143,6 +143,44 @@ describe('getDetailsToken', () => {
|
|
143
143
|
});
|
144
144
|
});
|
145
145
|
|
146
|
+
it('should handle outputImageTokens correctly', () => {
|
147
|
+
const usage = {
|
148
|
+
inputTextTokens: 100,
|
149
|
+
outputImageTokens: 60,
|
150
|
+
outputReasoningTokens: 30,
|
151
|
+
totalOutputTokens: 200,
|
152
|
+
totalTokens: 300,
|
153
|
+
} as ModelTokensUsage;
|
154
|
+
|
155
|
+
const result = getDetailsToken(usage, mockModelCard);
|
156
|
+
|
157
|
+
expect(result.outputImage).toEqual({
|
158
|
+
credit: 1, // 60 * 0.02 = 1.2 -> 1
|
159
|
+
id: 'outputImage',
|
160
|
+
token: 60,
|
161
|
+
});
|
162
|
+
|
163
|
+
expect(result.outputReasoning).toEqual({
|
164
|
+
credit: 1, // 30 * 0.02 = 0.6 -> 1
|
165
|
+
token: 30,
|
166
|
+
});
|
167
|
+
|
168
|
+
expect(result.outputText).toEqual({
|
169
|
+
credit: 2, // (200 - 30 - 60) * 0.02 = 2.2 -> 2
|
170
|
+
token: 110,
|
171
|
+
});
|
172
|
+
|
173
|
+
expect(result.totalOutput).toEqual({
|
174
|
+
credit: 4, // 200 * 0.02 = 4
|
175
|
+
token: 200,
|
176
|
+
});
|
177
|
+
|
178
|
+
expect(result.totalTokens).toEqual({
|
179
|
+
credit: 4, // total credit equals totalOutputCredit here
|
180
|
+
token: 300,
|
181
|
+
});
|
182
|
+
});
|
183
|
+
|
146
184
|
it('should handle inputCitationTokens correctly', () => {
|
147
185
|
const usage: ModelTokensUsage = {
|
148
186
|
inputCitationTokens: 75,
|
@@ -21,9 +21,14 @@ export const getDetailsToken = (
|
|
21
21
|
|
22
22
|
const outputReasoningTokens = usage.outputReasoningTokens || (usage as any).reasoningTokens || 0;
|
23
23
|
|
24
|
+
const outputImageTokens = usage.outputImageTokens || (usage as any).imageTokens || 0;
|
25
|
+
|
24
26
|
const outputTextTokens = usage.outputTextTokens
|
25
27
|
? usage.outputTextTokens
|
26
|
-
: totalOutputTokens -
|
28
|
+
: totalOutputTokens -
|
29
|
+
outputReasoningTokens -
|
30
|
+
(usage.outputAudioTokens || 0) -
|
31
|
+
outputImageTokens;
|
27
32
|
|
28
33
|
const inputWriteCacheTokens = usage.inputWriteCacheTokens || 0;
|
29
34
|
const inputCacheTokens = usage.inputCachedTokens || (usage as any).cachedTokens || 0;
|
@@ -93,6 +98,13 @@ export const getDetailsToken = (
|
|
93
98
|
token: usage.outputAudioTokens,
|
94
99
|
}
|
95
100
|
: undefined,
|
101
|
+
outputImage: !!outputImageTokens
|
102
|
+
? {
|
103
|
+
credit: calcCredit(outputImageTokens, formatPrice.output),
|
104
|
+
id: 'outputImage',
|
105
|
+
token: outputImageTokens,
|
106
|
+
}
|
107
|
+
: undefined,
|
96
108
|
outputReasoning: !!outputReasoningTokens
|
97
109
|
? {
|
98
110
|
credit: calcCredit(outputReasoningTokens, formatPrice.output),
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { exportFile } from '@lobechat/utils/client';
|
1
2
|
import { Button, copyToClipboard } from '@lobehub/ui';
|
2
3
|
import { App } from 'antd';
|
3
4
|
import isEqual from 'fast-deep-equal';
|
@@ -10,7 +11,6 @@ import { useIsMobile } from '@/hooks/useIsMobile';
|
|
10
11
|
import { useChatStore } from '@/store/chat';
|
11
12
|
import { topicSelectors } from '@/store/chat/selectors';
|
12
13
|
import { ChatMessage } from '@/types/message';
|
13
|
-
import { exportFile } from '@/utils/client/exportFile';
|
14
14
|
|
15
15
|
import { useStyles } from '../style';
|
16
16
|
import Preview from './Preview';
|
@@ -6,6 +6,7 @@ import { MouseEventHandler, ReactNode, memo, use, useCallback, useMemo } from 'r
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
7
7
|
import { Flexbox } from 'react-layout-kit';
|
8
8
|
|
9
|
+
import { HtmlPreviewAction } from '@/components/HtmlPreview';
|
9
10
|
import { isDesktop } from '@/const/version';
|
10
11
|
import ChatItem from '@/features/ChatItem';
|
11
12
|
import { VirtuosoContext } from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
|
@@ -33,6 +34,14 @@ import { normalizeThinkTags, processWithArtifact } from './utils';
|
|
33
34
|
const rehypePlugins = markdownElements.map((element) => element.rehypePlugin).filter(Boolean);
|
34
35
|
const remarkPlugins = markdownElements.map((element) => element.remarkPlugin).filter(Boolean);
|
35
36
|
|
37
|
+
const isHtmlCode = (content: string, language: string) => {
|
38
|
+
return (
|
39
|
+
language === 'html' ||
|
40
|
+
(language === '' && content.includes('<html>')) ||
|
41
|
+
(language === '' && content.includes('<!DOCTYPE html>'))
|
42
|
+
);
|
43
|
+
};
|
44
|
+
|
36
45
|
const useStyles = createStyles(({ css, prefixCls }) => ({
|
37
46
|
loading: css`
|
38
47
|
opacity: 0.6;
|
@@ -175,6 +184,20 @@ const Item = memo<ChatListItemProps>(
|
|
175
184
|
() => ({
|
176
185
|
animated,
|
177
186
|
citations: item?.role === 'user' ? undefined : item?.search?.citations,
|
187
|
+
componentProps: {
|
188
|
+
highlight: {
|
189
|
+
actionsRender: ({ content, actionIconSize, language, originalNode }: any) => {
|
190
|
+
const showHtmlPreview = isHtmlCode(content, language);
|
191
|
+
|
192
|
+
return (
|
193
|
+
<>
|
194
|
+
{showHtmlPreview && <HtmlPreviewAction content={content} size={actionIconSize} />}
|
195
|
+
{originalNode}
|
196
|
+
</>
|
197
|
+
);
|
198
|
+
},
|
199
|
+
},
|
200
|
+
},
|
178
201
|
components,
|
179
202
|
customRender: markdownCustomRender,
|
180
203
|
enableCustomFootnotes: item?.role === 'assistant',
|
@@ -1,3 +1,5 @@
|
|
1
|
+
import { FORM_STYLE } from '@lobechat/const';
|
2
|
+
import { exportFile } from '@lobechat/utils/client';
|
1
3
|
import { Button, Form, type FormItemProps, copyToClipboard } from '@lobehub/ui';
|
2
4
|
import { App, Switch } from 'antd';
|
3
5
|
import isEqual from 'fast-deep-equal';
|
@@ -6,13 +8,11 @@ import { memo, useState } from 'react';
|
|
6
8
|
import { useTranslation } from 'react-i18next';
|
7
9
|
import { Flexbox } from 'react-layout-kit';
|
8
10
|
|
9
|
-
import { FORM_STYLE } from '@/const/layoutTokens';
|
10
11
|
import { useIsMobile } from '@/hooks/useIsMobile';
|
11
12
|
import { useAgentStore } from '@/store/agent';
|
12
13
|
import { agentSelectors } from '@/store/agent/selectors';
|
13
14
|
import { useChatStore } from '@/store/chat';
|
14
15
|
import { chatSelectors, topicSelectors } from '@/store/chat/selectors';
|
15
|
-
import { exportFile } from '@/utils/client/exportFile';
|
16
16
|
|
17
17
|
import { useStyles } from '../style';
|
18
18
|
import Preview from './Preview';
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { exportFile } from '@lobechat/utils/client';
|
1
2
|
import { Button, Form, type FormItemProps, copyToClipboard } from '@lobehub/ui';
|
2
3
|
import { App, Switch } from 'antd';
|
3
4
|
import isEqual from 'fast-deep-equal';
|
@@ -12,7 +13,6 @@ import { useAgentStore } from '@/store/agent';
|
|
12
13
|
import { agentSelectors } from '@/store/agent/selectors';
|
13
14
|
import { useChatStore } from '@/store/chat';
|
14
15
|
import { chatSelectors, topicSelectors } from '@/store/chat/selectors';
|
15
|
-
import { exportFile } from '@/utils/client/exportFile';
|
16
16
|
|
17
17
|
import { useStyles } from '../style';
|
18
18
|
import Preview from './Preview';
|
@@ -164,7 +164,7 @@ class OIDCAdapter {
|
|
164
164
|
log('[%s] Setting userId: %s', this.name, payload.accountId);
|
165
165
|
} else {
|
166
166
|
try {
|
167
|
-
const { getUserAuth } = await import('
|
167
|
+
const { getUserAuth } = await import('@lobechat/utils/server');
|
168
168
|
try {
|
169
169
|
const { userId } = await getUserAuth();
|
170
170
|
if (userId) {
|
@@ -1,11 +1,11 @@
|
|
1
1
|
// @vitest-environment node
|
2
|
+
import * as utils from '@lobechat/utils/server';
|
2
3
|
import { TRPCError } from '@trpc/server';
|
3
4
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
4
5
|
|
5
6
|
import { createCallerFactory } from '@/libs/trpc/edge';
|
6
7
|
import { AuthContext, createContextInner } from '@/libs/trpc/edge/context';
|
7
8
|
import { edgeTrpc as trpc } from '@/libs/trpc/edge/init';
|
8
|
-
import * as utils from '@/utils/server/xor';
|
9
9
|
|
10
10
|
import { jwtPayloadChecker } from './jwtPayload';
|
11
11
|
|
@@ -1,7 +1,6 @@
|
|
1
|
+
import { getXorPayload } from '@lobechat/utils/server';
|
1
2
|
import { TRPCError } from '@trpc/server';
|
2
3
|
|
3
|
-
import { getXorPayload } from '@/utils/server/xor';
|
4
|
-
|
5
4
|
import { edgeTrpc } from '../init';
|
6
5
|
|
7
6
|
export const jwtPayloadChecker = edgeTrpc.middleware(async (opts) => {
|
@@ -75,6 +75,18 @@ export default {
|
|
75
75
|
GoBack: {
|
76
76
|
back: '返回',
|
77
77
|
},
|
78
|
+
HtmlPreview: {
|
79
|
+
actions: {
|
80
|
+
download: '下载',
|
81
|
+
preview: '预览',
|
82
|
+
},
|
83
|
+
iframeTitle: 'HTML 预览',
|
84
|
+
mode: {
|
85
|
+
code: '代码',
|
86
|
+
preview: '预览',
|
87
|
+
},
|
88
|
+
title: 'HTML 预览',
|
89
|
+
},
|
78
90
|
ImageUpload: {
|
79
91
|
actions: {
|
80
92
|
changeImage: '点击更换图片',
|
package/src/middleware.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
|
2
|
+
import { parseDefaultThemeFromCountry } from '@lobechat/utils/server';
|
2
3
|
import debug from 'debug';
|
3
4
|
import { NextRequest, NextResponse } from 'next/server';
|
4
5
|
import { UAParser } from 'ua-parser-js';
|
@@ -11,11 +12,10 @@ import { LOBE_THEME_APPEARANCE } from '@/const/theme';
|
|
11
12
|
import { appEnv } from '@/envs/app';
|
12
13
|
import NextAuthEdge from '@/libs/next-auth/edge';
|
13
14
|
import { Locales } from '@/locales/resources';
|
14
|
-
import { parseBrowserLanguage } from '@/utils/locale';
|
15
|
-
import { parseDefaultThemeFromCountry } from '@/utils/server/geo';
|
16
|
-
import { RouteVariants } from '@/utils/server/routeVariants';
|
17
15
|
|
18
16
|
import { oidcEnv } from './envs/oidc';
|
17
|
+
import { parseBrowserLanguage } from './utils/locale';
|
18
|
+
import { RouteVariants } from './utils/server/routeVariants';
|
19
19
|
|
20
20
|
// Create debug logger instances
|
21
21
|
const logDefault = debug('middleware:default');
|
@@ -9,7 +9,7 @@ import { SEARCH_SEARXNG_NOT_CONFIG } from '@/types/tool/search';
|
|
9
9
|
import { searchRouter } from './search';
|
10
10
|
|
11
11
|
// Mock JWT verification
|
12
|
-
vi.mock('
|
12
|
+
vi.mock('@lobechat/utils/server', () => ({
|
13
13
|
getXorPayload: vi.fn().mockReturnValue({ userId: '1' }),
|
14
14
|
}));
|
15
15
|
|
package/src/services/config.ts
CHANGED
@@ -1,11 +1,9 @@
|
|
1
|
+
import { BRANDING_NAME, isDeprecatedEdition, isServerMode } from '@lobechat/const';
|
2
|
+
import { downloadFile, exportJSONFile } from '@lobechat/utils/client';
|
1
3
|
import dayjs from 'dayjs';
|
2
4
|
|
3
|
-
import { BRANDING_NAME } from '@/const/branding';
|
4
|
-
import { isDeprecatedEdition, isServerMode } from '@/const/version';
|
5
5
|
import { CURRENT_CONFIG_VERSION } from '@/migrations';
|
6
6
|
import { ImportPgDataStructure } from '@/types/export';
|
7
|
-
import { downloadFile } from '@/utils/client/downloadFile';
|
8
|
-
import { exportJSONFile } from '@/utils/client/exportFile';
|
9
7
|
|
10
8
|
import { exportService } from './export';
|
11
9
|
import { configService as deprecatedExportService } from './export/_deprecated';
|
@@ -1,7 +1,7 @@
|
|
1
|
-
import { LOBE_LOCALE_COOKIE } from '@lobechat/const';
|
2
1
|
import { setCookie } from '@lobechat/utils';
|
3
2
|
import { changeLanguage } from 'i18next';
|
4
3
|
|
4
|
+
import { LOBE_LOCALE_COOKIE } from '@/const/locale';
|
5
5
|
import { LocaleMode } from '@/types/locale';
|
6
6
|
|
7
7
|
export const switchLang = (locale: LocaleMode) => {
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { translation } from '@/server/translation';
|
2
2
|
import { DynamicLayoutProps } from '@/types/next';
|
3
|
-
|
3
|
+
|
4
|
+
import { RouteVariants } from './routeVariants';
|
4
5
|
|
5
6
|
export const parsePageMetaProps = async (props: DynamicLayoutProps) => {
|
6
7
|
const { locale: hl, isMobile } = await RouteVariants.getVariantsFromProps(props);
|
package/tsconfig.json
CHANGED
@@ -21,7 +21,7 @@
|
|
21
21
|
"@/libs/model-runtime": ["./packages/model-runtime/src/index.ts"],
|
22
22
|
"@/libs/model-runtime/*": ["./packages/model-runtime/src/*"],
|
23
23
|
"@/database/*": ["./packages/database/src/*", "./src/database/*"],
|
24
|
-
"@/const/*": ["./packages/const/src/*"],
|
24
|
+
"@/const/*": ["./packages/const/src/*", "./src/const/*"],
|
25
25
|
"@/utils/*": ["./packages/utils/src/*", "./src/utils/*"],
|
26
26
|
"@/types/*": ["./packages/types/src/*", "./src/types/*"],
|
27
27
|
"@/*": ["./src/*"],
|
package/vitest.config.mts
CHANGED
@@ -13,6 +13,7 @@ export default defineConfig({
|
|
13
13
|
'@/database/_deprecated': resolve(__dirname, './src/database/_deprecated'),
|
14
14
|
'@/database': resolve(__dirname, './packages/database/src'),
|
15
15
|
'@/utils/client/switchLang': resolve(__dirname, './src/utils/client/switchLang'),
|
16
|
+
'@/const/locale': resolve(__dirname, './src/const/locale'),
|
16
17
|
// TODO: after refactor the errorResponse, we can remove it
|
17
18
|
'@/utils/errorResponse': resolve(__dirname, './src/utils/errorResponse'),
|
18
19
|
'@/utils': resolve(__dirname, './packages/utils/src'),
|
@@ -1,117 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* @see https://github.com/lobehub/lobe-chat/discussions/6563
|
3
|
-
*/
|
4
|
-
import { LobeRuntimeAI } from '../BaseAI';
|
5
|
-
import { LobeOpenAI } from '../openai';
|
6
|
-
import { providerRuntimeMap } from '../runtimeMap';
|
7
|
-
import {
|
8
|
-
type ChatCompletionErrorPayload,
|
9
|
-
ChatMethodOptions,
|
10
|
-
ChatStreamPayload,
|
11
|
-
EmbeddingsOptions,
|
12
|
-
EmbeddingsPayload,
|
13
|
-
TextToImagePayload,
|
14
|
-
TextToSpeechPayload,
|
15
|
-
} from '../types';
|
16
|
-
|
17
|
-
export interface RuntimeItem {
|
18
|
-
id: string;
|
19
|
-
models?: string[];
|
20
|
-
runtime: LobeRuntimeAI;
|
21
|
-
}
|
22
|
-
|
23
|
-
interface ProviderInitParams extends Record<string, any> {
|
24
|
-
accessKeyId?: string;
|
25
|
-
accessKeySecret?: string;
|
26
|
-
apiKey?: string;
|
27
|
-
apiVersion?: string;
|
28
|
-
baseURL?: string;
|
29
|
-
baseURLOrAccountID?: string;
|
30
|
-
dangerouslyAllowBrowser?: boolean;
|
31
|
-
region?: string;
|
32
|
-
sessionToken?: string;
|
33
|
-
}
|
34
|
-
|
35
|
-
interface ProviderInstance {
|
36
|
-
apiType: keyof typeof providerRuntimeMap;
|
37
|
-
models?: string[];
|
38
|
-
params: ProviderInitParams;
|
39
|
-
runtime?: typeof LobeOpenAI;
|
40
|
-
}
|
41
|
-
|
42
|
-
interface UniformRuntimeOptions {
|
43
|
-
chat?: {
|
44
|
-
handleError?: (error: any) => Omit<ChatCompletionErrorPayload, 'provider'> | undefined;
|
45
|
-
};
|
46
|
-
}
|
47
|
-
|
48
|
-
class UniformRuntime {
|
49
|
-
private _runtimes: RuntimeItem[];
|
50
|
-
private _options: UniformRuntimeOptions;
|
51
|
-
|
52
|
-
constructor(id: string, providers: ProviderInstance[], options: UniformRuntimeOptions) {
|
53
|
-
if (providers.length === 0) {
|
54
|
-
throw new Error('empty providers');
|
55
|
-
}
|
56
|
-
|
57
|
-
this._runtimes = providers.map((options) => {
|
58
|
-
const providerAI = options.runtime ?? providerRuntimeMap[options.apiType] ?? LobeOpenAI;
|
59
|
-
const runtime: LobeRuntimeAI = new providerAI({ ...options.params, id });
|
60
|
-
|
61
|
-
return { id: options.apiType, models: options.models, runtime };
|
62
|
-
});
|
63
|
-
|
64
|
-
this._options = options;
|
65
|
-
}
|
66
|
-
|
67
|
-
// 检查下是否能匹配到特定模型,否则默认使用第一个 runtime
|
68
|
-
getRuntimeByModel(model: string) {
|
69
|
-
const runtimeItem =
|
70
|
-
this._runtimes.find((runtime) => runtime.models && runtime.models.includes(model)) ||
|
71
|
-
this._runtimes[0];
|
72
|
-
|
73
|
-
return runtimeItem.runtime;
|
74
|
-
}
|
75
|
-
|
76
|
-
async chat(payload: ChatStreamPayload, options?: ChatMethodOptions) {
|
77
|
-
try {
|
78
|
-
const runtime = this.getRuntimeByModel(payload.model);
|
79
|
-
|
80
|
-
return await runtime.chat!(payload, options);
|
81
|
-
} catch (e) {
|
82
|
-
if (this._options.chat?.handleError) {
|
83
|
-
const error = this._options.chat.handleError(e);
|
84
|
-
|
85
|
-
if (error) {
|
86
|
-
throw error;
|
87
|
-
}
|
88
|
-
}
|
89
|
-
|
90
|
-
throw e;
|
91
|
-
}
|
92
|
-
}
|
93
|
-
|
94
|
-
async textToImage(payload: TextToImagePayload) {
|
95
|
-
const runtime = this.getRuntimeByModel(payload.model);
|
96
|
-
|
97
|
-
return runtime.textToImage?.(payload);
|
98
|
-
}
|
99
|
-
|
100
|
-
async models() {
|
101
|
-
return this._runtimes[0].runtime.models?.();
|
102
|
-
}
|
103
|
-
|
104
|
-
async embeddings(payload: EmbeddingsPayload, options?: EmbeddingsOptions) {
|
105
|
-
const runtime = this.getRuntimeByModel(payload.model);
|
106
|
-
|
107
|
-
return runtime.embeddings?.(payload, options);
|
108
|
-
}
|
109
|
-
|
110
|
-
async textToSpeech(payload: TextToSpeechPayload, options?: EmbeddingsOptions) {
|
111
|
-
const runtime = this.getRuntimeByModel(payload.model);
|
112
|
-
|
113
|
-
return runtime.textToSpeech?.(payload, options);
|
114
|
-
}
|
115
|
-
}
|
116
|
-
|
117
|
-
export default UniformRuntime;
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|