@lobehub/chat 0.137.0 → 0.138.1

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.
Files changed (78) hide show
  1. package/.env.example +11 -5
  2. package/CHANGELOG.md +50 -0
  3. package/docs/self-hosting/environment-variables/model-provider.mdx +9 -2
  4. package/docs/self-hosting/environment-variables/model-provider.zh-CN.mdx +9 -2
  5. package/locales/ar/common.json +1 -0
  6. package/locales/ar/error.json +6 -0
  7. package/locales/ar/setting.json +8 -0
  8. package/locales/de-DE/common.json +1 -0
  9. package/locales/de-DE/error.json +6 -0
  10. package/locales/de-DE/setting.json +8 -0
  11. package/locales/en-US/common.json +1 -0
  12. package/locales/en-US/error.json +6 -0
  13. package/locales/en-US/setting.json +8 -0
  14. package/locales/es-ES/common.json +1 -0
  15. package/locales/es-ES/error.json +6 -0
  16. package/locales/es-ES/setting.json +8 -0
  17. package/locales/fr-FR/common.json +1 -0
  18. package/locales/fr-FR/error.json +6 -0
  19. package/locales/fr-FR/setting.json +8 -0
  20. package/locales/it-IT/common.json +1 -0
  21. package/locales/it-IT/error.json +6 -0
  22. package/locales/it-IT/setting.json +8 -0
  23. package/locales/ja-JP/common.json +1 -0
  24. package/locales/ja-JP/error.json +6 -0
  25. package/locales/ja-JP/setting.json +8 -0
  26. package/locales/ko-KR/common.json +1 -0
  27. package/locales/ko-KR/error.json +6 -0
  28. package/locales/ko-KR/setting.json +8 -0
  29. package/locales/nl-NL/common.json +1 -0
  30. package/locales/nl-NL/error.json +6 -0
  31. package/locales/nl-NL/setting.json +8 -0
  32. package/locales/pl-PL/common.json +1 -0
  33. package/locales/pl-PL/error.json +6 -0
  34. package/locales/pl-PL/setting.json +8 -0
  35. package/locales/pt-BR/common.json +1 -0
  36. package/locales/pt-BR/error.json +6 -0
  37. package/locales/pt-BR/setting.json +8 -0
  38. package/locales/ru-RU/common.json +1 -0
  39. package/locales/ru-RU/error.json +6 -0
  40. package/locales/ru-RU/setting.json +8 -0
  41. package/locales/tr-TR/common.json +1 -0
  42. package/locales/tr-TR/error.json +6 -0
  43. package/locales/tr-TR/setting.json +8 -0
  44. package/locales/vi-VN/common.json +1 -0
  45. package/locales/vi-VN/error.json +6 -0
  46. package/locales/vi-VN/setting.json +8 -0
  47. package/locales/zh-CN/common.json +1 -0
  48. package/locales/zh-CN/error.json +6 -0
  49. package/locales/zh-CN/setting.json +8 -0
  50. package/locales/zh-TW/common.json +1 -0
  51. package/locales/zh-TW/error.json +6 -0
  52. package/locales/zh-TW/setting.json +8 -0
  53. package/package.json +1 -1
  54. package/src/app/api/chat/[provider]/agentRuntime.ts +15 -2
  55. package/src/app/api/config/route.ts +2 -0
  56. package/src/app/api/errorResponse.ts +3 -0
  57. package/src/app/settings/llm/Groq/index.tsx +47 -0
  58. package/src/app/settings/llm/index.tsx +2 -0
  59. package/src/components/ModelProviderIcon/index.tsx +5 -0
  60. package/src/config/modelProviders/groq.ts +24 -0
  61. package/src/config/modelProviders/index.ts +3 -0
  62. package/src/config/server/provider.ts +11 -3
  63. package/src/const/settings.ts +4 -0
  64. package/src/const/url.ts +1 -1
  65. package/src/features/Conversation/Error/APIKeyForm/Groq.tsx +60 -0
  66. package/src/features/Conversation/Error/APIKeyForm/index.tsx +5 -0
  67. package/src/features/Conversation/Error/index.tsx +1 -0
  68. package/src/features/Conversation/components/ChatItem/index.tsx +2 -1
  69. package/src/libs/agent-runtime/error.ts +3 -0
  70. package/src/libs/agent-runtime/groq/index.ts +78 -0
  71. package/src/libs/agent-runtime/index.ts +1 -0
  72. package/src/libs/agent-runtime/types/type.ts +1 -0
  73. package/src/locales/default/common.ts +1 -0
  74. package/src/locales/default/error.ts +7 -0
  75. package/src/locales/default/setting.ts +8 -0
  76. package/src/services/_auth.ts +5 -1
  77. package/src/store/global/slices/settings/selectors/modelProvider.ts +14 -5
  78. package/src/types/settings/modelProvider.ts +6 -0
