@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 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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#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.2",
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",
@@ -2,8 +2,6 @@
2
2
  import { nanoid } from 'nanoid';
3
3
  import { beforeEach, describe, expect, it, vi } from 'vitest';
4
4
 
5
- import { getServerConfig } from '@/config/server';
6
-
7
5
  import { ApiKeyManager } from './apiKeyManager';
8
6
 
9
7
  function generateKeys(count: number = 1) {
@@ -1,3 +1,4 @@
1
+ // @vitest-environment node
1
2
  import { describe, expect, it } from 'vitest';
2
3
 
3
4
  import { AgentMarket } from './AgentMarket';
@@ -1,6 +1,6 @@
1
1
  import urlJoin from 'url-join';
2
2
 
3
- import { getServerConfig } from '@/config/server';
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 || getServerConfig().AGENTS_INDEX_URL;
11
+ this.baseUrl = baseUrl || appEnv.AGENTS_INDEX_URL;
12
12
  }
13
13
 
14
14
  getAgentIndexUrl = (lang: Locales = DEFAULT_LANG) => {
@@ -4,6 +4,8 @@ import { AgentMarket } from './AgentMarket';
4
4
 
5
5
  export const runtime = 'edge';
6
6
 
7
+ export const revalidate = 3600; // revalidate at almost every hour
8
+
7
9
  export const GET = async (req: Request) => {
8
10
  const locale = new URL(req.url).searchParams.get('locale');
9
11
 
@@ -1,7 +1,7 @@
1
1
  import { type AuthObject } from '@clerk/backend/internal';
2
2
  import { importJWK, jwtVerify } from 'jose';
3
3
 
4
- import { getServerConfig } from '@/config/server';
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 } = getServerConfig();
74
+ const { ACCESS_CODES } = getAppConfig();
75
75
 
76
76
  // if accessCode doesn't exist
77
77
  if (!ACCESS_CODES.length) return;
@@ -1,3 +1,4 @@
1
+ // @vitest-environment node
1
2
  import { checkAuth } from './auth';
2
3
 
3
4
  describe('ACCESS_CODE', () => {
@@ -1,4 +1,4 @@
1
- import { getServerConfig } from '@/config/server';
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 } = getServerConfig();
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 { getServerConfig } from '@/config/server';
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 } = getServerConfig();
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 } = getServerConfig();
35
+ const { PLUGINS_INDEX_URL: pluginsIndexUrl, PLUGIN_SETTINGS } = getAppConfig();
36
36
 
37
37
  const defaultPluginSettings = parserPluginSettings(PLUGIN_SETTINGS);
38
38
 
@@ -1,7 +1,6 @@
1
+ // @vitest-environment node
1
2
  import { describe, expect, it, vi } from 'vitest';
2
3
 
3
- import { isLocaleNotSupport } from '@/const/locale';
4
-
5
4
  import { PluginStore } from './Store';
6
5
 
7
6
  const baseURL = 'https://chat-plugins.lobehub.com';
@@ -1,6 +1,6 @@
1
1
  import urlJoin from 'url-join';
2
2
 
3
- import { getServerConfig } from '@/config/server';
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 || getServerConfig().PLUGINS_INDEX_URL;
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), { next: { revalidate: 3600 } });
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));
@@ -1,14 +1,13 @@
1
1
  import { Metadata } from 'next';
2
2
 
3
- import { getClientConfig } from '@/config/client';
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 } = getServerConfig();
11
- const { BASE_PATH } = getClientConfig();
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 { getClientConfig } from '@/config/client';
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 (!ENABLE_SENTRY) return;
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 { getServerConfig } from '../server';
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 = getServerConfig();
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 = getServerConfig();
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 = getServerConfig();
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 = getServerConfig();
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();
@@ -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 { getServerConfig } from '@/config/server';
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
- beforeEach(() => {
316
- vi.mocked(getServerConfig).mockReturnValue({
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/server';
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, 'getServerConfig').mockReturnValue({
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, 'getServerConfig').mockReturnValue({
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, 'getServerConfig').mockReturnValue({
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, 'getServerConfig').mockReturnValue({
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',
@@ -1,7 +1,7 @@
1
1
  import { Langfuse } from 'langfuse';
2
2
  import { CreateLangfuseTraceBody } from 'langfuse-core';
3
3
 
4
- import { getServerConfig } from '@/config/server';
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
- getServerConfig();
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 serverConfig from '@/config/server';
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(serverConfig, 'getServerConfig').mockReturnValue({
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(serverConfig, 'getServerConfig').mockReturnValue({
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(serverConfig, 'getServerConfig').mockReturnValue({
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(serverConfig, 'getServerConfig').mockReturnValue({} as any);
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 { getServerConfig } from '@/config/server';
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 } = getServerConfig();
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, ENABLE_LANGFUSE, DEFAULT_AGENT_CONFIG } = getServerConfig();
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 } = getServerConfig();
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 { getServerConfig } from '@/config/server';
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 } = getServerConfig();
8
+ const { SITE_URL = OFFICIAL_URL } = getAppConfig();
9
9
  const LAST_MODIFIED = new Date().toISOString();
10
10
  export const AUTHOR_LIST = {
11
11
  arvinxx: {
@@ -1,3 +1,3 @@
1
- import { getClientConfig } from '@/config/client';
1
+ import { appEnv } from '@/config/app';
2
2
 
3
- export const withBasePath = (path: string) => getClientConfig().BASE_PATH + path;
3
+ export const withBasePath = (path: string) => appEnv.NEXT_PUBLIC_BASE_PATH + path;
@@ -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
- };
@@ -1,9 +0,0 @@
1
- import { getAppConfig } from './app';
2
-
3
- export const getServerConfig = () => {
4
- if (typeof process === 'undefined') {
5
- throw new Error('[Server Config] you are importing a server-only module outside of server');
6
- }
7
-
8
- return getAppConfig();
9
- };