@lobehub/chat 1.19.5 → 1.19.6

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.

Potentially problematic release.


This version of @lobehub/chat might be problematic. Click here for more details.

package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.19.6](https://github.com/lobehub/lobe-chat/compare/v1.19.5...v1.19.6)
6
+
7
+ <sup>Released on **2024-09-19**</sup>
8
+
9
+ #### ♻ Code Refactoring
10
+
11
+ - **misc**: Refactor the tts route url.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Code refactoring
19
+
20
+ - **misc**: Refactor the tts route url, closes [#4030](https://github.com/lobehub/lobe-chat/issues/4030) ([60dcf19](https://github.com/lobehub/lobe-chat/commit/60dcf19))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ### [Version 1.19.5](https://github.com/lobehub/lobe-chat/compare/v1.19.4...v1.19.5)
6
31
 
7
32
  <sup>Released on **2024-09-19**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.19.5",
3
+ "version": "1.19.6",
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",
@@ -8,6 +8,7 @@ import { checkAuth } from './auth';
8
8
  import { createOpenai } from './createOpenai';
9
9
 
10
10
  /**
11
+ * @deprecated
11
12
  * createOpenAI Instance with Auth and azure openai support
12
13
  * if auth not pass ,just return error response
13
14
  */
@@ -28,6 +28,7 @@ export const preferredRegion = [
28
28
  export const POST = async (req: Request) => {
29
29
  const payload = (await req.json()) as OpenAITTSPayload;
30
30
 
31
+ // need to be refactored with jwt auth mode
31
32
  const openaiOrErrResponse = createBizOpenAI(req);
32
33
 
33
34
  // if resOrOpenAI is a Response, it means there is an error,just return it
@@ -1,5 +1,6 @@
1
1
  export const OPENAI_END_POINT = 'X-openai-end-point';
2
2
  export const OPENAI_API_KEY_HEADER_KEY = 'X-openai-api-key';
3
+ export const LOBE_USER_ID = 'X-lobe-user-id';
3
4
 
4
5
  export const USE_AZURE_OPENAI = 'X-use-azure-openai';
5
6
 
@@ -19,9 +20,10 @@ export const getOpenAIAuthFromRequest = (req: Request) => {
19
20
  const useAzureStr = req.headers.get(USE_AZURE_OPENAI);
20
21
  const apiVersion = req.headers.get(AZURE_OPENAI_API_VERSION);
21
22
  const oauthAuthorizedStr = req.headers.get(OAUTH_AUTHORIZED);
23
+ const userId = req.headers.get(LOBE_USER_ID);
22
24
 
23
25
  const oauthAuthorized = !!oauthAuthorizedStr;
24
26
  const useAzure = !!useAzureStr;
25
27
 
26
- return { accessCode, apiKey, apiVersion, endpoint, oauthAuthorized, useAzure };
28
+ return { accessCode, apiKey, apiVersion, endpoint, oauthAuthorized, useAzure, userId };
27
29
  };
@@ -35,6 +35,7 @@ import {
35
35
  EmbeddingsPayload,
36
36
  ModelProvider,
37
37
  TextToImagePayload,
38
+ TextToSpeechPayload,
38
39
  } from './types';
39
40
  import { LobeUpstageAI } from './upstage';
40
41
  import { LobeZeroOneAI } from './zeroone';
@@ -97,6 +98,9 @@ class AgentRuntime {
97
98
  async embeddings(payload: EmbeddingsPayload, options?: EmbeddingsOptions) {
98
99
  return this._runtime.embeddings?.(payload, options);
99
100
  }
101
+ async textToSpeech(payload: TextToSpeechPayload, options?: EmbeddingsOptions) {
102
+ return this._runtime.textToSpeech?.(payload, options);
103
+ }
100
104
 
101
105
  /**
102
106
  * @description Initialize the runtime with the provider and the options
@@ -1,6 +1,5 @@
1
1
  import OpenAI from 'openai';
2
2
 
3
- import { TextToImagePayload } from '@/libs/agent-runtime/types/textToImage';
4
3
  import { ChatModelCard } from '@/types/llm';
5
4
 
6
5
  import {
@@ -9,6 +8,9 @@ import {
9
8
  EmbeddingItem,
10
9
  EmbeddingsOptions,
11
10
  EmbeddingsPayload,
11
+ TextToImagePayload,
12
+ TextToSpeechOptions,
13
+ TextToSpeechPayload,
12
14
  } from './types';
13
15
 
14
16
  export interface LobeRuntimeAI {
@@ -20,6 +22,11 @@ export interface LobeRuntimeAI {
20
22
  models?(): Promise<any>;
21
23
 
22
24
  textToImage?: (payload: TextToImagePayload) => Promise<string[]>;
25
+
26
+ textToSpeech?: (
27
+ payload: TextToSpeechPayload,
28
+ options?: TextToSpeechOptions,
29
+ ) => Promise<ArrayBuffer>;
23
30
  }
24
31
 
25
32
  export abstract class LobeOpenAICompatibleRuntime {
@@ -1,4 +1,5 @@
1
1
  export * from './chat';
2
2
  export * from './embeddings';
3
3
  export * from './textToImage';
4
+ export * from './tts';
4
5
  export * from './type';
@@ -0,0 +1,14 @@
1
+ export interface TextToSpeechPayload {
2
+ input: string;
3
+ model: string;
4
+ voice: string;
5
+ }
6
+
7
+ export interface TextToSpeechOptions {
8
+ headers?: Record<string, any>;
9
+ signal?: AbortSignal;
10
+ /**
11
+ * userId for the embeddings
12
+ */
13
+ user?: string;
14
+ }
@@ -1,7 +1,6 @@
1
1
  import OpenAI, { ClientOptions } from 'openai';
2
2
 
3
3
  import { LOBE_DEFAULT_MODEL_LIST } from '@/config/modelProviders';
4
- import { TextToImagePayload } from '@/libs/agent-runtime/types/textToImage';
5
4
  import { ChatModelCard } from '@/types/llm';
6
5
 
7
6
  import { LobeRuntimeAI } from '../../BaseAI';
@@ -13,6 +12,9 @@ import {
13
12
  EmbeddingItem,
14
13
  EmbeddingsOptions,
15
14
  EmbeddingsPayload,
15
+ TextToImagePayload,
16
+ TextToSpeechOptions,
17
+ TextToSpeechPayload,
16
18
  } from '../../types';
17
19
  import { AgentRuntimeError } from '../createError';
18
20
  import { debugResponse, debugStream } from '../debugStream';
@@ -253,6 +255,19 @@ export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>
253
255
  }
254
256
  }
255
257
 
258
+ async textToSpeech(payload: TextToSpeechPayload, options?: TextToSpeechOptions) {
259
+ try {
260
+ const mp3 = await this.client.audio.speech.create(payload as any, {
261
+ headers: options?.headers,
262
+ signal: options?.signal,
263
+ });
264
+
265
+ return mp3.arrayBuffer();
266
+ } catch (error) {
267
+ throw this.handleError(error);
268
+ }
269
+ }
270
+
256
271
  private handleError(error: any): ChatCompletionErrorPayload {
257
272
  let desensitizedEndpoint = this.baseURL;
258
273
 
@@ -1,4 +1,9 @@
1
- import { LOBE_CHAT_ACCESS_CODE, OPENAI_API_KEY_HEADER_KEY, OPENAI_END_POINT } from '@/const/fetch';
1
+ import {
2
+ LOBE_CHAT_ACCESS_CODE,
3
+ LOBE_USER_ID,
4
+ OPENAI_API_KEY_HEADER_KEY,
5
+ OPENAI_END_POINT,
6
+ } from '@/const/fetch';
2
7
  import { useUserStore } from '@/store/user';
3
8
  import { keyVaultsConfigSelectors } from '@/store/user/selectors';
4
9
 
@@ -8,12 +13,14 @@ import { keyVaultsConfigSelectors } from '@/store/user/selectors';
8
13
  */
9
14
  // eslint-disable-next-line no-undef
10
15
  export const createHeaderWithOpenAI = (header?: HeadersInit): HeadersInit => {
11
- const openAIConfig = keyVaultsConfigSelectors.openAIConfig(useUserStore.getState());
16
+ const state = useUserStore.getState();
17
+ const openAIConfig = keyVaultsConfigSelectors.openAIConfig(state);
12
18
 
13
19
  // eslint-disable-next-line no-undef
14
20
  return {
15
21
  ...header,
16
- [LOBE_CHAT_ACCESS_CODE]: keyVaultsConfigSelectors.password(useUserStore.getState()),
22
+ [LOBE_CHAT_ACCESS_CODE]: keyVaultsConfigSelectors.password(state),
23
+ [LOBE_USER_ID]: state.user?.id || '',
17
24
  [OPENAI_API_KEY_HEADER_KEY]: openAIConfig.apiKey || '',
18
25
  [OPENAI_END_POINT]: openAIConfig.baseURL || '',
19
26
  };
@@ -1,4 +1,4 @@
1
- // TODO: 未来所有路由需要全部迁移到 trpc
1
+ // TODO: 未来路由需要迁移到 trpc or /webapi
2
2
 
3
3
  /* eslint-disable sort-keys-fix/sort-keys-fix */
4
4
  import { transform } from 'lodash-es';
@@ -38,9 +38,11 @@ export const API_ENDPOINTS = mapWithBasePath({
38
38
  // image
39
39
  images: '/api/text-to-image/openai',
40
40
 
41
- // TTS & STT
42
- stt: '/api/openai/stt',
43
- tts: '/api/openai/tts',
44
- edge: '/api/tts/edge-speech',
45
- microsoft: '/api/tts/microsoft-speech',
41
+ // STT
42
+ stt: '/webapi/stt/openai',
43
+
44
+ // TTS
45
+ tts: '/webapi/tts/openai',
46
+ edge: '/webapi/tts/edge',
47
+ microsoft: '/webapi/tts/microsoft',
46
48
  });
@@ -39,7 +39,7 @@ export const createTTSFileSlice: StateCreator<
39
39
  };
40
40
  const file = new File([blob], fileName, fileOptions);
41
41
 
42
- const res = await get().uploadWithProgress({ file });
42
+ const res = await get().uploadWithProgress({ file, skipCheckFileType: true });
43
43
 
44
44
  return res?.id;
45
45
  },
@@ -29,6 +29,12 @@ interface UploadWithProgressParams {
29
29
  type: 'removeFile';
30
30
  },
31
31
  ) => void;
32
+ /**
33
+ * Optional flag to indicate whether to skip the file type check.
34
+ * When set to `true`, any file type checks will be bypassed.
35
+ * Default is `false`, which means file type checks will be performed.
36
+ */
37
+ skipCheckFileType?: boolean;
32
38
  }
