@lobehub/chat 0.161.2 → 0.161.4
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 +58 -0
- package/package.json +1 -1
- package/src/app/api/chat/apiKeyManager.test.ts +0 -2
- package/src/app/api/market/AgentMarket.test.ts +1 -0
- package/src/app/api/market/AgentMarket.ts +2 -2
- package/src/app/api/market/route.ts +2 -0
- package/src/app/api/middleware/auth/utils.ts +2 -2
- package/src/app/api/openai/createBizOpenAI/auth.test.ts +1 -0
- package/src/app/api/openai/createBizOpenAI/auth.ts +2 -2
- package/src/app/api/plugin/gateway/route.ts +3 -3
- package/src/app/api/plugin/store/Store.test.ts +1 -2
- package/src/app/api/plugin/store/Store.ts +2 -2
- package/src/app/api/plugin/store/route.ts +3 -1
- package/src/app/metadata.ts +3 -4
- package/src/components/Error/sentryCaptureException.ts +2 -4
- package/src/config/__tests__/{server.test.ts → app.test.ts} +6 -18
- package/src/config/app.ts +58 -0
- package/src/config/client.ts +0 -5
- package/src/config/langfuse.ts +23 -0
- package/src/libs/agent-runtime/AgentRuntime.test.ts +21 -5
- package/src/libs/traces/index.test.ts +6 -5
- package/src/libs/traces/index.ts +2 -2
- package/src/libs/trpc/middleware/password.test.ts +6 -5
- package/src/libs/trpc/middleware/password.ts +2 -2
- package/src/server/globalConfig/index.ts +5 -4
- package/src/server/ld.ts +2 -2
- package/src/utils/basePath.ts +2 -2
- package/src/config/server/app.ts +0 -57
- package/src/config/server/index.ts +0 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,64 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.161.4](https://github.com/lobehub/lobe-chat/compare/v0.161.3...v0.161.4)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2024-05-22**</sup>
|
|
8
|
+
|
|
9
|
+
#### ♻ Code Refactoring
|
|
10
|
+
|
|
11
|
+
- **misc**: Refactor the app ENV.
|
|
12
|
+
|
|
13
|
+
#### 🐛 Bug Fixes
|
|
14
|
+
|
|
15
|
+
- **misc**: Fix market and plugin cache.
|
|
16
|
+
|
|
17
|
+
<br/>
|
|
18
|
+
|
|
19
|
+
<details>
|
|
20
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
21
|
+
|
|
22
|
+
#### Code refactoring
|
|
23
|
+
|
|
24
|
+
- **misc**: Refactor the app ENV, closes [#2604](https://github.com/lobehub/lobe-chat/issues/2604) ([acc0fdc](https://github.com/lobehub/lobe-chat/commit/acc0fdc))
|
|
25
|
+
|
|
26
|
+
#### What's fixed
|
|
27
|
+
|
|
28
|
+
- **misc**: Fix market and plugin cache, closes [#2608](https://github.com/lobehub/lobe-chat/issues/2608) ([a3f161e](https://github.com/lobehub/lobe-chat/commit/a3f161e))
|
|
29
|
+
|
|
30
|
+
</details>
|
|
31
|
+
|
|
32
|
+
<div align="right">
|
|
33
|
+
|
|
34
|
+
[](#readme-top)
|
|
35
|
+
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
### [Version 0.161.3](https://github.com/lobehub/lobe-chat/compare/v0.161.2...v0.161.3)
|
|
39
|
+
|
|
40
|
+
<sup>Released on **2024-05-22**</sup>
|
|
41
|
+
|
|
42
|
+
#### ♻ Code Refactoring
|
|
43
|
+
|
|
44
|
+
- **misc**: Refactor the langfuse env.
|
|
45
|
+
|
|
46
|
+
<br/>
|
|
47
|
+
|
|
48
|
+
<details>
|
|
49
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
50
|
+
|
|
51
|
+
#### Code refactoring
|
|
52
|
+
|
|
53
|
+
- **misc**: Refactor the langfuse env, closes [#2602](https://github.com/lobehub/lobe-chat/issues/2602) ([cbebfbc](https://github.com/lobehub/lobe-chat/commit/cbebfbc))
|
|
54
|
+
|
|
55
|
+
</details>
|
|
56
|
+
|
|
57
|
+
<div align="right">
|
|
58
|
+
|
|
59
|
+
[](#readme-top)
|
|
60
|
+
|
|
61
|
+
</div>
|
|
62
|
+
|
|
5
63
|
### [Version 0.161.2](https://github.com/lobehub/lobe-chat/compare/v0.161.1...v0.161.2)
|
|
6
64
|
|
|
7
65
|
<sup>Released on **2024-05-22**</sup>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.161.
|
|
3
|
+
"version": "0.161.4",
|
|
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",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import urlJoin from 'url-join';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { appEnv } from '@/config/app';
|
|
4
4
|
import { DEFAULT_LANG, isLocaleNotSupport } from '@/const/locale';
|
|
5
5
|
import { Locales, normalizeLocale } from '@/locales/resources';
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ export class AgentMarket {
|
|
|
8
8
|
private readonly baseUrl: string;
|
|
9
9
|
|
|
10
10
|
constructor(baseUrl?: string) {
|
|
11
|
-
this.baseUrl = baseUrl ||
|
|
11
|
+
this.baseUrl = baseUrl || appEnv.AGENTS_INDEX_URL;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
getAgentIndexUrl = (lang: Locales = DEFAULT_LANG) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type AuthObject } from '@clerk/backend/internal';
|
|
2
2
|
import { importJWK, jwtVerify } from 'jose';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { getAppConfig } from '@/config/app';
|
|
5
5
|
import {
|
|
6
6
|
JWTPayload,
|
|
7
7
|
JWT_SECRET_KEY,
|
|
@@ -71,7 +71,7 @@ export const checkAuthMethod = ({
|
|
|
71
71
|
// if apiKey exist
|
|
72
72
|
if (apiKey) return;
|
|
73
73
|
|
|
74
|
-
const { ACCESS_CODES } =
|
|
74
|
+
const { ACCESS_CODES } = getAppConfig();
|
|
75
75
|
|
|
76
76
|
// if accessCode doesn't exist
|
|
77
77
|
if (!ACCESS_CODES.length) return;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getAppConfig } from '@/config/app';
|
|
2
2
|
import { ChatErrorType } from '@/types/fetch';
|
|
3
3
|
|
|
4
4
|
interface AuthConfig {
|
|
@@ -13,7 +13,7 @@ export const checkAuth = ({ apiKey, accessCode, oauthAuthorized }: AuthConfig) =
|
|
|
13
13
|
return { auth: true };
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
const { ACCESS_CODES } =
|
|
16
|
+
const { ACCESS_CODES } = getAppConfig();
|
|
17
17
|
|
|
18
18
|
// if apiKey exist
|
|
19
19
|
if (apiKey) {
|
|
@@ -3,7 +3,7 @@ import { createGatewayOnEdgeRuntime } from '@lobehub/chat-plugins-gateway';
|
|
|
3
3
|
|
|
4
4
|
import { createErrorResponse } from '@/app/api/errorResponse';
|
|
5
5
|
import { getJWTPayload } from '@/app/api/middleware/auth/utils';
|
|
6
|
-
import {
|
|
6
|
+
import { getAppConfig } from '@/config/app';
|
|
7
7
|
import { LOBE_CHAT_AUTH_HEADER, OAUTH_AUTHORIZED, enableNextAuth } from '@/const/auth';
|
|
8
8
|
import { LOBE_CHAT_TRACE_ID, TraceNameMap } from '@/const/trace';
|
|
9
9
|
import { AgentRuntimeError } from '@/libs/agent-runtime';
|
|
@@ -14,7 +14,7 @@ import { getTracePayload } from '@/utils/trace';
|
|
|
14
14
|
import { parserPluginSettings } from './settings';
|
|
15
15
|
|
|
16
16
|
const checkAuth = (accessCode: string | null, oauthAuthorized: boolean | null) => {
|
|
17
|
-
const { ACCESS_CODES, PLUGIN_SETTINGS } =
|
|
17
|
+
const { ACCESS_CODES, PLUGIN_SETTINGS } = getAppConfig();
|
|
18
18
|
|
|
19
19
|
// if there is no plugin settings, just skip the auth
|
|
20
20
|
if (!PLUGIN_SETTINGS) return { auth: true };
|
|
@@ -32,7 +32,7 @@ const checkAuth = (accessCode: string | null, oauthAuthorized: boolean | null) =
|
|
|
32
32
|
return { auth: true };
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
const { PLUGINS_INDEX_URL: pluginsIndexUrl, PLUGIN_SETTINGS } =
|
|
35
|
+
const { PLUGINS_INDEX_URL: pluginsIndexUrl, PLUGIN_SETTINGS } = getAppConfig();
|
|
36
36
|
|
|
37
37
|
const defaultPluginSettings = parserPluginSettings(PLUGIN_SETTINGS);
|
|
38
38
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import urlJoin from 'url-join';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { appEnv } from '@/config/app';
|
|
4
4
|
import { DEFAULT_LANG, isLocaleNotSupport } from '@/const/locale';
|
|
5
5
|
import { Locales, normalizeLocale } from '@/locales/resources';
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ export class PluginStore {
|
|
|
8
8
|
private readonly baseUrl: string;
|
|
9
9
|
|
|
10
10
|
constructor(baseUrl?: string) {
|
|
11
|
-
this.baseUrl = baseUrl ||
|
|
11
|
+
this.baseUrl = baseUrl || appEnv.PLUGINS_INDEX_URL;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
getPluginIndexUrl = (lang: Locales = DEFAULT_LANG) => {
|
|
@@ -4,6 +4,8 @@ import { PluginStore } from './Store';
|
|
|
4
4
|
|
|
5
5
|
export const runtime = 'edge';
|
|
6
6
|
|
|
7
|
+
export const revalidate = 43_200; // revalidate at almost every 12 hours
|
|
8
|
+
|
|
7
9
|
export const GET = async (req: Request) => {
|
|
8
10
|
const locale = new URL(req.url).searchParams.get('locale');
|
|
9
11
|
|
|
@@ -11,7 +13,7 @@ export const GET = async (req: Request) => {
|
|
|
11
13
|
|
|
12
14
|
let res: Response;
|
|
13
15
|
|
|
14
|
-
res = await fetch(pluginStore.getPluginIndexUrl(locale as any)
|
|
16
|
+
res = await fetch(pluginStore.getPluginIndexUrl(locale as any));
|
|
15
17
|
|
|
16
18
|
if (res.status === 404) {
|
|
17
19
|
res = await fetch(pluginStore.getPluginIndexUrl(DEFAULT_LANG));
|
package/src/app/metadata.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { Metadata } from 'next';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { getServerConfig } from '@/config/server';
|
|
3
|
+
import { appEnv, getAppConfig } from '@/config/app';
|
|
5
4
|
import { OFFICIAL_URL } from '@/const/url';
|
|
6
5
|
import { translation } from '@/server/translation';
|
|
7
6
|
|
|
8
7
|
const title = 'LobeChat';
|
|
9
8
|
|
|
10
|
-
const { SITE_URL = OFFICIAL_URL } =
|
|
11
|
-
const
|
|
9
|
+
const { SITE_URL = OFFICIAL_URL } = getAppConfig();
|
|
10
|
+
const BASE_PATH = appEnv.NEXT_PUBLIC_BASE_PATH;
|
|
12
11
|
|
|
13
12
|
// if there is a base path, then we don't need the manifest
|
|
14
13
|
const noManifest = !!BASE_PATH;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
const { ENABLE_SENTRY } = getClientConfig();
|
|
1
|
+
import { appEnv } from '@/config/app';
|
|
4
2
|
|
|
5
3
|
export type ErrorType = Error & { digest?: string };
|
|
6
4
|
|
|
7
5
|
export const sentryCaptureException = async (error: Error & { digest?: string }) => {
|
|
8
|
-
if (!
|
|
6
|
+
if (!appEnv.NEXT_PUBLIC_ENABLE_SENTRY) return;
|
|
9
7
|
const { captureException } = await import('@sentry/nextjs');
|
|
10
8
|
return captureException(error);
|
|
11
9
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
1
2
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import { getAppConfig } from '../app';
|
|
4
5
|
|
|
5
6
|
// Stub the global process object to safely mock environment variables
|
|
6
7
|
vi.stubGlobal('process', {
|
|
@@ -14,28 +15,15 @@ describe('getServerConfig', () => {
|
|
|
14
15
|
vi.restoreAllMocks();
|
|
15
16
|
});
|
|
16
17
|
|
|
17
|
-
it('throws an error if process is undefined', () => {
|
|
18
|
-
const originalProcess = global.process;
|
|
19
|
-
// To simulate the error condition, temporarily set the global process to `undefined`,
|
|
20
|
-
// @ts-ignore
|
|
21
|
-
global.process = undefined;
|
|
22
|
-
|
|
23
|
-
expect(() => getServerConfig()).toThrow(
|
|
24
|
-
'[Server Config] you are importing a server-only module outside of server',
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
global.process = originalProcess; // Restore the original process object
|
|
28
|
-
});
|
|
29
|
-
|
|
30
18
|
// it('correctly handles values for OPENAI_FUNCTION_REGIONS', () => {
|
|
31
19
|
// process.env.OPENAI_FUNCTION_REGIONS = 'iad1,sfo1';
|
|
32
|
-
// const config =
|
|
20
|
+
// const config = getAppConfig();
|
|
33
21
|
// expect(config.OPENAI_FUNCTION_REGIONS).toStrictEqual(['iad1', 'sfo1']);
|
|
34
22
|
// });
|
|
35
23
|
|
|
36
24
|
describe('index url', () => {
|
|
37
25
|
it('should return default URLs when no environment variables are set', () => {
|
|
38
|
-
const config =
|
|
26
|
+
const config = getAppConfig();
|
|
39
27
|
expect(config.AGENTS_INDEX_URL).toBe('https://chat-agents.lobehub.com');
|
|
40
28
|
expect(config.PLUGINS_INDEX_URL).toBe('https://chat-plugins.lobehub.com');
|
|
41
29
|
});
|
|
@@ -43,7 +31,7 @@ describe('getServerConfig', () => {
|
|
|
43
31
|
it('should return custom URLs when environment variables are set', () => {
|
|
44
32
|
process.env.AGENTS_INDEX_URL = 'https://custom-agents-url.com';
|
|
45
33
|
process.env.PLUGINS_INDEX_URL = 'https://custom-plugins-url.com';
|
|
46
|
-
const config =
|
|
34
|
+
const config = getAppConfig();
|
|
47
35
|
expect(config.AGENTS_INDEX_URL).toBe('https://custom-agents-url.com');
|
|
48
36
|
expect(config.PLUGINS_INDEX_URL).toBe('https://custom-plugins-url.com');
|
|
49
37
|
});
|
|
@@ -52,7 +40,7 @@ describe('getServerConfig', () => {
|
|
|
52
40
|
process.env.AGENTS_INDEX_URL = '';
|
|
53
41
|
process.env.PLUGINS_INDEX_URL = '';
|
|
54
42
|
|
|
55
|
-
const config =
|
|
43
|
+
const config = getAppConfig();
|
|
56
44
|
expect(config.AGENTS_INDEX_URL).toBe('https://chat-agents.lobehub.com');
|
|
57
45
|
expect(config.PLUGINS_INDEX_URL).toBe('https://chat-plugins.lobehub.com');
|
|
58
46
|
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
|
2
|
+
import { createEnv } from '@t3-oss/env-nextjs';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
declare global {
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
7
|
+
namespace NodeJS {
|
|
8
|
+
interface ProcessEnv {
|
|
9
|
+
ACCESS_CODE?: string;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const getAppConfig = () => {
|
|
15
|
+
const ACCESS_CODES = process.env.ACCESS_CODE?.split(',').filter(Boolean) || [];
|
|
16
|
+
|
|
17
|
+
return createEnv({
|
|
18
|
+
client: {
|
|
19
|
+
NEXT_PUBLIC_BASE_PATH: z.string(),
|
|
20
|
+
NEXT_PUBLIC_ENABLE_SENTRY: z.boolean(),
|
|
21
|
+
},
|
|
22
|
+
server: {
|
|
23
|
+
ACCESS_CODES: z.any(z.string()).optional(),
|
|
24
|
+
|
|
25
|
+
AGENTS_INDEX_URL: z.string().url(),
|
|
26
|
+
|
|
27
|
+
DEFAULT_AGENT_CONFIG: z.string(),
|
|
28
|
+
|
|
29
|
+
PLUGINS_INDEX_URL: z.string().url(),
|
|
30
|
+
PLUGIN_SETTINGS: z.string().optional(),
|
|
31
|
+
|
|
32
|
+
SITE_URL: z.string().optional(),
|
|
33
|
+
},
|
|
34
|
+
runtimeEnv: {
|
|
35
|
+
NEXT_PUBLIC_BASE_PATH: process.env.NEXT_PUBLIC_BASE_PATH || '',
|
|
36
|
+
|
|
37
|
+
// Sentry
|
|
38
|
+
NEXT_PUBLIC_ENABLE_SENTRY: !!process.env.NEXT_PUBLIC_SENTRY_DSN,
|
|
39
|
+
|
|
40
|
+
ACCESS_CODES: ACCESS_CODES as any,
|
|
41
|
+
|
|
42
|
+
AGENTS_INDEX_URL: !!process.env.AGENTS_INDEX_URL
|
|
43
|
+
? process.env.AGENTS_INDEX_URL
|
|
44
|
+
: 'https://chat-agents.lobehub.com',
|
|
45
|
+
|
|
46
|
+
DEFAULT_AGENT_CONFIG: process.env.DEFAULT_AGENT_CONFIG || '',
|
|
47
|
+
|
|
48
|
+
PLUGINS_INDEX_URL: !!process.env.PLUGINS_INDEX_URL
|
|
49
|
+
? process.env.PLUGINS_INDEX_URL
|
|
50
|
+
: 'https://chat-plugins.lobehub.com',
|
|
51
|
+
|
|
52
|
+
PLUGIN_SETTINGS: process.env.PLUGIN_SETTINGS,
|
|
53
|
+
SITE_URL: process.env.SITE_URL,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const appEnv = getAppConfig();
|
package/src/config/client.ts
CHANGED
|
@@ -22,11 +22,6 @@ declare global {
|
|
|
22
22
|
export const getClientConfig = () => ({
|
|
23
23
|
ENABLED_SERVER_SERVICE: process.env.NEXT_PUBLIC_SERVICE_MODE === 'server',
|
|
24
24
|
|
|
25
|
-
BASE_PATH: process.env.NEXT_PUBLIC_BASE_PATH || '',
|
|
26
|
-
|
|
27
|
-
// Sentry
|
|
28
|
-
ENABLE_SENTRY: !!process.env.NEXT_PUBLIC_SENTRY_DSN,
|
|
29
|
-
|
|
30
25
|
// i18n debug mode
|
|
31
26
|
I18N_DEBUG: process.env.NEXT_PUBLIC_I18N_DEBUG === '1',
|
|
32
27
|
I18N_DEBUG_BROWSER: process.env.NEXT_PUBLIC_I18N_DEBUG_BROWSER === '1',
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* eslint-disable sort-keys-fix/sort-keys-fix , typescript-sort-keys/interface */
|
|
2
|
+
import { createEnv } from '@t3-oss/env-nextjs';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
export const getLangfuseConfig = () => {
|
|
6
|
+
return createEnv({
|
|
7
|
+
runtimeEnv: {
|
|
8
|
+
ENABLE_LANGFUSE: process.env.ENABLE_LANGFUSE === '1',
|
|
9
|
+
LANGFUSE_SECRET_KEY: process.env.LANGFUSE_SECRET_KEY || '',
|
|
10
|
+
LANGFUSE_PUBLIC_KEY: process.env.LANGFUSE_PUBLIC_KEY || '',
|
|
11
|
+
LANGFUSE_HOST: process.env.LANGFUSE_HOST || 'https://cloud.langfuse.com',
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
server: {
|
|
15
|
+
ENABLE_LANGFUSE: z.boolean(),
|
|
16
|
+
LANGFUSE_SECRET_KEY: z.string().optional(),
|
|
17
|
+
LANGFUSE_PUBLIC_KEY: z.string().optional(),
|
|
18
|
+
LANGFUSE_HOST: z.string().url(),
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const langfuseEnv = getLangfuseConfig();
|
|
@@ -5,7 +5,7 @@ import { ClientOptions } from 'openai';
|
|
|
5
5
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
6
6
|
|
|
7
7
|
import { createTraceOptions } from '@/app/api/chat/agentRuntime';
|
|
8
|
-
import
|
|
8
|
+
import * as langfuseCfg from '@/config/langfuse';
|
|
9
9
|
import { JWTPayload } from '@/const/auth';
|
|
10
10
|
import { TraceNameMap } from '@/const/trace';
|
|
11
11
|
import {
|
|
@@ -312,15 +312,14 @@ describe('AgentRuntime', () => {
|
|
|
312
312
|
};
|
|
313
313
|
|
|
314
314
|
const updateMock = vi.fn();
|
|
315
|
-
|
|
316
|
-
|
|
315
|
+
|
|
316
|
+
it('should call experimental_onToolCall correctly', async () => {
|
|
317
|
+
vi.spyOn(langfuseCfg, 'getLangfuseConfig').mockReturnValue({
|
|
317
318
|
ENABLE_LANGFUSE: true,
|
|
318
319
|
LANGFUSE_PUBLIC_KEY: 'abc',
|
|
319
320
|
LANGFUSE_SECRET_KEY: 'DDD',
|
|
320
321
|
} as any);
|
|
321
|
-
});
|
|
322
322
|
|
|
323
|
-
it('should call experimental_onToolCall correctly', async () => {
|
|
324
323
|
// 使用 spyOn 模拟 chat 方法
|
|
325
324
|
vi.spyOn(LobeOpenAI.prototype, 'chat').mockImplementation(
|
|
326
325
|
async (payload, { callback }: any) => {
|
|
@@ -338,6 +337,12 @@ describe('AgentRuntime', () => {
|
|
|
338
337
|
expect(updateMock).toHaveBeenCalledWith({ tags: ['Tools Call'] });
|
|
339
338
|
});
|
|
340
339
|
it('should call onStart correctly', async () => {
|
|
340
|
+
vi.spyOn(langfuseCfg, 'getLangfuseConfig').mockReturnValue({
|
|
341
|
+
ENABLE_LANGFUSE: true,
|
|
342
|
+
LANGFUSE_PUBLIC_KEY: 'abc',
|
|
343
|
+
LANGFUSE_SECRET_KEY: 'DDD',
|
|
344
|
+
} as any);
|
|
345
|
+
|
|
341
346
|
vi.spyOn(LangfuseGenerationClient.prototype, 'update').mockImplementation(updateMock);
|
|
342
347
|
vi.spyOn(LobeOpenAI.prototype, 'chat').mockImplementation(
|
|
343
348
|
async (payload, { callback }: any) => {
|
|
@@ -355,6 +360,11 @@ describe('AgentRuntime', () => {
|
|
|
355
360
|
});
|
|
356
361
|
|
|
357
362
|
it('should call onCompletion correctly', async () => {
|
|
363
|
+
vi.spyOn(langfuseCfg, 'getLangfuseConfig').mockReturnValue({
|
|
364
|
+
ENABLE_LANGFUSE: true,
|
|
365
|
+
LANGFUSE_PUBLIC_KEY: 'abc',
|
|
366
|
+
LANGFUSE_SECRET_KEY: 'DDD',
|
|
367
|
+
} as any);
|
|
358
368
|
// Spy on the chat method and trigger onCompletion callback
|
|
359
369
|
vi.spyOn(LangfuseGenerationClient.prototype, 'update').mockImplementation(updateMock);
|
|
360
370
|
vi.spyOn(LobeOpenAI.prototype, 'chat').mockImplementation(
|
|
@@ -379,6 +389,12 @@ describe('AgentRuntime', () => {
|
|
|
379
389
|
});
|
|
380
390
|
});
|
|
381
391
|
it('should call onFinal correctly', async () => {
|
|
392
|
+
vi.spyOn(langfuseCfg, 'getLangfuseConfig').mockReturnValue({
|
|
393
|
+
ENABLE_LANGFUSE: true,
|
|
394
|
+
LANGFUSE_PUBLIC_KEY: 'abc',
|
|
395
|
+
LANGFUSE_SECRET_KEY: 'DDD',
|
|
396
|
+
} as any);
|
|
397
|
+
|
|
382
398
|
vi.spyOn(LobeOpenAI.prototype, 'chat').mockImplementation(
|
|
383
399
|
async (payload, { callback }: any) => {
|
|
384
400
|
if (callback?.onFinal) {
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
1
2
|
import { Langfuse } from 'langfuse';
|
|
2
3
|
import { CreateLangfuseTraceBody } from 'langfuse-core';
|
|
3
4
|
import { describe, expect, it, vi } from 'vitest';
|
|
4
5
|
|
|
5
|
-
import * as server from '@/config/
|
|
6
|
+
import * as server from '@/config/langfuse';
|
|
6
7
|
|
|
7
8
|
import { TraceClient } from './index';
|
|
8
9
|
|
|
9
10
|
describe('TraceClient', () => {
|
|
10
11
|
it('should not initialize Langfuse client when ENABLE_LANGFUSE is false', () => {
|
|
11
|
-
vi.spyOn(server, '
|
|
12
|
+
vi.spyOn(server, 'getLangfuseConfig').mockReturnValue({
|
|
12
13
|
ENABLE_LANGFUSE: false,
|
|
13
14
|
} as any);
|
|
14
15
|
const client = new TraceClient();
|
|
@@ -16,7 +17,7 @@ describe('TraceClient', () => {
|
|
|
16
17
|
});
|
|
17
18
|
|
|
18
19
|
it('should throw error if LANGFUSE keys are missing', () => {
|
|
19
|
-
vi.spyOn(server, '
|
|
20
|
+
vi.spyOn(server, 'getLangfuseConfig').mockReturnValue({
|
|
20
21
|
ENABLE_LANGFUSE: true,
|
|
21
22
|
} as any);
|
|
22
23
|
expect(() => new TraceClient()).toThrow('NO_LANGFUSE_KEY_ERROR');
|
|
@@ -27,7 +28,7 @@ describe('TraceClient', () => {
|
|
|
27
28
|
|
|
28
29
|
vi.spyOn(Langfuse.prototype, 'trace').mockImplementation(mockTrace);
|
|
29
30
|
|
|
30
|
-
vi.spyOn(server, '
|
|
31
|
+
vi.spyOn(server, 'getLangfuseConfig').mockReturnValue({
|
|
31
32
|
ENABLE_LANGFUSE: true,
|
|
32
33
|
LANGFUSE_PUBLIC_KEY: 'public-key',
|
|
33
34
|
LANGFUSE_SECRET_KEY: 'secret-key',
|
|
@@ -45,7 +46,7 @@ describe('TraceClient', () => {
|
|
|
45
46
|
const mockShutdownAsync = vi.fn();
|
|
46
47
|
|
|
47
48
|
vi.spyOn(Langfuse.prototype, 'shutdownAsync').mockImplementation(mockShutdownAsync);
|
|
48
|
-
vi.spyOn(server, '
|
|
49
|
+
vi.spyOn(server, 'getLangfuseConfig').mockReturnValue({
|
|
49
50
|
ENABLE_LANGFUSE: true,
|
|
50
51
|
LANGFUSE_PUBLIC_KEY: 'public-key',
|
|
51
52
|
LANGFUSE_SECRET_KEY: 'secret-key',
|
package/src/libs/traces/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Langfuse } from 'langfuse';
|
|
2
2
|
import { CreateLangfuseTraceBody } from 'langfuse-core';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { getLangfuseConfig } from '@/config/langfuse';
|
|
5
5
|
import { CURRENT_VERSION } from '@/const/version';
|
|
6
6
|
import { TraceEventClient } from '@/libs/traces/event';
|
|
7
7
|
|
|
@@ -13,7 +13,7 @@ export class TraceClient {
|
|
|
13
13
|
|
|
14
14
|
constructor() {
|
|
15
15
|
const { ENABLE_LANGFUSE, LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, LANGFUSE_HOST } =
|
|
16
|
-
|
|
16
|
+
getLangfuseConfig();
|
|
17
17
|
|
|
18
18
|
if (!ENABLE_LANGFUSE) return;
|
|
19
19
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
1
2
|
import { TRPCError } from '@trpc/server';
|
|
2
3
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
4
|
|
|
4
5
|
import * as utils from '@/app/api/middleware/auth/utils';
|
|
5
|
-
import * as
|
|
6
|
+
import * as appConfig from '@/config/app';
|
|
6
7
|
import { createCallerFactory } from '@/libs/trpc';
|
|
7
8
|
import { trpc } from '@/libs/trpc/init';
|
|
8
9
|
import { AuthContext, createContextInner } from '@/server/context';
|
|
@@ -32,7 +33,7 @@ describe('passwordChecker middleware', () => {
|
|
|
32
33
|
});
|
|
33
34
|
|
|
34
35
|
it('should throw UNAUTHORIZED error if access code is not correct', async () => {
|
|
35
|
-
vi.spyOn(
|
|
36
|
+
vi.spyOn(appConfig, 'getAppConfig').mockReturnValue({
|
|
36
37
|
ACCESS_CODES: ['123'],
|
|
37
38
|
} as any);
|
|
38
39
|
vi.spyOn(utils, 'getJWTPayload').mockResolvedValue({ accessCode: '456' });
|
|
@@ -45,7 +46,7 @@ describe('passwordChecker middleware', () => {
|
|
|
45
46
|
|
|
46
47
|
it('should call next with jwtPayload in context if access code is correct', async () => {
|
|
47
48
|
const jwtPayload = { accessCode: '123' };
|
|
48
|
-
vi.spyOn(
|
|
49
|
+
vi.spyOn(appConfig, 'getAppConfig').mockReturnValue({
|
|
49
50
|
ACCESS_CODES: ['123'],
|
|
50
51
|
} as any);
|
|
51
52
|
vi.spyOn(utils, 'getJWTPayload').mockResolvedValue(jwtPayload);
|
|
@@ -60,7 +61,7 @@ describe('passwordChecker middleware', () => {
|
|
|
60
61
|
|
|
61
62
|
it('should call next with jwtPayload in context if no access codes are set', async () => {
|
|
62
63
|
const jwtPayload = {};
|
|
63
|
-
vi.spyOn(
|
|
64
|
+
vi.spyOn(appConfig, 'getAppConfig').mockReturnValue({
|
|
64
65
|
ACCESS_CODES: [],
|
|
65
66
|
} as any);
|
|
66
67
|
vi.spyOn(utils, 'getJWTPayload').mockResolvedValue(jwtPayload);
|
|
@@ -74,7 +75,7 @@ describe('passwordChecker middleware', () => {
|
|
|
74
75
|
});
|
|
75
76
|
it('should call next with jwtPayload in context if access codes is undefined', async () => {
|
|
76
77
|
const jwtPayload = {};
|
|
77
|
-
vi.spyOn(
|
|
78
|
+
vi.spyOn(appConfig, 'getAppConfig').mockReturnValue({} as any);
|
|
78
79
|
vi.spyOn(utils, 'getJWTPayload').mockResolvedValue(jwtPayload);
|
|
79
80
|
|
|
80
81
|
ctx = await createContextInner({ authorizationHeader: 'Bearer token' });
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { TRPCError } from '@trpc/server';
|
|
2
2
|
|
|
3
3
|
import { getJWTPayload } from '@/app/api/middleware/auth/utils';
|
|
4
|
-
import {
|
|
4
|
+
import { getAppConfig } from '@/config/app';
|
|
5
5
|
import { trpc } from '@/libs/trpc/init';
|
|
6
6
|
|
|
7
7
|
export const passwordChecker = trpc.middleware(async (opts) => {
|
|
8
|
-
const { ACCESS_CODES } =
|
|
8
|
+
const { ACCESS_CODES } = getAppConfig();
|
|
9
9
|
|
|
10
10
|
const { ctx } = opts;
|
|
11
11
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { getAppConfig } from '@/config/app';
|
|
1
2
|
import { fileEnv } from '@/config/file';
|
|
3
|
+
import { langfuseEnv } from '@/config/langfuse';
|
|
2
4
|
import { getLLMConfig } from '@/config/llm';
|
|
3
5
|
import {
|
|
4
6
|
OllamaProviderCard,
|
|
@@ -6,7 +8,6 @@ import {
|
|
|
6
8
|
OpenRouterProviderCard,
|
|
7
9
|
TogetherAIProviderCard,
|
|
8
10
|
} from '@/config/modelProviders';
|
|
9
|
-
import { getServerConfig } from '@/config/server';
|
|
10
11
|
import { enableNextAuth } from '@/const/auth';
|
|
11
12
|
import { GlobalServerConfig } from '@/types/serverConfig';
|
|
12
13
|
import { extractEnabledModels, transformToChatModelCards } from '@/utils/parseModels';
|
|
@@ -14,7 +15,7 @@ import { extractEnabledModels, transformToChatModelCards } from '@/utils/parseMo
|
|
|
14
15
|
import { parseAgentConfig } from './parseDefaultAgent';
|
|
15
16
|
|
|
16
17
|
export const getServerGlobalConfig = () => {
|
|
17
|
-
const { ACCESS_CODES,
|
|
18
|
+
const { ACCESS_CODES, DEFAULT_AGENT_CONFIG } = getAppConfig();
|
|
18
19
|
|
|
19
20
|
const {
|
|
20
21
|
ENABLED_OPENAI,
|
|
@@ -114,7 +115,7 @@ export const getServerGlobalConfig = () => {
|
|
|
114
115
|
zhipu: { enabled: ENABLED_ZHIPU },
|
|
115
116
|
},
|
|
116
117
|
telemetry: {
|
|
117
|
-
langfuse: ENABLE_LANGFUSE,
|
|
118
|
+
langfuse: langfuseEnv.ENABLE_LANGFUSE,
|
|
118
119
|
},
|
|
119
120
|
};
|
|
120
121
|
|
|
@@ -122,7 +123,7 @@ export const getServerGlobalConfig = () => {
|
|
|
122
123
|
};
|
|
123
124
|
|
|
124
125
|
export const getServerDefaultAgentConfig = () => {
|
|
125
|
-
const { DEFAULT_AGENT_CONFIG } =
|
|
126
|
+
const { DEFAULT_AGENT_CONFIG } = getAppConfig();
|
|
126
127
|
|
|
127
128
|
return parseAgentConfig(DEFAULT_AGENT_CONFIG) || {};
|
|
128
129
|
};
|
package/src/server/ld.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import urlJoin from 'url-join';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { getAppConfig } from '@/config/app';
|
|
4
4
|
import { EMAIL_BUSINESS, EMAIL_SUPPORT, OFFICIAL_SITE, OFFICIAL_URL, X } from '@/const/url';
|
|
5
5
|
|
|
6
6
|
import pkg from '../../package.json';
|
|
7
7
|
|
|
8
|
-
const { SITE_URL = OFFICIAL_URL } =
|
|
8
|
+
const { SITE_URL = OFFICIAL_URL } = getAppConfig();
|
|
9
9
|
const LAST_MODIFIED = new Date().toISOString();
|
|
10
10
|
export const AUTHOR_LIST = {
|
|
11
11
|
arvinxx: {
|
package/src/utils/basePath.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { appEnv } from '@/config/app';
|
|
2
2
|
|
|
3
|
-
export const withBasePath = (path: string) =>
|
|
3
|
+
export const withBasePath = (path: string) => appEnv.NEXT_PUBLIC_BASE_PATH + path;
|
package/src/config/server/app.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/* eslint-disable sort-keys-fix/sort-keys-fix , typescript-sort-keys/interface */
|
|
2
|
-
|
|
3
|
-
declare global {
|
|
4
|
-
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
5
|
-
namespace NodeJS {
|
|
6
|
-
interface ProcessEnv {
|
|
7
|
-
ACCESS_CODE?: string;
|
|
8
|
-
|
|
9
|
-
SITE_URL?: string;
|
|
10
|
-
|
|
11
|
-
AGENTS_INDEX_URL?: string;
|
|
12
|
-
|
|
13
|
-
PLUGINS_INDEX_URL?: string;
|
|
14
|
-
PLUGIN_SETTINGS?: string;
|
|
15
|
-
|
|
16
|
-
DEFAULT_AGENT_CONFIG?: string;
|
|
17
|
-
|
|
18
|
-
ENABLE_LANGFUSE?: string;
|
|
19
|
-
LANGFUSE_PUBLIC_KEY?: string;
|
|
20
|
-
LANGFUSE_SECRET_KEY?: string;
|
|
21
|
-
LANGFUSE_HOST?: string;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const getAppConfig = () => {
|
|
27
|
-
if (typeof process === 'undefined') {
|
|
28
|
-
throw new Error('[Server Config] you are importing a server-only module outside of server');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const ACCESS_CODES = process.env.ACCESS_CODE?.split(',').filter(Boolean) || [];
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
ACCESS_CODES,
|
|
35
|
-
|
|
36
|
-
DEFAULT_AGENT_CONFIG: process.env.DEFAULT_AGENT_CONFIG || '',
|
|
37
|
-
|
|
38
|
-
SHOW_ACCESS_CODE_CONFIG: !!ACCESS_CODES.length,
|
|
39
|
-
|
|
40
|
-
SITE_URL: process.env.SITE_URL,
|
|
41
|
-
|
|
42
|
-
AGENTS_INDEX_URL: !!process.env.AGENTS_INDEX_URL
|
|
43
|
-
? process.env.AGENTS_INDEX_URL
|
|
44
|
-
: 'https://chat-agents.lobehub.com',
|
|
45
|
-
|
|
46
|
-
PLUGINS_INDEX_URL: !!process.env.PLUGINS_INDEX_URL
|
|
47
|
-
? process.env.PLUGINS_INDEX_URL
|
|
48
|
-
: 'https://chat-plugins.lobehub.com',
|
|
49
|
-
|
|
50
|
-
PLUGIN_SETTINGS: process.env.PLUGIN_SETTINGS,
|
|
51
|
-
|
|
52
|
-
ENABLE_LANGFUSE: process.env.ENABLE_LANGFUSE === '1',
|
|
53
|
-
LANGFUSE_SECRET_KEY: process.env.LANGFUSE_SECRET_KEY || '',
|
|
54
|
-
LANGFUSE_PUBLIC_KEY: process.env.LANGFUSE_PUBLIC_KEY || '',
|
|
55
|
-
LANGFUSE_HOST: process.env.LANGFUSE_HOST || 'https://cloud.langfuse.com',
|
|
56
|
-
};
|
|
57
|
-
};
|