@lobehub/chat 1.134.7 → 1.135.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 (64) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/ar/chat.json +2 -2
  4. package/locales/ar/models.json +3 -0
  5. package/locales/bg-BG/chat.json +2 -2
  6. package/locales/bg-BG/models.json +3 -0
  7. package/locales/de-DE/chat.json +2 -2
  8. package/locales/de-DE/models.json +3 -0
  9. package/locales/en-US/chat.json +2 -2
  10. package/locales/en-US/models.json +3 -0
  11. package/locales/es-ES/chat.json +2 -2
  12. package/locales/es-ES/models.json +3 -0
  13. package/locales/fa-IR/chat.json +2 -2
  14. package/locales/fa-IR/models.json +3 -0
  15. package/locales/fr-FR/chat.json +2 -2
  16. package/locales/fr-FR/models.json +3 -0
  17. package/locales/it-IT/chat.json +2 -2
  18. package/locales/it-IT/models.json +3 -0
  19. package/locales/ja-JP/chat.json +2 -2
  20. package/locales/ja-JP/models.json +3 -0
  21. package/locales/ko-KR/chat.json +2 -2
  22. package/locales/ko-KR/models.json +3 -0
  23. package/locales/nl-NL/chat.json +2 -2
  24. package/locales/nl-NL/models.json +3 -0
  25. package/locales/pl-PL/chat.json +2 -2
  26. package/locales/pl-PL/models.json +3 -0
  27. package/locales/pt-BR/chat.json +2 -2
  28. package/locales/pt-BR/models.json +3 -0
  29. package/locales/ru-RU/chat.json +2 -2
  30. package/locales/ru-RU/models.json +3 -0
  31. package/locales/tr-TR/chat.json +2 -2
  32. package/locales/tr-TR/models.json +3 -0
  33. package/locales/vi-VN/chat.json +2 -2
  34. package/locales/vi-VN/models.json +3 -0
  35. package/locales/zh-CN/chat.json +2 -2
  36. package/locales/zh-CN/models.json +3 -0
  37. package/locales/zh-TW/chat.json +2 -2
  38. package/locales/zh-TW/models.json +3 -0
  39. package/next.config.ts +5 -6
  40. package/package.json +1 -1
  41. package/packages/context-engine/src/tools/ToolsEngine.ts +27 -5
  42. package/packages/context-engine/src/tools/__tests__/ToolsEngine.test.ts +89 -0
  43. package/packages/model-bank/src/aiModels/fal.ts +28 -0
  44. package/packages/model-runtime/src/core/openaiCompatibleFactory/createImage.ts +16 -27
  45. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.test.ts +51 -11
  46. package/packages/model-runtime/src/core/streams/protocol.ts +2 -15
  47. package/packages/model-runtime/src/providers/fal/index.ts +12 -7
  48. package/packages/model-runtime/src/providers/newapi/index.test.ts +28 -3
  49. package/packages/model-runtime/src/providers/newapi/index.ts +34 -88
  50. package/packages/model-runtime/src/types/index.ts +0 -1
  51. package/packages/types/src/message/base.ts +1 -0
  52. package/packages/utils/package.json +2 -1
  53. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/index.tsx +1 -17
  54. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatMinimap/index.tsx +7 -4
  55. package/src/app/[variants]/(main)/chat/@session/features/SessionListContent/List/Item/index.tsx +16 -17
  56. package/src/app/[variants]/(main)/chat/@session/features/SessionListContent/ListItem/index.tsx +2 -2
  57. package/src/app/[variants]/(main)/image/@menu/components/SizeSelect/index.tsx +24 -1
  58. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/BuiltinPluginTitle.tsx +15 -17
  59. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/ToolTitle.tsx +5 -7
  60. package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments/index.tsx +1 -8
  61. package/src/locales/default/chat.ts +2 -2
  62. package/src/server/modules/EdgeConfig/index.ts +15 -33
  63. package/src/server/modules/EdgeConfig/types.ts +13 -0
  64. package/packages/model-runtime/src/types/usage.ts +0 -27
