@lobehub/lobehub 2.0.0-next.100 → 2.0.0-next.102
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/AGENTS.md +1 -0
- package/CHANGELOG.md +50 -0
- package/CLAUDE.md +1 -0
- package/changelog/v1.json +18 -0
- package/package.json +1 -1
- package/packages/model-bank/package.json +1 -0
- package/packages/model-bank/src/aiModels/aihubmix.ts +27 -0
- package/packages/model-bank/src/aiModels/google.ts +69 -10
- package/packages/model-bank/src/aiModels/index.ts +3 -0
- package/packages/model-bank/src/aiModels/infiniai.ts +5 -22
- package/packages/model-bank/src/aiModels/ollamacloud.ts +12 -0
- package/packages/model-bank/src/aiModels/siliconcloud.ts +0 -61
- package/packages/model-bank/src/aiModels/vertexai.ts +88 -1
- package/packages/model-bank/src/aiModels/zenmux.ts +1423 -0
- package/packages/model-bank/src/const/modelProvider.ts +1 -0
- package/packages/model-bank/src/standard-parameters/index.ts +9 -0
- package/packages/model-runtime/src/core/RouterRuntime/createRuntime.ts +42 -18
- package/packages/model-runtime/src/core/openaiCompatibleFactory/index.test.ts +2 -2
- package/packages/model-runtime/src/core/streams/bedrock/claude.ts +17 -3
- package/packages/model-runtime/src/core/streams/google/index.ts +7 -2
- package/packages/model-runtime/src/core/streams/openai/__snapshots__/responsesStream.test.ts.snap +166 -166
- package/packages/model-runtime/src/index.ts +1 -1
- package/packages/model-runtime/src/providers/anthropic/index.ts +1 -38
- package/packages/model-runtime/src/providers/anthropic/resolveCacheTTL.ts +44 -0
- package/packages/model-runtime/src/providers/bedrock/index.test.ts +127 -11
- package/packages/model-runtime/src/providers/bedrock/index.ts +47 -13
- package/packages/model-runtime/src/providers/google/createImage.ts +1 -0
- package/packages/model-runtime/src/providers/google/index.ts +11 -1
- package/packages/model-runtime/src/providers/zenmux/index.test.ts +320 -0
- package/packages/model-runtime/src/providers/zenmux/index.ts +84 -0
- package/packages/model-runtime/src/runtimeMap.ts +2 -0
- package/packages/types/src/user/settings/keyVaults.ts +1 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ResolutionSelect.tsx +88 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/index.tsx +9 -0
- package/src/config/modelProviders/index.ts +3 -0
- package/src/config/modelProviders/zenmux.ts +21 -0
- package/src/envs/llm.ts +6 -0
- package/src/locales/default/image.ts +8 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +3 -0
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +11 -0
|
@@ -149,6 +149,15 @@ export const ModelParamsMetaSchema = z.object({
|
|
|
149
149
|
})
|
|
150
150
|
.optional(),
|
|
151
151
|
|
|
152
|
+
resolution: z
|
|
153
|
+
.object({
|
|
154
|
+
default: z.string(),
|
|
155
|
+
description: z.string().optional(),
|
|
156
|
+
enum: z.array(z.string()),
|
|
157
|
+
type: z.literal('string').optional(),
|
|
158
|
+
})
|
|
159
|
+
.optional(),
|
|
160
|
+
|
|
152
161
|
cfg: z
|
|
153
162
|
.object({
|
|
154
163
|
default: z.number(),
|
|
@@ -148,10 +148,9 @@ export const createRouterRuntime = ({
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
/**
|
|
151
|
-
*
|
|
151
|
+
* Resolve routers configuration and validate
|
|
152
152
|
*/
|
|
153
|
-
private async
|
|
154
|
-
// 动态获取 routers,支持传入 model
|
|
153
|
+
private async resolveRouters(model?: string): Promise<RouterInstance[]> {
|
|
155
154
|
const resolvedRouters =
|
|
156
155
|
typeof this._routers === 'function'
|
|
157
156
|
? await this._routers(this._options, { model })
|
|
@@ -161,6 +160,41 @@ export const createRouterRuntime = ({
|
|
|
161
160
|
throw new Error('empty providers');
|
|
162
161
|
}
|
|
163
162
|
|
|
163
|
+
return resolvedRouters;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Create runtime for inference requests (chat, generateObject, etc.)
|
|
168
|
+
* Finds the router that matches the model, or uses the last router as fallback
|
|
169
|
+
*/
|
|
170
|
+
private async createRuntimeForInference(model: string): Promise<RuntimeItem> {
|
|
171
|
+
const resolvedRouters = await this.resolveRouters(model);
|
|
172
|
+
|
|
173
|
+
const matchedRouter =
|
|
174
|
+
resolvedRouters.find((router) => {
|
|
175
|
+
if (router.models && router.models.length > 0) {
|
|
176
|
+
return router.models.includes(model);
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
}) ?? resolvedRouters.at(-1)!;
|
|
180
|
+
|
|
181
|
+
const providerAI =
|
|
182
|
+
matchedRouter.runtime ?? baseRuntimeMap[matchedRouter.apiType] ?? LobeOpenAI;
|
|
183
|
+
const finalOptions = { ...this._params, ...this._options, ...matchedRouter.options };
|
|
184
|
+
const runtime: LobeRuntimeAI = new providerAI({ ...finalOptions, id: this._id });
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
id: matchedRouter.apiType,
|
|
188
|
+
models: matchedRouter.models,
|
|
189
|
+
runtime,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Create all runtimes for listing models
|
|
195
|
+
*/
|
|
196
|
+
private async createRuntimes(): Promise<RuntimeItem[]> {
|
|
197
|
+
const resolvedRouters = await this.resolveRouters();
|
|
164
198
|
return resolvedRouters.map((router) => {
|
|
165
199
|
const providerAI = router.runtime ?? baseRuntimeMap[router.apiType] ?? LobeOpenAI;
|
|
166
200
|
const finalOptions = { ...this._params, ...this._options, ...router.options };
|
|
@@ -176,16 +210,8 @@ export const createRouterRuntime = ({
|
|
|
176
210
|
|
|
177
211
|
// Check if it can match a specific model, otherwise default to using the last runtime
|
|
178
212
|
async getRuntimeByModel(model: string) {
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
for (const runtimeItem of runtimes) {
|
|
182
|
-
const models = runtimeItem.models || [];
|
|
183
|
-
if (models.includes(model)) {
|
|
184
|
-
return runtimeItem.runtime;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return runtimes.at(-1)!.runtime;
|
|
213
|
+
const runtimeItem = await this.createRuntimeForInference(model);
|
|
214
|
+
return runtimeItem.runtime;
|
|
189
215
|
}
|
|
190
216
|
|
|
191
217
|
async chat(payload: ChatStreamPayload, options?: ChatMethodOptions) {
|
|
@@ -222,9 +248,8 @@ export const createRouterRuntime = ({
|
|
|
222
248
|
|
|
223
249
|
async models() {
|
|
224
250
|
if (modelsOption && typeof modelsOption === 'function') {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
// 如果是函数式配置,使用最后一个运行时的客户端来调用函数
|
|
251
|
+
const runtimes = await this.createRuntimes();
|
|
252
|
+
// If it's a functional configuration, use the last runtime's client to call the function
|
|
228
253
|
const lastRuntime = runtimes.at(-1)?.runtime;
|
|
229
254
|
if (lastRuntime && 'client' in lastRuntime) {
|
|
230
255
|
const modelList = await modelsOption({ client: (lastRuntime as any).client });
|
|
@@ -232,8 +257,7 @@ export const createRouterRuntime = ({
|
|
|
232
257
|
}
|
|
233
258
|
}
|
|
234
259
|
|
|
235
|
-
|
|
236
|
-
const runtimes = await this.createRuntimesByRouters();
|
|
260
|
+
const runtimes = await this.createRuntimes();
|
|
237
261
|
return runtimes.at(-1)?.runtime.models?.();
|
|
238
262
|
}
|
|
239
263
|
|
|
@@ -426,7 +426,7 @@ describe('LobeOpenAICompatibleFactory', () => {
|
|
|
426
426
|
'data: "Hello"\n\n',
|
|
427
427
|
'id: a\n',
|
|
428
428
|
'event: usage\n',
|
|
429
|
-
'data: {"inputTextTokens":5,"outputTextTokens":5,"totalInputTokens":5,"totalOutputTokens":5,"totalTokens":10}\n\n',
|
|
429
|
+
'data: {"inputTextTokens":5,"outputTextTokens":5,"totalInputTokens":5,"totalOutputTokens":5,"totalTokens":10,"cost":0.000005}\n\n',
|
|
430
430
|
'id: output_speed\n',
|
|
431
431
|
'event: speed\n',
|
|
432
432
|
expect.stringMatching(/^data: {.*"tps":.*,"ttft":.*}\n\n$/), // tps ttft should be calculated with elapsed time
|
|
@@ -601,7 +601,7 @@ describe('LobeOpenAICompatibleFactory', () => {
|
|
|
601
601
|
signal: controller.signal,
|
|
602
602
|
}),
|
|
603
603
|
);
|
|
604
|
-
});
|
|
604
|
+
}, 10000);
|
|
605
605
|
});
|
|
606
606
|
|
|
607
607
|
describe('Error', () => {
|
|
@@ -7,18 +7,32 @@ import {
|
|
|
7
7
|
StreamContext,
|
|
8
8
|
createCallbacksTransformer,
|
|
9
9
|
createSSEProtocolTransformer,
|
|
10
|
+
createTokenSpeedCalculator,
|
|
10
11
|
} from '../protocol';
|
|
11
12
|
import { createBedrockStream } from './common';
|
|
12
13
|
|
|
13
14
|
export const AWSBedrockClaudeStream = (
|
|
14
15
|
res: InvokeModelWithResponseStreamResponse | ReadableStream,
|
|
15
|
-
|
|
16
|
+
options?: {
|
|
17
|
+
callbacks?: ChatStreamCallbacks;
|
|
18
|
+
inputStartAt?: number;
|
|
19
|
+
payload?: Parameters<typeof transformAnthropicStream>[2];
|
|
20
|
+
},
|
|
16
21
|
): ReadableStream<string> => {
|
|
17
22
|
const streamStack: StreamContext = { id: 'chat_' + nanoid() };
|
|
18
23
|
|
|
19
24
|
const stream = res instanceof ReadableStream ? res : createBedrockStream(res);
|
|
20
25
|
|
|
26
|
+
const transformWithPayload: typeof transformAnthropicStream = (chunk, ctx) =>
|
|
27
|
+
transformAnthropicStream(chunk, ctx, options?.payload);
|
|
28
|
+
|
|
21
29
|
return stream
|
|
22
|
-
.pipeThrough(
|
|
23
|
-
|
|
30
|
+
.pipeThrough(
|
|
31
|
+
createTokenSpeedCalculator(transformWithPayload, {
|
|
32
|
+
inputStartAt: options?.inputStartAt,
|
|
33
|
+
streamStack,
|
|
34
|
+
}),
|
|
35
|
+
)
|
|
36
|
+
.pipeThrough(createSSEProtocolTransformer((c) => c, streamStack))
|
|
37
|
+
.pipeThrough(createCallbacksTransformer(options?.callbacks));
|
|
24
38
|
};
|
|
@@ -148,7 +148,8 @@ const transformGoogleGenerativeAIStream = (
|
|
|
148
148
|
|
|
149
149
|
// Check for image data before handling finishReason
|
|
150
150
|
if (Array.isArray(candidate.content?.parts) && candidate.content.parts.length > 0) {
|
|
151
|
-
|
|
151
|
+
// Filter out reasoning content and get first non-reasoning part
|
|
152
|
+
const part = candidate.content.parts.find((p: any) => !p.thought);
|
|
152
153
|
|
|
153
154
|
if (part && part.inlineData && part.inlineData.data && part.inlineData.mimeType) {
|
|
154
155
|
const imageChunk = {
|
|
@@ -182,7 +183,11 @@ const transformGoogleGenerativeAIStream = (
|
|
|
182
183
|
...usageChunks,
|
|
183
184
|
].filter(Boolean) as StreamProtocolChunk[];
|
|
184
185
|
}
|
|
185
|
-
|
|
186
|
+
// 当有 finishReason 但没有 text 内容时,发送一个空的 text 块以停止加载动画
|
|
187
|
+
return [
|
|
188
|
+
{ data: '', id: context?.id, type: 'text' },
|
|
189
|
+
{ data: candidate.finishReason, id: context?.id, type: 'stop' },
|
|
190
|
+
];
|
|
186
191
|
}
|
|
187
192
|
|
|
188
193
|
if (!!text?.trim()) return { data: text, id: context?.id, type: 'text' };
|