@@ -0,0 +1,60 @@
1
+ import { Groq } from '@lobehub/icons';
2
+ import { Input } from 'antd';
3
+ import { memo } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { ModelProvider } from '@/libs/agent-runtime';
7
+ import { useGlobalStore } from '@/store/global';
8
+ import { modelProviderSelectors } from '@/store/global/selectors';
9
+
10
+ import { FormAction } from '../style';
11
+
12
+ const GroqForm = memo(() => {
13
+ const { t } = useTranslation('error');
14
+ // const [showProxy, setShow] = useState(false);
15
+
16
+ const [apiKey, setConfig] = useGlobalStore((s) => [
17
+ modelProviderSelectors.groqAPIKey(s),
18
+ s.setModelProviderConfig,
19
+ ]);
20
+
21
+ return (
22
+ <FormAction
23
+ avatar={<Groq size={56} />}
24
+ description={t('unlock.apikey.Groq.description')}
25
+ title={t('unlock.apikey.Groq.title')}
26
+ >
27
+ <Input.Password
28
+ autoComplete={'new-password'}
29
+ onChange={(e) => {
30
+ setConfig(ModelProvider.Groq, { apiKey: e.target.value });
31
+ }}
32
+ placeholder={'*********************************'}
33
+ type={'block'}
34
+ value={apiKey}
35
+ />
36
+ {/*{showProxy ? (*/}
37
+ {/* <Input*/}
38
+ {/* onChange={(e) => {*/}
39
+ {/* setConfig({ endpoint: e.target.value });*/}
40
+ {/* }}*/}
41
+ {/* placeholder={'https://api.openai.com/v1'}*/}
42
+ {/* type={'block'}*/}
43
+ {/* value={proxyUrl}*/}
44
+ {/* />*/}
45
+ {/*) : (*/}
46
+ {/* <Button*/}
47
+ {/* icon={<Icon icon={Network} />}*/}
48
+ {/* onClick={() => {*/}
49
+ {/* setShow(true);*/}
50
+ {/* }}*/}
51
+ {/* type={'text'}*/}
52
+ {/* >*/}
53
+ {/* {t('unlock.apikey.addProxyUrl')}*/}
54
+ {/* </Button>*/}
55
+ {/*)}*/}
56
+ </FormAction>
57
+ );
58
+ });
59
+
60
+ export default GroqForm;
@@ -9,6 +9,7 @@ import { useChatStore } from '@/store/chat';
9
9
  import AnthropicForm from './Anthropic';
10
10
  import BedrockForm from './Bedrock';
11
11
  import GoogleForm from './Google';
12
+ import GroqForm from './Groq';
12
13
  import MistralForm from './Mistral';
13
14
  import MoonshotForm from './Moonshot';
14
15
  import OpenAIForm from './OpenAI';
