Skip to main content
Glama

mcp-google-sheets

ai-provider-service.ts11.2 kB
import { AI_USAGE_AGENT_ID_HEADER, AI_USAGE_FEATURE_HEADER, AI_USAGE_MCP_ID_HEADER, AIProvider, AIProviderWithoutSensitiveData, AIUsageFeature, CreateAIProviderRequest, SUPPORTED_AI_PROVIDERS, SupportedAIProvider, } from '@activepieces/common-ai' import { AppSystemProp } from '@activepieces/server-shared' import { ActivepiecesError, ApEdition, apId, ErrorCode, isNil, PlatformId, SeekPage, } from '@activepieces/shared' import { FastifyRequest, RawServerBase, RequestGenericInterface } from 'fastify' import { repoFactory } from '../core/db/repo-factory' import { encryptUtils } from '../helper/encryption' import { system } from '../helper/system/system' import { platformService } from '../platform/platform.service' import { platformUtils } from '../platform/platform.utils' import { AIProviderEntity, AIProviderSchema } from './ai-provider-entity' import { aiProvidersStrategies, Usage } from './providers' import { StreamingParser } from './providers/types' const aiProviderRepo = repoFactory<AIProviderSchema>(AIProviderEntity) const isCloudEdition = system.getEdition() === ApEdition.CLOUD export const aiProviderService = { async list(userPlatformId: PlatformId): Promise<SeekPage<AIProviderWithoutSensitiveData>> { const platformId = await this.getAIProviderPlatformId(userPlatformId) const providers = await aiProviderRepo().findBy({ platformId }) const aiProviders = providers.map((provider): AIProviderWithoutSensitiveData => ({ id: provider.id, created: provider.created, updated: provider.updated, provider: provider.provider, platformId: provider.platformId, })) return { data: aiProviders, next: null, previous: null, } }, async isAgentConfigured(): Promise<boolean> { return aiProviderRepo().existsBy({ provider: 'openai', }) }, async upsert(platformId: PlatformId, request: CreateAIProviderRequest): Promise<void> { assertOnlyCloudPlatformCanEditOnCloud(platformId) if (request.useAzureOpenAI && system.getEdition() !== ApEdition.ENTERPRISE) { throw new ActivepiecesError({ code: ErrorCode.FEATURE_DISABLED, params: { message: 'Azure OpenAI is only available for enterprise customers', }, }) } await aiProviderRepo().upsert({ id: apId(), config: await encryptUtils.encryptObject({ apiKey: request.apiKey, azureOpenAI: request.useAzureOpenAI ? { resourceName: request.resourceName, } : undefined, }), provider: request.provider, platformId, }, ['provider', 'platformId']) }, async delete(platformId: PlatformId, provider: string): Promise<void> { assertOnlyCloudPlatformCanEditOnCloud(platformId) await aiProviderRepo().delete({ platformId, provider, }) }, async getConfig(provider: string, platformId: PlatformId): Promise<AIProvider['config']> { const aiProvider = await aiProviderRepo().findOneOrFail({ where: { provider, platformId, }, select: { config: { iv: true, data: true, }, }, }) return encryptUtils.decryptObject(aiProvider.config) }, async getAIProviderPlatformId(userPlatformId: string): Promise<string> { if (!isCloudEdition) return userPlatformId const cloudPlatformId = system.getOrThrow(AppSystemProp.CLOUD_PLATFORM_ID) if (cloudPlatformId === userPlatformId) return cloudPlatformId const platform = await platformService.getOneWithPlanOrThrow(userPlatformId) const isEnterpriseCustomer = platformUtils.isCustomerOnDedicatedDomain(platform) return isEnterpriseCustomer ? userPlatformId : cloudPlatformId }, getBaseUrl(provider: string, config: AIProvider['config']): string { const providerStrategy = aiProvidersStrategies[provider] if (providerStrategy?.getBaseUrl) { return providerStrategy.getBaseUrl(config) } const providerConfig = getProviderConfig(provider)! return providerConfig.baseUrl }, isNonUsageRequest(provider: string, request: FastifyRequest<RequestGenericInterface, RawServerBase>): boolean { const providerStrategy = aiProvidersStrategies[provider] if (providerStrategy?.isNonUsageRequest) { return providerStrategy.isNonUsageRequest(request) } return false }, calculateUsage(provider: string, request: FastifyRequest<RequestGenericInterface, RawServerBase>, response: Record<string, unknown>): Usage | null { const providerStrategy = aiProvidersStrategies[provider] return providerStrategy.calculateUsage(request, response) }, extractModelId(provider: string, request: FastifyRequest<RequestGenericInterface, RawServerBase>): string | null { const providerStrategy = aiProvidersStrategies[provider] if (!providerStrategy) return null return providerStrategy.extractModelId(request) }, isModelSupported(provider: string, model: string | null, request: FastifyRequest<RequestGenericInterface, RawServerBase>): boolean { const providerConfig = getProviderConfig(provider)! if (this.isNonUsageRequest(provider, request)) { return true } return ( !isNil(model) && !isNil(providerConfig.languageModels.find((m) => m.instance.modelId === model)) || !isNil(providerConfig.imageModels.find((m) => m.instance.modelId === model)) || !isNil(providerConfig.videoModels.find((m) => m.instance.modelId === model)) ) }, getVideoModelCost({ provider, request }: { provider: string, request: FastifyRequest<RequestGenericInterface, RawServerBase> }) { const providerStrategy = aiProvidersStrategies[provider] const modelConfig = providerStrategy.extractModelId(request) const providerConfig = getProviderConfig(provider) const videoModelConfig = providerConfig?.videoModels.find((m) => m.instance.modelId === modelConfig) if (videoModelConfig) { return videoModelConfig.pricing.costPerSecond * videoModelConfig.minimumDurationInSeconds } return 0 }, isStreaming(provider: string, request: FastifyRequest<RequestGenericInterface, RawServerBase>): boolean { const providerStrategy = aiProvidersStrategies[provider] return providerStrategy.isStreaming(request) }, providerSupportsStreaming(provider: string): boolean { const providerConfig = getProviderConfig(provider)! return providerConfig.streaming }, streamingParser(provider: string): StreamingParser { const providerStrategy = aiProvidersStrategies[provider] return providerStrategy.streamingParser!() }, rewriteUrl(provider: string, config: AIProvider['config'], originalUrl: string): string { const providerStrategy = aiProvidersStrategies[provider] if (providerStrategy?.rewriteUrl) { return providerStrategy.rewriteUrl(config, originalUrl) } return originalUrl }, getAuthHeaders(provider: string, config: AIProvider['config']): Record<string, string> { const providerStrategy = aiProvidersStrategies[provider] if (providerStrategy.getAuthHeaders) { return providerStrategy.getAuthHeaders(config) } const providerConfig = getProviderConfig(provider)! return { [providerConfig.auth.headerName]: providerConfig.auth.bearer ? `Bearer ${config.apiKey}` : config.apiKey, } }, validateRequest(provider: string, request: FastifyRequest<RequestGenericInterface, RawServerBase>): void { validateAIUsageHeaders(request.headers) if (this.isStreaming(provider, request) && !this.providerSupportsStreaming(provider)) { throw new ActivepiecesError({ code: ErrorCode.AI_REQUEST_NOT_SUPPORTED, params: { message: 'Streaming is not supported for this provider', }, }) } const model = this.extractModelId(provider, request) if (!this.isModelSupported(provider, model, request)) { throw new ActivepiecesError({ code: ErrorCode.AI_MODEL_NOT_SUPPORTED, params: { provider, model: model ?? 'unknown', }, }) } const providerStrategy = aiProvidersStrategies[provider] if (providerStrategy.validateRequest) { providerStrategy.validateRequest(request) } }, } function assertOnlyCloudPlatformCanEditOnCloud(platformId: PlatformId): void { if (!isCloudEdition) { return } const cloudPlatformId = system.getOrThrow(AppSystemProp.CLOUD_PLATFORM_ID) if (platformId === cloudPlatformId) { return } throw new ActivepiecesError({ code: ErrorCode.AUTHORIZATION, params: { message: 'invalid route for principal type', }, }) } function getProviderConfig(provider: string | undefined): SupportedAIProvider | undefined { if (isNil(provider)) { return undefined } return SUPPORTED_AI_PROVIDERS.find((p) => p.provider === provider) } function validateAIUsageHeaders(headers: Record<string, string | string[] | undefined>): void { const feature = headers[AI_USAGE_FEATURE_HEADER] as AIUsageFeature const agentId = headers[AI_USAGE_AGENT_ID_HEADER] as string | undefined const mcpId = headers[AI_USAGE_MCP_ID_HEADER] as string | undefined // Validate feature header const supportedFeatures = Object.values(AIUsageFeature).filter(f => f !== AIUsageFeature.UNKNOWN) as AIUsageFeature[] if (feature && !supportedFeatures.includes(feature)) { throw new ActivepiecesError({ code: ErrorCode.VALIDATION, params: { message: `${AI_USAGE_FEATURE_HEADER} header must be one of the following: ${supportedFeatures.join(', ')}`, }, }) } if (feature === AIUsageFeature.AGENTS && !agentId) { throw new ActivepiecesError({ code: ErrorCode.VALIDATION, params: { message: `${AI_USAGE_AGENT_ID_HEADER} header is required when feature is ${AIUsageFeature.AGENTS}`, }, }) } if (feature === AIUsageFeature.MCP && !mcpId) { throw new ActivepiecesError({ code: ErrorCode.VALIDATION, params: { message: `${AI_USAGE_MCP_ID_HEADER} header is required when feature is ${AIUsageFeature.MCP}`, }, }) } }

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