@@ -20,41 +20,21 @@ export interface NewAPIPricing {
20
20
  model_name: string;
21
21
  model_price?: number;
22
22
  model_ratio?: number;
23
- quota_type: number; // 0: 按量计费, 1: 按次计费
23
+ /** 0: Pay-per-token, 1: Pay-per-call */
24
+ quota_type: number;
24
25
  supported_endpoint_types?: string[];
25
26
  }
26
27
 
27
28
  const handlePayload = (payload: ChatStreamPayload) => {
28
- // 处理 OpenAI responses API 模式
29
+ // Handle OpenAI responses API mode
29
30
  if (
30
31
  responsesAPIModels.has(payload.model) ||
31
32
  payload.model.includes('gpt-') ||
32
33
  /^o\d/.test(payload.model)
33
34
  ) {
34
- return { ...payload, apiMode: 'responses' } as any;
35
+ return { ...payload, apiMode: 'responses' };
35
36
  }
36
- return payload as any;
37
- };
38
-
39
- // 根据 owned_by 字段判断提供商(基于 NewAPI 的 channel name)
40
- const getProviderFromOwnedBy = (ownedBy: string): string => {
41
- const normalizedOwnedBy = ownedBy.toLowerCase();
42
-
43
- if (normalizedOwnedBy.includes('claude') || normalizedOwnedBy.includes('anthropic')) {
44
- return 'anthropic';
45
- }
46
- if (normalizedOwnedBy.includes('google') || normalizedOwnedBy.includes('gemini')) {
47
- return 'google';
48
- }
49
- if (normalizedOwnedBy.includes('xai') || normalizedOwnedBy.includes('grok')) {
50
- return 'xai';
51
- }
52
- if (normalizedOwnedBy.includes('ali') || normalizedOwnedBy.includes('qwen')) {
53
- return 'qwen';
54
- }
55
-
56
- // 默认为 openai
57
- return 'openai';
37
+ return payload;
58
38
  };
59
39
 
