Skip to main content
Glama
ai-provider-service.ts9.76 kB
import { ActivepiecesError, ActivePiecesProviderAuthConfig, AIProviderAuthConfig, AIProviderModel, AIProviderName, AIProviderWithoutSensitiveData, apId, CreateAIProviderRequest, ErrorCode, GetProviderConfigResponse, isNil, PlatformId, UpdateAIProviderRequest, } from '@activepieces/shared' import dayjs from 'dayjs' import { FastifyBaseLogger } from 'fastify' import cron from 'node-cron' import { In } from 'typeorm' import { repoFactory } from '../core/db/repo-factory' import { openRouterApi } from '../ee/platform/platform-plan/openrouter/openrouter-api' import { platformPlanService } from '../ee/platform/platform-plan/platform-plan.service' import { flagService } from '../flags/flag.service' import { encryptUtils } from '../helper/encryption' import { SystemJobName } from '../helper/system-jobs/common' import { systemJobsSchedule } from '../helper/system-jobs/system-job' import { AIProviderEntity, AIProviderSchema } from './ai-provider-entity' import { aiProviders } from './providers' const aiProviderRepo = repoFactory<AIProviderSchema>(AIProviderEntity) const modelsCache = new Map<string, AIProviderModel[]>() export const aiProviderService = (log: FastifyBaseLogger) => ({ async setup(): Promise<void> { cron.schedule('0 0 * * *', () => { log.info('Clearing AI provider models cache') modelsCache.clear() }) }, async listProviders(platformId: PlatformId): Promise<AIProviderWithoutSensitiveData[]> { const activepiecesExists = await aiProviderRepo().existsBy({ platformId, provider: AIProviderName.ACTIVEPIECES, }) if (flagService.aiCreditsEnabled() && !activepiecesExists) { await aiProviderRepo().save({ id: apId(), auth: await encryptUtils.encryptObject({}), config: {}, provider: AIProviderName.ACTIVEPIECES, displayName: 'Activepieces', platformId, }) } const configuredProviders = await aiProviderRepo().findBy({ platformId }) const formattedProviders: AIProviderWithoutSensitiveData[] = await Promise.all(configuredProviders.map(async p => { return { id: p.id, name: p.displayName, provider: p.provider, config: p.config, } })) return formattedProviders }, async listModels(platformId: PlatformId, provider: AIProviderName): Promise<AIProviderModel[]> { const { config, auth } = await this.getConfigOrThrow({ platformId, provider }) const cacheKey = `${provider}-${auth.apiKey}` if (modelsCache.has(cacheKey) && !('models' in config)) { return modelsCache.get(cacheKey)! } const data = await aiProviders[provider].listModels(auth, config) modelsCache.set(cacheKey, data.map(model => ({ id: model.id, name: model.name, type: model.type, }))) return modelsCache.get(cacheKey)! }, async create(platformId: PlatformId, request: CreateAIProviderRequest): Promise<void> { await aiProviderRepo().save({ id: apId(), auth: await encryptUtils.encryptObject(request.auth), config: request.config, provider: request.provider, displayName: request.displayName, platformId, }) }, async update(platformId: PlatformId, providerId: string, request: UpdateAIProviderRequest): Promise<void> { const aiProvider = await aiProviderRepo().findOneBy({ platformId, id: providerId, }) if (isNil(aiProvider) || aiProvider.provider === AIProviderName.ACTIVEPIECES) { throw new ActivepiecesError({ code: ErrorCode.ENTITY_NOT_FOUND, params: { entityId: providerId, entityType: 'AIProvider' }, }) } await aiProviderRepo().upsert({ id: providerId ?? apId(), auth: await encryptUtils.encryptObject(request.auth), config: request.config, provider: aiProvider.provider, displayName: request.displayName, platformId, }, ['id']) }, async delete(platformId: PlatformId, providerId: string): Promise<void> { await aiProviderRepo().delete({ platformId, id: providerId, }) }, async getConfigOrThrow({ platformId, provider }: GetOrCreateActivepiecesConfigResponse): Promise<GetProviderConfigResponse> { const aiProvider = await aiProviderRepo().findOneBy({ platformId, provider, }) if (isNil(aiProvider)) { throw new ActivepiecesError({ code: ErrorCode.ENTITY_NOT_FOUND, params: { entityId: provider, entityType: 'AIProvider', }, }) } let auth = await encryptUtils.decryptObject<AIProviderAuthConfig>(aiProvider.auth) if (aiProvider.provider === AIProviderName.ACTIVEPIECES) { const doesHaveKeys = !isNil(auth) && !isNil(auth.apiKey) && auth.apiKey !== '' if (!doesHaveKeys) { const { auth: activePiecesAuth } = await enrichWithKeysIfNeeded(aiProvider, platformId, log) auth = activePiecesAuth } await systemJobsSchedule(log).upsertJob({ job: { name: SystemJobName.AI_CREDIT_UPDATE_CHECK, data: { apiKeyHash: (auth as ActivePiecesProviderAuthConfig).apiKeyHash, platformId }, }, schedule: { type: 'one-time', date: dayjs(), }, }) } return { provider: aiProvider.provider, auth, config: aiProvider.config } }, async getActivepiecesProviderIfEnriched(platformId: PlatformId): Promise<ActivePiecesProviderAuthConfig | null> { const aiProvider = await aiProviderRepo().findOneBy({ platformId, provider: AIProviderName.ACTIVEPIECES, }) if (isNil(aiProvider)) { return null } const doesHaveKeys = await doesActivepiecesProviderHasKeys(aiProvider) if (!doesHaveKeys) { return null } const { auth } = await this.getConfigOrThrow({ platformId, provider: aiProvider.provider }) return auth as ActivePiecesProviderAuthConfig }, async getOrCreateActivePiecesProviderAuthConfig(platformId: PlatformId): Promise<ActivePiecesProviderAuthConfig> { const aiProvider = await aiProviderRepo().findOneBy({ platformId, provider: AIProviderName.ACTIVEPIECES, }) if (isNil(aiProvider)) { await aiProviderRepo().save({ id: apId(), auth: await encryptUtils.encryptObject({}), config: {}, provider: AIProviderName.ACTIVEPIECES, displayName: 'Activepieces', platformId, }) } const { auth } = await this.getConfigOrThrow({ platformId, provider: AIProviderName.ACTIVEPIECES }) return auth as ActivePiecesProviderAuthConfig }, async getAllActivePiecesProvidersConfigs(platformIds?: string[]): Promise<{ [platformId: string]: ActivePiecesProviderAuthConfig }> { const aiProviders = await aiProviderRepo().find({ where: { provider: AIProviderName.ACTIVEPIECES, platformId: platformIds?.length ? In(platformIds) : undefined, }, }) const result: { [platformId: string]: ActivePiecesProviderAuthConfig } = {} for (const aiProvider of aiProviders) { const hasKeys = await doesActivepiecesProviderHasKeys(aiProvider) if (!hasKeys) continue result[aiProvider.platformId] = await encryptUtils.decryptObject<ActivePiecesProviderAuthConfig>(aiProvider.auth) } return result }, }) type GetOrCreateActivepiecesConfigResponse = { platformId: PlatformId provider: AIProviderName } async function enrichWithKeysIfNeeded(aiProvider: AIProviderSchema, platformId: PlatformId, log: FastifyBaseLogger): Promise<GetProviderConfigResponse> { const platformPlan = await platformPlanService(log).getOrCreateForPlatform(platformId) const limit = platformPlan.includedAiCredits / 1000 const { key, data } = await openRouterApi.createKey({ name: `Platform ${platformId}`, limit, }) const rawAuth: ActivePiecesProviderAuthConfig = { apiKey: key, apiKeyHash: data.hash } const savedAiProvider = await aiProviderRepo().save({ id: aiProvider.id, platformId, provider: AIProviderName.ACTIVEPIECES, displayName: 'Activepieces', config: {}, auth: await encryptUtils.encryptObject(rawAuth), }) await platformPlanService(log).update({ platformId, lastFreeAiCreditsRenewalDate: new Date().toISOString(), }) return { provider: savedAiProvider.provider, auth: rawAuth, config: savedAiProvider.config } } async function doesActivepiecesProviderHasKeys(aiProvider: AIProviderSchema): Promise<boolean> { if (isNil(aiProvider) || isNil(aiProvider.auth)) { return false } const decryptedAuth = await encryptUtils.decryptObject<ActivePiecesProviderAuthConfig>(aiProvider.auth) return !isNil(decryptedAuth) && !isNil(decryptedAuth.apiKey) && decryptedAuth.apiKey !== '' }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/activepieces/activepieces'

If you have feedback or need assistance with the MCP directory API, please join our Discord server