33
39
 
34
40
  interface UploadWithProgressResult {
@@ -52,8 +58,8 @@ export const createFileUploadSlice: StateCreator<
52
58
  [],
53
59
  FileUploadAction
54
60
  > = (set, get) => ({
55
- internal_uploadToClientDB: async ({ file, onStatusUpdate }) => {
56
- if (!file.type.startsWith('image')) {
61
+ internal_uploadToClientDB: async ({ file, onStatusUpdate, skipCheckFileType }) => {
62
+ if (!skipCheckFileType && !file.type.startsWith('image')) {
57
63
  onStatusUpdate?.({ id: file.name, type: 'removeFile' });
58
64
  message.info({
59
65
  content: t('upload.fileOnlySupportInServerMode', {
@@ -158,11 +164,11 @@ export const createFileUploadSlice: StateCreator<
158
164
  return data;
159
165
  },
160
166
 
161
- uploadWithProgress: async ({ file, onStatusUpdate, knowledgeBaseId }) => {
167
+ uploadWithProgress: async (payload) => {
162
168
  const { internal_uploadToServer, internal_uploadToClientDB } = get();
163
169
 
164
- if (isServerMode) return internal_uploadToServer({ file, knowledgeBaseId, onStatusUpdate });
170
+ if (isServerMode) return internal_uploadToServer(payload);
165
171
 
166
- return internal_uploadToClientDB({ file, onStatusUpdate });
172
+ return internal_uploadToClientDB(payload);
167
173
  },
168
174
  });