60
40
  export const LobeNewAPIAI = createRouterRuntime({
@@ -66,16 +46,16 @@ export const LobeNewAPIAI = createRouterRuntime({
66
46
  },
67
47
  id: ModelProvider.NewAPI,
68
48
  models: async ({ client: openAIClient }) => {
69
- // 获取基础 URL(移除末尾的 API 版本路径如 /v1、/v1beta 等)
49
+ // Get base URL (remove trailing API version paths like /v1, /v1beta, etc.)
70
50
  const baseURL = openAIClient.baseURL.replace(/\/v\d+[a-z]*\/?$/, '');
71
51
 
72
52
  const modelsPage = (await openAIClient.models.list()) as any;
73
53
  const modelList: NewAPIModelCard[] = modelsPage.data || [];
74
54
 
75
- // 尝试获取 pricing 信息以补充模型详细信息
55
+ // Try to get pricing information to enrich model details
76
56
  let pricingMap: Map<string, NewAPIPricing> = new Map();
77
57
  try {
78
- // 使用保存的 baseURL
58
+ // Use saved baseURL
79
59
  const pricingResponse = await fetch(`${baseURL}/api/pricing`, {
80
60
  headers: {
81
61
  Authorization: `Bearer ${openAIClient.apiKey}`,
@@ -99,22 +79,22 @@ export const LobeNewAPIAI = createRouterRuntime({
99
79
  const enrichedModelList = modelList.map((model) => {
100
80
  let enhancedModel: any = { ...model };
101
81
 
102
- // 1. 添加 pricing 信息
82
+ // add pricing info
103
83
  const pricing = pricingMap.get(model.id);
104
84
  if (pricing) {
105
- // NewAPI 的价格计算逻辑:
106
- // - quota_type: 0 表示按量计费(按 token),1 表示按次计费
107
- // - model_ratio: 相对于基础价格的倍率(基础价格 = $0.002/1K tokens
108
- // - model_price: 直接指定的价格(优先使用)
109
- // - completion_ratio: 输出价格相对于输入价格的倍率
85
+ // NewAPI pricing calculation logic:
86
+ // - quota_type: 0 means pay-per-token, 1 means pay-per-call
87
+ // - model_ratio: multiplier relative to base price (base price = $0.002/1K tokens)
88
+ // - model_price: directly specified price (takes priority)
89
+ // - completion_ratio: output price multiplier relative to input price
110
90
  //
111
- // LobeChat 需要的格式:美元/百万 token
91
+ // LobeChat required format: USD per million tokens
112
92
 
113
93
  let inputPrice: number | undefined;
114
94
  let outputPrice: number | undefined;
115
95
 
116
96
  if (pricing.quota_type === 0) {
117
- // 按量计费
97
+ // Pay-per-token
118
98
  if (pricing.model_price && pricing.model_price > 0) {
119
99
  // model_price is a direct price value; need to confirm its unit.
120
100
  // Assumption: model_price is the price per 1,000 tokens (i.e., $/1K tokens).
@@ -124,62 +104,38 @@ export const LobeNewAPIAI = createRouterRuntime({
124
104
  inputPrice = pricing.model_price * 2;
125
105
  } else if (pricing.model_ratio) {
126
106
  // model_ratio × $0.002/1K = model_ratio × $2/1M
127
- inputPrice = pricing.model_ratio * 2; // 转换为 $/1M tokens
107
+ inputPrice = pricing.model_ratio * 2; // Convert to $/1M tokens
128
108
  }
129
109
 
130
110
  if (inputPrice !== undefined) {
131
- // 计算输出价格
111
+ // Calculate output price
132
112
  outputPrice = inputPrice * (pricing.completion_ratio || 1);
133
113
 
134
114
  enhancedModel.pricing = {
135
- input: inputPrice,
136
- output: outputPrice,
115
+ units: [
116
+ {
117
+ name: 'textInput',
118
+ rate: inputPrice,
119
+ strategy: 'fixed',
120
+ unit: 'millionTokens',
121
+ },
122
+ {
123
+ name: 'textOutput',
124
+ rate: outputPrice,
125
+ strategy: 'fixed',
126
+ unit: 'millionTokens',
127
+ },
128
+ ],
137
129
  };
138
130
  }
139
131
  }
140
- // quota_type === 1 按次计费暂不支持
141
- }
142
-
143
- // 2. 根据优先级处理 provider 信息并缓存路由
144
- let detectedProvider = 'openai'; // 默认
145
-
146
- // 优先级1:使用 supported_endpoint_types
147
- if (model.supported_endpoint_types && model.supported_endpoint_types.length > 0) {
148
- if (model.supported_endpoint_types.includes('anthropic')) {
149
- detectedProvider = 'anthropic';
150
- } else if (model.supported_endpoint_types.includes('gemini')) {
151
- detectedProvider = 'google';
152
- } else if (model.supported_endpoint_types.includes('xai')) {
153
- detectedProvider = 'xai';
154
- } else if (model.supported_endpoint_types.includes('qwen')) {
155
- detectedProvider = 'qwen';
156
- }
157
- }
158
- // 优先级2:使用 owned_by 字段
159
- else if (model.owned_by) {
160
- detectedProvider = getProviderFromOwnedBy(model.owned_by);
161
- }
162
- // 优先级3:基于模型名称检测
163
- else {
164
- detectedProvider = detectModelProvider(model.id);
132
+ // quota_type === 1 pay-per-call is not currently supported
165
133
  }
166
134
 
167
- // 将检测到的 provider 信息附加到模型上
168
- enhancedModel._detectedProvider = detectedProvider;
169
-
170
135
  return enhancedModel;
171
136
  });
172
137
 
173
- // 使用 processMultiProviderModelList 处理模型能力
174
- const processedModels = await processMultiProviderModelList(enrichedModelList, 'newapi');
175
-
176
- // 清理临时字段
177
- return processedModels.map((model: any) => {
178
- if (model._detectedProvider) {
179
- delete model._detectedProvider;
180
- }
181
- return model;
182
- });
138
+ return processMultiProviderModelList(enrichedModelList, 'newapi');
183
139
  },
184
140
  routers: (options) => {
185
141
  const userBaseURL = options.baseURL?.replace(/\/v\d+[a-z]*\/?$/, '') || '';
@@ -215,16 +171,6 @@ export const LobeNewAPIAI = createRouterRuntime({
215
171
  baseURL: urlJoin(userBaseURL, '/v1'),
216
172
  },
217
173
  },
218
- {
219
- apiType: 'qwen',
220
- models: LOBE_DEFAULT_MODEL_LIST.map((m) => m.id).filter(
221
- (id) => detectModelProvider(id) === 'qwen',
222
- ),
223
- options: {
224
- ...options,
225
- baseURL: urlJoin(userBaseURL, '/v1'),
226
- },
227
- },
228
174
  {
229
175
  apiType: 'openai',
230
176
  options: {
@@ -8,4 +8,3 @@ export * from './textToImage';
8
8
  export * from './toolsCalling';
9
9
  export * from './tts';
10
10
  export * from './type';
11
- export * from './usage';
@@ -61,6 +61,7 @@ export interface ModelTokensUsage {
61
61
  rejectedPredictionTokens?: number;
62
62
 
63
63
  // Total tokens
64
+ // TODO: make all following fields required
64
65
  totalInputTokens?: number;
65
66
  totalOutputTokens?: number;
66
67
  totalTokens?: number;
@@ -5,7 +5,8 @@
5
5
  "exports": {
6
6
  ".": "./src/index.ts",
7
7
  "./server": "./src/server/index.ts",
8
- "./client": "./src/client/index.ts"
8
+ "./client": "./src/client/index.ts",
9
+ "./object": "./src/object.ts"
9
10
  },
10
11
  "scripts": {
11
12
  "test": "vitest",
@@ -1,17 +1 @@
1
- import React, { memo } from 'react';
2
-
3
- import { useChatStore } from '@/store/chat';
4
- import { chatSelectors } from '@/store/chat/selectors';
5
-
6
- import InboxWelcome from './InboxWelcome';
7
- import WelcomeMessage from './WelcomeMessage';
8
-
9
- const WelcomeChatItem = memo(() => {
10
- const showInboxWelcome = useChatStore(chatSelectors.showInboxWelcome);
11
-
12
- if (showInboxWelcome) return <InboxWelcome />;
13
-
14
- return <WelcomeMessage />;
15
- });
16
-
17
- export default WelcomeChatItem;
1
+ export { default } from './WelcomeMessage';
@@ -3,6 +3,7 @@
3
3
  import { Icon } from '@lobehub/ui';
4
4
  import { Tooltip } from 'antd';
5
5
  import { createStyles, useTheme } from 'antd-style';
6
+ import debug from 'debug';
6
7
  import { ChevronDown, ChevronUp } from 'lucide-react';
7
8
  import { memo, useCallback, useMemo, useState, useSyncExternalStore } from 'react';
8
9
  import { useTranslation } from 'react-i18next';
@@ -17,10 +18,12 @@ import {
17
18
  import { useChatStore } from '@/store/chat';
18
19
  import { chatSelectors } from '@/store/chat/selectors';
19
20
 
21
+ const log = debug('lobe-react:chat-minimap');
22
+
20
23
  const MIN_WIDTH = 16;
21
24
  const MAX_WIDTH = 30;
22
25
  const MAX_CONTENT_LENGTH = 320;
23
- const MIN_MESSAGES = 6;
26
+ const MIN_MESSAGES = 4;
24
27
 
25
28
  const useStyles = createStyles(({ css, token }) => ({
26
29
  arrow: css`
@@ -219,8 +222,8 @@ const ChatMinimap = () => {
219
222
  const activeIndicatorPosition = useMemo(() => {
220
223
  if (activeIndex === null) return null;
221
224
 
222
- console.log('> activeIndex', activeIndex);
223
- console.log('> indicatorIndexMap', indicatorIndexMap);
225
+ log('> activeIndex', activeIndex);
226
+ log('> indicatorIndexMap', indicatorIndexMap);
224
227
 
225
228
  return indicatorIndexMap.get(activeIndex) ?? null;
226
229
  }, [activeIndex, indicatorIndexMap]);
@@ -246,7 +249,7 @@ const ChatMinimap = () => {
246
249
  let targetPosition: number;
247
250
 
248
251
  if (activeIndicatorPosition !== null) {
249
- console.log('activeIndicatorPosition', activeIndicatorPosition);
252
+ log('activeIndicatorPosition', activeIndicatorPosition);
250
253
  // We're on an indicator, move to prev/next
251
254
  const delta = direction === 'prev' ? -1 : 1;
252
255
  targetPosition = Math.min(
@@ -31,22 +31,21 @@ const SessionItem = memo<SessionItemProps>(({ id }) => {
31
31
  const [active] = useSessionStore((s) => [s.activeId === id]);
32
32
  const [loading] = useChatStore((s) => [chatSelectors.isAIGenerating(s) && id === s.activeId]);
33
33
 
34
- const [pin, title, description, avatar, avatarBackground, updateAt, model, group] =
35
- useSessionStore((s) => {
36
- const session = sessionSelectors.getSessionById(id)(s);
37
- const meta = session.meta;
38
-
39
- return [
40
- sessionHelpers.getSessionPinned(session),
41
- sessionMetaSelectors.getTitle(meta),
42
- sessionMetaSelectors.getDescription(meta),
43
- sessionMetaSelectors.getAvatar(meta),
44
- meta.backgroundColor,
45
- session?.updatedAt,
46
- session.model,
47
- session?.group,
48
- ];
49
- });
34
+ const [pin, title, avatar, avatarBackground, updateAt, model, group] = useSessionStore((s) => {
35
+ const session = sessionSelectors.getSessionById(id)(s);
36
+ const meta = session.meta;
37
+
38
+ return [
39
+ sessionHelpers.getSessionPinned(session),
40
+ sessionMetaSelectors.getTitle(meta),
41
+ sessionMetaSelectors.getAvatar(meta),
42
+ meta.backgroundColor,
43
+ session?.updatedAt,
44
+ session.model,
45
+ session?.group,
46
+ // sessionMetaSelectors.getDescription(meta),
47
+ ];
48
+ });
50
49
 
51
50
  const showModel = model !== defaultModel;
52
51
 
@@ -99,7 +98,7 @@ const SessionItem = memo<SessionItemProps>(({ id }) => {
99
98
  avatar={avatar}
100
99
  avatarBackground={avatarBackground}
101
100
  date={updateAt?.valueOf()}
102
- description={description}
101
+ // description={description}
103
102
  draggable={isDesktop}
104
103
  key={id}
105
104
  loading={loading}
@@ -12,7 +12,7 @@ const useStyles = createStyles(({ css, token }) => {
12
12
  container: css`
13
13
  position: relative;
14
14
  margin-block: 2px;
15
- padding-inline: 8px 16px;
15
+ padding-inline: 12px 16px;
16
16
  border-radius: ${token.borderRadius}px;
17
17
  `,
18
18
  mobile: css`
@@ -40,7 +40,7 @@ const ListItem = memo<ListItemProps & { avatar: string; avatarBackground?: strin
40
40
  avatar={avatar}
41
41
  background={avatarBackground}
42
42
  shape="circle"
43
- size={40}
43
+ size={32}
44
44
  />
45
45
  ),
46
46
  [isHovering, avatar, avatarBackground],
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { Block, Grid, GridProps, Text } from '@lobehub/ui';
3
+ import { Block, Grid, GridProps, Select, Text } from '@lobehub/ui';
4
4
  import { useTheme } from 'antd-style';
5
5
  import { ReactNode, memo } from 'react';
6
6
  import { Center } from 'react-layout-kit';
@@ -13,6 +13,19 @@ export interface SizeSelectProps extends Omit<GridProps, 'children' | 'onChange'
13
13
  value?: 'auto' | string;
14
14
  }
15
15
 
16
+ /**
17
+ * Check if a size value can be parsed as valid aspect ratio
18
+ */
19
+ const canParseAsRatio = (value: string): boolean => {
20
+ if (value === 'auto') return true;
21
+
22
+ const parts = value.split('x');
23
+ if (parts.length !== 2) return false;
24
+
25
+ const [width, height] = parts.map(Number);
26
+ return !isNaN(width) && !isNaN(height) && width > 0 && height > 0;
27
+ };
28
+
16
29
  const SizeSelect = memo<SizeSelectProps>(({ options, onChange, value, defaultValue, ...rest }) => {
17
30
  const theme = useTheme();
18
31
  const [active, setActive] = useMergeState('auto', {
@@ -20,6 +33,16 @@ const SizeSelect = memo<SizeSelectProps>(({ options, onChange, value, defaultVal
20
33
  onChange,
21
34
  value,
22
35
  });
36
+
37
+ // Check if all options can be parsed as valid ratios
38
+ const hasInvalidRatio = options?.some((item) => !canParseAsRatio(item.value));
39
+
40
+ // If any option cannot be parsed as ratio, fallback to regular Select
41
+ if (hasInvalidRatio) {
42
+ return (
43
+ <Select onChange={onChange} options={options} style={{ width: '100%' }} value={active} />
44
+ );
45
+ }
23
46
  return (
24
47
  <Block padding={4} variant={'filled'} {...rest}>
25
48
  <Grid gap={4} maxItemWidth={72} rows={16}>
@@ -1,8 +1,9 @@
1
+ import { Icon } from '@lobehub/ui';
1
2
  import { createStyles } from 'antd-style';
3
+ import { ChevronRight } from 'lucide-react';
2
4
  import { ReactNode, memo } from 'react';
3
5
  import { Flexbox } from 'react-layout-kit';
4
6
 
5
- import Loader from '@/components/CircleLoader';
6
7
  import { useChatStore } from '@/store/chat';
7
8
  import { chatSelectors } from '@/store/chat/selectors';
8
9
  import { shinyTextStylish } from '@/styles/loading';
@@ -32,21 +33,18 @@ interface BuiltinPluginTitleProps {
32
33
  toolCallId: string;
33
34
  }
34
35
 
35
- const BuiltinPluginTitle = memo<BuiltinPluginTitleProps>(
36
- ({ messageId, index, apiName, icon, title }) => {
37
- const { styles } = useStyles();
38
-
39
- const isLoading = useChatStore(chatSelectors.isInToolsCalling(messageId, index));
40
-
41
- return (
42
- <Flexbox align={'center'} className={isLoading ? styles.shinyText : ''} gap={4} horizontal>
43
- {isLoading ? <Loader /> : icon}
44
- <Flexbox align={'baseline'} gap={4} horizontal>
45
- <div>{title}</div>/<span className={styles.apiName}>{apiName}</span>
46
- </Flexbox>
47
- </Flexbox>
48
- );
49
- },
50
- );
36
+ const BuiltinPluginTitle = memo<BuiltinPluginTitleProps>(({ messageId, index, apiName, title }) => {
37
+ const { styles } = useStyles();
38
+
39
+ const isLoading = useChatStore(chatSelectors.isInToolsCalling(messageId, index));
40
+
41
+ return (
42
+ <Flexbox align={'center'} className={isLoading ? styles.shinyText : ''} gap={4} horizontal>
43
+ <div>{title}</div>
44
+ <Icon icon={ChevronRight} />
45
+ <span className={styles.apiName}>{apiName}</span>
46
+ </Flexbox>
47
+ );
48
+ });
51
49
 
52
50
  export default BuiltinPluginTitle;
@@ -1,13 +1,11 @@
1
1
  import { Icon } from '@lobehub/ui';
2
2
  import { createStyles } from 'antd-style';
3
3
  import isEqual from 'fast-deep-equal';
4
- import { Globe, Laptop } from 'lucide-react';
4
+ import { ChevronRight } from 'lucide-react';
5
5
  import { memo, useMemo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Flexbox } from 'react-layout-kit';
8
8
 
9
- import Loader from '@/components/CircleLoader';
10
- import PluginAvatar from '@/features/PluginAvatar';
11
9
  import { useChatStore } from '@/store/chat';
12
10
  import { chatSelectors } from '@/store/chat/selectors';
13
11
  import { pluginHelpers, useToolStore } from '@/store/tool';
@@ -60,13 +58,13 @@ const ToolTitle = memo<ToolTitleProps>(({ identifier, messageId, index, apiName,
60
58
  () => [
61
59
  {
62
60
  apiName: t(`search.apiName.${apiName}`, apiName),
63
- icon: <Icon icon={Globe} size={13} />,
61
+ // icon: <Icon icon={Globe} size={13} />,
64
62
  id: WebBrowsingManifest.identifier,
65
63
  title: t('search.title'),
66
64
  },
67
65
  {
68
66
  apiName: t(`localSystem.apiName.${apiName}`, apiName),
69
- icon: <Icon icon={Laptop} size={13} />,
67
+ // icon: <Icon icon={Laptop} size={13} />,
70
68
  id: LocalSystemManifest.identifier,
71
69
  title: t('localSystem.title'),
72
70
  },
@@ -92,8 +90,8 @@ const ToolTitle = memo<ToolTitleProps>(({ identifier, messageId, index, apiName,
92
90
 
93
91
  return (
94
92
  <Flexbox align={'center'} className={isLoading ? styles.shinyText : ''} gap={6} horizontal>
95
- {isLoading ? <Loader /> : <PluginAvatar identifier={identifier} size={18} />}
96
- <div>{pluginTitle}</div>/<span className={styles.apiName}>{apiName}</span>
93
+ <div>{pluginTitle}</div> <Icon icon={ChevronRight} />
94
+ <span className={styles.apiName}>{apiName}</span>
97
95
  </Flexbox>
98
96
  );
99
97
  });
@@ -116,14 +116,7 @@ const Arguments = memo<ArgumentsProps>(({ arguments: args = '', shine, actions }
116
116
  </Flexbox>
117
117
  )}
118
118
  {args.length > 100 ? (
119
- <Highlighter
120
- language={'json'}
121
- showLanguage={false}
122
- style={{ padding: 8 }}
123
- variant={'borderless'}
124
- >
125
- {JSON.stringify(displayArgs, null, 2)}
126
- </Highlighter>
119
+ <pre style={{ padding: 8 }}>{JSON.stringify(displayArgs, null, 2)}</pre>
127
120
  ) : (
128
121
  Object.entries(displayArgs).map(([key, value]) => {
129
122
  return (
@@ -4,8 +4,8 @@ export default {
4
4
  },
5
5
  agentDefaultMessage:
6
6
  '你好,我是 **{{name}}**,你可以立即与我开始对话,也可以前往 [助手设置]({{url}}) 完善我的信息。',
7
- agentDefaultMessageWithSystemRole: '你好,我是 **{{name}}**,{{systemRole}},让我们开始对话吧!',
8
- agentDefaultMessageWithoutEdit: '你好,我是 **{{name}}**,让我们开始对话吧!',
7
+ agentDefaultMessageWithSystemRole: '你好,我是 **{{name}}**,有什么我可以帮忙的吗?',
8
+ agentDefaultMessageWithoutEdit: '你好,我是 **{{name}}**,有什么我可以帮忙的吗?',
9
9
  agents: '助手',
10
10
  artifact: {
11
11
  generating: '生成中',
@@ -3,22 +3,9 @@ import createDebug from 'debug';
3
3
 
4
4
  import { appEnv } from '@/envs/app';
5
5
 
6
- const debug = createDebug('lobe-server:edge-config');
6
+ import { EdgeConfigData, EdgeConfigKeys } from './types';
7
7
 
8
- const EdgeConfigKeys = {
9
- /**
10
- * Assistant whitelist
11
- */
12
- AssistantBlacklist: 'assistant_blacklist',
13
- /**
14
- * Assistant whitelist
15
- */
16
- AssistantWhitelist: 'assistant_whitelist',
17
- /**
18
- * Feature flags configuration
19
- */
20
- FeatureFlags: 'feature_flags',
21
- };
8
+ const debug = createDebug('lobe-server:edge-config');
22
9
 
23
10
  export class EdgeConfig {
24
11
  get client(): EdgeConfigClient {
@@ -38,29 +25,24 @@ export class EdgeConfig {
38
25
  return isEnabled;
39
26
  }
40
27
 
41
- getAgentRestrictions = async () => {
42
- const { assistant_blacklist: blacklist, assistant_whitelist: whitelist } =
43
- await this.client.getAll([
44
- EdgeConfigKeys.AssistantWhitelist,
45
- EdgeConfigKeys.AssistantBlacklist,
46
- ]);
28
+ private async getValue<K extends EdgeConfigKeys>(key: K) {
29
+ return this.client.get<EdgeConfigData[K]>(key);
30
+ }
47
31
 
48
- return { blacklist, whitelist } as {
49
- blacklist: string[] | undefined;
50
- whitelist: string[] | undefined;
51
- };
52
- };
32
+ private async getValues<const K extends EdgeConfigKeys>(keys: K[]) {
33
+ return this.client.getAll<Pick<EdgeConfigData, K>>(keys);
34
+ }
53
35
 
54
- getFlagByKey = async (key: string) => {
55
- const value = await this.client.get(key);
56
- return value;
36
+ getAgentRestrictions = async () => {
37
+ const { assistant_blacklist: blacklist, assistant_whitelist: whitelist } = await this.getValues(
38
+ ['assistant_blacklist', 'assistant_whitelist'],
39
+ );
40
+ return { blacklist, whitelist };
57
41
  };
58
42
 
59
43
  getFeatureFlags = async () => {
60
- const featureFlags = await this.client.get(EdgeConfigKeys.FeatureFlags);
44
+ const featureFlags = await this.getValue('feature_flags');
61
45
  debug('Feature flags retrieved: %O', featureFlags);
62
- return featureFlags as Record<string, boolean | string[]> | undefined;
46
+ return featureFlags;
63
47
  };
64
48
  }
65
-
66
- export { EdgeConfigKeys };
@@ -4,6 +4,19 @@
4
4
  * EdgeConfig 完整配置类型
5
5
  */
6
6
  export interface EdgeConfigData {
7
+ /**
8
+ * Assistant blacklist
9
+ */
7
10
  assistant_blacklist?: string[];
11
+ /**
12
+ * Assistant whitelist
13
+ */
8
14
  assistant_whitelist?: string[];
15
+
16
+ /**
17
+ * Feature flags configuration
18
+ */
19
+ feature_flags?: Record<string, boolean | string[]>;
9
20
  }
21
+
22
+ export type EdgeConfigKeys = keyof EdgeConfigData;
@@ -1,27 +0,0 @@
1
- export interface ModelTokensUsage {
2
- acceptedPredictionTokens?: number;
3
- inputAudioTokens?: number;
4
- inputCacheMissTokens?: number;
5
- inputCachedTokens?: number;
6
- /**
7
- * currently only pplx has citation_tokens
8
- */
9
- inputCitationTokens?: number;
10
- /**
11
- * user prompt image
12
- */
13
- inputImageTokens?: number;
14
- /**
15
- * user prompt input
16
- */
17
- inputTextTokens?: number;
18
- inputWriteCacheTokens?: number;
19
- outputAudioTokens?: number;
20
- outputImageTokens?: number;
21
- outputReasoningTokens?: number;
22
- outputTextTokens?: number;
23
- rejectedPredictionTokens?: number;
24
- totalInputTokens?: number;
25
- totalOutputTokens?: number;
26
- totalTokens?: number;
27
- }