@@ -55,6 +56,10 @@ const APIKeyForm = memo<APIKeyFormProps>(({ id, provider }) => {
55
56
  return <AnthropicForm />;
56
57
  }
57
58
 
59
+ case ModelProvider.Groq: {
60
+ return <GroqForm />;
61
+ }
62
+
58
63
  default:
59
64
  case ModelProvider.OpenAI: {
60
65
  return <OpenAIForm />;
@@ -74,6 +74,7 @@ const ErrorMessageExtra = memo<{ data: ChatMessage }>(({ data }) => {
74
74
  case AgentRuntimeErrorType.InvalidGoogleAPIKey:
75
75
  case AgentRuntimeErrorType.InvalidPerplexityAPIKey:
76
76
  case AgentRuntimeErrorType.InvalidAnthropicAPIKey:
77
+ case AgentRuntimeErrorType.InvalidGroqAPIKey:
77
78
  case AgentRuntimeErrorType.NoOpenAIAPIKey: {
78
79
  return <InvalidAPIKey id={data.id} provider={data.error?.body?.provider} />;
79
80
  }
@@ -81,13 +81,14 @@ const Item = memo<ChatListItemProps>(({ index, id }) => {
81
81
  [item?.role],
82
82
  );
83
83
 
84
+ const { t: errorT } = useTranslation('error');
84
85
  const error = useMemo<AlertProps | undefined>(() => {
85
86
  if (!item?.error) return;
86
87
  const messageError = item.error;
87
88
 
88
89
  const alertConfig = getErrorAlertConfig(messageError.type);
89
90
 
90
- return { message: t(`response.${messageError.type}` as any, { ns: 'error' }), ...alertConfig };
91
+ return { message: errorT(`response.${messageError.type}` as any), ...alertConfig };
91
92
  }, [item?.error]);
92
93
 
93
94
  const enableHistoryDivider = useSessionStore((s) => {
@@ -34,6 +34,9 @@ export const AgentRuntimeErrorType = {
34
34
 
35
35
  InvalidAnthropicAPIKey: 'InvalidAnthropicAPIKey',
36
36
  AnthropicBizError: 'AnthropicBizError',
37
+
38
+ InvalidGroqAPIKey: 'InvalidGroqAPIKey',
39
+ GroqBizError: 'GroqBizError',
37
40
  } as const;
38
41
 
39
42
  export type ILobeAgentRuntimeErrorType =
@@ -0,0 +1,78 @@
1
+ import { OpenAIStream, StreamingTextResponse } from 'ai';
2
+ import OpenAI, { ClientOptions } from 'openai';
3
+
4
+ import { LobeRuntimeAI } from '../BaseAI';
5
+ import { AgentRuntimeErrorType } from '../error';
6
+ import { ChatCompetitionOptions, ChatStreamPayload, ModelProvider } from '../types';
7
+ import { AgentRuntimeError } from '../utils/createError';
8
+ import { debugStream } from '../utils/debugStream';
9
+ import { desensitizeUrl } from '../utils/desensitizeUrl';
10
+ import { handleOpenAIError } from '../utils/handleOpenAIError';
11
+
12
+ const DEFAULT_BASE_URL = 'https://api.groq.com/openai/v1';
13
+
14
+ export class LobeGroq implements LobeRuntimeAI {
15
+ private client: OpenAI;
16
+
17
+ baseURL: string;
18
+
19
+ constructor({ apiKey, baseURL = DEFAULT_BASE_URL, ...res }: ClientOptions) {
20
+ if (!apiKey) throw AgentRuntimeError.createError(AgentRuntimeErrorType.InvalidGroqAPIKey);
21
+
22
+ this.client = new OpenAI({ apiKey, baseURL, ...res });
23
+ this.baseURL = this.client.baseURL;
24
+ }
25
+
26
+ async chat(payload: ChatStreamPayload, options?: ChatCompetitionOptions) {
27
+ try {
28
+ const response = await this.client.chat.completions.create(
29
+ payload as unknown as OpenAI.ChatCompletionCreateParamsStreaming,
30
+ );
31
+ const [prod, debug] = response.tee();
32
+
33
+ if (process.env.DEBUG_GROQ_CHAT_COMPLETION === '1') {
34
+ debugStream(debug.toReadableStream()).catch(console.error);
35
+ }
36
+
37
+ return new StreamingTextResponse(OpenAIStream(prod, options?.callback), {
38
+ headers: options?.headers,
39
+ });
40
+ } catch (error) {
41
+ let desensitizedEndpoint = this.baseURL;
42
+
43
+ if (this.baseURL !== DEFAULT_BASE_URL) {
44
+ desensitizedEndpoint = desensitizeUrl(this.baseURL);
45
+ }
46
+
47
+ if ('status' in (error as any)) {
48
+ switch ((error as Response).status) {
49
+ case 401: {
50
+ throw AgentRuntimeError.chat({
51
+ endpoint: desensitizedEndpoint,
52
+ error: error as any,
53
+ errorType: AgentRuntimeErrorType.InvalidGroqAPIKey,
54
+ provider: ModelProvider.Groq,
55
+ });
56
+ }
57
+
58
+ default: {
59
+ break;
60
+ }
61
+ }
62
+ }
63
+
64
+ const { errorResult, RuntimeError } = handleOpenAIError(error);
65
+
66
+ const errorType = RuntimeError || AgentRuntimeErrorType.GroqBizError;
67
+
68
+ throw AgentRuntimeError.chat({
69
+ endpoint: desensitizedEndpoint,
70
+ error: errorResult,
71
+ errorType,
72
+ provider: ModelProvider.Groq,
73
+ });
74
+ }
75
+ }
76
+ }
77
+
78
+ export default LobeGroq;
@@ -4,6 +4,7 @@ export * from './BaseAI';
4
4
  export { LobeBedrockAI } from './bedrock';
5
5
  export * from './error';
6
6
  export { LobeGoogleAI } from './google';
7
+ export { LobeGroq } from './groq';
7
8
  export { LobeMistralAI } from './mistral';
8
9
  export { LobeMoonshotAI } from './moonshot';
9
10
  export { LobeOllamaAI } from './ollama';
@@ -27,6 +27,7 @@ export enum ModelProvider {
27
27
  Bedrock = 'bedrock',
28
28
  ChatGLM = 'chatglm',
29
29
  Google = 'google',
30
+ Groq = 'groq',
30
31
  Mistral = 'mistral',
31
32
  Moonshot = 'moonshot',
32
33
  Ollama = 'ollama',
@@ -104,6 +104,7 @@ export default {
104
104
  azure: 'Azure',
105
105
  bedrock: 'AWS Bedrock',
106
106
  google: 'Google',
107
+ groq: 'Groq',
107
108
  mistral: 'Mistral AI',
108
109
  moonshot: 'Moonshot AI',
109
110
  ollama: 'Ollama',
@@ -82,6 +82,9 @@ export default {
82
82
  InvalidAnthropicAPIKey: 'Anthropic API Key 不正确或为空,请检查 Anthropic API Key 后重试',
83
83
  AnthropicBizError: '请求 Anthropic AI 服务出错,请根据以下信息排查或重试',
84
84
 
85
+ InvalidGroqAPIKey: 'Groq API Key 不正确或为空,请检查 Groq API Key 后重试',
86
+ GroqBizError: '请求 Groq 服务出错,请根据以下信息排查或重试',
87
+
85
88
  InvalidOllamaArgs: 'Ollama 配置不正确,请检查 Ollama 配置后重试',
86
89
  OllamaBizError: '请求 Ollama 服务出错,请根据以下信息排查或重试',
87
90
  OllamaServiceUnavailable: '未检测到 Ollama 服务,请检查是否正常启动',
@@ -111,6 +114,10 @@ export default {
111
114
  description: '输入你的 Google API Key 即可开始会话。应用不会记录你的 API Key',
112
115
  title: '使用自定义 Google API Key',
113
116
  },
117
+ Groq: {
118
+ description: '输入你的 Groq API Key 即可开始会话。应用不会记录你的 API Key',
119
+ title: '使用自定义 Groq API Key',
120
+ },
114
121
  Mistral: {
115
122
  description: '输入你的 Mistral AI API Key 即可开始会话。应用不会记录你的 API Key',
116
123
  title: '使用自定义 Mistral AI API Key',
@@ -100,6 +100,14 @@ export default {
100
100
  title: 'API Key',
101
101
  },
102
102
  },
103
+ Groq: {
104
+ title: 'Groq',
105
+ token: {
106
+ desc: '填入来自 Groq 的 API Key',
107
+ placeholder: 'Groq API Key',
108
+ title: 'API Key',
109
+ },
110
+ },
103
111
  Mistral: {
104
112
  title: 'Mistral AI',
105
113
  token: {
@@ -57,11 +57,15 @@ export const getProviderAuthPayload = (provider: string) => {
57
57
  const endpoint = modelProviderSelectors.anthropicProxyUrl(useGlobalStore.getState());
58
58
  return { apiKey, endpoint };
59
59
  }
60
-
60
+
61
61
  case ModelProvider.Mistral: {
62
62
  return { apiKey: modelProviderSelectors.mistralAPIKey(useGlobalStore.getState()) };
63
63
  }
64
64
 
65
+ case ModelProvider.Groq: {
66
+ return { apiKey: modelProviderSelectors.groqAPIKey(useGlobalStore.getState()) };
67
+ }
68
+
65
69
  default:
66
70
  case ModelProvider.OpenAI: {
67
71
  const openai = modelProviderSelectors.openAIConfig(useGlobalStore.getState());
@@ -4,6 +4,7 @@ import {
4
4
  AnthropicProvider,
5
5
  BedrockProvider,
6
6
  GoogleProvider,
7
+ GroqProvider,
7
8
  LOBE_DEFAULT_MODEL_LIST,
8
9
  MistralProvider,
9
10
  MoonshotProvider,
@@ -58,6 +59,9 @@ const enableAnthropic = (s: GlobalStore) => modelProvider(s).anthropic.enabled;
58
59
  const anthropicAPIKey = (s: GlobalStore) => modelProvider(s).anthropic.apiKey;
59
60
  const anthropicProxyUrl = (s: GlobalStore) => modelProvider(s).anthropic.endpoint;
60
61
 
62
+ const enableGroq = (s: GlobalStore) => modelProvider(s).groq.enabled;
63
+ const groqAPIKey = (s: GlobalStore) => modelProvider(s).groq.apiKey;
64
+
61
65
  // const azureModelList = (s: GlobalStore): ModelProviderCard => {
62
66
  // const azure = azureConfig(s);
63
67
  // return {
@@ -143,14 +147,15 @@ const modelSelectList = (s: GlobalStore): ModelProviderCard[] => {
143
147
  chatModels: openaiChatModels,
144
148
  },
145
149
  // { ...azureModelList(s), enabled: enableAzure(s) },
146
- { ...ZhiPuProvider, enabled: enableZhipu(s) },
147
- { ...MoonshotProvider, enabled: enableMoonshot(s) },
150
+ { ...OllamaProvider, chatModels: ollamaChatModels, enabled: enableOllama(s) },
151
+ { ...AnthropicProvider, enabled: enableAnthropic(s) },
148
152
  { ...GoogleProvider, enabled: enableGoogle(s) },
149
153
  { ...BedrockProvider, enabled: enableBedrock(s) },
150
- { ...OllamaProvider, chatModels: ollamaChatModels, enabled: enableOllama(s) },
151
154
  { ...PerplexityProvider, enabled: enablePerplexity(s) },
152
- { ...AnthropicProvider, enabled: enableAnthropic(s) },
153
155
  { ...MistralProvider, enabled: enableMistral(s) },
156
+ { ...GroqProvider, enabled: enableGroq(s) },
157
+ { ...ZhiPuProvider, enabled: enableZhipu(s) },
158
+ { ...MoonshotProvider, enabled: enableMoonshot(s) },
154
159
  ];
155
160
  };
156
161
 
@@ -229,8 +234,12 @@ export const modelProviderSelectors = {
229
234
  enableAnthropic,
230
235
  anthropicAPIKey,
231
236
  anthropicProxyUrl,
232
-
237
+
233
238
  // Mistral
234
239
  enableMistral,
235
240
  mistralAPIKey,
241
+
242
+ // Groq
243
+ enableGroq,
244
+ groqAPIKey,
236
245
  };
@@ -71,11 +71,17 @@ export interface MistralConfig {
71
71
  enabled: boolean;
72
72
  }
73
73
 
74
+ export interface GroqConfig {
75
+ apiKey?: string;
76
+ enabled: boolean;
77
+ }
78
+
74
79
  export interface GlobalLLMConfig {
75
80
  anthropic: AnthropicConfig;
76
81
  azure: AzureOpenAIConfig;
77
82
  bedrock: AWSBedrockConfig;
78
83
  google: GoogleConfig;
84
+ groq: GroqConfig;
79
85
  mistral: MistralConfig;
80
86
  moonshot: MoonshotConfig;
81
87
  ollama: OllamaConfig;