@lobehub/chat 1.112.4 → 1.113.0
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/.vscode/settings.json +1 -1
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +14 -0
- package/package.json +2 -2
- package/packages/const/src/image.ts +9 -1
- package/packages/model-runtime/src/bfl/createImage.test.ts +846 -0
- package/packages/model-runtime/src/bfl/createImage.ts +279 -0
- package/packages/model-runtime/src/bfl/index.test.ts +269 -0
- package/packages/model-runtime/src/bfl/index.ts +49 -0
- package/packages/model-runtime/src/bfl/types.ts +113 -0
- package/packages/model-runtime/src/index.ts +1 -0
- package/packages/model-runtime/src/qwen/createImage.ts +37 -82
- package/packages/model-runtime/src/runtimeMap.ts +2 -0
- package/packages/model-runtime/src/utils/asyncifyPolling.test.ts +491 -0
- package/packages/model-runtime/src/utils/asyncifyPolling.ts +175 -0
- package/src/app/[variants]/(main)/chat/(workspace)/features/TelemetryNotification.tsx +1 -4
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/about/index.tsx +1 -4
- package/src/config/aiModels/bfl.ts +145 -0
- package/src/config/aiModels/index.ts +3 -0
- package/src/config/llm.ts +6 -1
- package/src/config/modelProviders/bfl.ts +21 -0
- package/src/config/modelProviders/index.ts +3 -0
- package/src/server/routers/lambda/market/index.ts +0 -3
- package/src/services/discover.ts +6 -0
- package/src/services/mcp.ts +0 -2
- package/src/store/image/slices/generationConfig/hooks.ts +6 -10
@@ -0,0 +1,175 @@
|
|
1
|
+
export interface TaskResult<T> {
|
2
|
+
data?: T;
|
3
|
+
error?: any;
|
4
|
+
status: 'pending' | 'success' | 'failed';
|
5
|
+
}
|
6
|
+
|
7
|
+
export interface PollingErrorContext {
|
8
|
+
consecutiveFailures: number;
|
9
|
+
error: any;
|
10
|
+
retries: number;
|
11
|
+
}
|
12
|
+
|
13
|
+
export interface PollingErrorResult {
|
14
|
+
error?: any;
|
15
|
+
isContinuePolling: boolean; // If provided, will replace the original error when thrown
|
16
|
+
}
|
17
|
+
|
18
|
+
export interface AsyncifyPollingOptions<T, R> {
|
19
|
+
// Default 5000ms
|
20
|
+
backoffMultiplier?: number;
|
21
|
+
|
22
|
+
// Status check function to determine task result
|
23
|
+
checkStatus: (result: T) => TaskResult<R>;
|
24
|
+
|
25
|
+
// Retry configuration
|
26
|
+
initialInterval?: number;
|
27
|
+
// Optional logger
|
28
|
+
logger?: {
|
29
|
+
debug?: (...args: any[]) => void;
|
30
|
+
error?: (...args: any[]) => void;
|
31
|
+
};
|
32
|
+
// Default 1.5
|
33
|
+
maxConsecutiveFailures?: number;
|
34
|
+
// Default 500ms
|
35
|
+
maxInterval?: number; // Default 3
|
36
|
+
maxRetries?: number; // Default Infinity
|
37
|
+
|
38
|
+
// Optional custom error handler for polling query failures
|
39
|
+
onPollingError?: (context: PollingErrorContext) => PollingErrorResult;
|
40
|
+
|
41
|
+
// The polling function to execute repeatedly
|
42
|
+
pollingQuery: () => Promise<T>;
|
43
|
+
}
|
44
|
+
|
45
|
+
/**
|
46
|
+
* Convert polling pattern to async/await pattern
|
47
|
+
*
|
48
|
+
* @param options Polling configuration options
|
49
|
+
* @returns Promise<R> The data returned when task completes
|
50
|
+
* @throws Error When task fails or times out
|
51
|
+
*/
|
52
|
+
export async function asyncifyPolling<T, R>(options: AsyncifyPollingOptions<T, R>): Promise<R> {
|
53
|
+
const {
|
54
|
+
pollingQuery,
|
55
|
+
checkStatus,
|
56
|
+
initialInterval = 500,
|
57
|
+
maxInterval = 5000,
|
58
|
+
backoffMultiplier = 1.5,
|
59
|
+
maxConsecutiveFailures = 3,
|
60
|
+
maxRetries = Infinity,
|
61
|
+
onPollingError,
|
62
|
+
logger,
|
63
|
+
} = options;
|
64
|
+
|
65
|
+
let retries = 0;
|
66
|
+
let consecutiveFailures = 0;
|
67
|
+
|
68
|
+
while (retries < maxRetries) {
|
69
|
+
let pollingResult: T;
|
70
|
+
|
71
|
+
try {
|
72
|
+
// Execute polling function
|
73
|
+
pollingResult = await pollingQuery();
|
74
|
+
|
75
|
+
// Reset consecutive failures counter on successful execution
|
76
|
+
consecutiveFailures = 0;
|
77
|
+
} catch (error) {
|
78
|
+
// Polling function execution failed (network error, etc.)
|
79
|
+
consecutiveFailures++;
|
80
|
+
|
81
|
+
logger?.error?.(
|
82
|
+
`Failed to execute polling function (attempt ${retries + 1}/${maxRetries === Infinity ? '∞' : maxRetries}, consecutive failures: ${consecutiveFailures}/${maxConsecutiveFailures}):`,
|
83
|
+
error,
|
84
|
+
);
|
85
|
+
|
86
|
+
// Handle custom error processing if provided
|
87
|
+
if (onPollingError) {
|
88
|
+
const errorResult = onPollingError({
|
89
|
+
consecutiveFailures,
|
90
|
+
error,
|
91
|
+
retries,
|
92
|
+
});
|
93
|
+
|
94
|
+
if (!errorResult.isContinuePolling) {
|
95
|
+
// Custom error handler decided to stop polling
|
96
|
+
throw errorResult.error || error;
|
97
|
+
}
|
98
|
+
|
99
|
+
// Custom error handler decided to continue polling
|
100
|
+
logger?.debug?.('Custom error handler decided to continue polling');
|
101
|
+
} else {
|
102
|
+
// Default behavior: check if maximum consecutive failures reached
|
103
|
+
if (consecutiveFailures >= maxConsecutiveFailures) {
|
104
|
+
throw new Error(
|
105
|
+
`Failed to execute polling function after ${consecutiveFailures} consecutive attempts: ${error}`,
|
106
|
+
);
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
// Wait before retry and continue to next loop iteration
|
111
|
+
if (retries < maxRetries - 1) {
|
112
|
+
const currentInterval = Math.min(
|
113
|
+
initialInterval * Math.pow(backoffMultiplier, retries),
|
114
|
+
maxInterval,
|
115
|
+
);
|
116
|
+
|
117
|
+
logger?.debug?.(`Waiting ${currentInterval}ms before next retry`);
|
118
|
+
|
119
|
+
await new Promise((resolve) => {
|
120
|
+
setTimeout(resolve, currentInterval);
|
121
|
+
});
|
122
|
+
}
|
123
|
+
|
124
|
+
retries++;
|
125
|
+
continue;
|
126
|
+
}
|
127
|
+
|
128
|
+
// Check task status
|
129
|
+
const statusResult = checkStatus(pollingResult);
|
130
|
+
|
131
|
+
logger?.debug?.(`Task status: ${statusResult.status} (attempt ${retries + 1})`);
|
132
|
+
|
133
|
+
switch (statusResult.status) {
|
134
|
+
case 'success': {
|
135
|
+
return statusResult.data as R;
|
136
|
+
}
|
137
|
+
|
138
|
+
case 'failed': {
|
139
|
+
// Task logic failed, throw error immediately (not counted as consecutive failure)
|
140
|
+
throw statusResult.error || new Error('Task failed');
|
141
|
+
}
|
142
|
+
|
143
|
+
case 'pending': {
|
144
|
+
// Continue polling
|
145
|
+
break;
|
146
|
+
}
|
147
|
+
|
148
|
+
default: {
|
149
|
+
// Unknown status, treat as pending
|
150
|
+
break;
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
// Wait before next retry if not the last attempt
|
155
|
+
if (retries < maxRetries - 1) {
|
156
|
+
// Calculate dynamic retry interval with exponential backoff
|
157
|
+
const currentInterval = Math.min(
|
158
|
+
initialInterval * Math.pow(backoffMultiplier, retries),
|
159
|
+
maxInterval,
|
160
|
+
);
|
161
|
+
|
162
|
+
logger?.debug?.(`Waiting ${currentInterval}ms before next retry`);
|
163
|
+
|
164
|
+
// Wait for retry interval
|
165
|
+
await new Promise((resolve) => {
|
166
|
+
setTimeout(resolve, currentInterval);
|
167
|
+
});
|
168
|
+
}
|
169
|
+
|
170
|
+
retries++;
|
171
|
+
}
|
172
|
+
|
173
|
+
// Maximum retries reached
|
174
|
+
throw new Error(`Task timeout after ${maxRetries} attempts`);
|
175
|
+
}
|
@@ -11,8 +11,6 @@ import { Flexbox } from 'react-layout-kit';
|
|
11
11
|
import Notification from '@/components/Notification';
|
12
12
|
import { BRANDING_NAME } from '@/const/branding';
|
13
13
|
import { PRIVACY_URL } from '@/const/url';
|
14
|
-
import { useServerConfigStore } from '@/store/serverConfig';
|
15
|
-
import { serverConfigSelectors } from '@/store/serverConfig/selectors';
|
16
14
|
import { useUserStore } from '@/store/user';
|
17
15
|
import { preferenceSelectors } from '@/store/user/selectors';
|
18
16
|
|
@@ -30,7 +28,6 @@ const TelemetryNotification = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
30
28
|
const { styles, theme } = useStyles();
|
31
29
|
|
32
30
|
const { t } = useTranslation('common');
|
33
|
-
const shouldCheck = useServerConfigStore(serverConfigSelectors.enabledTelemetryChat);
|
34
31
|
const isPreferenceInit = useUserStore(preferenceSelectors.isPreferenceInit);
|
35
32
|
|
36
33
|
const [useCheckTrace, updatePreference] = useUserStore((s) => [
|
@@ -38,7 +35,7 @@ const TelemetryNotification = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
38
35
|
s.updatePreference,
|
39
36
|
]);
|
40
37
|
|
41
|
-
const { data: showModal, mutate } = useCheckTrace(
|
38
|
+
const { data: showModal, mutate } = useCheckTrace(isPreferenceInit);
|
42
39
|
|
43
40
|
const updateTelemetry = (telemetry: boolean) => {
|
44
41
|
updatePreference({ telemetry });
|
@@ -46,7 +46,7 @@ const ConfigPanel = memo(() => {
|
|
46
46
|
const { showDimensionControl } = useDimensionControl();
|
47
47
|
|
48
48
|
return (
|
49
|
-
<Flexbox gap={32} padding={12}>
|
49
|
+
<Flexbox gap={32} padding={12} style={{ overflow: 'auto' }}>
|
50
50
|
<ConfigItemLayout>
|
51
51
|
<ModelSelect />
|
52
52
|
</ConfigItemLayout>
|
@@ -10,8 +10,6 @@ import { Flexbox } from 'react-layout-kit';
|
|
10
10
|
|
11
11
|
import { BRANDING_EMAIL, BRANDING_NAME, SOCIAL_URL } from '@/const/branding';
|
12
12
|
import { BLOG, OFFICIAL_SITE, PRIVACY_URL, TERMS_URL, mailTo } from '@/const/url';
|
13
|
-
import { useServerConfigStore } from '@/store/serverConfig';
|
14
|
-
import { serverConfigSelectors } from '@/store/serverConfig/selectors';
|
15
13
|
|
16
14
|
import AboutList from './features/AboutList';
|
17
15
|
import Analytics from './features/Analytics';
|
@@ -30,7 +28,6 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
30
28
|
const Page = memo<{ mobile?: boolean }>(({ mobile }) => {
|
31
29
|
const { t } = useTranslation('common');
|
32
30
|
const { styles } = useStyles();
|
33
|
-
const enabledTelemetryChat = useServerConfigStore(serverConfigSelectors.enabledTelemetryChat);
|
34
31
|
|
35
32
|
return (
|
36
33
|
<>
|
@@ -122,7 +119,7 @@ const Page = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
122
119
|
/>
|
123
120
|
</Flexbox>
|
124
121
|
</Form.Group>
|
125
|
-
|
122
|
+
<Analytics />
|
126
123
|
</>
|
127
124
|
);
|
128
125
|
});
|
@@ -0,0 +1,145 @@
|
|
1
|
+
import { PRESET_ASPECT_RATIOS } from '@/const/image';
|
2
|
+
import { ModelParamsSchema } from '@/libs/standard-parameters';
|
3
|
+
import { AIImageModelCard } from '@/types/aiModel';
|
4
|
+
|
5
|
+
// https://docs.bfl.ai/api-reference/tasks/edit-or-create-an-image-with-flux-kontext-pro
|
6
|
+
// official support 21:9 ~ 9:21 (ratio 0.43 ~ 2.33)
|
7
|
+
const calculateRatio = (aspectRatio: string): number => {
|
8
|
+
const [width, height] = aspectRatio.split(':').map(Number);
|
9
|
+
return width / height;
|
10
|
+
};
|
11
|
+
|
12
|
+
const defaultAspectRatios = PRESET_ASPECT_RATIOS.filter((ratio) => {
|
13
|
+
const value = calculateRatio(ratio);
|
14
|
+
// BFL API supports ratio range: 21:9 ~ 9:21 (approximately 0.43 ~ 2.33)
|
15
|
+
// Use a small tolerance for floating point comparison
|
16
|
+
return value >= 9 / 21 - 0.001 && value <= 21 / 9 + 0.001;
|
17
|
+
});
|
18
|
+
|
19
|
+
const fluxKontextSeriesParamsSchema: ModelParamsSchema = {
|
20
|
+
aspectRatio: {
|
21
|
+
default: '1:1',
|
22
|
+
enum: defaultAspectRatios,
|
23
|
+
},
|
24
|
+
imageUrls: {
|
25
|
+
default: [],
|
26
|
+
},
|
27
|
+
prompt: { default: '' },
|
28
|
+
seed: { default: null },
|
29
|
+
};
|
30
|
+
|
31
|
+
const imageModels: AIImageModelCard[] = [
|
32
|
+
// https://docs.bfl.ai/api-reference/tasks/edit-or-create-an-image-with-flux-kontext-pro
|
33
|
+
{
|
34
|
+
description: '最先进的上下文图像生成和编辑——结合文本和图像以获得精确、连贯的结果。',
|
35
|
+
displayName: 'FLUX.1 Kontext [pro]',
|
36
|
+
enabled: true,
|
37
|
+
id: 'flux-kontext-pro',
|
38
|
+
parameters: fluxKontextSeriesParamsSchema,
|
39
|
+
// check: https://bfl.ai/pricing
|
40
|
+
pricing: {
|
41
|
+
units: [{ name: 'imageGeneration', rate: 0.04, strategy: 'fixed', unit: 'image' }],
|
42
|
+
},
|
43
|
+
releasedAt: '2025-05-29',
|
44
|
+
type: 'image',
|
45
|
+
},
|
46
|
+
// https://docs.bfl.ai/api-reference/tasks/edit-or-create-an-image-with-flux-kontext-max
|
47
|
+
{
|
48
|
+
description: '最先进的上下文图像生成和编辑——结合文本和图像以获得精确、连贯的结果。',
|
49
|
+
displayName: 'FLUX.1 Kontext [max]',
|
50
|
+
enabled: true,
|
51
|
+
id: 'flux-kontext-max',
|
52
|
+
parameters: fluxKontextSeriesParamsSchema,
|
53
|
+
pricing: {
|
54
|
+
units: [{ name: 'imageGeneration', rate: 0.08, strategy: 'fixed', unit: 'image' }],
|
55
|
+
},
|
56
|
+
releasedAt: '2025-05-29',
|
57
|
+
type: 'image',
|
58
|
+
},
|
59
|
+
// https://docs.bfl.ai/api-reference/tasks/generate-an-image-with-flux-11-[pro]
|
60
|
+
{
|
61
|
+
description: '升级版专业级AI图像生成模型——提供卓越的图像质量和精确的提示词遵循能力。',
|
62
|
+
displayName: 'FLUX1.1 [pro] ',
|
63
|
+
enabled: true,
|
64
|
+
id: 'flux-pro-1.1',
|
65
|
+
parameters: {
|
66
|
+
height: { default: 768, max: 1440, min: 256, step: 32 },
|
67
|
+
imageUrl: { default: null },
|
68
|
+
prompt: { default: '' },
|
69
|
+
seed: { default: null },
|
70
|
+
width: { default: 1024, max: 1440, min: 256, step: 32 },
|
71
|
+
},
|
72
|
+
pricing: {
|
73
|
+
units: [{ name: 'imageGeneration', rate: 0.06, strategy: 'fixed', unit: 'image' }],
|
74
|
+
},
|
75
|
+
releasedAt: '2024-10-02',
|
76
|
+
type: 'image',
|
77
|
+
},
|
78
|
+
// https://docs.bfl.ai/api-reference/tasks/generate-an-image-with-flux-11-[pro]-with-ultra-mode-and-optional-raw-mode
|
79
|
+
{
|
80
|
+
description: '超高分辨率AI图像生成——支持4兆像素输出,10秒内生成超清图像。',
|
81
|
+
displayName: 'FLUX1.1 [pro] Ultra',
|
82
|
+
enabled: true,
|
83
|
+
id: 'flux-pro-1.1-ultra',
|
84
|
+
parameters: {
|
85
|
+
aspectRatio: {
|
86
|
+
default: '16:9',
|
87
|
+
enum: defaultAspectRatios,
|
88
|
+
},
|
89
|
+
imageUrl: { default: null },
|
90
|
+
prompt: { default: '' },
|
91
|
+
seed: { default: null },
|
92
|
+
},
|
93
|
+
pricing: {
|
94
|
+
units: [{ name: 'imageGeneration', rate: 0.06, strategy: 'fixed', unit: 'image' }],
|
95
|
+
},
|
96
|
+
releasedAt: '2024-11-06',
|
97
|
+
type: 'image',
|
98
|
+
},
|
99
|
+
// https://docs.bfl.ai/api-reference/tasks/generate-an-image-with-flux1-[pro]
|
100
|
+
{
|
101
|
+
description: '顶级商用AI图像生成模型——无与伦比的图像质量和多样化输出表现。',
|
102
|
+
displayName: 'FLUX.1 [pro]',
|
103
|
+
enabled: true,
|
104
|
+
id: 'flux-pro',
|
105
|
+
parameters: {
|
106
|
+
cfg: { default: 2.5, max: 5, min: 1.5, step: 0.1 },
|
107
|
+
height: { default: 768, max: 1440, min: 256, step: 32 },
|
108
|
+
imageUrl: { default: null },
|
109
|
+
prompt: { default: '' },
|
110
|
+
seed: { default: null },
|
111
|
+
steps: { default: 40, max: 50, min: 1 },
|
112
|
+
width: { default: 1024, max: 1440, min: 256, step: 32 },
|
113
|
+
},
|
114
|
+
pricing: {
|
115
|
+
units: [{ name: 'imageGeneration', rate: 0.025, strategy: 'fixed', unit: 'image' }],
|
116
|
+
},
|
117
|
+
releasedAt: '2024-08-01',
|
118
|
+
type: 'image',
|
119
|
+
},
|
120
|
+
// https://docs.bfl.ai/api-reference/tasks/generate-an-image-with-flux1-[dev]
|
121
|
+
{
|
122
|
+
description: '开源研发版AI图像生成模型——高效优化,适合非商业用途的创新研究。',
|
123
|
+
displayName: 'FLUX.1 [dev]',
|
124
|
+
enabled: true,
|
125
|
+
id: 'flux-dev',
|
126
|
+
parameters: {
|
127
|
+
cfg: { default: 3, max: 5, min: 1.5, step: 0.1 },
|
128
|
+
height: { default: 768, max: 1440, min: 256, step: 32 },
|
129
|
+
imageUrl: { default: null },
|
130
|
+
prompt: { default: '' },
|
131
|
+
seed: { default: null },
|
132
|
+
steps: { default: 28, max: 50, min: 1 },
|
133
|
+
width: { default: 1024, max: 1440, min: 256, step: 32 },
|
134
|
+
},
|
135
|
+
pricing: {
|
136
|
+
units: [{ name: 'imageGeneration', rate: 0.025, strategy: 'fixed', unit: 'image' }],
|
137
|
+
},
|
138
|
+
releasedAt: '2024-08-01',
|
139
|
+
type: 'image',
|
140
|
+
},
|
141
|
+
];
|
142
|
+
|
143
|
+
export const allModels = [...imageModels];
|
144
|
+
|
145
|
+
export default allModels;
|
@@ -9,6 +9,7 @@ import { default as azure } from './azure';
|
|
9
9
|
import { default as azureai } from './azureai';
|
10
10
|
import { default as baichuan } from './baichuan';
|
11
11
|
import { default as bedrock } from './bedrock';
|
12
|
+
import { default as bfl } from './bfl';
|
12
13
|
import { default as cloudflare } from './cloudflare';
|
13
14
|
import { default as cohere } from './cohere';
|
14
15
|
import { default as deepseek } from './deepseek';
|
@@ -87,6 +88,7 @@ export const LOBE_DEFAULT_MODEL_LIST = buildDefaultModelList({
|
|
87
88
|
azureai,
|
88
89
|
baichuan,
|
89
90
|
bedrock,
|
91
|
+
bfl,
|
90
92
|
cloudflare,
|
91
93
|
cohere,
|
92
94
|
deepseek,
|
@@ -146,6 +148,7 @@ export { default as azure } from './azure';
|
|
146
148
|
export { default as azureai } from './azureai';
|
147
149
|
export { default as baichuan } from './baichuan';
|
148
150
|
export { default as bedrock } from './bedrock';
|
151
|
+
export { default as bfl } from './bfl';
|
149
152
|
export { default as cloudflare } from './cloudflare';
|
150
153
|
export { default as cohere } from './cohere';
|
151
154
|
export { default as deepseek } from './deepseek';
|
package/src/config/llm.ts
CHANGED
@@ -166,13 +166,15 @@ export const getLLMConfig = () => {
|
|
166
166
|
ENABLED_FAL: z.boolean(),
|
167
167
|
FAL_API_KEY: z.string().optional(),
|
168
168
|
|
169
|
+
ENABLED_BFL: z.boolean(),
|
170
|
+
BFL_API_KEY: z.string().optional(),
|
171
|
+
|
169
172
|
ENABLED_MODELSCOPE: z.boolean(),
|
170
173
|
MODELSCOPE_API_KEY: z.string().optional(),
|
171
174
|
|
172
175
|
ENABLED_V0: z.boolean(),
|
173
176
|
V0_API_KEY: z.string().optional(),
|
174
177
|
|
175
|
-
|
176
178
|
ENABLED_AI302: z.boolean(),
|
177
179
|
AI302_API_KEY: z.string().optional(),
|
178
180
|
|
@@ -342,6 +344,9 @@ export const getLLMConfig = () => {
|
|
342
344
|
ENABLED_FAL: process.env.ENABLED_FAL !== '0',
|
343
345
|
FAL_API_KEY: process.env.FAL_API_KEY,
|
344
346
|
|
347
|
+
ENABLED_BFL: !!process.env.BFL_API_KEY,
|
348
|
+
BFL_API_KEY: process.env.BFL_API_KEY,
|
349
|
+
|
345
350
|
ENABLED_MODELSCOPE: !!process.env.MODELSCOPE_API_KEY,
|
346
351
|
MODELSCOPE_API_KEY: process.env.MODELSCOPE_API_KEY,
|
347
352
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { ModelProviderCard } from '@/types/llm';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @see https://docs.bfl.ai/
|
5
|
+
*/
|
6
|
+
const Bfl: ModelProviderCard = {
|
7
|
+
chatModels: [],
|
8
|
+
description: '领先的前沿人工智能研究实验室,构建明日的视觉基础设施。',
|
9
|
+
enabled: true,
|
10
|
+
id: 'bfl',
|
11
|
+
name: 'Black Forest Labs',
|
12
|
+
settings: {
|
13
|
+
disableBrowserRequest: true,
|
14
|
+
showAddNewModel: false,
|
15
|
+
showChecker: false,
|
16
|
+
showModelFetcher: false,
|
17
|
+
},
|
18
|
+
url: 'https://bfl.ai/',
|
19
|
+
};
|
20
|
+
|
21
|
+
export default Bfl;
|
@@ -9,6 +9,7 @@ import AzureProvider from './azure';
|
|
9
9
|
import AzureAIProvider from './azureai';
|
10
10
|
import BaichuanProvider from './baichuan';
|
11
11
|
import BedrockProvider from './bedrock';
|
12
|
+
import BflProvider from './bfl';
|
12
13
|
import CloudflareProvider from './cloudflare';
|
13
14
|
import CohereProvider from './cohere';
|
14
15
|
import DeepSeekProvider from './deepseek';
|
@@ -132,6 +133,7 @@ export const DEFAULT_MODEL_PROVIDER_LIST = [
|
|
132
133
|
HuggingFaceProvider,
|
133
134
|
CloudflareProvider,
|
134
135
|
GithubProvider,
|
136
|
+
BflProvider,
|
135
137
|
NovitaProvider,
|
136
138
|
PPIOProvider,
|
137
139
|
NvidiaProvider,
|
@@ -191,6 +193,7 @@ export { default as AzureProviderCard } from './azure';
|
|
191
193
|
export { default as AzureAIProviderCard } from './azureai';
|
192
194
|
export { default as BaichuanProviderCard } from './baichuan';
|
193
195
|
export { default as BedrockProviderCard } from './bedrock';
|
196
|
+
export { default as BflProviderCard } from './bfl';
|
194
197
|
export { default as CloudflareProviderCard } from './cloudflare';
|
195
198
|
export { default as CohereProviderCard } from './cohere';
|
196
199
|
export { default as DeepSeekProviderCard } from './deepseek';
|
@@ -540,17 +540,14 @@ export const marketRouter = router({
|
|
540
540
|
z.object({
|
541
541
|
callDurationMs: z.number(),
|
542
542
|
clientId: z.string().optional(),
|
543
|
-
clientIp: z.string().optional(),
|
544
543
|
customPluginInfo: z.any().optional(),
|
545
544
|
errorCode: z.string().optional(),
|
546
545
|
errorMessage: z.string().optional(),
|
547
546
|
identifier: z.string(),
|
548
|
-
inputParams: z.any().optional(),
|
549
547
|
isCustomPlugin: z.boolean().optional(),
|
550
548
|
metadata: z.record(z.any()).optional(),
|
551
549
|
methodName: z.string(),
|
552
550
|
methodType: z.enum(['tool', 'prompt', 'resource']),
|
553
|
-
outputResult: z.any().optional(),
|
554
551
|
platform: z.string().optional(),
|
555
552
|
requestSizeBytes: z.number().optional(),
|
556
553
|
responseSizeBytes: z.number().optional(),
|
package/src/services/discover.ts
CHANGED
@@ -3,6 +3,8 @@ import { CallReportRequest, InstallReportRequest } from '@lobehub/market-types';
|
|
3
3
|
|
4
4
|
import { lambdaClient } from '@/libs/trpc/client';
|
5
5
|
import { globalHelpers } from '@/store/global/helpers';
|
6
|
+
import { useUserStore } from '@/store/user';
|
7
|
+
import { preferenceSelectors } from '@/store/user/selectors';
|
6
8
|
import {
|
7
9
|
AssistantListResponse,
|
8
10
|
AssistantQueryParams,
|
@@ -162,6 +164,10 @@ class DiscoverService {
|
|
162
164
|
* 上报插件调用结果
|
163
165
|
*/
|
164
166
|
reportPluginCall = async (reportData: CallReportRequest) => {
|
167
|
+
const allow = useUserStore(preferenceSelectors.userAllowTrace);
|
168
|
+
|
169
|
+
if (!allow) return;
|
170
|
+
|
165
171
|
await this.injectMPToken();
|
166
172
|
|
167
173
|
lambdaClient.market.reportCall.mutate(cleanObject(reportData)).catch((reportError) => {
|
package/src/services/mcp.ts
CHANGED
@@ -103,7 +103,6 @@ class MCPService {
|
|
103
103
|
errorCode,
|
104
104
|
errorMessage,
|
105
105
|
identifier,
|
106
|
-
inputParams,
|
107
106
|
isCustomPlugin,
|
108
107
|
metadata: {
|
109
108
|
appVersion: CURRENT_VERSION,
|
@@ -112,7 +111,6 @@ class MCPService {
|
|
112
111
|
},
|
113
112
|
methodName: apiName,
|
114
113
|
methodType: 'tool' as const,
|
115
|
-
outputResult: success ? result : undefined,
|
116
114
|
requestSizeBytes,
|
117
115
|
responseSizeBytes,
|
118
116
|
sessionId: topicId,
|
@@ -77,17 +77,13 @@ export function useDimensionControl() {
|
|
77
77
|
const aspectRatioOptions = useMemo(() => {
|
78
78
|
const modelOptions = paramsSchema?.aspectRatio?.enum || [];
|
79
79
|
|
80
|
-
//
|
81
|
-
|
80
|
+
// 如果 schema 里面有 aspectRatio 并且不为空,直接使用 schema 里面的选项
|
81
|
+
if (modelOptions.length > 0) {
|
82
|
+
return modelOptions;
|
83
|
+
}
|
82
84
|
|
83
|
-
//
|
84
|
-
|
85
|
-
if (!allOptions.includes(option)) {
|
86
|
-
allOptions.push(option);
|
87
|
-
}
|
88
|
-
});
|
89
|
-
|
90
|
-
return allOptions;
|
85
|
+
// 否则使用预设选项
|
86
|
+
return PRESET_ASPECT_RATIOS;
|
91
87
|
}, [paramsSchema]);
|
92
88
|
|
93
89
|
// 只要不是所有维度相关的控件都不显示,那么这个容器就应该显示
|