mirror of
https://github.com/lobehub/lobehub
synced 2026-04-21 09:37:28 +00:00
✨ feat: support model alias mapping for image and video runtimes (#13896)
This commit is contained in:
parent
a0471d5906
commit
fb471123fc
7 changed files with 102 additions and 16 deletions
|
|
@ -234,6 +234,7 @@
|
|||
"@lobechat/builtin-tools": "workspace:*",
|
||||
"@lobechat/business-config": "workspace:*",
|
||||
"@lobechat/business-const": "workspace:*",
|
||||
"@lobechat/business-model-runtime": "workspace:*",
|
||||
"@lobechat/chat-adapter-feishu": "workspace:*",
|
||||
"@lobechat/chat-adapter-qq": "workspace:*",
|
||||
"@lobechat/chat-adapter-wechat": "workspace:*",
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
export * from './model-mapping';
|
||||
export * from './router-runtime-options';
|
||||
|
|
|
|||
35
packages/business/model-runtime/src/model-mapping.ts
Normal file
35
packages/business/model-runtime/src/model-mapping.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
export interface MappedBusinessModelFields {
|
||||
modelId: string;
|
||||
providerId: string;
|
||||
requestedModelId?: string;
|
||||
}
|
||||
|
||||
export interface ResolvedBusinessModel {
|
||||
requestedModelId?: string;
|
||||
resolvedModelId: string;
|
||||
}
|
||||
|
||||
interface BuildMappedBusinessModelFieldsParams {
|
||||
provider: string;
|
||||
requestedModelId?: string;
|
||||
resolvedModelId: string;
|
||||
}
|
||||
|
||||
export const buildMappedBusinessModelFields = ({
|
||||
provider,
|
||||
requestedModelId,
|
||||
resolvedModelId,
|
||||
}: BuildMappedBusinessModelFieldsParams): MappedBusinessModelFields => ({
|
||||
modelId: resolvedModelId,
|
||||
providerId: provider,
|
||||
...(requestedModelId ? { requestedModelId } : {}),
|
||||
});
|
||||
|
||||
export const resolveBusinessModelMapping = async (
|
||||
_provider: string,
|
||||
model: string,
|
||||
): Promise<ResolvedBusinessModel> => {
|
||||
return {
|
||||
resolvedModelId: model,
|
||||
};
|
||||
};
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
import { timingSafeEqual } from 'node:crypto';
|
||||
|
||||
import {
|
||||
buildMappedBusinessModelFields,
|
||||
resolveBusinessModelMapping,
|
||||
} from '@lobechat/business-model-runtime';
|
||||
import { ModelRuntime } from '@lobechat/model-runtime';
|
||||
import {
|
||||
AsyncTaskError,
|
||||
|
|
@ -136,8 +140,18 @@ export const POST = async (req: Request, { params }: { params: Promise<{ provide
|
|||
const batch = await db.query.generationBatches.findFirst({
|
||||
where: eq(generationBatches.id, generation.generationBatchId!),
|
||||
});
|
||||
const resolvedModel =
|
||||
result.status === 'success' ? (result.model ?? batch?.model ?? '') : (batch?.model ?? '');
|
||||
const requestedModel = batch?.model ?? '';
|
||||
// Resolve mapping so spend log metadata and pricing lookup use the billed model id,
|
||||
// not the user-facing alias nor the provider-reported internal name.
|
||||
const { resolvedModelId } = requestedModel
|
||||
? await resolveBusinessModelMapping(provider, requestedModel)
|
||||
: { resolvedModelId: '' };
|
||||
|
||||
const mappedModelFields = buildMappedBusinessModelFields({
|
||||
provider,
|
||||
requestedModelId: resolvedModelId === requestedModel ? undefined : requestedModel,
|
||||
resolvedModelId,
|
||||
});
|
||||
|
||||
// Handle error result: refund precharge and mark task as error
|
||||
if (result.status === 'error') {
|
||||
|
|
@ -153,10 +167,10 @@ export const POST = async (req: Request, { params }: { params: Promise<{ provide
|
|||
metadata: {
|
||||
asyncTaskId: asyncTask.id,
|
||||
generationBatchId: generation.generationBatchId!,
|
||||
modelId: resolvedModel,
|
||||
topicId: batch?.generationTopicId,
|
||||
...mappedModelFields,
|
||||
},
|
||||
model: resolvedModel,
|
||||
model: resolvedModelId,
|
||||
prechargeResult: metadata?.precharge as any,
|
||||
provider,
|
||||
userId: asyncTask.userId,
|
||||
|
|
@ -206,7 +220,7 @@ export const POST = async (req: Request, { params }: { params: Promise<{ provide
|
|||
// TODO: temporarily disabled until notification UI is polished
|
||||
// notifyVideoCompleted({
|
||||
// generationBatchId: generation.generationBatchId!,
|
||||
// model: resolvedModel,
|
||||
// model: requestedModel,
|
||||
// prompt: batch?.prompt ?? '',
|
||||
// topicId: batch?.generationTopicId,
|
||||
// userId: asyncTask.userId,
|
||||
|
|
@ -222,10 +236,10 @@ export const POST = async (req: Request, { params }: { params: Promise<{ provide
|
|||
metadata: {
|
||||
asyncTaskId: asyncTask.id,
|
||||
generationBatchId: generation.generationBatchId!,
|
||||
modelId: resolvedModel,
|
||||
topicId: batch?.generationTopicId,
|
||||
...mappedModelFields,
|
||||
},
|
||||
model: resolvedModel,
|
||||
model: resolvedModelId,
|
||||
prechargeResult: metadata?.precharge as any,
|
||||
provider,
|
||||
usage: result.usage,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { ASYNC_TASK_TIMEOUT } from '@lobechat/business-config/server';
|
||||
import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
|
||||
import {
|
||||
buildMappedBusinessModelFields,
|
||||
resolveBusinessModelMapping,
|
||||
} from '@lobechat/business-model-runtime';
|
||||
import { AgentRuntimeErrorType } from '@lobechat/model-runtime';
|
||||
import { AsyncTaskError, AsyncTaskErrorType, AsyncTaskStatus } from '@lobechat/types';
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
|
@ -257,6 +261,10 @@ export const imageRouter = router({
|
|||
try {
|
||||
const imageGenerationPromise = async (signal: AbortSignal) => {
|
||||
log('Initializing agent runtime for provider: %s', provider);
|
||||
const { requestedModelId, resolvedModelId } = await resolveBusinessModelMapping(
|
||||
provider,
|
||||
model,
|
||||
);
|
||||
|
||||
// Read user's provider config from database
|
||||
const modelRuntime = await initModelRuntimeFromDB(ctx.serverDB, ctx.userId, provider);
|
||||
|
|
@ -265,7 +273,7 @@ export const imageRouter = router({
|
|||
checkAbortSignal(signal);
|
||||
log('Agent runtime initialized, calling createImage');
|
||||
const response = await modelRuntime.createImage!({
|
||||
model,
|
||||
model: resolvedModelId,
|
||||
params: params as unknown as RuntimeImageGenParams,
|
||||
});
|
||||
|
||||
|
|
@ -376,8 +384,12 @@ export const imageRouter = router({
|
|||
metadata: {
|
||||
asyncTaskId: taskId,
|
||||
generationBatchId,
|
||||
modelId: model,
|
||||
topicId: generationTopicId,
|
||||
...buildMappedBusinessModelFields({
|
||||
provider,
|
||||
requestedModelId,
|
||||
resolvedModelId,
|
||||
}),
|
||||
},
|
||||
modelUsage,
|
||||
provider,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { ASYNC_TASK_TIMEOUT } from '@lobechat/business-config/server';
|
||||
import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
|
||||
import {
|
||||
buildMappedBusinessModelFields,
|
||||
resolveBusinessModelMapping,
|
||||
} from '@lobechat/business-model-runtime';
|
||||
import { AsyncTaskError, AsyncTaskErrorType, AsyncTaskStatus } from '@lobechat/types';
|
||||
import debug from 'debug';
|
||||
import { z } from 'zod';
|
||||
|
|
@ -133,6 +137,8 @@ export const videoRouter = router({
|
|||
provider,
|
||||
});
|
||||
|
||||
const { resolvedModelId } = await resolveBusinessModelMapping(provider, model);
|
||||
|
||||
const abortController = new AbortController();
|
||||
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
|
|
@ -208,10 +214,14 @@ export const videoRouter = router({
|
|||
metadata: {
|
||||
asyncTaskId,
|
||||
generationBatchId,
|
||||
modelId: model,
|
||||
topicId: generationTopicId,
|
||||
...buildMappedBusinessModelFields({
|
||||
provider,
|
||||
requestedModelId: resolvedModelId === model ? undefined : model,
|
||||
resolvedModelId,
|
||||
}),
|
||||
},
|
||||
model,
|
||||
model: resolvedModelId,
|
||||
prechargeResult,
|
||||
provider,
|
||||
usage: undefined,
|
||||
|
|
@ -270,10 +280,14 @@ export const videoRouter = router({
|
|||
metadata: {
|
||||
asyncTaskId,
|
||||
generationBatchId,
|
||||
modelId: model,
|
||||
topicId: generationTopicId,
|
||||
...buildMappedBusinessModelFields({
|
||||
provider,
|
||||
requestedModelId: resolvedModelId === model ? undefined : model,
|
||||
resolvedModelId,
|
||||
}),
|
||||
},
|
||||
model,
|
||||
model: resolvedModelId,
|
||||
prechargeResult,
|
||||
provider,
|
||||
userId: ctx.userId,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { randomBytes } from 'node:crypto';
|
||||
|
||||
import {
|
||||
buildMappedBusinessModelFields,
|
||||
resolveBusinessModelMapping,
|
||||
} from '@lobechat/business-model-runtime';
|
||||
import debug from 'debug';
|
||||
import { and, eq } from 'drizzle-orm';
|
||||
import { after } from 'next/server';
|
||||
|
|
@ -67,6 +71,7 @@ export const videoRouter = router({
|
|||
createVideo: videoProcedure.input(createVideoInputSchema).mutation(async ({ input, ctx }) => {
|
||||
const { userId, serverDB, asyncTaskModel, fileService } = ctx;
|
||||
const { generationTopicId, provider, model, params } = input;
|
||||
const { resolvedModelId } = await resolveBusinessModelMapping(provider, model);
|
||||
|
||||
log('Starting video creation process, input: %O', input);
|
||||
|
||||
|
|
@ -214,7 +219,7 @@ export const videoRouter = router({
|
|||
|
||||
const response = await modelRuntime.createVideo({
|
||||
callbackUrl,
|
||||
model,
|
||||
model: resolvedModelId,
|
||||
params: generationParams,
|
||||
});
|
||||
|
||||
|
|
@ -289,10 +294,14 @@ export const videoRouter = router({
|
|||
metadata: {
|
||||
asyncTaskId,
|
||||
generationBatchId: createdBatch.id,
|
||||
modelId: model,
|
||||
topicId: generationTopicId,
|
||||
...buildMappedBusinessModelFields({
|
||||
provider,
|
||||
requestedModelId: resolvedModelId === model ? undefined : model,
|
||||
resolvedModelId,
|
||||
}),
|
||||
},
|
||||
model,
|
||||
model: resolvedModelId,
|
||||
prechargeResult,
|
||||
provider,
|
||||
userId,
|
||||
|
|
|
|||
Loading…
Reference in a new issue