{"version":3,"file":"ai-metadata.mjs","sources":["../../../server/src/services/ai-metadata.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\nimport { z } from 'zod';\nimport { InputFile } from '../types';\nimport { Settings } from '../controllers/validation/admin/settings';\n\nconst createAIMetadataService = ({ strapi }: { strapi: Core.Strapi }) => {\n  const aiServerUrl = process.env.STRAPI_AI_URL || 'https://strapi-ai.apps.strapi.io';\n\n  return {\n    async isEnabled() {\n      // Check if user disabled AI features globally\n      const isAIEnabled = strapi.config.get('admin.ai.enabled', true);\n      if (!isAIEnabled) {\n        return false;\n      }\n\n      // Check if the user's license grants access to AI features\n      const hasAccess = strapi.ee.features.isEnabled('cms-ai');\n      if (!hasAccess) {\n        return false;\n      }\n\n      // Check if feature is specifically enabled, defaulting to true\n      const settings: Settings = await strapi.plugin('upload').service('upload').getSettings();\n      const aiMetadata: boolean = settings.aiMetadata ?? true;\n\n      return aiMetadata;\n    },\n\n    async processFiles(\n      files: InputFile[]\n    ): Promise<Array<{ altText: string; caption: string } | null>> {\n      if (!(await this.isEnabled()) || !aiServerUrl) {\n        throw new Error('AI Metadata service is not enabled');\n      }\n\n      // Filter for image files only and track their original positions\n      // We need to maintain the original indices so we can map AI results back correctly\n      const imageFiles = files\n        .map((file, index) => ({ file, originalIndex: index }))\n        .filter(({ file }) => file.mimetype?.startsWith('image/'));\n\n      // If no image files, return sparse array with all nulls to avoid calling the AI server\n      // This maintains the same array length as input files for proper index alignment\n      if (imageFiles.length === 0) {\n        return new Array(files.length).fill(null);\n      }\n\n      const formData = new FormData();\n\n      for (const { file } of imageFiles) {\n        const fullUrl =\n          file.provider === 'local'\n            ? strapi.config.get('server.absoluteUrl') + file.filepath\n            : file.filepath;\n\n        const resp = await fetch(fullUrl);\n        if (!resp.ok) {\n          throw new Error(`Failed to fetch image from URL: ${fullUrl} (${resp.status})`);\n        }\n        const ab = await resp.arrayBuffer();\n        const blob: Blob = new Blob([ab], { type: file.mimetype || undefined });\n        formData.append('files', blob);\n      }\n\n      let token: string;\n      try {\n        const tokenData = await strapi.service('admin::user').getAiToken();\n        token = tokenData.token;\n      } catch (error) {\n        throw new Error('Failed to retrieve AI token', {\n          cause: error instanceof Error ? error : undefined,\n        });\n      }\n\n      strapi.log.http('Contacting AI Server for media metadata generation');\n      const res = await fetch(`${aiServerUrl}/media-library/generate-metadata`, {\n        method: 'POST',\n        body: formData,\n        headers: {\n          Authorization: `Bearer ${token}`,\n        },\n      });\n\n      if (!res.ok) {\n        throw Error(`AI metadata generation failed`, { cause: await res.text() });\n      }\n\n      const responseSchema = z.object({\n        results: z.array(\n          z.object({\n            altText: z.string(),\n            caption: z.string(),\n          })\n        ),\n      });\n\n      const { results } = responseSchema.parse(await res.json());\n      strapi.log.http(`Media metadata generated successfully for ${results.length} files`);\n\n      // Create sparse array with results at original indices\n      // Example: files=[img1, pdf, img2] -> imageFiles=[{img1, index:0}, {img2, index:2}]\n      // AI results=[meta1, meta2] -> sparse=[meta1, null, meta2]\n      // This ensures metadata[i] corresponds to files[i], with null for non-images\n      return imageFiles.reduce((sparseResults, { originalIndex }, resultIndex) => {\n        sparseResults[originalIndex] = results[resultIndex];\n        return sparseResults;\n      }, new Array(files.length).fill(null));\n    },\n  };\n};\n\nexport { createAIMetadataService };\n"],"names":["createAIMetadataService","strapi","aiServerUrl","process","env","STRAPI_AI_URL","isEnabled","isAIEnabled","config","get","hasAccess","ee","features","settings","plugin","service","getSettings","aiMetadata","processFiles","files","Error","imageFiles","map","file","index","originalIndex","filter","mimetype","startsWith","length","Array","fill","formData","FormData","fullUrl","provider","filepath","resp","fetch","ok","status","ab","arrayBuffer","blob","Blob","type","undefined","append","token","tokenData","getAiToken","error","cause","log","http","res","method","body","headers","Authorization","text","responseSchema","z","object","results","array","altText","string","caption","parse","json","reduce","sparseResults","resultIndex"],"mappings":"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"}