diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-generate-text/ai-generate-text.module.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-generate-text/ai-generate-text.module.ts index 198625aa4dc..e1be39dd361 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-generate-text/ai-generate-text.module.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-generate-text/ai-generate-text.module.ts @@ -1,13 +1,19 @@ import { Module } from '@nestjs/common'; import { TokenModule } from 'src/engine/core-modules/auth/token/token.module'; +import { AiBillingModule } from 'src/engine/metadata-modules/ai/ai-billing/ai-billing.module'; import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module'; import { AiGenerateTextController } from './controllers/ai-generate-text.controller'; @Module({ - imports: [TokenModule, WorkspaceCacheStorageModule, PermissionsModule], + imports: [ + TokenModule, + WorkspaceCacheStorageModule, + PermissionsModule, + AiBillingModule, + ], controllers: [AiGenerateTextController], }) export class AiGenerateTextModule {} diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-generate-text/controllers/ai-generate-text.controller.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-generate-text/controllers/ai-generate-text.controller.ts index 175de197604..bbc67bb5782 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-generate-text/controllers/ai-generate-text.controller.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-generate-text/controllers/ai-generate-text.controller.ts @@ -4,7 +4,9 @@ import { generateText } from 'ai'; import { PermissionFlagType } from 'twenty-shared/constants'; import { RestApiExceptionFilter } from 'src/engine/api/rest/rest-api-exception.filter'; +import { UsageOperationType } from 'src/engine/core-modules/usage/enums/usage-operation-type.enum'; import type { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { AuthUserWorkspaceId } from 'src/engine/decorators/auth/auth-user-workspace-id.decorator'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { JwtAuthGuard } from 'src/engine/guards/jwt-auth.guard'; import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; @@ -13,6 +15,7 @@ import { AiException, AiExceptionCode, } from 'src/engine/metadata-modules/ai/ai.exception'; +import { AiBillingService } from 'src/engine/metadata-modules/ai/ai-billing/services/ai-billing.service'; import { AiRestApiExceptionFilter } from 'src/engine/metadata-modules/ai/filters/ai-api-exception.filter'; import { GenerateTextInput } from 'src/engine/metadata-modules/ai/ai-generate-text/dtos/generate-text.input'; import { AiModelRegistryService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service'; @@ -23,6 +26,7 @@ import { AiModelRegistryService } from 'src/engine/metadata-modules/ai/ai-models export class AiGenerateTextController { constructor( private readonly aiModelRegistryService: AiModelRegistryService, + private readonly aiBillingService: AiBillingService, ) {} @Post('generate-text') @@ -30,6 +34,7 @@ export class AiGenerateTextController { async handleGenerateText( @Body() body: GenerateTextInput, @AuthWorkspace() workspace: WorkspaceEntity, + @AuthUserWorkspaceId() userWorkspaceId: string, ) { if (this.aiModelRegistryService.getAvailableModels().length === 0) { throw new AiException( @@ -50,18 +55,37 @@ export class AiGenerateTextController { modelId: resolvedModelId, }); - const result = await generateText({ - model: registeredModel.model, - system: body.systemPrompt, - prompt: body.userPrompt, - }); + let result: Awaited> | undefined; - return { - text: result.text, - usage: { - inputTokens: result.usage?.inputTokens ?? 0, - outputTokens: result.usage?.outputTokens ?? 0, - }, - }; + try { + result = await generateText({ + model: registeredModel.model, + system: body.systemPrompt, + prompt: body.userPrompt, + }); + + return { + text: result.text, + usage: { + inputTokens: result.usage?.inputTokens ?? 0, + outputTokens: result.usage?.outputTokens ?? 0, + }, + }; + } finally { + if (result) { + this.aiBillingService.calculateAndBillUsage( + resolvedModelId, + { + usage: result.usage, + cacheCreationTokens: + result.usage.inputTokenDetails?.cacheWriteTokens ?? 0, + }, + workspace.id, + UsageOperationType.AI_WORKFLOW_TOKEN, + null, + userWorkspaceId, + ); + } + } } }