@ai-stack/payloadcms 3.68.0-beta.4 → 3.68.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -48,6 +48,9 @@ function isZodSchema(schema) {
48
48
  if (providerOptions) {
49
49
  options.providerOptions = providerOptions;
50
50
  }
51
+ if (rest.onFinish) {
52
+ options.onFinish = rest.onFinish;
53
+ }
51
54
  return sdkStreamObject(options);
52
55
  }
53
56
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ai/core/streamObject.ts"],"sourcesContent":["import { type ImagePart, jsonSchema, streamObject as sdkStreamObject, type TextPart } from 'ai'\n\nimport type { PayloadGenerateObjectArgs } from './types.js'\n\nimport { getLanguageModel } from '../providers/registry.js'\n\nfunction isZodSchema(schema: unknown): boolean {\n return typeof schema === 'object' && schema !== null && '_def' in schema\n}\n\n/**\n * Stream structured output using AI SDK's streamObject\n * This is a thin wrapper that resolves the model from the registry\n * and passes everything directly to the AI SDK for streaming\n */\nexport async function streamObject(args: PayloadGenerateObjectArgs) {\n const { \n maxTokens, \n mode,\n model: modelId,\n payload,\n prompt,\n provider,\n providerOptions,\n schema,\n system,\n temperature,\n ...rest\n } = args\n \n // Resolve model from registry\n const model = await getLanguageModel(payload, provider, modelId, providerOptions)\n \n // Return streaming result from AI SDK\n const options: Record<string, unknown> = {\n mode: mode || 'auto',\n model,\n schema: schema ? (isZodSchema(schema) ? schema : jsonSchema(schema as Record<string, unknown>)) : undefined,\n system,\n temperature: temperature ?? 0.7,\n ...(maxTokens ? { maxOutputTokens: maxTokens } : {}),\n }\n\n // Handle multimodal input\n if ((args.images && args.images.length > 0) || (args.messages && args.messages.length > 0)) {\n if (args.messages) {\n options.messages = args.messages\n } else {\n // Construct multimodal message from prompt and images\n const content: Array<ImagePart | TextPart> = [\n { type: 'text', text: prompt },\n ...(args.images || []),\n ]\n\n options.messages = [\n { content, role: 'user' as const },\n ]\n }\n } else {\n options.prompt = prompt\n }\n\n\n if (providerOptions) {\n options.providerOptions = providerOptions\n }\n\n return sdkStreamObject(options as Parameters<typeof sdkStreamObject>[0])\n}\n"],"names":["jsonSchema","streamObject","sdkStreamObject","getLanguageModel","isZodSchema","schema","args","maxTokens","mode","model","modelId","payload","prompt","provider","providerOptions","system","temperature","rest","options","undefined","maxOutputTokens","images","length","messages","content","type","text","role"],"mappings":"AAAA,SAAyBA,UAAU,EAAEC,gBAAgBC,eAAe,QAAuB,KAAI;AAI/F,SAASC,gBAAgB,QAAQ,2BAA0B;AAE3D,SAASC,YAAYC,MAAe;IAClC,OAAO,OAAOA,WAAW,YAAYA,WAAW,QAAQ,UAAUA;AACpE;AAEA;;;;CAIC,GACD,OAAO,eAAeJ,aAAaK,IAA+B;IAChE,MAAM,EACJC,SAAS,EACTC,IAAI,EACJC,OAAOC,OAAO,EACdC,OAAO,EACPC,MAAM,EACNC,QAAQ,EACRC,eAAe,EACfT,MAAM,EACNU,MAAM,EACNC,WAAW,EACX,GAAGC,MACJ,GAAGX;IAEJ,8BAA8B;IAC9B,MAAMG,QAAQ,MAAMN,iBAAiBQ,SAASE,UAAUH,SAASI;IAEjE,sCAAsC;IACtC,MAAMI,UAAmC;QACvCV,MAAMA,QAAQ;QACdC;QACAJ,QAAQA,SAAUD,YAAYC,UAAUA,SAASL,WAAWK,UAAsCc;QAClGJ;QACAC,aAAaA,eAAe;QAC5B,GAAIT,YAAY;YAAEa,iBAAiBb;QAAU,IAAI,CAAC,CAAC;IACrD;IAEA,0BAA0B;IAC1B,IAAI,AAACD,KAAKe,MAAM,IAAIf,KAAKe,MAAM,CAACC,MAAM,GAAG,KAAOhB,KAAKiB,QAAQ,IAAIjB,KAAKiB,QAAQ,CAACD,MAAM,GAAG,GAAI;QAC1F,IAAIhB,KAAKiB,QAAQ,EAAE;YACjBL,QAAQK,QAAQ,GAAGjB,KAAKiB,QAAQ;QAClC,OAAO;YACL,sDAAsD;YACtD,MAAMC,UAAuC;gBAC3C;oBAAEC,MAAM;oBAAQC,MAAMd;gBAAO;mBACzBN,KAAKe,MAAM,IAAI,EAAE;aACtB;YAEDH,QAAQK,QAAQ,GAAG;gBACjB;oBAAEC;oBAASG,MAAM;gBAAgB;aAClC;QACH;IACF,OAAO;QACLT,QAAQN,MAAM,GAAGA;IACnB;IAGA,IAAIE,iBAAiB;QACnBI,QAAQJ,eAAe,GAAGA;IAC5B;IAEA,OAAOZ,gBAAgBgB;AACzB"}
1
+ {"version":3,"sources":["../../../src/ai/core/streamObject.ts"],"sourcesContent":["import { type ImagePart, jsonSchema, streamObject as sdkStreamObject, type TextPart } from 'ai'\n\nimport type { PayloadGenerateObjectArgs } from './types.js'\n\nimport { getLanguageModel } from '../providers/registry.js'\n\nfunction isZodSchema(schema: unknown): boolean {\n return typeof schema === 'object' && schema !== null && '_def' in schema\n}\n\n/**\n * Stream structured output using AI SDK's streamObject\n * This is a thin wrapper that resolves the model from the registry\n * and passes everything directly to the AI SDK for streaming\n */\nexport async function streamObject(args: PayloadGenerateObjectArgs) {\n const { \n maxTokens, \n mode,\n model: modelId,\n payload,\n prompt,\n provider,\n providerOptions,\n schema,\n system,\n temperature,\n ...rest\n } = args\n \n // Resolve model from registry\n const model = await getLanguageModel(payload, provider, modelId, providerOptions)\n \n // Return streaming result from AI SDK\n const options: Record<string, unknown> = {\n mode: mode || 'auto',\n model,\n schema: schema ? (isZodSchema(schema) ? schema : jsonSchema(schema as Record<string, unknown>)) : undefined,\n system,\n temperature: temperature ?? 0.7,\n ...(maxTokens ? { maxOutputTokens: maxTokens } : {}),\n }\n\n // Handle multimodal input\n if ((args.images && args.images.length > 0) || (args.messages && args.messages.length > 0)) {\n if (args.messages) {\n options.messages = args.messages\n } else {\n // Construct multimodal message from prompt and images\n const content: Array<ImagePart | TextPart> = [\n { type: 'text', text: prompt },\n ...(args.images || []),\n ]\n\n options.messages = [\n { content, role: 'user' as const },\n ]\n }\n } else {\n options.prompt = prompt\n }\n\n\n if (providerOptions) {\n options.providerOptions = providerOptions\n }\n\n if (rest.onFinish) {\n options.onFinish = rest.onFinish\n }\n\n return sdkStreamObject(options as Parameters<typeof sdkStreamObject>[0])\n}\n"],"names":["jsonSchema","streamObject","sdkStreamObject","getLanguageModel","isZodSchema","schema","args","maxTokens","mode","model","modelId","payload","prompt","provider","providerOptions","system","temperature","rest","options","undefined","maxOutputTokens","images","length","messages","content","type","text","role","onFinish"],"mappings":"AAAA,SAAyBA,UAAU,EAAEC,gBAAgBC,eAAe,QAAuB,KAAI;AAI/F,SAASC,gBAAgB,QAAQ,2BAA0B;AAE3D,SAASC,YAAYC,MAAe;IAClC,OAAO,OAAOA,WAAW,YAAYA,WAAW,QAAQ,UAAUA;AACpE;AAEA;;;;CAIC,GACD,OAAO,eAAeJ,aAAaK,IAA+B;IAChE,MAAM,EACJC,SAAS,EACTC,IAAI,EACJC,OAAOC,OAAO,EACdC,OAAO,EACPC,MAAM,EACNC,QAAQ,EACRC,eAAe,EACfT,MAAM,EACNU,MAAM,EACNC,WAAW,EACX,GAAGC,MACJ,GAAGX;IAEJ,8BAA8B;IAC9B,MAAMG,QAAQ,MAAMN,iBAAiBQ,SAASE,UAAUH,SAASI;IAEjE,sCAAsC;IACtC,MAAMI,UAAmC;QACvCV,MAAMA,QAAQ;QACdC;QACAJ,QAAQA,SAAUD,YAAYC,UAAUA,SAASL,WAAWK,UAAsCc;QAClGJ;QACAC,aAAaA,eAAe;QAC5B,GAAIT,YAAY;YAAEa,iBAAiBb;QAAU,IAAI,CAAC,CAAC;IACrD;IAEA,0BAA0B;IAC1B,IAAI,AAACD,KAAKe,MAAM,IAAIf,KAAKe,MAAM,CAACC,MAAM,GAAG,KAAOhB,KAAKiB,QAAQ,IAAIjB,KAAKiB,QAAQ,CAACD,MAAM,GAAG,GAAI;QAC1F,IAAIhB,KAAKiB,QAAQ,EAAE;YACjBL,QAAQK,QAAQ,GAAGjB,KAAKiB,QAAQ;QAClC,OAAO;YACL,sDAAsD;YACtD,MAAMC,UAAuC;gBAC3C;oBAAEC,MAAM;oBAAQC,MAAMd;gBAAO;mBACzBN,KAAKe,MAAM,IAAI,EAAE;aACtB;YAEDH,QAAQK,QAAQ,GAAG;gBACjB;oBAAEC;oBAASG,MAAM;gBAAgB;aAClC;QACH;IACF,OAAO;QACLT,QAAQN,MAAM,GAAGA;IACnB;IAGA,IAAIE,iBAAiB;QACnBI,QAAQJ,eAAe,GAAGA;IAC5B;IAEA,IAAIG,KAAKW,QAAQ,EAAE;QACjBV,QAAQU,QAAQ,GAAGX,KAAKW,QAAQ;IAClC;IAEA,OAAO1B,gBAAgBgB;AACzB"}
@@ -27,6 +27,9 @@ export interface PayloadGenerationBaseArgs {
27
27
  export interface PayloadGenerateObjectArgs extends PayloadGenerationBaseArgs {
28
28
  images?: ImagePart[];
29
29
  mode?: 'auto' | 'json' | 'tool';
30
+ onFinish?: (event: {
31
+ object?: any;
32
+ }) => Promise<void> | void;
30
33
  schema?: Record<string, unknown> | z.ZodTypeAny;
31
34
  }
32
35
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ai/core/types.ts"],"sourcesContent":["import type { ImagePart, JSONValue, ModelMessage } from 'ai'\nimport type { Payload } from 'payload'\nimport type { z } from 'zod'\n\n/**\n * Provider options compatible with AI SDK's SharedV2ProviderOptions\n * SharedV2ProviderOptions = Record<string, Record<string, JSONValue>>\n */\nexport type ProviderOptions = Record<string, Record<string, JSONValue>>\n\n/**\n * Base arguments for all generation methods\n */\nexport interface PayloadGenerationBaseArgs {\n extractAttachments?: boolean\n maxTokens?: number\n messages?: ModelMessage[]\n model?: string\n payload: Payload\n prompt: string\n provider?: string\n providerOptions?: ProviderOptions\n system?: string\n temperature?: number\n}\n\n/**\n * Arguments for generateObject - structured output generation\n */\nexport interface PayloadGenerateObjectArgs extends PayloadGenerationBaseArgs {\n images?: ImagePart[]\n mode?: 'auto' | 'json' | 'tool'\n schema?: Record<string, unknown> | z.ZodTypeAny\n}\n\n/**\n * Arguments for generateText - simple text generation\n */\nexport interface PayloadGenerateTextArgs extends PayloadGenerationBaseArgs {\n // No additional fields needed for basic text generation\n}\n\n/**\n * Arguments for generateMedia - image/video generation\n */\nexport interface PayloadGenerateMediaArgs {\n aspectRatio?: string\n audioFormat?: string\n callbackUrl?: string\n duration?: number\n fps?: number\n images?: ImagePart[]\n instructionId?: number | string\n mode?: 'i2v' | 't2v'\n model?: string\n n?: number\n payload: Payload\n prompt: string\n provider?: string\n providerOptions?: ProviderOptions\n seed?: number\n size?: { height: number; width: number }\n speed?: number\n voice?: string\n}\n\n/**\n * Result from generateMedia - can be immediate file or async job\n */\nexport interface MediaResult {\n // Immediate result (image generation)\n file?: {\n data: Buffer\n mimetype: string\n name: string\n size: number\n }\n \n // Async job result (video generation)\n jobId?: string\n progress?: number\n status?: 'completed' | 'failed' | 'queued' | 'running'\n taskId?: string\n}\n\n/**\n * Internal type for multimodal image files\n */\nexport interface MultimodalImageFile {\n base64Data?: string\n mediaType?: string\n uint8Array?: Uint8Array\n}\n"],"names":[],"mappings":"AAqFA;;CAEC,GACD,WAIC"}
1
+ {"version":3,"sources":["../../../src/ai/core/types.ts"],"sourcesContent":["import type { ImagePart, JSONValue, ModelMessage } from 'ai'\nimport type { Payload } from 'payload'\nimport type { z } from 'zod'\n\n/**\n * Provider options compatible with AI SDK's SharedV2ProviderOptions\n * SharedV2ProviderOptions = Record<string, Record<string, JSONValue>>\n */\nexport type ProviderOptions = Record<string, Record<string, JSONValue>>\n\n/**\n * Base arguments for all generation methods\n */\nexport interface PayloadGenerationBaseArgs {\n extractAttachments?: boolean\n maxTokens?: number\n messages?: ModelMessage[]\n model?: string\n payload: Payload\n prompt: string\n provider?: string\n providerOptions?: ProviderOptions\n system?: string\n temperature?: number\n}\n\n/**\n * Arguments for generateObject - structured output generation\n */\nexport interface PayloadGenerateObjectArgs extends PayloadGenerationBaseArgs {\n images?: ImagePart[]\n mode?: 'auto' | 'json' | 'tool'\n onFinish?: (event: { object?: any }) => Promise<void> | void\n schema?: Record<string, unknown> | z.ZodTypeAny\n}\n\n/**\n * Arguments for generateText - simple text generation\n */\nexport interface PayloadGenerateTextArgs extends PayloadGenerationBaseArgs {\n // No additional fields needed for basic text generation\n}\n\n/**\n * Arguments for generateMedia - image/video generation\n */\nexport interface PayloadGenerateMediaArgs {\n aspectRatio?: string\n audioFormat?: string\n callbackUrl?: string\n duration?: number\n fps?: number\n images?: ImagePart[]\n instructionId?: number | string\n mode?: 'i2v' | 't2v'\n model?: string\n n?: number\n payload: Payload\n prompt: string\n provider?: string\n providerOptions?: ProviderOptions\n seed?: number\n size?: { height: number; width: number }\n speed?: number\n voice?: string\n}\n\n/**\n * Result from generateMedia - can be immediate file or async job\n */\nexport interface MediaResult {\n // Immediate result (image generation)\n file?: {\n data: Buffer\n mimetype: string\n name: string\n size: number\n }\n \n // Async job result (video generation)\n jobId?: string\n progress?: number\n status?: 'completed' | 'failed' | 'queued' | 'running'\n taskId?: string\n}\n\n/**\n * Internal type for multimodal image files\n */\nexport interface MultimodalImageFile {\n base64Data?: string\n mediaType?: string\n uint8Array?: Uint8Array\n}\n"],"names":[],"mappings":"AAsFA;;CAEC,GACD,WAIC"}
@@ -7,7 +7,7 @@ const defaultAccessConfig = {
7
7
  };
8
8
  const defaultAdminConfig = {
9
9
  group: 'Plugins',
10
- hidden: false
10
+ hidden: true
11
11
  };
12
12
  export const aiJobsCollection = ()=>({
13
13
  slug: PLUGIN_AI_JOBS_TABLE,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/collections/AIJobs.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nimport { PLUGIN_AI_JOBS_TABLE, PLUGIN_INSTRUCTIONS_TABLE } from '../defaults.js'\n\nconst defaultAccessConfig = {\n create: ({ req }: { req: { user?: any } }) => !!req.user,\n delete: ({ req }: { req: { user?: any } }) => !!req.user,\n read: ({ req }: { req: { user?: any } }) => !!req.user,\n update: ({ req }: { req: { user?: any } }) => !!req.user,\n}\n\nconst defaultAdminConfig = {\n group: 'Plugins',\n hidden: false,\n}\n\nexport const aiJobsCollection = (): CollectionConfig => ({\n slug: PLUGIN_AI_JOBS_TABLE,\n access: defaultAccessConfig,\n admin: defaultAdminConfig,\n fields: [\n {\n name: 'instructionId',\n type: 'relationship',\n relationTo: PLUGIN_INSTRUCTIONS_TABLE,\n required: true,\n },\n {\n name: 'task_id',\n type: 'text',\n required: true,\n unique: true,\n },\n {\n name: 'status',\n type: 'select',\n defaultValue: 'queued',\n options: [\n { label: 'Queued', value: 'queued' },\n { label: 'Running', value: 'running' },\n { label: 'Completed', value: 'completed' },\n { label: 'Failed', value: 'failed' },\n { label: 'Canceled', value: 'canceled' },\n ],\n },\n {\n name: 'progress',\n type: 'number',\n max: 100,\n min: 0,\n },\n {\n name: 'result_id',\n type: 'text',\n },\n {\n name: 'error',\n type: 'textarea',\n },\n {\n name: 'meta',\n type: 'json',\n },\n ],\n labels: {\n plural: 'AI Jobs',\n singular: 'AI Job',\n },\n})\n"],"names":["PLUGIN_AI_JOBS_TABLE","PLUGIN_INSTRUCTIONS_TABLE","defaultAccessConfig","create","req","user","delete","read","update","defaultAdminConfig","group","hidden","aiJobsCollection","slug","access","admin","fields","name","type","relationTo","required","unique","defaultValue","options","label","value","max","min","labels","plural","singular"],"mappings":"AAEA,SAASA,oBAAoB,EAAEC,yBAAyB,QAAQ,iBAAgB;AAEhF,MAAMC,sBAAsB;IAC1BC,QAAQ,CAAC,EAAEC,GAAG,EAA2B,GAAK,CAAC,CAACA,IAAIC,IAAI;IACxDC,QAAQ,CAAC,EAAEF,GAAG,EAA2B,GAAK,CAAC,CAACA,IAAIC,IAAI;IACxDE,MAAM,CAAC,EAAEH,GAAG,EAA2B,GAAK,CAAC,CAACA,IAAIC,IAAI;IACtDG,QAAQ,CAAC,EAAEJ,GAAG,EAA2B,GAAK,CAAC,CAACA,IAAIC,IAAI;AAC1D;AAEA,MAAMI,qBAAqB;IACzBC,OAAO;IACPC,QAAQ;AACV;AAEA,OAAO,MAAMC,mBAAmB,IAAyB,CAAA;QACvDC,MAAMb;QACNc,QAAQZ;QACRa,OAAON;QACPO,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNC,YAAYlB;gBACZmB,UAAU;YACZ;YACA;gBACEH,MAAM;gBACNC,MAAM;gBACNE,UAAU;gBACVC,QAAQ;YACV;YACA;gBACEJ,MAAM;gBACNC,MAAM;gBACNI,cAAc;gBACdC,SAAS;oBACP;wBAAEC,OAAO;wBAAUC,OAAO;oBAAS;oBACnC;wBAAED,OAAO;wBAAWC,OAAO;oBAAU;oBACrC;wBAAED,OAAO;wBAAaC,OAAO;oBAAY;oBACzC;wBAAED,OAAO;wBAAUC,OAAO;oBAAS;oBACnC;wBAAED,OAAO;wBAAYC,OAAO;oBAAW;iBACxC;YACH;YACA;gBACER,MAAM;gBACNC,MAAM;gBACNQ,KAAK;gBACLC,KAAK;YACP;YACA;gBACEV,MAAM;gBACNC,MAAM;YACR;YACA;gBACED,MAAM;gBACNC,MAAM;YACR;YACA;gBACED,MAAM;gBACNC,MAAM;YACR;SACD;QACDU,QAAQ;YACNC,QAAQ;YACRC,UAAU;QACZ;IACF,CAAA,EAAE"}
1
+ {"version":3,"sources":["../../src/collections/AIJobs.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nimport { PLUGIN_AI_JOBS_TABLE, PLUGIN_INSTRUCTIONS_TABLE } from '../defaults.js'\n\nconst defaultAccessConfig = {\n create: ({ req }: { req: { user?: any } }) => !!req.user,\n delete: ({ req }: { req: { user?: any } }) => !!req.user,\n read: ({ req }: { req: { user?: any } }) => !!req.user,\n update: ({ req }: { req: { user?: any } }) => !!req.user,\n}\n\nconst defaultAdminConfig = {\n group: 'Plugins',\n hidden: true,\n}\n\nexport const aiJobsCollection = (): CollectionConfig => ({\n slug: PLUGIN_AI_JOBS_TABLE,\n access: defaultAccessConfig,\n admin: defaultAdminConfig,\n fields: [\n {\n name: 'instructionId',\n type: 'relationship',\n relationTo: PLUGIN_INSTRUCTIONS_TABLE,\n required: true,\n },\n {\n name: 'task_id',\n type: 'text',\n required: true,\n unique: true,\n },\n {\n name: 'status',\n type: 'select',\n defaultValue: 'queued',\n options: [\n { label: 'Queued', value: 'queued' },\n { label: 'Running', value: 'running' },\n { label: 'Completed', value: 'completed' },\n { label: 'Failed', value: 'failed' },\n { label: 'Canceled', value: 'canceled' },\n ],\n },\n {\n name: 'progress',\n type: 'number',\n max: 100,\n min: 0,\n },\n {\n name: 'result_id',\n type: 'text',\n },\n {\n name: 'error',\n type: 'textarea',\n },\n {\n name: 'meta',\n type: 'json',\n },\n ],\n labels: {\n plural: 'AI Jobs',\n singular: 'AI Job',\n },\n})\n"],"names":["PLUGIN_AI_JOBS_TABLE","PLUGIN_INSTRUCTIONS_TABLE","defaultAccessConfig","create","req","user","delete","read","update","defaultAdminConfig","group","hidden","aiJobsCollection","slug","access","admin","fields","name","type","relationTo","required","unique","defaultValue","options","label","value","max","min","labels","plural","singular"],"mappings":"AAEA,SAASA,oBAAoB,EAAEC,yBAAyB,QAAQ,iBAAgB;AAEhF,MAAMC,sBAAsB;IAC1BC,QAAQ,CAAC,EAAEC,GAAG,EAA2B,GAAK,CAAC,CAACA,IAAIC,IAAI;IACxDC,QAAQ,CAAC,EAAEF,GAAG,EAA2B,GAAK,CAAC,CAACA,IAAIC,IAAI;IACxDE,MAAM,CAAC,EAAEH,GAAG,EAA2B,GAAK,CAAC,CAACA,IAAIC,IAAI;IACtDG,QAAQ,CAAC,EAAEJ,GAAG,EAA2B,GAAK,CAAC,CAACA,IAAIC,IAAI;AAC1D;AAEA,MAAMI,qBAAqB;IACzBC,OAAO;IACPC,QAAQ;AACV;AAEA,OAAO,MAAMC,mBAAmB,IAAyB,CAAA;QACvDC,MAAMb;QACNc,QAAQZ;QACRa,OAAON;QACPO,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNC,YAAYlB;gBACZmB,UAAU;YACZ;YACA;gBACEH,MAAM;gBACNC,MAAM;gBACNE,UAAU;gBACVC,QAAQ;YACV;YACA;gBACEJ,MAAM;gBACNC,MAAM;gBACNI,cAAc;gBACdC,SAAS;oBACP;wBAAEC,OAAO;wBAAUC,OAAO;oBAAS;oBACnC;wBAAED,OAAO;wBAAWC,OAAO;oBAAU;oBACrC;wBAAED,OAAO;wBAAaC,OAAO;oBAAY;oBACzC;wBAAED,OAAO;wBAAUC,OAAO;oBAAS;oBACnC;wBAAED,OAAO;wBAAYC,OAAO;oBAAW;iBACxC;YACH;YACA;gBACER,MAAM;gBACNC,MAAM;gBACNQ,KAAK;gBACLC,KAAK;YACP;YACA;gBACEV,MAAM;gBACNC,MAAM;YACR;YACA;gBACED,MAAM;gBACNC,MAAM;YACR;YACA;gBACED,MAAM;gBACNC,MAAM;YACR;SACD;QACDU,QAAQ;YACNC,QAAQ;YACRC,UAAU;QACZ;IACF,CAAA,EAAE"}
@@ -5,9 +5,9 @@ export const aiSettingsGlobal = {
5
5
  read: ({ req })=>!!req.user,
6
6
  update: ({ req })=>!!req.user
7
7
  },
8
- // admin: {
9
- // description: 'Configure AI providers, models, and default settings',
10
- // },
8
+ admin: {
9
+ group: false
10
+ },
11
11
  fields: [
12
12
  {
13
13
  name: 'providers',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/collections/AISettings.ts"],"sourcesContent":["import type { GlobalConfig } from 'payload'\n\nimport { allProviderBlocks } from '../ai/providers/blocks/index.js'\n\nexport const aiSettingsGlobal: GlobalConfig = {\n slug: 'ai-settings',\n access: {\n read: ({ req }) => !!req.user,\n update: ({ req }) => !!req.user,\n },\n // admin: {\n // description: 'Configure AI providers, models, and default settings',\n // },\n fields: [\n {\n name: 'providers',\n type: 'blocks',\n admin: {\n description: 'Configure which AI providers to use and their settings',\n initCollapsed: true,\n },\n blocks: allProviderBlocks,\n label: 'AI Providers',\n required: true,\n },\n {\n name: 'enabledCollections',\n type: 'json',\n admin: {\n hidden: true,\n },\n },\n {\n name: 'defaults',\n type: 'group',\n admin: {\n description: 'Please provide default provider/model behavior for each use case',\n },\n fields: [\n {\n type: 'tabs',\n tabs: [\n {\n fields: [\n {\n name: 'text',\n type: 'group',\n fields: [\n {\n name: 'provider',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicProviderSelect',\n },\n },\n label: 'Default Provider',\n },\n {\n name: 'model',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicModelSelect',\n },\n },\n label: 'Default Model',\n },\n {\n name: 'options',\n type: 'json',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#ProviderOptionsEditor',\n },\n description: 'Default options for this model (global)',\n },\n label: 'Global Model Options',\n },\n ],\n label: '',\n },\n ],\n label: 'Text Generation',\n },\n {\n fields: [\n {\n name: 'image',\n type: 'group',\n fields: [\n {\n name: 'provider',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicProviderSelect',\n },\n },\n label: 'Default Provider',\n },\n {\n name: 'model',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicModelSelect',\n },\n },\n label: 'Default Model',\n },\n {\n name: 'options',\n type: 'json',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#ProviderOptionsEditor',\n },\n description: 'Default options for this model (global)',\n },\n label: 'Global Model Options',\n },\n ],\n label: '',\n },\n ],\n label: 'Image Generation',\n },\n {\n fields: [\n {\n name: 'tts',\n type: 'group',\n fields: [\n {\n name: 'provider',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicProviderSelect',\n },\n },\n label: 'Default Provider',\n },\n {\n name: 'model',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicModelSelect',\n },\n },\n label: 'Default Model',\n },\n {\n name: 'voice',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicVoiceSelect',\n },\n },\n label: 'Default Voice',\n },\n {\n name: 'options',\n type: 'json',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#ProviderOptionsEditor',\n },\n description: 'Default options for this model (global)',\n },\n label: 'Global Model Options',\n },\n ],\n label: '',\n },\n ],\n label: 'Speech Generation',\n },\n {\n admin: {\n disabled: true,\n },\n fields: [\n {\n name: 'video',\n type: 'group',\n fields: [\n {\n name: 'provider',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicProviderSelect',\n },\n },\n label: 'Default Provider',\n },\n {\n name: 'model',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicModelSelect',\n },\n },\n label: 'Default Model',\n },\n {\n name: 'options',\n type: 'json',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#ProviderOptionsEditor',\n },\n description: 'Default options for this model (global)',\n },\n label: 'Global Model Options',\n },\n ],\n label: '',\n },\n ],\n label: 'Video Generation',\n },\n ],\n },\n ],\n label: 'Default Models',\n },\n ],\n hooks: {\n afterChange: [\n async ({ doc, req }) => {\n if (doc.enabledCollections && doc.enabledCollections.length > 0) {\n const { seedProperties } = await import('../utilities/seedProperties.js')\n await seedProperties({\n enabledCollections: doc.enabledCollections,\n req,\n })\n }\n return doc\n },\n ],\n afterRead: [\n async ({ context, doc, req }) => {\n if (!req.payload.secret) {\n return doc\n }\n\n const { decrypt } = await import('../utilities/encryption.js')\n\n if (doc.providers) {\n doc.providers = doc.providers.map((provider: any) => {\n if (provider.apiKey) {\n if (context.unsafe) {\n // Internal use: decrypt\n provider.apiKey = decrypt(provider.apiKey, req.payload.secret)\n } else {\n // Admin UI: mask\n // We can't easily check if it's valid without decrypting, but for UI we just show mask\n // If we want to show \"present\", we can return a mask\n provider.apiKey = 'sk-****' + provider.apiKey.slice(-4)\n }\n }\n return provider\n })\n }\n return doc\n },\n ],\n beforeChange: [\n async ({ data, originalDoc, req }) => {\n if (!req.payload.secret) {\n return data\n }\n\n const { encrypt } = await import('../utilities/encryption.js')\n\n // Iterate over providers and encrypt API keys\n if (data.providers) {\n data.providers = data.providers.map((provider: any) => {\n if (provider.apiKey) {\n const originalProvider = originalDoc?.providers?.find(\n (p: any) => p.id === provider.id,\n )\n\n // If the key strictly equals the existing one (e.g. partial update merge), do nothing.\n // This prevents re-encrypting an already encrypted key.\n if (originalProvider?.apiKey && provider.apiKey === originalProvider.apiKey) {\n return provider\n }\n\n // If it looks like a masked key, don't re-encrypt (it means it wasn't changed via UI)\n if (provider.apiKey.startsWith('sk-') && provider.apiKey.includes('****')) {\n // Restore the original encrypted key from originalDoc\n if (originalProvider?.apiKey) {\n provider.apiKey = originalProvider.apiKey\n }\n } else {\n // Encrypt new key\n provider.apiKey = encrypt(provider.apiKey, req.payload.secret)\n }\n }\n return provider\n })\n }\n return data\n },\n ],\n },\n label: 'AI Configuration',\n}\n"],"names":["allProviderBlocks","aiSettingsGlobal","slug","access","read","req","user","update","fields","name","type","admin","description","initCollapsed","blocks","label","required","hidden","tabs","components","Field","disabled","hooks","afterChange","doc","enabledCollections","length","seedProperties","afterRead","context","payload","secret","decrypt","providers","map","provider","apiKey","unsafe","slice","beforeChange","data","originalDoc","encrypt","originalProvider","find","p","id","startsWith","includes"],"mappings":"AAEA,SAASA,iBAAiB,QAAQ,kCAAiC;AAEnE,OAAO,MAAMC,mBAAiC;IAC5CC,MAAM;IACNC,QAAQ;QACNC,MAAM,CAAC,EAAEC,GAAG,EAAE,GAAK,CAAC,CAACA,IAAIC,IAAI;QAC7BC,QAAQ,CAAC,EAAEF,GAAG,EAAE,GAAK,CAAC,CAACA,IAAIC,IAAI;IACjC;IACA,WAAW;IACX,yEAAyE;IACzE,KAAK;IACLE,QAAQ;QACN;YACEC,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,aAAa;gBACbC,eAAe;YACjB;YACAC,QAAQd;YACRe,OAAO;YACPC,UAAU;QACZ;QACA;YACEP,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLM,QAAQ;YACV;QACF;QACA;YACER,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,aAAa;YACf;YACAJ,QAAQ;gBACN;oBACEE,MAAM;oBACNQ,MAAM;wBACJ;4BACEV,QAAQ;gCACN;oCACEC,MAAM;oCACNC,MAAM;oCACNF,QAAQ;wCACN;4CACEC,MAAM;4CACNC,MAAM;4CACNC,OAAO;gDACLQ,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEN,MAAM;4CACNC,MAAM;4CACNC,OAAO;gDACLQ,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEN,MAAM;4CACNC,MAAM;4CACNC,OAAO;gDACLQ,YAAY;oDACVC,OAAO;gDACT;gDACAR,aAAa;4CACf;4CACAG,OAAO;wCACT;qCACD;oCACDA,OAAO;gCACT;6BACD;4BACDA,OAAO;wBACT;wBACA;4BACEP,QAAQ;gCACN;oCACEC,MAAM;oCACNC,MAAM;oCACNF,QAAQ;wCACN;4CACEC,MAAM;4CACNC,MAAM;4CACNC,OAAO;gDACLQ,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEN,MAAM;4CACNC,MAAM;4CACNC,OAAO;gDACLQ,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEN,MAAM;4CACNC,MAAM;4CACNC,OAAO;gDACLQ,YAAY;oDACVC,OAAO;gDACT;gDACAR,aAAa;4CACf;4CACAG,OAAO;wCACT;qCACD;oCACDA,OAAO;gCACT;6BACD;4BACDA,OAAO;wBACT;wBACA;4BACEP,QAAQ;gCACN;oCACEC,MAAM;oCACNC,MAAM;oCACNF,QAAQ;wCACN;4CACEC,MAAM;4CACNC,MAAM;4CACNC,OAAO;gDACLQ,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEN,MAAM;4CACNC,MAAM;4CACNC,OAAO;gDACLQ,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEN,MAAM;4CACNC,MAAM;4CACNC,OAAO;gDACLQ,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEN,MAAM;4CACNC,MAAM;4CACNC,OAAO;gDACLQ,YAAY;oDACVC,OAAO;gDACT;gDACAR,aAAa;4CACf;4CACAG,OAAO;wCACT;qCACD;oCACDA,OAAO;gCACT;6BACD;4BACDA,OAAO;wBACT;wBACA;4BACEJ,OAAO;gCACLU,UAAU;4BACZ;4BACAb,QAAQ;gCACN;oCACEC,MAAM;oCACNC,MAAM;oCACNF,QAAQ;wCACN;4CACEC,MAAM;4CACNC,MAAM;4CACNC,OAAO;gDACLQ,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEN,MAAM;4CACNC,MAAM;4CACNC,OAAO;gDACLQ,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEN,MAAM;4CACNC,MAAM;4CACNC,OAAO;gDACLQ,YAAY;oDACVC,OAAO;gDACT;gDACAR,aAAa;4CACf;4CACAG,OAAO;wCACT;qCACD;oCACDA,OAAO;gCACT;6BACD;4BACDA,OAAO;wBACT;qBACD;gBACH;aACD;YACDA,OAAO;QACT;KACD;IACDO,OAAO;QACLC,aAAa;YACX,OAAO,EAAEC,GAAG,EAAEnB,GAAG,EAAE;gBACjB,IAAImB,IAAIC,kBAAkB,IAAID,IAAIC,kBAAkB,CAACC,MAAM,GAAG,GAAG;oBAC/D,MAAM,EAAEC,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC;oBACxC,MAAMA,eAAe;wBACnBF,oBAAoBD,IAAIC,kBAAkB;wBAC1CpB;oBACF;gBACF;gBACA,OAAOmB;YACT;SACD;QACDI,WAAW;YACT,OAAO,EAAEC,OAAO,EAAEL,GAAG,EAAEnB,GAAG,EAAE;gBAC1B,IAAI,CAACA,IAAIyB,OAAO,CAACC,MAAM,EAAE;oBACvB,OAAOP;gBACT;gBAEA,MAAM,EAAEQ,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC;gBAEjC,IAAIR,IAAIS,SAAS,EAAE;oBACjBT,IAAIS,SAAS,GAAGT,IAAIS,SAAS,CAACC,GAAG,CAAC,CAACC;wBACjC,IAAIA,SAASC,MAAM,EAAE;4BACnB,IAAIP,QAAQQ,MAAM,EAAE;gCAClB,wBAAwB;gCACxBF,SAASC,MAAM,GAAGJ,QAAQG,SAASC,MAAM,EAAE/B,IAAIyB,OAAO,CAACC,MAAM;4BAC/D,OAAO;gCACL,iBAAiB;gCACjB,uFAAuF;gCACvF,qDAAqD;gCACrDI,SAASC,MAAM,GAAG,YAAYD,SAASC,MAAM,CAACE,KAAK,CAAC,CAAC;4BACvD;wBACF;wBACA,OAAOH;oBACT;gBACF;gBACA,OAAOX;YACT;SACD;QACDe,cAAc;YACZ,OAAO,EAAEC,IAAI,EAAEC,WAAW,EAAEpC,GAAG,EAAE;gBAC/B,IAAI,CAACA,IAAIyB,OAAO,CAACC,MAAM,EAAE;oBACvB,OAAOS;gBACT;gBAEA,MAAM,EAAEE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC;gBAEjC,8CAA8C;gBAC9C,IAAIF,KAAKP,SAAS,EAAE;oBAClBO,KAAKP,SAAS,GAAGO,KAAKP,SAAS,CAACC,GAAG,CAAC,CAACC;wBACnC,IAAIA,SAASC,MAAM,EAAE;4BACnB,MAAMO,mBAAmBF,aAAaR,WAAWW,KAC/C,CAACC,IAAWA,EAAEC,EAAE,KAAKX,SAASW,EAAE;4BAGlC,uFAAuF;4BACvF,wDAAwD;4BACxD,IAAIH,kBAAkBP,UAAUD,SAASC,MAAM,KAAKO,iBAAiBP,MAAM,EAAE;gCAC3E,OAAOD;4BACT;4BAEA,sFAAsF;4BACtF,IAAIA,SAASC,MAAM,CAACW,UAAU,CAAC,UAAUZ,SAASC,MAAM,CAACY,QAAQ,CAAC,SAAS;gCACzE,sDAAsD;gCACtD,IAAIL,kBAAkBP,QAAQ;oCAC5BD,SAASC,MAAM,GAAGO,iBAAiBP,MAAM;gCAC3C;4BACF,OAAO;gCACL,kBAAkB;gCAClBD,SAASC,MAAM,GAAGM,QAAQP,SAASC,MAAM,EAAE/B,IAAIyB,OAAO,CAACC,MAAM;4BAC/D;wBACF;wBACA,OAAOI;oBACT;gBACF;gBACA,OAAOK;YACT;SACD;IACH;IACAzB,OAAO;AACT,EAAC"}
1
+ {"version":3,"sources":["../../src/collections/AISettings.ts"],"sourcesContent":["import type { GlobalConfig } from 'payload'\n\nimport { allProviderBlocks } from '../ai/providers/blocks/index.js'\n\nexport const aiSettingsGlobal: GlobalConfig = {\n slug: 'ai-settings',\n access: {\n read: ({ req }) => !!req.user,\n update: ({ req }) => !!req.user,\n },\n admin: {\n group: false,\n },\n fields: [\n {\n name: 'providers',\n type: 'blocks',\n admin: {\n description: 'Configure which AI providers to use and their settings',\n initCollapsed: true,\n },\n blocks: allProviderBlocks,\n label: 'AI Providers',\n required: true,\n },\n {\n name: 'enabledCollections',\n type: 'json',\n admin: {\n hidden: true,\n },\n },\n {\n name: 'defaults',\n type: 'group',\n admin: {\n description: 'Please provide default provider/model behavior for each use case',\n },\n fields: [\n {\n type: 'tabs',\n tabs: [\n {\n fields: [\n {\n name: 'text',\n type: 'group',\n fields: [\n {\n name: 'provider',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicProviderSelect',\n },\n },\n label: 'Default Provider',\n },\n {\n name: 'model',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicModelSelect',\n },\n },\n label: 'Default Model',\n },\n {\n name: 'options',\n type: 'json',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#ProviderOptionsEditor',\n },\n description: 'Default options for this model (global)',\n },\n label: 'Global Model Options',\n },\n ],\n label: '',\n },\n ],\n label: 'Text Generation',\n },\n {\n fields: [\n {\n name: 'image',\n type: 'group',\n fields: [\n {\n name: 'provider',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicProviderSelect',\n },\n },\n label: 'Default Provider',\n },\n {\n name: 'model',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicModelSelect',\n },\n },\n label: 'Default Model',\n },\n {\n name: 'options',\n type: 'json',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#ProviderOptionsEditor',\n },\n description: 'Default options for this model (global)',\n },\n label: 'Global Model Options',\n },\n ],\n label: '',\n },\n ],\n label: 'Image Generation',\n },\n {\n fields: [\n {\n name: 'tts',\n type: 'group',\n fields: [\n {\n name: 'provider',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicProviderSelect',\n },\n },\n label: 'Default Provider',\n },\n {\n name: 'model',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicModelSelect',\n },\n },\n label: 'Default Model',\n },\n {\n name: 'voice',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicVoiceSelect',\n },\n },\n label: 'Default Voice',\n },\n {\n name: 'options',\n type: 'json',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#ProviderOptionsEditor',\n },\n description: 'Default options for this model (global)',\n },\n label: 'Global Model Options',\n },\n ],\n label: '',\n },\n ],\n label: 'Speech Generation',\n },\n {\n admin: {\n disabled: true,\n },\n fields: [\n {\n name: 'video',\n type: 'group',\n fields: [\n {\n name: 'provider',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicProviderSelect',\n },\n },\n label: 'Default Provider',\n },\n {\n name: 'model',\n type: 'text',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#DynamicModelSelect',\n },\n },\n label: 'Default Model',\n },\n {\n name: 'options',\n type: 'json',\n admin: {\n components: {\n Field: '@ai-stack/payloadcms/client#ProviderOptionsEditor',\n },\n description: 'Default options for this model (global)',\n },\n label: 'Global Model Options',\n },\n ],\n label: '',\n },\n ],\n label: 'Video Generation',\n },\n ],\n },\n ],\n label: 'Default Models',\n },\n ],\n hooks: {\n afterChange: [\n async ({ doc, req }) => {\n if (doc.enabledCollections && doc.enabledCollections.length > 0) {\n const { seedProperties } = await import('../utilities/seedProperties.js')\n await seedProperties({\n enabledCollections: doc.enabledCollections,\n req,\n })\n }\n return doc\n },\n ],\n afterRead: [\n async ({ context, doc, req }) => {\n if (!req.payload.secret) {\n return doc\n }\n\n const { decrypt } = await import('../utilities/encryption.js')\n\n if (doc.providers) {\n doc.providers = doc.providers.map((provider: any) => {\n if (provider.apiKey) {\n if (context.unsafe) {\n // Internal use: decrypt\n provider.apiKey = decrypt(provider.apiKey, req.payload.secret)\n } else {\n // Admin UI: mask\n // We can't easily check if it's valid without decrypting, but for UI we just show mask\n // If we want to show \"present\", we can return a mask\n provider.apiKey = 'sk-****' + provider.apiKey.slice(-4)\n }\n }\n return provider\n })\n }\n return doc\n },\n ],\n beforeChange: [\n async ({ data, originalDoc, req }) => {\n if (!req.payload.secret) {\n return data\n }\n\n const { encrypt } = await import('../utilities/encryption.js')\n\n // Iterate over providers and encrypt API keys\n if (data.providers) {\n data.providers = data.providers.map((provider: any) => {\n if (provider.apiKey) {\n const originalProvider = originalDoc?.providers?.find(\n (p: any) => p.id === provider.id,\n )\n\n // If the key strictly equals the existing one (e.g. partial update merge), do nothing.\n // This prevents re-encrypting an already encrypted key.\n if (originalProvider?.apiKey && provider.apiKey === originalProvider.apiKey) {\n return provider\n }\n\n // If it looks like a masked key, don't re-encrypt (it means it wasn't changed via UI)\n if (provider.apiKey.startsWith('sk-') && provider.apiKey.includes('****')) {\n // Restore the original encrypted key from originalDoc\n if (originalProvider?.apiKey) {\n provider.apiKey = originalProvider.apiKey\n }\n } else {\n // Encrypt new key\n provider.apiKey = encrypt(provider.apiKey, req.payload.secret)\n }\n }\n return provider\n })\n }\n return data\n },\n ],\n },\n label: 'AI Configuration',\n}\n"],"names":["allProviderBlocks","aiSettingsGlobal","slug","access","read","req","user","update","admin","group","fields","name","type","description","initCollapsed","blocks","label","required","hidden","tabs","components","Field","disabled","hooks","afterChange","doc","enabledCollections","length","seedProperties","afterRead","context","payload","secret","decrypt","providers","map","provider","apiKey","unsafe","slice","beforeChange","data","originalDoc","encrypt","originalProvider","find","p","id","startsWith","includes"],"mappings":"AAEA,SAASA,iBAAiB,QAAQ,kCAAiC;AAEnE,OAAO,MAAMC,mBAAiC;IAC5CC,MAAM;IACNC,QAAQ;QACNC,MAAM,CAAC,EAAEC,GAAG,EAAE,GAAK,CAAC,CAACA,IAAIC,IAAI;QAC7BC,QAAQ,CAAC,EAAEF,GAAG,EAAE,GAAK,CAAC,CAACA,IAAIC,IAAI;IACjC;IACAE,OAAO;QACLC,OAAO;IACT;IACAC,QAAQ;QACN;YACEC,MAAM;YACNC,MAAM;YACNJ,OAAO;gBACLK,aAAa;gBACbC,eAAe;YACjB;YACAC,QAAQf;YACRgB,OAAO;YACPC,UAAU;QACZ;QACA;YACEN,MAAM;YACNC,MAAM;YACNJ,OAAO;gBACLU,QAAQ;YACV;QACF;QACA;YACEP,MAAM;YACNC,MAAM;YACNJ,OAAO;gBACLK,aAAa;YACf;YACAH,QAAQ;gBACN;oBACEE,MAAM;oBACNO,MAAM;wBACJ;4BACET,QAAQ;gCACN;oCACEC,MAAM;oCACNC,MAAM;oCACNF,QAAQ;wCACN;4CACEC,MAAM;4CACNC,MAAM;4CACNJ,OAAO;gDACLY,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEL,MAAM;4CACNC,MAAM;4CACNJ,OAAO;gDACLY,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEL,MAAM;4CACNC,MAAM;4CACNJ,OAAO;gDACLY,YAAY;oDACVC,OAAO;gDACT;gDACAR,aAAa;4CACf;4CACAG,OAAO;wCACT;qCACD;oCACDA,OAAO;gCACT;6BACD;4BACDA,OAAO;wBACT;wBACA;4BACEN,QAAQ;gCACN;oCACEC,MAAM;oCACNC,MAAM;oCACNF,QAAQ;wCACN;4CACEC,MAAM;4CACNC,MAAM;4CACNJ,OAAO;gDACLY,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEL,MAAM;4CACNC,MAAM;4CACNJ,OAAO;gDACLY,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEL,MAAM;4CACNC,MAAM;4CACNJ,OAAO;gDACLY,YAAY;oDACVC,OAAO;gDACT;gDACAR,aAAa;4CACf;4CACAG,OAAO;wCACT;qCACD;oCACDA,OAAO;gCACT;6BACD;4BACDA,OAAO;wBACT;wBACA;4BACEN,QAAQ;gCACN;oCACEC,MAAM;oCACNC,MAAM;oCACNF,QAAQ;wCACN;4CACEC,MAAM;4CACNC,MAAM;4CACNJ,OAAO;gDACLY,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEL,MAAM;4CACNC,MAAM;4CACNJ,OAAO;gDACLY,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEL,MAAM;4CACNC,MAAM;4CACNJ,OAAO;gDACLY,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEL,MAAM;4CACNC,MAAM;4CACNJ,OAAO;gDACLY,YAAY;oDACVC,OAAO;gDACT;gDACAR,aAAa;4CACf;4CACAG,OAAO;wCACT;qCACD;oCACDA,OAAO;gCACT;6BACD;4BACDA,OAAO;wBACT;wBACA;4BACER,OAAO;gCACLc,UAAU;4BACZ;4BACAZ,QAAQ;gCACN;oCACEC,MAAM;oCACNC,MAAM;oCACNF,QAAQ;wCACN;4CACEC,MAAM;4CACNC,MAAM;4CACNJ,OAAO;gDACLY,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEL,MAAM;4CACNC,MAAM;4CACNJ,OAAO;gDACLY,YAAY;oDACVC,OAAO;gDACT;4CACF;4CACAL,OAAO;wCACT;wCACA;4CACEL,MAAM;4CACNC,MAAM;4CACNJ,OAAO;gDACLY,YAAY;oDACVC,OAAO;gDACT;gDACAR,aAAa;4CACf;4CACAG,OAAO;wCACT;qCACD;oCACDA,OAAO;gCACT;6BACD;4BACDA,OAAO;wBACT;qBACD;gBACH;aACD;YACDA,OAAO;QACT;KACD;IACDO,OAAO;QACLC,aAAa;YACX,OAAO,EAAEC,GAAG,EAAEpB,GAAG,EAAE;gBACjB,IAAIoB,IAAIC,kBAAkB,IAAID,IAAIC,kBAAkB,CAACC,MAAM,GAAG,GAAG;oBAC/D,MAAM,EAAEC,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC;oBACxC,MAAMA,eAAe;wBACnBF,oBAAoBD,IAAIC,kBAAkB;wBAC1CrB;oBACF;gBACF;gBACA,OAAOoB;YACT;SACD;QACDI,WAAW;YACT,OAAO,EAAEC,OAAO,EAAEL,GAAG,EAAEpB,GAAG,EAAE;gBAC1B,IAAI,CAACA,IAAI0B,OAAO,CAACC,MAAM,EAAE;oBACvB,OAAOP;gBACT;gBAEA,MAAM,EAAEQ,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC;gBAEjC,IAAIR,IAAIS,SAAS,EAAE;oBACjBT,IAAIS,SAAS,GAAGT,IAAIS,SAAS,CAACC,GAAG,CAAC,CAACC;wBACjC,IAAIA,SAASC,MAAM,EAAE;4BACnB,IAAIP,QAAQQ,MAAM,EAAE;gCAClB,wBAAwB;gCACxBF,SAASC,MAAM,GAAGJ,QAAQG,SAASC,MAAM,EAAEhC,IAAI0B,OAAO,CAACC,MAAM;4BAC/D,OAAO;gCACL,iBAAiB;gCACjB,uFAAuF;gCACvF,qDAAqD;gCACrDI,SAASC,MAAM,GAAG,YAAYD,SAASC,MAAM,CAACE,KAAK,CAAC,CAAC;4BACvD;wBACF;wBACA,OAAOH;oBACT;gBACF;gBACA,OAAOX;YACT;SACD;QACDe,cAAc;YACZ,OAAO,EAAEC,IAAI,EAAEC,WAAW,EAAErC,GAAG,EAAE;gBAC/B,IAAI,CAACA,IAAI0B,OAAO,CAACC,MAAM,EAAE;oBACvB,OAAOS;gBACT;gBAEA,MAAM,EAAEE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC;gBAEjC,8CAA8C;gBAC9C,IAAIF,KAAKP,SAAS,EAAE;oBAClBO,KAAKP,SAAS,GAAGO,KAAKP,SAAS,CAACC,GAAG,CAAC,CAACC;wBACnC,IAAIA,SAASC,MAAM,EAAE;4BACnB,MAAMO,mBAAmBF,aAAaR,WAAWW,KAC/C,CAACC,IAAWA,EAAEC,EAAE,KAAKX,SAASW,EAAE;4BAGlC,uFAAuF;4BACvF,wDAAwD;4BACxD,IAAIH,kBAAkBP,UAAUD,SAASC,MAAM,KAAKO,iBAAiBP,MAAM,EAAE;gCAC3E,OAAOD;4BACT;4BAEA,sFAAsF;4BACtF,IAAIA,SAASC,MAAM,CAACW,UAAU,CAAC,UAAUZ,SAASC,MAAM,CAACY,QAAQ,CAAC,SAAS;gCACzE,sDAAsD;gCACtD,IAAIL,kBAAkBP,QAAQ;oCAC5BD,SAASC,MAAM,GAAGO,iBAAiBP,MAAM;gCAC3C;4BACF,OAAO;gCACL,kBAAkB;gCAClBD,SAASC,MAAM,GAAGM,QAAQP,SAASC,MAAM,EAAEhC,IAAI0B,OAAO,CAACC,MAAM;4BAC/D;wBACF;wBACA,OAAOI;oBACT;gBACF;gBACA,OAAOK;YACT;SACD;IACH;IACAzB,OAAO;AACT,EAAC"}
@@ -35,34 +35,51 @@ export const fetchVoices = {
35
35
  status: 400
36
36
  });
37
37
  }
38
- // Call ElevenLabs API to fetch voices
39
- const response = await fetch('https://api.elevenlabs.io/v1/voices', {
40
- headers: {
41
- 'xi-api-key': apiKey
38
+ // Call ElevenLabs API to fetch voices with timeout
39
+ const controller = new AbortController();
40
+ const timeoutId = setTimeout(()=>controller.abort(), 15000) // 15s timeout
41
+ ;
42
+ try {
43
+ const response = await fetch('https://api.elevenlabs.io/v1/voices', {
44
+ headers: {
45
+ 'xi-api-key': apiKey
46
+ },
47
+ signal: controller.signal
48
+ });
49
+ clearTimeout(timeoutId);
50
+ if (!response.ok) {
51
+ const errorText = await response.text();
52
+ return Response.json({
53
+ message: `ElevenLabs API error: ${errorText}`
54
+ }, {
55
+ status: response.status
56
+ });
42
57
  }
43
- });
44
- if (!response.ok) {
45
- const errorText = await response.text();
58
+ const data = await response.json();
59
+ // Transform voices to match our schema
60
+ const voices = (data.voices || []).map((voice)=>({
61
+ id: voice.voice_id,
62
+ name: voice.name,
63
+ category: voice.category || 'premade',
64
+ enabled: true,
65
+ labels: voice.labels || {},
66
+ preview_url: voice.preview_url || ''
67
+ }));
46
68
  return Response.json({
47
- message: `ElevenLabs API error: ${errorText}`
48
- }, {
49
- status: response.status
69
+ success: true,
70
+ voices
50
71
  });
72
+ } catch (error) {
73
+ clearTimeout(timeoutId);
74
+ if (error instanceof Error && error.name === 'AbortError') {
75
+ return Response.json({
76
+ message: 'ElevenLabs API request timed out'
77
+ }, {
78
+ status: 504
79
+ });
80
+ }
81
+ throw error;
51
82
  }
52
- const data = await response.json();
53
- // Transform voices to match our schema
54
- const voices = (data.voices || []).map((voice)=>({
55
- id: voice.voice_id,
56
- name: voice.name,
57
- category: voice.category || 'premade',
58
- enabled: true,
59
- labels: voice.labels || {},
60
- preview_url: voice.preview_url || ''
61
- }));
62
- return Response.json({
63
- success: true,
64
- voices
65
- });
66
83
  } catch (error) {
67
84
  req.payload.logger.error(error, 'Error fetching ElevenLabs voices');
68
85
  return Response.json({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/endpoints/fetchVoices.ts"],"sourcesContent":["import type { Endpoint, PayloadRequest } from 'payload'\n\nimport { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../defaults.js'\n\ninterface ElevenLabsVoice {\n category?: string\n labels?: Record<string, string>\n name: string\n preview_url?: string\n voice_id: string\n}\n\nexport const fetchVoices: Endpoint = {\n handler: async (req: PayloadRequest) => {\n try {\n // Check authentication\n if (!req.user) {\n return Response.json({ message: 'Authentication required' }, { status: 401 })\n }\n\n // Fetch AI Settings global to get the encrypted API key\n const aiSettings = await req.payload.findGlobal({\n slug: 'ai-settings',\n context: { unsafe: true },\n })\n\n // Find the ElevenLabs provider block\n const elevenlabsProvider = aiSettings?.providers?.find(\n (provider: any) => provider.blockType === 'elevenlabs' && provider.enabled,\n )\n\n if (!elevenlabsProvider) {\n return Response.json(\n { message: 'ElevenLabs provider not found or not enabled in AI Settings' },\n { status: 400 },\n )\n }\n\n // Get the API key (already decrypted by afterRead hook due to unsafe context)\n const apiKey = elevenlabsProvider.apiKey\n\n if (!apiKey) {\n return Response.json(\n {\n message: 'API key not found. Please configure your ElevenLabs API key in AI Settings.',\n },\n { status: 400 },\n )\n }\n\n // Call ElevenLabs API to fetch voices\n const response = await fetch('https://api.elevenlabs.io/v1/voices', {\n headers: {\n 'xi-api-key': apiKey,\n },\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n return Response.json(\n { message: `ElevenLabs API error: ${errorText}` },\n { status: response.status },\n )\n }\n\n const data = await response.json()\n\n // Transform voices to match our schema\n const voices = (data.voices || []).map((voice: ElevenLabsVoice) => ({\n id: voice.voice_id,\n name: voice.name,\n category: voice.category || 'premade',\n enabled: true,\n labels: voice.labels || {},\n preview_url: voice.preview_url || '',\n }))\n\n return Response.json({\n success: true,\n voices,\n })\n } catch (error) {\n req.payload.logger.error(error, 'Error fetching ElevenLabs voices')\n return Response.json(\n { message: error instanceof Error ? error.message : 'Internal server error' },\n { status: 500 },\n )\n }\n },\n method: 'post',\n path: PLUGIN_API_ENDPOINT_FETCH_VOICES,\n}\n"],"names":["PLUGIN_API_ENDPOINT_FETCH_VOICES","fetchVoices","handler","req","user","Response","json","message","status","aiSettings","payload","findGlobal","slug","context","unsafe","elevenlabsProvider","providers","find","provider","blockType","enabled","apiKey","response","fetch","headers","ok","errorText","text","data","voices","map","voice","id","voice_id","name","category","labels","preview_url","success","error","logger","Error","method","path"],"mappings":"AAEA,SAASA,gCAAgC,QAAQ,iBAAgB;AAUjE,OAAO,MAAMC,cAAwB;IACnCC,SAAS,OAAOC;QACd,IAAI;YACF,uBAAuB;YACvB,IAAI,CAACA,IAAIC,IAAI,EAAE;gBACb,OAAOC,SAASC,IAAI,CAAC;oBAAEC,SAAS;gBAA0B,GAAG;oBAAEC,QAAQ;gBAAI;YAC7E;YAEA,wDAAwD;YACxD,MAAMC,aAAa,MAAMN,IAAIO,OAAO,CAACC,UAAU,CAAC;gBAC9CC,MAAM;gBACNC,SAAS;oBAAEC,QAAQ;gBAAK;YAC1B;YAEA,qCAAqC;YACrC,MAAMC,qBAAqBN,YAAYO,WAAWC,KAChD,CAACC,WAAkBA,SAASC,SAAS,KAAK,gBAAgBD,SAASE,OAAO;YAG5E,IAAI,CAACL,oBAAoB;gBACvB,OAAOV,SAASC,IAAI,CAClB;oBAAEC,SAAS;gBAA8D,GACzE;oBAAEC,QAAQ;gBAAI;YAElB;YAEA,8EAA8E;YAC9E,MAAMa,SAASN,mBAAmBM,MAAM;YAExC,IAAI,CAACA,QAAQ;gBACX,OAAOhB,SAASC,IAAI,CAClB;oBACEC,SAAS;gBACX,GACA;oBAAEC,QAAQ;gBAAI;YAElB;YAEA,sCAAsC;YACtC,MAAMc,WAAW,MAAMC,MAAM,uCAAuC;gBAClEC,SAAS;oBACP,cAAcH;gBAChB;YACF;YAEA,IAAI,CAACC,SAASG,EAAE,EAAE;gBAChB,MAAMC,YAAY,MAAMJ,SAASK,IAAI;gBACrC,OAAOtB,SAASC,IAAI,CAClB;oBAAEC,SAAS,CAAC,sBAAsB,EAAEmB,UAAU,CAAC;gBAAC,GAChD;oBAAElB,QAAQc,SAASd,MAAM;gBAAC;YAE9B;YAEA,MAAMoB,OAAO,MAAMN,SAAShB,IAAI;YAEhC,uCAAuC;YACvC,MAAMuB,SAAS,AAACD,CAAAA,KAAKC,MAAM,IAAI,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC,QAA4B,CAAA;oBAClEC,IAAID,MAAME,QAAQ;oBAClBC,MAAMH,MAAMG,IAAI;oBAChBC,UAAUJ,MAAMI,QAAQ,IAAI;oBAC5Bf,SAAS;oBACTgB,QAAQL,MAAMK,MAAM,IAAI,CAAC;oBACzBC,aAAaN,MAAMM,WAAW,IAAI;gBACpC,CAAA;YAEA,OAAOhC,SAASC,IAAI,CAAC;gBACnBgC,SAAS;gBACTT;YACF;QACF,EAAE,OAAOU,OAAO;YACdpC,IAAIO,OAAO,CAAC8B,MAAM,CAACD,KAAK,CAACA,OAAO;YAChC,OAAOlC,SAASC,IAAI,CAClB;gBAAEC,SAASgC,iBAAiBE,QAAQF,MAAMhC,OAAO,GAAG;YAAwB,GAC5E;gBAAEC,QAAQ;YAAI;QAElB;IACF;IACAkC,QAAQ;IACRC,MAAM3C;AACR,EAAC"}
1
+ {"version":3,"sources":["../../src/endpoints/fetchVoices.ts"],"sourcesContent":["import type { Endpoint, PayloadRequest } from 'payload'\n\nimport { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../defaults.js'\n\ninterface ElevenLabsVoice {\n category?: string\n labels?: Record<string, string>\n name: string\n preview_url?: string\n voice_id: string\n}\n\nexport const fetchVoices: Endpoint = {\n handler: async (req: PayloadRequest) => {\n try {\n // Check authentication\n if (!req.user) {\n return Response.json({ message: 'Authentication required' }, { status: 401 })\n }\n\n // Fetch AI Settings global to get the encrypted API key\n const aiSettings = await req.payload.findGlobal({\n slug: 'ai-settings',\n context: { unsafe: true },\n })\n\n // Find the ElevenLabs provider block\n const elevenlabsProvider = aiSettings?.providers?.find(\n (provider: any) => provider.blockType === 'elevenlabs' && provider.enabled,\n )\n\n if (!elevenlabsProvider) {\n return Response.json(\n { message: 'ElevenLabs provider not found or not enabled in AI Settings' },\n { status: 400 },\n )\n }\n\n // Get the API key (already decrypted by afterRead hook due to unsafe context)\n const apiKey = elevenlabsProvider.apiKey\n\n if (!apiKey) {\n return Response.json(\n {\n message: 'API key not found. Please configure your ElevenLabs API key in AI Settings.',\n },\n { status: 400 },\n )\n }\n\n // Call ElevenLabs API to fetch voices with timeout\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), 15000) // 15s timeout\n\n try {\n const response = await fetch('https://api.elevenlabs.io/v1/voices', {\n headers: {\n 'xi-api-key': apiKey,\n },\n signal: controller.signal,\n })\n\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n const errorText = await response.text()\n return Response.json(\n { message: `ElevenLabs API error: ${errorText}` },\n { status: response.status },\n )\n }\n\n const data = await response.json()\n\n // Transform voices to match our schema\n const voices = (data.voices || []).map((voice: ElevenLabsVoice) => ({\n id: voice.voice_id,\n name: voice.name,\n category: voice.category || 'premade',\n enabled: true,\n labels: voice.labels || {},\n preview_url: voice.preview_url || '',\n }))\n\n return Response.json({\n success: true,\n voices,\n })\n } catch (error: unknown) {\n clearTimeout(timeoutId)\n if (error instanceof Error && error.name === 'AbortError') {\n return Response.json({ message: 'ElevenLabs API request timed out' }, { status: 504 })\n }\n throw error\n }\n } catch (error) {\n req.payload.logger.error(error, 'Error fetching ElevenLabs voices')\n return Response.json(\n { message: error instanceof Error ? error.message : 'Internal server error' },\n { status: 500 },\n )\n }\n },\n method: 'post',\n path: PLUGIN_API_ENDPOINT_FETCH_VOICES,\n}\n"],"names":["PLUGIN_API_ENDPOINT_FETCH_VOICES","fetchVoices","handler","req","user","Response","json","message","status","aiSettings","payload","findGlobal","slug","context","unsafe","elevenlabsProvider","providers","find","provider","blockType","enabled","apiKey","controller","AbortController","timeoutId","setTimeout","abort","response","fetch","headers","signal","clearTimeout","ok","errorText","text","data","voices","map","voice","id","voice_id","name","category","labels","preview_url","success","error","Error","logger","method","path"],"mappings":"AAEA,SAASA,gCAAgC,QAAQ,iBAAgB;AAUjE,OAAO,MAAMC,cAAwB;IACnCC,SAAS,OAAOC;QACd,IAAI;YACF,uBAAuB;YACvB,IAAI,CAACA,IAAIC,IAAI,EAAE;gBACb,OAAOC,SAASC,IAAI,CAAC;oBAAEC,SAAS;gBAA0B,GAAG;oBAAEC,QAAQ;gBAAI;YAC7E;YAEA,wDAAwD;YACxD,MAAMC,aAAa,MAAMN,IAAIO,OAAO,CAACC,UAAU,CAAC;gBAC9CC,MAAM;gBACNC,SAAS;oBAAEC,QAAQ;gBAAK;YAC1B;YAEA,qCAAqC;YACrC,MAAMC,qBAAqBN,YAAYO,WAAWC,KAChD,CAACC,WAAkBA,SAASC,SAAS,KAAK,gBAAgBD,SAASE,OAAO;YAG5E,IAAI,CAACL,oBAAoB;gBACvB,OAAOV,SAASC,IAAI,CAClB;oBAAEC,SAAS;gBAA8D,GACzE;oBAAEC,QAAQ;gBAAI;YAElB;YAEA,8EAA8E;YAC9E,MAAMa,SAASN,mBAAmBM,MAAM;YAExC,IAAI,CAACA,QAAQ;gBACX,OAAOhB,SAASC,IAAI,CAClB;oBACEC,SAAS;gBACX,GACA;oBAAEC,QAAQ;gBAAI;YAElB;YAEA,mDAAmD;YACnD,MAAMc,aAAa,IAAIC;YACvB,MAAMC,YAAYC,WAAW,IAAMH,WAAWI,KAAK,IAAI,OAAO,cAAc;;YAE5E,IAAI;gBACF,MAAMC,WAAW,MAAMC,MAAM,uCAAuC;oBAClEC,SAAS;wBACP,cAAcR;oBAChB;oBACAS,QAAQR,WAAWQ,MAAM;gBAC3B;gBAEAC,aAAaP;gBAEb,IAAI,CAACG,SAASK,EAAE,EAAE;oBAChB,MAAMC,YAAY,MAAMN,SAASO,IAAI;oBACrC,OAAO7B,SAASC,IAAI,CAClB;wBAAEC,SAAS,CAAC,sBAAsB,EAAE0B,UAAU,CAAC;oBAAC,GAChD;wBAAEzB,QAAQmB,SAASnB,MAAM;oBAAC;gBAE9B;gBAEA,MAAM2B,OAAO,MAAMR,SAASrB,IAAI;gBAEhC,uCAAuC;gBACvC,MAAM8B,SAAS,AAACD,CAAAA,KAAKC,MAAM,IAAI,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC,QAA4B,CAAA;wBAClEC,IAAID,MAAME,QAAQ;wBAClBC,MAAMH,MAAMG,IAAI;wBAChBC,UAAUJ,MAAMI,QAAQ,IAAI;wBAC5BtB,SAAS;wBACTuB,QAAQL,MAAMK,MAAM,IAAI,CAAC;wBACzBC,aAAaN,MAAMM,WAAW,IAAI;oBACpC,CAAA;gBAEA,OAAOvC,SAASC,IAAI,CAAC;oBACnBuC,SAAS;oBACTT;gBACF;YACF,EAAE,OAAOU,OAAgB;gBACvBf,aAAaP;gBACb,IAAIsB,iBAAiBC,SAASD,MAAML,IAAI,KAAK,cAAc;oBACzD,OAAOpC,SAASC,IAAI,CAAC;wBAAEC,SAAS;oBAAmC,GAAG;wBAAEC,QAAQ;oBAAI;gBACtF;gBACA,MAAMsC;YACR;QACF,EAAE,OAAOA,OAAO;YACd3C,IAAIO,OAAO,CAACsC,MAAM,CAACF,KAAK,CAACA,OAAO;YAChC,OAAOzC,SAASC,IAAI,CAClB;gBAAEC,SAASuC,iBAAiBC,QAAQD,MAAMvC,OAAO,GAAG;YAAwB,GAC5E;gBAAEC,QAAQ;YAAI;QAElB;IACF;IACAyC,QAAQ;IACRC,MAAMlD;AACR,EAAC"}
@@ -96,10 +96,11 @@ export const endpoints = (pluginConfig)=>({
96
96
  }
97
97
  // Build per-field JSON schema for structured generation when applicable
98
98
  let jsonSchema = allowedEditorSchema;
99
+ let targetField;
99
100
  try {
100
101
  const targetCollection = req.payload.config.collections.find((c)=>c.slug === collectionName);
101
102
  if (targetCollection && fieldName) {
102
- const targetField = getFieldBySchemaPath(targetCollection, schemaPath);
103
+ targetField = getFieldBySchemaPath(targetCollection, schemaPath);
103
104
  const supported = [
104
105
  'array',
105
106
  'text',
@@ -158,12 +159,51 @@ export const endpoints = (pluginConfig)=>({
158
159
  images = imageParts;
159
160
  }
160
161
  }
161
- // Use payload.ai.streamObject directly! 🎉
162
+ let promptToUse = processedPrompt;
163
+ let systemToUse = prompts.system;
164
+ // let messagesToUse: any = undefined
165
+ // Execute beforeGenerate hooks
166
+ if (targetField && targetField.custom?.ai?.beforeGenerate) {
167
+ const beforeHooks = targetField.custom.ai.beforeGenerate;
168
+ for (const hook of beforeHooks){
169
+ const result = await hook({
170
+ doc: contextData,
171
+ field: targetField,
172
+ headers: req.headers,
173
+ instructions,
174
+ payload: req.payload,
175
+ prompt: promptToUse,
176
+ req,
177
+ system: systemToUse
178
+ });
179
+ if (result) {
180
+ if (result.prompt) promptToUse = result.prompt;
181
+ if (result.system) systemToUse = result.system;
182
+ }
183
+ }
184
+ }
162
185
  const streamResult = await req.payload.ai.streamObject({
163
186
  // extractAttachments: modelSettings.extractAttachments as boolean | undefined,
164
187
  images,
165
188
  maxTokens: modelSettings.maxTokens,
189
+ // messages: messagesToUse,
166
190
  model: modelSettings.model,
191
+ onFinish: async ({ object })=>{
192
+ if (targetField && targetField.custom?.ai?.afterGenerate) {
193
+ const afterHooks = targetField.custom.ai.afterGenerate;
194
+ for (const hook of afterHooks){
195
+ await hook({
196
+ doc: contextData,
197
+ field: targetField,
198
+ headers: req.headers,
199
+ instructions,
200
+ payload: req.payload,
201
+ req,
202
+ result: object
203
+ });
204
+ }
205
+ }
206
+ },
167
207
  prompt: processedPrompt,
168
208
  provider: modelSettings.provider,
169
209
  providerOptions: {
@@ -172,7 +212,7 @@ export const endpoints = (pluginConfig)=>({
172
212
  }
173
213
  },
174
214
  schema: jsonSchema,
175
- system: prompts.system,
215
+ system: systemToUse,
176
216
  temperature: modelSettings.temperature
177
217
  });
178
218
  return streamResult;
@@ -253,7 +293,6 @@ export const endpoints = (pluginConfig)=>({
253
293
  collection: collectionSlug
254
294
  }, pluginConfig);
255
295
  const text = await replacePlaceholders(promptTemplate, extendedContext);
256
- const modelId = instructions['model-id'];
257
296
  const uploadCollectionSlug = instructions['relation-to'];
258
297
  // Resolve @field:filename references from the prompt
259
298
  const { images: resolvedImages, processedPrompt } = await resolveImageReferences(text, contextData, req, collectionSlug);
@@ -265,15 +304,52 @@ export const endpoints = (pluginConfig)=>({
265
304
  ];
266
305
  // Process images - convert to ImagePart format using helper
267
306
  const editImages = await fetchImages(req, images);
307
+ let promptToUse = text;
308
+ let targetField;
309
+ try {
310
+ const targetCollection = req.payload.config.collections.find((c)=>c.slug === collectionSlug);
311
+ if (targetCollection && schemaPath) {
312
+ targetField = getFieldBySchemaPath(targetCollection, schemaPath);
313
+ }
314
+ } catch (e) {
315
+ req.payload.logger.error(e, '— AI Plugin: Error finding field for hooks');
316
+ }
317
+ if (targetField && targetField.custom?.ai?.beforeGenerate) {
318
+ const beforeHooks = targetField.custom.ai.beforeGenerate;
319
+ for (const hook of beforeHooks){
320
+ const result = await hook({
321
+ doc: contextData,
322
+ field: targetField,
323
+ headers: req.headers,
324
+ instructions,
325
+ payload: req.payload,
326
+ prompt: promptToUse,
327
+ req
328
+ });
329
+ if (result) {
330
+ if (result.prompt) {
331
+ promptToUse = result.prompt;
332
+ }
333
+ if (result.instructions) {
334
+ instructions = {
335
+ ...instructions,
336
+ ...result.instructions
337
+ };
338
+ }
339
+ }
340
+ }
341
+ }
268
342
  if (pluginConfig.debugging) {
269
343
  req.payload.logger.info({
270
- text
344
+ text: promptToUse
271
345
  }, `— AI Plugin: Executing media generation`);
272
346
  }
273
347
  // Prepare callback URL for async jobs
274
348
  const serverURL = req.payload.config?.serverURL || process.env.SERVER_URL || process.env.NEXT_PUBLIC_SERVER_URL;
275
349
  const callbackUrl = serverURL ? `${serverURL.replace(/\/$/, '')}/api${PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK}?instructionId=${instructionId}` : undefined;
276
350
  // Get model settings
351
+ // Re-evaluate settings name and settings in case instructions changed
352
+ const modelId = instructions['model-id'];
277
353
  const settingsName = modelId === 'image' ? 'image-settings' : modelId === 'video' ? 'video-settings' : modelId === 'tts' ? 'tts-settings' : undefined;
278
354
  if (!settingsName) {
279
355
  throw new Error(`Unsupported model-id: ${modelId}`);
@@ -317,10 +393,24 @@ export const endpoints = (pluginConfig)=>({
317
393
  images: editImages,
318
394
  instructionId,
319
395
  model: modelSettings.model,
320
- prompt: text,
396
+ prompt: promptToUse,
321
397
  provider: modelSettings.provider,
322
398
  ...modelSettings
323
399
  });
400
+ if (targetField && targetField.custom?.ai?.afterGenerate) {
401
+ const afterHooks = targetField.custom.ai.afterGenerate;
402
+ for (const hook of afterHooks){
403
+ await hook({
404
+ doc: contextData,
405
+ field: targetField,
406
+ headers: req.headers,
407
+ instructions,
408
+ payload: req.payload,
409
+ req,
410
+ result
411
+ });
412
+ }
413
+ }
324
414
  // If model returned a file immediately, proceed with upload
325
415
  if (result && 'file' in result) {
326
416
  let assetData;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/endpoints/index.ts"],"sourcesContent":["import type { ImagePart } from 'ai'\nimport type { PayloadRequest } from 'payload'\n\nimport * as process from 'node:process'\n\nimport type { Endpoints, PluginConfig } from '../types.js'\n\nimport { checkAccess } from '../access/checkAccess.js'\nimport { filterEditorSchemaByNodes } from '../ai/utils/filterEditorSchemaByNodes.js'\nimport {\n PLUGIN_AI_JOBS_TABLE,\n PLUGIN_API_ENDPOINT_GENERATE,\n PLUGIN_API_ENDPOINT_GENERATE_UPLOAD,\n PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK,\n PLUGIN_INSTRUCTIONS_TABLE,\n PLUGIN_NAME,\n} from '../defaults.js'\nimport { registerEditorHelper } from '../libraries/handlebars/helpers.js'\nimport { replacePlaceholders } from '../libraries/handlebars/replacePlaceholders.js'\nimport { buildSmartPrompt, isGenericPrompt } from '../utilities/buildSmartPrompt.js'\nimport { extractImageData } from '../utilities/extractImageData.js'\nimport { type FetchableImage, fetchImages } from '../utilities/fetchImages.js'\nimport { fieldToJsonSchema } from '../utilities/fieldToJsonSchema.js'\nimport { getFieldBySchemaPath } from '../utilities/getFieldBySchemaPath.js'\nimport { resolveImageReferences } from '../utilities/resolveImageReferences.js'\nimport { assignPrompt, extendContextWithPromptFields } from './buildPromptUtils.js'\n\nexport const endpoints: (pluginConfig: PluginConfig) => Endpoints = (pluginConfig) =>\n ({\n textarea: {\n // Text/rich-text generation endpoint using payload.ai.streamObject\n handler: async (req: PayloadRequest) => {\n try {\n // Check authentication and authorization first\n await checkAccess(req, pluginConfig)\n\n const data = await req.json?.()\n\n const { allowedEditorNodes = [], locale = 'en', options } = data\n const { action, actionParams, instructionId } = options\n const contextData = data.doc\n\n if (!instructionId) {\n throw new Error(\n `Instruction ID is required for \"${PLUGIN_NAME}\" to work, please check your configuration, or try again`,\n )\n }\n\n // Verify user has access to the specific instruction\n const instructions = await req.payload.findByID({\n id: instructionId,\n collection: PLUGIN_INSTRUCTIONS_TABLE,\n req, // Pass req to ensure access control is applied\n })\n\n const { collections } = req.payload.config\n const collection = collections.find(\n (collection) => collection.slug === PLUGIN_INSTRUCTIONS_TABLE,\n )\n\n if (!collection) {\n throw new Error('Collection not found')\n }\n\n const { custom: { [PLUGIN_NAME]: { editorConfig = {} } = {} } = {} } = collection.admin\n const { schema: editorSchema = {} } = editorConfig\n let { prompt: promptTemplate = '' } = instructions\n\n // Smart fallback: if prompt is generic, build a contextual prompt from field metadata\n if (isGenericPrompt(promptTemplate)) {\n const schemaPath = String(instructions['schema-path'])\n promptTemplate = buildSmartPrompt({\n documentData: contextData,\n payload: req.payload,\n schemaPath,\n })\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n { smartPrompt: promptTemplate },\n `— AI Plugin: Using smart fallback prompt for ${schemaPath}`,\n )\n }\n }\n\n let allowedEditorSchema = editorSchema\n if (allowedEditorNodes.length) {\n allowedEditorSchema = filterEditorSchemaByNodes(editorSchema, allowedEditorNodes)\n // Debug: Log what nodes were received and what definitions remain\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n {\n receivedNodes: allowedEditorNodes,\n remainingDefinitions: Object.keys(allowedEditorSchema.definitions || {}),\n },\n '— AI Plugin: Schema filtering debug',\n )\n }\n }\n\n const schemaPath = String(instructions['schema-path'])\n const parts = (schemaPath || '').split('.') || []\n const collectionName = parts[0]\n const fieldName = parts.length > 1 ? parts[parts.length - 1] : ''\n\n registerEditorHelper(req.payload, schemaPath)\n\n const { defaultLocale, locales = [] } = req.payload.config.localization || {}\n const localeData = locales.find((l) => {\n return l.code === locale\n })\n\n let localeInfo = locale\n if (\n localeData &&\n defaultLocale &&\n localeData.label &&\n typeof localeData.label === 'object' &&\n defaultLocale in localeData.label\n ) {\n localeInfo = localeData.label[defaultLocale]\n }\n\n const prompts = await assignPrompt(action, {\n type: String(instructions['field-type']),\n actionParams,\n collection: collectionName,\n context: contextData,\n field: fieldName || '',\n layout: instructions.layout,\n locale: localeInfo,\n pluginConfig,\n systemPrompt: instructions.system,\n template: String(promptTemplate),\n })\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n { prompts },\n `— AI Plugin: Executing text prompt on ${schemaPath}`,\n )\n }\n\n // Build per-field JSON schema for structured generation when applicable\n let jsonSchema = allowedEditorSchema\n try {\n const targetCollection = req.payload.config.collections.find(\n (c) => c.slug === collectionName,\n )\n if (targetCollection && fieldName) {\n const targetField = getFieldBySchemaPath(targetCollection, schemaPath)\n const supported = [\n 'array',\n 'text',\n 'textarea',\n 'select',\n 'number',\n 'date',\n 'code',\n 'email',\n 'json',\n ]\n const t = String(targetField?.type || '')\n if (targetField && supported.includes(t)) {\n // For array fields, use count from array-settings if available\n if (t === 'array') {\n const arraySettings = (instructions['array-settings'] || {}) as Record<\n string,\n unknown\n >\n const count = (arraySettings.count as number) || 3\n // Override the field's maxRows with the requested count\n const modifiedField = {\n ...targetField,\n maxRows: count,\n minRows: count,\n } as typeof targetField\n jsonSchema = fieldToJsonSchema(modifiedField, { nameOverride: fieldName })\n } else {\n jsonSchema = fieldToJsonSchema(targetField, { nameOverride: fieldName })\n }\n }\n }\n } catch (e) {\n req.payload.logger.error(e, '— AI Plugin: Error building field JSON schema')\n }\n\n // Get model settings from instruction\n const settingsName =\n instructions['model-id'] === 'richtext'\n ? 'richtext-settings'\n : instructions['model-id'] === 'text'\n ? 'text-settings'\n : instructions['model-id'] === 'array'\n ? 'array-settings'\n : undefined\n\n if (!settingsName) {\n throw new Error(`Unsupported model-id: ${instructions['model-id']}`)\n }\n\n const modelSettings = instructions[settingsName] || {}\n\n // Resolve @field:filename references from the prompt\n const { images: resolvedImages, processedPrompt } = await resolveImageReferences(\n prompts.prompt,\n contextData,\n req,\n collectionName,\n )\n\n // Extract hardcoded URLs from the processed prompt\n const hardcodedImages = extractImageData(processedPrompt)\n\n // Combine images\n const allImages = [...hardcodedImages, ...resolvedImages] as FetchableImage[]\n\n let images: ImagePart[] | undefined\n\n if (allImages.length > 0) {\n const imageParts = await fetchImages(req, allImages)\n\n if (imageParts.length > 0) {\n images = imageParts\n }\n }\n\n // Use payload.ai.streamObject directly! 🎉\n const streamResult = await req.payload.ai.streamObject({\n // extractAttachments: modelSettings.extractAttachments as boolean | undefined,\n images,\n maxTokens: modelSettings.maxTokens as number | undefined,\n model: modelSettings.model as string,\n prompt: processedPrompt,\n provider: modelSettings.provider as string,\n providerOptions: {\n openai: {\n strictJsonSchema: true,\n },\n },\n schema: jsonSchema,\n system: prompts.system,\n temperature: modelSettings.temperature as number | undefined,\n })\n\n return streamResult\n } catch (error) {\n req.payload.logger.error(error, 'Error generating content: ')\n const message =\n error && typeof error === 'object' && 'message' in error\n ? (error as Error).message\n : String(error)\n return new Response(JSON.stringify({ error: message }), {\n headers: { 'Content-Type': 'application/json' },\n status:\n message.includes('Authentication required') ||\n message.includes('Insufficient permissions')\n ? 401\n : 500,\n })\n }\n },\n method: 'post',\n path: PLUGIN_API_ENDPOINT_GENERATE,\n },\n upload: {\n // Image/video generation endpoint using payload.ai.generateMedia\n handler: async (req: PayloadRequest) => {\n try {\n // Check authentication and authorization first\n await checkAccess(req, pluginConfig)\n\n const data = await req.json?.()\n\n const { collectionSlug, documentId, options } = data\n const { instructionId } = options\n let docData = {}\n\n if (documentId) {\n try {\n docData = await req.payload.findByID({\n id: documentId,\n collection: collectionSlug,\n draft: true,\n req, // Pass req to ensure access control is applied\n })\n } catch (e) {\n req.payload.logger.error(\n e,\n '— AI Plugin: Error fetching document, you should try again after enabling drafts for this collection',\n )\n }\n }\n\n const contextData = {\n ...data.doc,\n ...docData,\n }\n\n let instructions: Record<string, unknown> = { images: [], 'model-id': '', prompt: '' }\n\n if (instructionId) {\n // Verify user has access to the specific instruction\n instructions = await req.payload.findByID({\n id: instructionId,\n collection: PLUGIN_INSTRUCTIONS_TABLE,\n req, // Pass req to ensure access control is applied\n })\n }\n\n let { prompt: promptTemplate = '' } = instructions\n const { images: sampleImages = [] } = instructions\n const schemaPath = String(instructions['schema-path'])\n registerEditorHelper(req.payload, schemaPath)\n\n // Smart fallback: if prompt is generic, build a contextual prompt from field metadata\n if (isGenericPrompt(promptTemplate as string)) {\n promptTemplate = buildSmartPrompt({\n documentData: contextData,\n payload: req.payload,\n schemaPath,\n })\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n { smartPrompt: promptTemplate },\n `— AI Plugin: Using smart fallback prompt for ${schemaPath}`,\n )\n }\n }\n\n const extendedContext = extendContextWithPromptFields(\n contextData,\n { type: String(instructions['field-type']), collection: collectionSlug },\n pluginConfig,\n )\n const text = await replacePlaceholders(promptTemplate as string, extendedContext)\n const modelId = instructions['model-id']\n const uploadCollectionSlug = instructions['relation-to']\n\n // Resolve @field:filename references from the prompt\n const { images: resolvedImages, processedPrompt } = await resolveImageReferences(\n text,\n contextData,\n req,\n collectionSlug,\n )\n\n // Extract hardcoded URLs from the processed prompt and merge with resolved images and sample images\n const images = [\n ...extractImageData(processedPrompt),\n ...resolvedImages,\n ...(sampleImages as unknown[]),\n ] as FetchableImage[]\n\n // Process images - convert to ImagePart format using helper\n const editImages: ImagePart[] = await fetchImages(req, images)\n\n if (pluginConfig.debugging) {\n req.payload.logger.info({ text }, `— AI Plugin: Executing media generation`)\n }\n\n // Prepare callback URL for async jobs\n const serverURL =\n req.payload.config?.serverURL ||\n process.env.SERVER_URL ||\n process.env.NEXT_PUBLIC_SERVER_URL\n\n const callbackUrl = serverURL\n ? `${serverURL.replace(/\\/$/, '')}/api${PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK}?instructionId=${instructionId}`\n : undefined\n\n // Get model settings\n const settingsName =\n modelId === 'image'\n ? 'image-settings'\n : modelId === 'video'\n ? 'video-settings'\n : modelId === 'tts'\n ? 'tts-settings'\n : undefined\n if (!settingsName) {\n throw new Error(`Unsupported model-id: ${modelId}`)\n }\n\n // Get model settings from instruction\n const instructionSettings = (instructions[settingsName] || {}) as Record<string, unknown>\n\n // Fallback to AISettings global defaults if instruction-level settings are missing\n let globalDefaults: Record<string, unknown> = {}\n if (!instructionSettings.provider || !instructionSettings.model) {\n try {\n const aiSettings = await req.payload.findGlobal({\n slug: 'ai-settings',\n context: { unsafe: true }, // Get decrypted values for internal use\n })\n\n // Map modelId to the corresponding default settings key\n const defaultsKey =\n modelId === 'image'\n ? 'image'\n : modelId === 'video'\n ? 'video'\n : modelId === 'tts'\n ? 'tts'\n : undefined\n\n if (defaultsKey && aiSettings?.defaults?.[defaultsKey]) {\n globalDefaults = aiSettings.defaults[defaultsKey] as Record<string, unknown>\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n { globalDefaults },\n `— AI Plugin: Using AISettings defaults for ${modelId}`,\n )\n }\n }\n } catch (e) {\n req.payload.logger.error(e, '— AI Plugin: Error fetching AISettings defaults')\n }\n }\n\n // Merge: instruction settings take priority over global defaults\n // Filter out null/undefined values so they don't overwrite valid defaults\n const filteredInstructionSettings = Object.fromEntries(\n Object.entries(instructionSettings).filter(([_, v]) => v != null),\n )\n const modelSettings = {\n ...globalDefaults,\n ...filteredInstructionSettings,\n }\n\n // Use payload.ai.generateMedia directly! 🎉\n const result = await req.payload.ai.generateMedia({\n callbackUrl,\n images: editImages,\n instructionId,\n model: modelSettings.model as string,\n prompt: text,\n provider: modelSettings.provider as string,\n ...modelSettings,\n })\n\n // If model returned a file immediately, proceed with upload\n if (result && 'file' in result) {\n let assetData: { alt?: string; id: number | string }\n if (typeof pluginConfig.mediaUpload === 'function') {\n assetData = await pluginConfig.mediaUpload(result, {\n collection: uploadCollectionSlug as string,\n request: req,\n })\n } else {\n assetData = await req.payload.create({\n collection: uploadCollectionSlug as string,\n data: { alt: text },\n file: result.file,\n req, // Pass req to ensure access control is applied\n })\n }\n\n if (!assetData.id) {\n req.payload.logger.error(\n 'Error uploading generated media, is your media upload function correct?',\n )\n throw new Error('Error uploading generated media!')\n }\n\n return new Response(\n JSON.stringify({\n result: {\n id: assetData.id,\n alt: assetData.alt,\n },\n }),\n )\n }\n\n // Otherwise, assume async job launch\n if (result && ('jobId' in result || 'taskId' in result)) {\n const externalTaskId = result.jobId || result.taskId\n const status = result.status || 'queued'\n const progress = result.progress ?? 0\n\n // Create AI Job doc and return only its id\n const createdJob = await req.payload.create({\n collection: PLUGIN_AI_JOBS_TABLE,\n data: {\n instructionId,\n progress,\n status,\n task_id: externalTaskId,\n },\n overrideAccess: true,\n req,\n })\n\n return new Response(JSON.stringify({ job: { id: createdJob.id } }), {\n headers: { 'Content-Type': 'application/json' },\n })\n }\n\n throw new Error('Unexpected model response.')\n } catch (error) {\n req.payload.logger.error(\n // @ts-expect-error\n error?.type || (error as Error).message,\n 'Error generating upload: ',\n )\n const message =\n error && typeof error === 'object' && 'message' in error\n ? (error as Error).message\n : String(error)\n return new Response(JSON.stringify({ error: message }), {\n headers: { 'Content-Type': 'application/json' },\n status:\n message.includes('Authentication required') ||\n message.includes('Insufficient permissions')\n ? 401\n : 500,\n })\n }\n },\n method: 'post',\n path: PLUGIN_API_ENDPOINT_GENERATE_UPLOAD,\n },\n videogenWebhook: {\n handler: async (req: PayloadRequest) => {\n console.log('videogenWebhook --> ', req)\n try {\n const urlAll = new URL(req.url || '')\n const qpSecret = urlAll.searchParams.get('secret') || ''\n const headerSecret = req.headers.get('x-webhook-secret') || ''\n const falSecret = process.env.FAL_WEBHOOK_SECRET\n const legacySecret = process.env.VIDEOGEN_WEBHOOK_SECRET\n const provided = qpSecret || headerSecret\n // TODO: fal is failing because of auth but webhook seem to work\n if (!provided || (falSecret ? provided !== falSecret : provided !== legacySecret)) {\n return new Response('Unauthorized', { status: 401 })\n }\n\n const instructionId = urlAll.searchParams.get('instructionId')\n if (!instructionId) {\n throw new Error('instructionId missing')\n }\n\n const body = await req.json?.()\n // Normalize fal webhook payload\n const status: string | undefined =\n (body && (body.status || body.data?.status || body.response?.status)) || undefined\n const progress: number | undefined =\n (body && (body.progress ?? body.data?.progress ?? body.response?.progress)) ?? undefined\n const requestId: string | undefined =\n (body &&\n (body.taskId ||\n body.request_id ||\n body.gateway_request_id ||\n body.request?.request_id)) ||\n undefined\n const error = body?.error || body?.data?.error || body?.response?.error\n\n // Update AI Job row by task_id (and instructionId)\n const jobSearch = await req.payload.find({\n collection: PLUGIN_AI_JOBS_TABLE,\n depth: 0,\n limit: 1,\n where: {\n and: [\n { task_id: { equals: requestId } },\n { instructionId: { equals: instructionId } },\n ],\n },\n })\n\n const jobDoc = jobSearch.docs?.[0]\n if (jobDoc) {\n await req.payload.update({\n id: jobDoc.id,\n collection: PLUGIN_AI_JOBS_TABLE,\n data: {\n progress,\n status,\n task_id: requestId,\n },\n overrideAccess: true,\n req,\n })\n }\n\n console.log('fal webhook body: ', body)\n\n const videoUrl =\n body?.outputs?.[0]?.url ||\n body?.data?.outputs?.[0]?.url ||\n body?.video?.url ||\n body?.data?.video?.url ||\n body?.response?.video?.url ||\n body?.videos?.[0]?.url ||\n body?.data?.videos?.[0]?.url\n\n if (status === 'completed' && videoUrl) {\n // Fetch the related instruction to get upload collection\n const instructions = await req.payload.findByID({\n id: instructionId,\n collection: PLUGIN_INSTRUCTIONS_TABLE,\n req,\n })\n\n const uploadCollectionSlug = instructions['relation-to']\n\n const videoResp = await fetch(videoUrl)\n if (!videoResp.ok) {\n throw new Error(`Failed to fetch output: ${videoResp.status}`)\n }\n const buffer = Buffer.from(await videoResp.arrayBuffer())\n\n const created = await req.payload.create({\n collection: uploadCollectionSlug,\n data: { alt: 'video generation' },\n file: {\n name: 'video_generation.mp4',\n data: buffer,\n mimetype: 'video/mp4',\n size: buffer.byteLength,\n },\n overrideAccess: true,\n req,\n })\n\n // Persist the result on the AI Job record\n if (jobDoc) {\n await req.payload.update({\n id: jobDoc.id,\n collection: PLUGIN_AI_JOBS_TABLE,\n data: {\n progress: 100,\n result_id: created?.id,\n status: 'completed',\n },\n overrideAccess: true,\n req,\n })\n }\n }\n\n if (status === 'failed' && error) {\n req.payload.logger.error(error, 'Video generation failed: ')\n }\n\n return new Response(JSON.stringify({ ok: true }), {\n headers: { 'Content-Type': 'application/json' },\n })\n } catch (error) {\n req.payload.logger.error(error, 'Error in videogen webhook: ')\n const message =\n error && typeof error === 'object' && 'message' in error\n ? (error as any).message\n : String(error)\n return new Response(JSON.stringify({ error: message }), {\n headers: { 'Content-Type': 'application/json' },\n status: 500,\n })\n }\n },\n method: 'post',\n path: PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK,\n },\n }) satisfies Endpoints\n"],"names":["process","checkAccess","filterEditorSchemaByNodes","PLUGIN_AI_JOBS_TABLE","PLUGIN_API_ENDPOINT_GENERATE","PLUGIN_API_ENDPOINT_GENERATE_UPLOAD","PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK","PLUGIN_INSTRUCTIONS_TABLE","PLUGIN_NAME","registerEditorHelper","replacePlaceholders","buildSmartPrompt","isGenericPrompt","extractImageData","fetchImages","fieldToJsonSchema","getFieldBySchemaPath","resolveImageReferences","assignPrompt","extendContextWithPromptFields","endpoints","pluginConfig","textarea","handler","req","data","json","allowedEditorNodes","locale","options","action","actionParams","instructionId","contextData","doc","Error","instructions","payload","findByID","id","collection","collections","config","find","slug","custom","editorConfig","admin","schema","editorSchema","prompt","promptTemplate","schemaPath","String","documentData","debugging","logger","info","smartPrompt","allowedEditorSchema","length","receivedNodes","remainingDefinitions","Object","keys","definitions","parts","split","collectionName","fieldName","defaultLocale","locales","localization","localeData","l","code","localeInfo","label","prompts","type","context","field","layout","systemPrompt","system","template","jsonSchema","targetCollection","c","targetField","supported","t","includes","arraySettings","count","modifiedField","maxRows","minRows","nameOverride","e","error","settingsName","undefined","modelSettings","images","resolvedImages","processedPrompt","hardcodedImages","allImages","imageParts","streamResult","ai","streamObject","maxTokens","model","provider","providerOptions","openai","strictJsonSchema","temperature","message","Response","JSON","stringify","headers","status","method","path","upload","collectionSlug","documentId","docData","draft","sampleImages","extendedContext","text","modelId","uploadCollectionSlug","editImages","serverURL","env","SERVER_URL","NEXT_PUBLIC_SERVER_URL","callbackUrl","replace","instructionSettings","globalDefaults","aiSettings","findGlobal","unsafe","defaultsKey","defaults","filteredInstructionSettings","fromEntries","entries","filter","_","v","result","generateMedia","assetData","mediaUpload","request","create","alt","file","externalTaskId","jobId","taskId","progress","createdJob","task_id","overrideAccess","job","videogenWebhook","console","log","urlAll","URL","url","qpSecret","searchParams","get","headerSecret","falSecret","FAL_WEBHOOK_SECRET","legacySecret","VIDEOGEN_WEBHOOK_SECRET","provided","body","response","requestId","request_id","gateway_request_id","jobSearch","depth","limit","where","and","equals","jobDoc","docs","update","videoUrl","outputs","video","videos","videoResp","fetch","ok","buffer","Buffer","from","arrayBuffer","created","name","mimetype","size","byteLength","result_id"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/endpoints/index.ts"],"sourcesContent":["import type { ImagePart } from 'ai'\nimport type { Field, PayloadRequest } from 'payload'\n\nimport * as process from 'node:process'\n\nimport type { Endpoints, PluginConfig } from '../types.js'\n\nimport { checkAccess } from '../access/checkAccess.js'\nimport { filterEditorSchemaByNodes } from '../ai/utils/filterEditorSchemaByNodes.js'\nimport {\n PLUGIN_AI_JOBS_TABLE,\n PLUGIN_API_ENDPOINT_GENERATE,\n PLUGIN_API_ENDPOINT_GENERATE_UPLOAD,\n PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK,\n PLUGIN_INSTRUCTIONS_TABLE,\n PLUGIN_NAME,\n} from '../defaults.js'\nimport { registerEditorHelper } from '../libraries/handlebars/helpers.js'\nimport { replacePlaceholders } from '../libraries/handlebars/replacePlaceholders.js'\nimport { buildSmartPrompt, isGenericPrompt } from '../utilities/buildSmartPrompt.js'\nimport { extractImageData } from '../utilities/extractImageData.js'\nimport { type FetchableImage, fetchImages } from '../utilities/fetchImages.js'\nimport { fieldToJsonSchema } from '../utilities/fieldToJsonSchema.js'\nimport { getFieldBySchemaPath } from '../utilities/getFieldBySchemaPath.js'\nimport { resolveImageReferences } from '../utilities/resolveImageReferences.js'\nimport { assignPrompt, extendContextWithPromptFields } from './buildPromptUtils.js'\n\nexport const endpoints: (pluginConfig: PluginConfig) => Endpoints = (pluginConfig) =>\n ({\n textarea: {\n // Text/rich-text generation endpoint using payload.ai.streamObject\n handler: async (req: PayloadRequest) => {\n try {\n // Check authentication and authorization first\n await checkAccess(req, pluginConfig)\n\n const data = await req.json?.()\n\n const { allowedEditorNodes = [], locale = 'en', options } = data\n const { action, actionParams, instructionId } = options\n const contextData = data.doc\n\n if (!instructionId) {\n throw new Error(\n `Instruction ID is required for \"${PLUGIN_NAME}\" to work, please check your configuration, or try again`,\n )\n }\n\n // Verify user has access to the specific instruction\n const instructions = await req.payload.findByID({\n id: instructionId,\n collection: PLUGIN_INSTRUCTIONS_TABLE,\n req, // Pass req to ensure access control is applied\n })\n\n const { collections } = req.payload.config\n const collection = collections.find(\n (collection) => collection.slug === PLUGIN_INSTRUCTIONS_TABLE,\n )\n\n if (!collection) {\n throw new Error('Collection not found')\n }\n\n const { custom: { [PLUGIN_NAME]: { editorConfig = {} } = {} } = {} } = collection.admin\n const { schema: editorSchema = {} } = editorConfig\n let { prompt: promptTemplate = '' } = instructions\n\n // Smart fallback: if prompt is generic, build a contextual prompt from field metadata\n if (isGenericPrompt(promptTemplate)) {\n const schemaPath = String(instructions['schema-path'])\n promptTemplate = buildSmartPrompt({\n documentData: contextData,\n payload: req.payload,\n schemaPath,\n })\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n { smartPrompt: promptTemplate },\n `— AI Plugin: Using smart fallback prompt for ${schemaPath}`,\n )\n }\n }\n\n let allowedEditorSchema = editorSchema\n if (allowedEditorNodes.length) {\n allowedEditorSchema = filterEditorSchemaByNodes(editorSchema, allowedEditorNodes)\n // Debug: Log what nodes were received and what definitions remain\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n {\n receivedNodes: allowedEditorNodes,\n remainingDefinitions: Object.keys(allowedEditorSchema.definitions || {}),\n },\n '— AI Plugin: Schema filtering debug',\n )\n }\n }\n\n const schemaPath = String(instructions['schema-path'])\n const parts = (schemaPath || '').split('.') || []\n const collectionName = parts[0]\n const fieldName = parts.length > 1 ? parts[parts.length - 1] : ''\n\n registerEditorHelper(req.payload, schemaPath)\n\n const { defaultLocale, locales = [] } = req.payload.config.localization || {}\n const localeData = locales.find((l) => {\n return l.code === locale\n })\n\n let localeInfo = locale\n if (\n localeData &&\n defaultLocale &&\n localeData.label &&\n typeof localeData.label === 'object' &&\n defaultLocale in localeData.label\n ) {\n localeInfo = localeData.label[defaultLocale]\n }\n\n const prompts = await assignPrompt(action, {\n type: String(instructions['field-type']),\n actionParams,\n collection: collectionName,\n context: contextData,\n field: fieldName || '',\n layout: instructions.layout,\n locale: localeInfo,\n pluginConfig,\n systemPrompt: instructions.system,\n template: String(promptTemplate),\n })\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n { prompts },\n `— AI Plugin: Executing text prompt on ${schemaPath}`,\n )\n }\n\n // Build per-field JSON schema for structured generation when applicable\n let jsonSchema = allowedEditorSchema\n let targetField: Field | null | undefined\n\n try {\n const targetCollection = req.payload.config.collections.find(\n (c) => c.slug === collectionName,\n )\n if (targetCollection && fieldName) {\n targetField = getFieldBySchemaPath(targetCollection, schemaPath)\n const supported = [\n 'array',\n 'text',\n 'textarea',\n 'select',\n 'number',\n 'date',\n 'code',\n 'email',\n 'json',\n ]\n const t = String(targetField?.type || '')\n if (targetField && supported.includes(t)) {\n // For array fields, use count from array-settings if available\n if (t === 'array') {\n const arraySettings = (instructions['array-settings'] || {}) as Record<\n string,\n unknown\n >\n const count = (arraySettings.count as number) || 3\n // Override the field's maxRows with the requested count\n const modifiedField = {\n ...targetField,\n maxRows: count,\n minRows: count,\n } as typeof targetField\n jsonSchema = fieldToJsonSchema(modifiedField, { nameOverride: fieldName })\n } else {\n jsonSchema = fieldToJsonSchema(targetField, { nameOverride: fieldName })\n }\n }\n }\n } catch (e) {\n req.payload.logger.error(e, '— AI Plugin: Error building field JSON schema')\n }\n\n // Get model settings from instruction\n const settingsName =\n instructions['model-id'] === 'richtext'\n ? 'richtext-settings'\n : instructions['model-id'] === 'text'\n ? 'text-settings'\n : instructions['model-id'] === 'array'\n ? 'array-settings'\n : undefined\n\n if (!settingsName) {\n throw new Error(`Unsupported model-id: ${instructions['model-id']}`)\n }\n\n const modelSettings = instructions[settingsName] || {}\n\n // Resolve @field:filename references from the prompt\n const { images: resolvedImages, processedPrompt } = await resolveImageReferences(\n prompts.prompt,\n contextData,\n req,\n collectionName,\n )\n\n // Extract hardcoded URLs from the processed prompt\n const hardcodedImages = extractImageData(processedPrompt)\n\n // Combine images\n const allImages = [...hardcodedImages, ...resolvedImages] as FetchableImage[]\n\n let images: ImagePart[] | undefined\n\n if (allImages.length > 0) {\n const imageParts = await fetchImages(req, allImages)\n\n if (imageParts.length > 0) {\n images = imageParts\n }\n }\n\n let promptToUse = processedPrompt\n let systemToUse = prompts.system\n // let messagesToUse: any = undefined\n\n // Execute beforeGenerate hooks\n if (targetField && (targetField as any).custom?.ai?.beforeGenerate) {\n const beforeHooks = (targetField as any).custom.ai.beforeGenerate as Array<\n (args: any) => Promise<any>\n >\n for (const hook of beforeHooks) {\n const result = await hook({\n doc: contextData,\n field: targetField,\n headers: req.headers,\n instructions,\n payload: req.payload,\n prompt: promptToUse,\n req,\n system: systemToUse,\n })\n\n if (result) {\n if (result.prompt) promptToUse = result.prompt\n if (result.system) systemToUse = result.system\n }\n }\n }\n\n const streamResult = await req.payload.ai.streamObject({\n // extractAttachments: modelSettings.extractAttachments as boolean | undefined,\n images,\n maxTokens: modelSettings.maxTokens as number | undefined,\n // messages: messagesToUse,\n model: modelSettings.model as string,\n onFinish: async ({ object }) => {\n if (targetField && (targetField as any).custom?.ai?.afterGenerate) {\n const afterHooks = (targetField as any).custom.ai.afterGenerate as Array<\n (args: any) => Promise<any>\n >\n for (const hook of afterHooks) {\n await hook({\n doc: contextData,\n field: targetField,\n headers: req.headers,\n instructions,\n payload: req.payload,\n req,\n result: object,\n })\n }\n }\n },\n prompt: processedPrompt,\n provider: modelSettings.provider as string,\n providerOptions: {\n openai: {\n strictJsonSchema: true,\n },\n },\n schema: jsonSchema,\n system: systemToUse,\n temperature: modelSettings.temperature as number | undefined,\n })\n\n return streamResult\n } catch (error) {\n req.payload.logger.error(error, 'Error generating content: ')\n const message =\n error && typeof error === 'object' && 'message' in error\n ? (error as Error).message\n : String(error)\n return new Response(JSON.stringify({ error: message }), {\n headers: { 'Content-Type': 'application/json' },\n status:\n message.includes('Authentication required') ||\n message.includes('Insufficient permissions')\n ? 401\n : 500,\n })\n }\n },\n method: 'post',\n path: PLUGIN_API_ENDPOINT_GENERATE,\n },\n upload: {\n // Image/video generation endpoint using payload.ai.generateMedia\n handler: async (req: PayloadRequest) => {\n try {\n // Check authentication and authorization first\n await checkAccess(req, pluginConfig)\n\n const data = await req.json?.()\n\n const { collectionSlug, documentId, options } = data\n const { instructionId } = options\n let docData = {}\n\n if (documentId) {\n try {\n docData = await req.payload.findByID({\n id: documentId,\n collection: collectionSlug as string,\n draft: true,\n req, // Pass req to ensure access control is applied\n })\n } catch (e) {\n req.payload.logger.error(\n e,\n '— AI Plugin: Error fetching document, you should try again after enabling drafts for this collection',\n )\n }\n }\n\n const contextData = {\n ...data.doc,\n ...docData,\n }\n\n let instructions: Record<string, unknown> = { images: [], 'model-id': '', prompt: '' }\n\n if (instructionId) {\n // Verify user has access to the specific instruction\n instructions = await req.payload.findByID({\n id: instructionId,\n collection: PLUGIN_INSTRUCTIONS_TABLE,\n req, // Pass req to ensure access control is applied\n })\n }\n\n let { prompt: promptTemplate = '' } = instructions\n const { images: sampleImages = [] } = instructions\n const schemaPath = String(instructions['schema-path'])\n registerEditorHelper(req.payload, schemaPath)\n\n // Smart fallback: if prompt is generic, build a contextual prompt from field metadata\n if (isGenericPrompt(promptTemplate as string)) {\n promptTemplate = buildSmartPrompt({\n documentData: contextData,\n payload: req.payload,\n schemaPath,\n })\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n { smartPrompt: promptTemplate },\n `— AI Plugin: Using smart fallback prompt for ${schemaPath}`,\n )\n }\n }\n\n const extendedContext = extendContextWithPromptFields(\n contextData,\n { type: String(instructions['field-type']), collection: collectionSlug },\n pluginConfig,\n )\n const text = await replacePlaceholders(promptTemplate as string, extendedContext)\n const uploadCollectionSlug = instructions['relation-to']\n\n // Resolve @field:filename references from the prompt\n const { images: resolvedImages, processedPrompt } = await resolveImageReferences(\n text,\n contextData,\n req,\n collectionSlug,\n )\n\n // Extract hardcoded URLs from the processed prompt and merge with resolved images and sample images\n const images = [\n ...extractImageData(processedPrompt),\n ...resolvedImages,\n ...(sampleImages as unknown[]),\n ] as FetchableImage[]\n\n // Process images - convert to ImagePart format using helper\n const editImages: ImagePart[] = await fetchImages(req, images)\n\n let promptToUse = text\n let targetField: Field | null | undefined\n\n try {\n const targetCollection = req.payload.config.collections.find(\n (c) => c.slug === collectionSlug,\n )\n if (targetCollection && schemaPath) {\n targetField = getFieldBySchemaPath(targetCollection, schemaPath)\n }\n } catch (e) {\n req.payload.logger.error(e, '— AI Plugin: Error finding field for hooks')\n }\n\n if (targetField && (targetField as any).custom?.ai?.beforeGenerate) {\n const beforeHooks = (targetField as any).custom.ai.beforeGenerate as Array<\n (args: any) => Promise<any>\n >\n for (const hook of beforeHooks) {\n const result = await hook({\n doc: contextData,\n field: targetField,\n headers: req.headers,\n instructions,\n payload: req.payload,\n prompt: promptToUse,\n req,\n })\n\n if (result) {\n if (result.prompt) {\n promptToUse = result.prompt\n }\n if (result.instructions) {\n instructions = {\n ...instructions,\n ...result.instructions,\n }\n }\n }\n }\n }\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n { text: promptToUse },\n `— AI Plugin: Executing media generation`,\n )\n }\n\n // Prepare callback URL for async jobs\n const serverURL =\n req.payload.config?.serverURL ||\n process.env.SERVER_URL ||\n process.env.NEXT_PUBLIC_SERVER_URL\n\n const callbackUrl = serverURL\n ? `${serverURL.replace(/\\/$/, '')}/api${PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK}?instructionId=${instructionId}`\n : undefined\n\n // Get model settings\n // Re-evaluate settings name and settings in case instructions changed\n const modelId = instructions['model-id']\n const settingsName =\n modelId === 'image'\n ? 'image-settings'\n : modelId === 'video'\n ? 'video-settings'\n : modelId === 'tts'\n ? 'tts-settings'\n : undefined\n if (!settingsName) {\n throw new Error(`Unsupported model-id: ${modelId}`)\n }\n\n // Get model settings from instruction\n const instructionSettings = (instructions[settingsName] || {}) as Record<string, unknown>\n\n // Fallback to AISettings global defaults if instruction-level settings are missing\n let globalDefaults: Record<string, unknown> = {}\n if (!instructionSettings.provider || !instructionSettings.model) {\n try {\n const aiSettings = await req.payload.findGlobal({\n slug: 'ai-settings',\n context: { unsafe: true }, // Get decrypted values for internal use\n })\n\n // Map modelId to the corresponding default settings key\n const defaultsKey =\n modelId === 'image'\n ? 'image'\n : modelId === 'video'\n ? 'video'\n : modelId === 'tts'\n ? 'tts'\n : undefined\n\n if (defaultsKey && aiSettings?.defaults?.[defaultsKey]) {\n globalDefaults = aiSettings.defaults[defaultsKey] as Record<string, unknown>\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n { globalDefaults },\n `— AI Plugin: Using AISettings defaults for ${modelId}`,\n )\n }\n }\n } catch (e) {\n req.payload.logger.error(e, '— AI Plugin: Error fetching AISettings defaults')\n }\n }\n\n // Merge: instruction settings take priority over global defaults\n // Filter out null/undefined values so they don't overwrite valid defaults\n const filteredInstructionSettings = Object.fromEntries(\n Object.entries(instructionSettings).filter(([_, v]) => v != null),\n )\n const modelSettings = {\n ...globalDefaults,\n ...filteredInstructionSettings,\n }\n\n // Use payload.ai.generateMedia directly! 🎉\n const result = await req.payload.ai.generateMedia({\n callbackUrl,\n images: editImages,\n instructionId,\n model: modelSettings.model as string,\n prompt: promptToUse,\n provider: modelSettings.provider as string,\n ...modelSettings,\n })\n\n if (targetField && (targetField as any).custom?.ai?.afterGenerate) {\n const afterHooks = (targetField as any).custom.ai.afterGenerate as Array<\n (args: any) => Promise<any>\n >\n for (const hook of afterHooks) {\n await hook({\n doc: contextData,\n field: targetField,\n headers: req.headers,\n instructions,\n payload: req.payload,\n req,\n result,\n })\n }\n }\n\n // If model returned a file immediately, proceed with upload\n if (result && 'file' in result) {\n let assetData: { alt?: string; id: number | string }\n if (typeof pluginConfig.mediaUpload === 'function') {\n assetData = await pluginConfig.mediaUpload(result, {\n collection: uploadCollectionSlug as string,\n request: req,\n })\n } else {\n assetData = await req.payload.create({\n collection: uploadCollectionSlug as string,\n data: { alt: text },\n file: result.file,\n req, // Pass req to ensure access control is applied\n })\n }\n\n if (!assetData.id) {\n req.payload.logger.error(\n 'Error uploading generated media, is your media upload function correct?',\n )\n throw new Error('Error uploading generated media!')\n }\n\n return new Response(\n JSON.stringify({\n result: {\n id: assetData.id,\n alt: assetData.alt,\n },\n }),\n )\n }\n\n // Otherwise, assume async job launch\n if (result && ('jobId' in result || 'taskId' in result)) {\n const externalTaskId = result.jobId || result.taskId\n const status = result.status || 'queued'\n const progress = result.progress ?? 0\n\n // Create AI Job doc and return only its id\n const createdJob = await req.payload.create({\n collection: PLUGIN_AI_JOBS_TABLE,\n data: {\n instructionId,\n progress,\n status,\n task_id: externalTaskId,\n },\n overrideAccess: true,\n req,\n })\n\n return new Response(JSON.stringify({ job: { id: createdJob.id } }), {\n headers: { 'Content-Type': 'application/json' },\n })\n }\n\n throw new Error('Unexpected model response.')\n } catch (error) {\n req.payload.logger.error(\n // @ts-expect-error\n error?.type || (error as Error).message,\n 'Error generating upload: ',\n )\n const message =\n error && typeof error === 'object' && 'message' in error\n ? (error as Error).message\n : String(error)\n return new Response(JSON.stringify({ error: message }), {\n headers: { 'Content-Type': 'application/json' },\n status:\n message.includes('Authentication required') ||\n message.includes('Insufficient permissions')\n ? 401\n : 500,\n })\n }\n },\n method: 'post',\n path: PLUGIN_API_ENDPOINT_GENERATE_UPLOAD,\n },\n videogenWebhook: {\n handler: async (req: PayloadRequest) => {\n console.log('videogenWebhook --> ', req)\n try {\n const urlAll = new URL(req.url || '')\n const qpSecret = urlAll.searchParams.get('secret') || ''\n const headerSecret = req.headers.get('x-webhook-secret') || ''\n const falSecret = process.env.FAL_WEBHOOK_SECRET\n const legacySecret = process.env.VIDEOGEN_WEBHOOK_SECRET\n const provided = qpSecret || headerSecret\n // TODO: fal is failing because of auth but webhook seem to work\n if (!provided || (falSecret ? provided !== falSecret : provided !== legacySecret)) {\n return new Response('Unauthorized', { status: 401 })\n }\n\n const instructionId = urlAll.searchParams.get('instructionId')\n if (!instructionId) {\n throw new Error('instructionId missing')\n }\n\n const body = await req.json?.()\n // Normalize fal webhook payload\n const status: string | undefined =\n (body && (body.status || body.data?.status || body.response?.status)) || undefined\n const progress: number | undefined =\n (body && (body.progress ?? body.data?.progress ?? body.response?.progress)) ?? undefined\n const requestId: string | undefined =\n (body &&\n (body.taskId ||\n body.request_id ||\n body.gateway_request_id ||\n body.request?.request_id)) ||\n undefined\n const error = body?.error || body?.data?.error || body?.response?.error\n\n // Update AI Job row by task_id (and instructionId)\n const jobSearch = await req.payload.find({\n collection: PLUGIN_AI_JOBS_TABLE,\n depth: 0,\n limit: 1,\n where: {\n and: [\n { task_id: { equals: requestId } },\n { instructionId: { equals: instructionId } },\n ],\n },\n })\n\n const jobDoc = jobSearch.docs?.[0]\n if (jobDoc) {\n await req.payload.update({\n id: jobDoc.id,\n collection: PLUGIN_AI_JOBS_TABLE,\n data: {\n progress,\n status,\n task_id: requestId,\n },\n overrideAccess: true,\n req,\n })\n }\n\n console.log('fal webhook body: ', body)\n\n const videoUrl =\n body?.outputs?.[0]?.url ||\n body?.data?.outputs?.[0]?.url ||\n body?.video?.url ||\n body?.data?.video?.url ||\n body?.response?.video?.url ||\n body?.videos?.[0]?.url ||\n body?.data?.videos?.[0]?.url\n\n if (status === 'completed' && videoUrl) {\n // Fetch the related instruction to get upload collection\n const instructions = await req.payload.findByID({\n id: instructionId,\n collection: PLUGIN_INSTRUCTIONS_TABLE,\n req,\n })\n\n const uploadCollectionSlug = instructions['relation-to']\n\n const videoResp = await fetch(videoUrl)\n if (!videoResp.ok) {\n throw new Error(`Failed to fetch output: ${videoResp.status}`)\n }\n const buffer = Buffer.from(await videoResp.arrayBuffer())\n\n const created = await req.payload.create({\n collection: uploadCollectionSlug,\n data: { alt: 'video generation' },\n file: {\n name: 'video_generation.mp4',\n data: buffer,\n mimetype: 'video/mp4',\n size: buffer.byteLength,\n },\n overrideAccess: true,\n req,\n })\n\n // Persist the result on the AI Job record\n if (jobDoc) {\n await req.payload.update({\n id: jobDoc.id,\n collection: PLUGIN_AI_JOBS_TABLE,\n data: {\n progress: 100,\n result_id: created?.id,\n status: 'completed',\n },\n overrideAccess: true,\n req,\n })\n }\n }\n\n if (status === 'failed' && error) {\n req.payload.logger.error(error, 'Video generation failed: ')\n }\n\n return new Response(JSON.stringify({ ok: true }), {\n headers: { 'Content-Type': 'application/json' },\n })\n } catch (error) {\n req.payload.logger.error(error, 'Error in videogen webhook: ')\n const message =\n error && typeof error === 'object' && 'message' in error\n ? (error as any).message\n : String(error)\n return new Response(JSON.stringify({ error: message }), {\n headers: { 'Content-Type': 'application/json' },\n status: 500,\n })\n }\n },\n method: 'post',\n path: PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK,\n },\n }) satisfies Endpoints\n"],"names":["process","checkAccess","filterEditorSchemaByNodes","PLUGIN_AI_JOBS_TABLE","PLUGIN_API_ENDPOINT_GENERATE","PLUGIN_API_ENDPOINT_GENERATE_UPLOAD","PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK","PLUGIN_INSTRUCTIONS_TABLE","PLUGIN_NAME","registerEditorHelper","replacePlaceholders","buildSmartPrompt","isGenericPrompt","extractImageData","fetchImages","fieldToJsonSchema","getFieldBySchemaPath","resolveImageReferences","assignPrompt","extendContextWithPromptFields","endpoints","pluginConfig","textarea","handler","req","data","json","allowedEditorNodes","locale","options","action","actionParams","instructionId","contextData","doc","Error","instructions","payload","findByID","id","collection","collections","config","find","slug","custom","editorConfig","admin","schema","editorSchema","prompt","promptTemplate","schemaPath","String","documentData","debugging","logger","info","smartPrompt","allowedEditorSchema","length","receivedNodes","remainingDefinitions","Object","keys","definitions","parts","split","collectionName","fieldName","defaultLocale","locales","localization","localeData","l","code","localeInfo","label","prompts","type","context","field","layout","systemPrompt","system","template","jsonSchema","targetField","targetCollection","c","supported","t","includes","arraySettings","count","modifiedField","maxRows","minRows","nameOverride","e","error","settingsName","undefined","modelSettings","images","resolvedImages","processedPrompt","hardcodedImages","allImages","imageParts","promptToUse","systemToUse","ai","beforeGenerate","beforeHooks","hook","result","headers","streamResult","streamObject","maxTokens","model","onFinish","object","afterGenerate","afterHooks","provider","providerOptions","openai","strictJsonSchema","temperature","message","Response","JSON","stringify","status","method","path","upload","collectionSlug","documentId","docData","draft","sampleImages","extendedContext","text","uploadCollectionSlug","editImages","serverURL","env","SERVER_URL","NEXT_PUBLIC_SERVER_URL","callbackUrl","replace","modelId","instructionSettings","globalDefaults","aiSettings","findGlobal","unsafe","defaultsKey","defaults","filteredInstructionSettings","fromEntries","entries","filter","_","v","generateMedia","assetData","mediaUpload","request","create","alt","file","externalTaskId","jobId","taskId","progress","createdJob","task_id","overrideAccess","job","videogenWebhook","console","log","urlAll","URL","url","qpSecret","searchParams","get","headerSecret","falSecret","FAL_WEBHOOK_SECRET","legacySecret","VIDEOGEN_WEBHOOK_SECRET","provided","body","response","requestId","request_id","gateway_request_id","jobSearch","depth","limit","where","and","equals","jobDoc","docs","update","videoUrl","outputs","video","videos","videoResp","fetch","ok","buffer","Buffer","from","arrayBuffer","created","name","mimetype","size","byteLength","result_id"],"mappings":"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"}
@@ -30,6 +30,7 @@ interface PayloadGenerationBaseArgs {
30
30
  interface PayloadGenerateObjectArgs extends PayloadGenerationBaseArgs {
31
31
  images?: ImagePart[]
32
32
  mode?: 'auto' | 'json' | 'tool'
33
+ onFinish?: (event: { object?: unknown }) => Promise<void> | void
33
34
  schema?: Record<string, unknown> | z.ZodTypeAny
34
35
  }
35
36
 
@@ -119,7 +120,11 @@ declare module 'payload' {
119
120
  * @param type - Model type ('text', 'image', or 'tts')
120
121
  * @returns Promise resolving to the model instance
121
122
  */
122
- getModel: (provider: string, modelId: string, type?: 'image' | 'text' | 'tts') => Promise<unknown>
123
+ getModel: (
124
+ provider: string,
125
+ modelId: string,
126
+ type?: 'image' | 'text' | 'tts',
127
+ ) => Promise<unknown>
123
128
 
124
129
  /**
125
130
  * Get the provider registry
@@ -132,9 +137,7 @@ declare module 'payload' {
132
137
  * @param args - Generation arguments including provider, model, prompt, and schema
133
138
  * @returns Response stream
134
139
  */
135
- streamObject: <T = unknown>(
136
- args: PayloadGenerateObjectArgs,
137
- ) => Promise<Response>
140
+ streamObject: <T = unknown>(args: PayloadGenerateObjectArgs) => Promise<Response>
138
141
 
139
142
  /**
140
143
  * Stream text output
package/dist/plugin.js CHANGED
@@ -156,7 +156,7 @@ const payloadAiPlugin = (pluginConfig)=>(incomingConfig)=>{
156
156
  payload.logger.info(sponsorMessage);
157
157
  }, 3000);
158
158
  }
159
- // Inject AI capabilities with enhanced abstraction layer
159
+ // Inject AI capabilities with the abstraction layer
160
160
  ;
161
161
  payload.ai = {
162
162
  // Core generation methods
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["import type { CollectionConfig, Config, GlobalConfig } from 'payload'\n\nimport { deepMerge } from 'payload/shared'\n\nimport type { PluginConfig } from './types.js'\n\nimport { lexicalJsonSchema } from './ai/schemas/lexicalJsonSchema.js'\nimport { aiJobsCollection } from './collections/AIJobs.js'\nimport { aiSettingsGlobal } from './collections/AISettings.js'\nimport { instructionsCollection } from './collections/Instructions.js'\nimport { PLUGIN_NAME } from './defaults.js'\nimport { fetchFields } from './endpoints/fetchFields.js'\nimport { fetchVoices } from './endpoints/fetchVoices.js'\nimport { endpoints } from './endpoints/index.js'\nimport { translations } from './translations/index.js'\nimport { isPluginActivated } from './utilities/isPluginActivated.js'\nimport { updateFieldsConfig } from './utilities/updateFieldsConfig.js'\n\nconst defaultPluginConfig: PluginConfig = {\n access: {\n generate: ({ req }) => !!req.user,\n settings: ({ req }) => !!req.user,\n },\n disableSponsorMessage: false,\n}\n\nconst sponsorMessage = `\n╔═══════════════════════════════════════════════════════════════╗\n║ THANK YOU FOR USING THE PAYLOAD AI PLUGIN! ║\n║ ║\n║ If this plugin makes your life easier, please ║\n║ consider supporting its development and maintenance: ║\n║ ║\n║ • Buy me a coffee: https://buymeacoffee.com/ashbuilds ║\n║ • Sponsor on GitHub: https://github.com/sponsors/ashbuilds ║\n║ ║\n║ Your support fuels continued improvements, ║\n║ new features, and more caffeinated coding sessions! ☕ ║\n║ ║\n║ Got feedback or need help? Submit an issue here: ║\n║ • https://github.com/ashbuilds/payload-ai/issues/new ║\n║ ║\n║ Thank you again, and happy building! ║\n╚═══════════════════════════════════════════════════════════════╝\n`\n\nconst securityMessage = `\n╔═══════════════════════════════════════════════════════════════╗\n║ SECURITY NOTICE ║\n║ ║\n║ The AI Plugin now requires authentication by default. ║\n║ All AI features are restricted to authenticated users. ║\n║ ║\n║ To customize access control, configure the 'access' option ║\n║ in your plugin settings. See documentation for details. ║\n║ ║\n║ If you need different access patterns, please configure ║\n║ them explicitly in your plugin configuration. ║\n╚═══════════════════════════════════════════════════════════════╝\n`\n\nconst payloadAiPlugin =\n (pluginConfig: PluginConfig) =>\n (incomingConfig: Config): Config => {\n pluginConfig = {\n ...defaultPluginConfig,\n ...pluginConfig,\n access: {\n ...defaultPluginConfig.access,\n ...pluginConfig.access,\n },\n }\n\n const isActivated = isPluginActivated(pluginConfig)\n let updatedConfig: Config = { ...incomingConfig }\n\n if (isActivated) {\n const Instructions = instructionsCollection(pluginConfig)\n const AIJobs = aiJobsCollection()\n // Inject editor schema to config, so that it can be accessed when /textarea endpoint will hit\n const lexicalSchema = lexicalJsonSchema(pluginConfig.editorConfig?.nodes)\n\n Instructions.admin = {\n ...Instructions.admin,\n }\n\n if (pluginConfig.debugging) {\n Instructions.admin.hidden = false\n }\n\n Instructions.admin.custom = {\n ...(Instructions.admin.custom || {}),\n [PLUGIN_NAME]: {\n editorConfig: {\n // Used in admin client for useObject hook\n schema: lexicalSchema,\n },\n },\n }\n\n const collections = [...(incomingConfig.collections ?? []), Instructions, AIJobs]\n const globals = [...(incomingConfig.globals ?? []), aiSettingsGlobal]\n const { globals: globalsSlugs } = pluginConfig\n\n const { components: { providers = [] } = {} } = incomingConfig.admin || {}\n const updatedProviders = [\n ...(providers ?? []),\n {\n path: '@ai-stack/payloadcms/client#InstructionsProvider',\n },\n ]\n\n incomingConfig.admin = {\n ...(incomingConfig.admin || {}),\n components: {\n ...(incomingConfig.admin?.components ?? {}),\n providers: updatedProviders,\n },\n }\n\n const pluginEndpoints = endpoints(pluginConfig)\n updatedConfig = {\n ...incomingConfig,\n collections: collections.map((collection) => {\n // Always inject fields, but they will be dynamically enabled/disabled by the InstructionsProvider\n const { updatedCollectionConfig } = updateFieldsConfig(collection)\n return updatedCollectionConfig as CollectionConfig\n }),\n endpoints: [\n ...(incomingConfig.endpoints ?? []),\n pluginEndpoints.textarea,\n pluginEndpoints.upload,\n ...(pluginEndpoints.videogenWebhook ? [pluginEndpoints.videogenWebhook] : []),\n fetchFields(pluginConfig),\n fetchVoices,\n ],\n globals: globals.map((global) => {\n if (globalsSlugs && globalsSlugs[global.slug]) {\n const { updatedCollectionConfig } = updateFieldsConfig(global)\n return updatedCollectionConfig as GlobalConfig\n }\n\n return global\n }),\n i18n: {\n ...(incomingConfig.i18n || {}),\n translations: {\n ...deepMerge(translations, incomingConfig.i18n?.translations ?? {}),\n },\n },\n }\n }\n\n updatedConfig.onInit = async (payload) => {\n if (incomingConfig.onInit) {\n await incomingConfig.onInit(payload)\n }\n\n if (!isActivated) {\n payload.logger.warn(`— AI Plugin: Not activated. Please verify your environment keys.`)\n return\n }\n\n if (!pluginConfig.disableSponsorMessage) {\n setTimeout(() => {\n payload.logger.info(securityMessage)\n }, 1000)\n setTimeout(() => {\n payload.logger.info(sponsorMessage)\n }, 3000)\n }\n\n // Inject AI capabilities with enhanced abstraction layer\n ;(payload as any).ai = {\n // Core generation methods\n generateObject: async (args: any) => {\n const { generateObject } = await import('./ai/core/index.js')\n return generateObject({ ...args, payload })\n },\n\n generateText: async (args: any) => {\n const { generateText } = await import('./ai/core/index.js')\n return generateText({ ...args, payload })\n },\n\n generateMedia: async (args: any) => {\n const { generateMedia } = await import('./ai/core/index.js')\n return generateMedia({ ...args, payload })\n },\n\n // Streaming variants\n streamObject: async (args: any) => {\n const { streamObject } = await import('./ai/core/index.js')\n const result = await streamObject({ ...args, payload })\n return result.toTextStreamResponse()\n },\n\n streamText: async (args: any) => {\n const { streamText } = await import('./ai/core/index.js')\n return streamText({ ...args, payload })\n },\n\n // Helper utilities\n getModel: async (provider: string, modelId: string, type?: 'image' | 'text' | 'tts') => {\n const { getImageModel, getLanguageModel, getTTSModel } = await import(\n './ai/providers/registry.js'\n )\n if (type === 'image') {\n return getImageModel(payload, provider, modelId)\n }\n if (type === 'tts') {\n return getTTSModel(payload, provider, modelId)\n }\n return getLanguageModel(payload, provider, modelId)\n },\n\n getRegistry: async () => {\n const { getProviderRegistry } = await import('./ai/providers/registry.js')\n return getProviderRegistry(payload)\n },\n\n // Legacy method for backward compatibility\n /** @deprecated Use generateObject or generateText instead */\n generate: async (args: any) => {\n const { generate } = await import('./ai/index.js')\n return generate({ ...args, payload })\n },\n }\n }\n\n return updatedConfig\n }\n\nexport { payloadAiPlugin }\n"],"names":["deepMerge","lexicalJsonSchema","aiJobsCollection","aiSettingsGlobal","instructionsCollection","PLUGIN_NAME","fetchFields","fetchVoices","endpoints","translations","isPluginActivated","updateFieldsConfig","defaultPluginConfig","access","generate","req","user","settings","disableSponsorMessage","sponsorMessage","securityMessage","payloadAiPlugin","pluginConfig","incomingConfig","isActivated","updatedConfig","Instructions","AIJobs","lexicalSchema","editorConfig","nodes","admin","debugging","hidden","custom","schema","collections","globals","globalsSlugs","components","providers","updatedProviders","path","pluginEndpoints","map","collection","updatedCollectionConfig","textarea","upload","videogenWebhook","global","slug","i18n","onInit","payload","logger","warn","setTimeout","info","ai","generateObject","args","generateText","generateMedia","streamObject","result","toTextStreamResponse","streamText","getModel","provider","modelId","type","getImageModel","getLanguageModel","getTTSModel","getRegistry","getProviderRegistry"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["import type { CollectionConfig, Config, GlobalConfig } from 'payload'\n\nimport { deepMerge } from 'payload/shared'\n\nimport type { PluginConfig } from './types.js'\n\nimport { lexicalJsonSchema } from './ai/schemas/lexicalJsonSchema.js'\nimport { aiJobsCollection } from './collections/AIJobs.js'\nimport { aiSettingsGlobal } from './collections/AISettings.js'\nimport { instructionsCollection } from './collections/Instructions.js'\nimport { PLUGIN_NAME } from './defaults.js'\nimport { fetchFields } from './endpoints/fetchFields.js'\nimport { fetchVoices } from './endpoints/fetchVoices.js'\nimport { endpoints } from './endpoints/index.js'\nimport { translations } from './translations/index.js'\nimport { isPluginActivated } from './utilities/isPluginActivated.js'\nimport { updateFieldsConfig } from './utilities/updateFieldsConfig.js'\n\nconst defaultPluginConfig: PluginConfig = {\n access: {\n generate: ({ req }) => !!req.user,\n settings: ({ req }) => !!req.user,\n },\n disableSponsorMessage: false,\n}\n\nconst sponsorMessage = `\n╔═══════════════════════════════════════════════════════════════╗\n║ THANK YOU FOR USING THE PAYLOAD AI PLUGIN! ║\n║ ║\n║ If this plugin makes your life easier, please ║\n║ consider supporting its development and maintenance: ║\n║ ║\n║ • Buy me a coffee: https://buymeacoffee.com/ashbuilds ║\n║ • Sponsor on GitHub: https://github.com/sponsors/ashbuilds ║\n║ ║\n║ Your support fuels continued improvements, ║\n║ new features, and more caffeinated coding sessions! ☕ ║\n║ ║\n║ Got feedback or need help? Submit an issue here: ║\n║ • https://github.com/ashbuilds/payload-ai/issues/new ║\n║ ║\n║ Thank you again, and happy building! ║\n╚═══════════════════════════════════════════════════════════════╝\n`\n\nconst securityMessage = `\n╔═══════════════════════════════════════════════════════════════╗\n║ SECURITY NOTICE ║\n║ ║\n║ The AI Plugin now requires authentication by default. ║\n║ All AI features are restricted to authenticated users. ║\n║ ║\n║ To customize access control, configure the 'access' option ║\n║ in your plugin settings. See documentation for details. ║\n║ ║\n║ If you need different access patterns, please configure ║\n║ them explicitly in your plugin configuration. ║\n╚═══════════════════════════════════════════════════════════════╝\n`\n\nconst payloadAiPlugin =\n (pluginConfig: PluginConfig) =>\n (incomingConfig: Config): Config => {\n pluginConfig = {\n ...defaultPluginConfig,\n ...pluginConfig,\n access: {\n ...defaultPluginConfig.access,\n ...pluginConfig.access,\n },\n }\n\n const isActivated = isPluginActivated(pluginConfig)\n let updatedConfig: Config = { ...incomingConfig }\n\n if (isActivated) {\n const Instructions = instructionsCollection(pluginConfig)\n const AIJobs = aiJobsCollection()\n // Inject editor schema to config, so that it can be accessed when /textarea endpoint will hit\n const lexicalSchema = lexicalJsonSchema(pluginConfig.editorConfig?.nodes)\n\n Instructions.admin = {\n ...Instructions.admin,\n }\n\n if (pluginConfig.debugging) {\n Instructions.admin.hidden = false\n }\n\n Instructions.admin.custom = {\n ...(Instructions.admin.custom || {}),\n [PLUGIN_NAME]: {\n editorConfig: {\n // Used in admin client for useObject hook\n schema: lexicalSchema,\n },\n },\n }\n\n const collections = [...(incomingConfig.collections ?? []), Instructions, AIJobs]\n const globals = [...(incomingConfig.globals ?? []), aiSettingsGlobal]\n const { globals: globalsSlugs } = pluginConfig\n\n const { components: { providers = [] } = {} } = incomingConfig.admin || {}\n const updatedProviders = [\n ...(providers ?? []),\n {\n path: '@ai-stack/payloadcms/client#InstructionsProvider',\n },\n ]\n\n incomingConfig.admin = {\n ...(incomingConfig.admin || {}),\n components: {\n ...(incomingConfig.admin?.components ?? {}),\n providers: updatedProviders,\n },\n }\n\n const pluginEndpoints = endpoints(pluginConfig)\n updatedConfig = {\n ...incomingConfig,\n collections: collections.map((collection) => {\n // Always inject fields, but they will be dynamically enabled/disabled by the InstructionsProvider\n const { updatedCollectionConfig } = updateFieldsConfig(collection)\n return updatedCollectionConfig as CollectionConfig\n }),\n endpoints: [\n ...(incomingConfig.endpoints ?? []),\n pluginEndpoints.textarea,\n pluginEndpoints.upload,\n ...(pluginEndpoints.videogenWebhook ? [pluginEndpoints.videogenWebhook] : []),\n fetchFields(pluginConfig),\n fetchVoices,\n ],\n globals: globals.map((global) => {\n if (globalsSlugs && globalsSlugs[global.slug]) {\n const { updatedCollectionConfig } = updateFieldsConfig(global)\n return updatedCollectionConfig as GlobalConfig\n }\n\n return global\n }),\n i18n: {\n ...(incomingConfig.i18n || {}),\n translations: {\n ...deepMerge(translations, incomingConfig.i18n?.translations ?? {}),\n },\n },\n }\n }\n\n updatedConfig.onInit = async (payload) => {\n if (incomingConfig.onInit) {\n await incomingConfig.onInit(payload)\n }\n\n if (!isActivated) {\n payload.logger.warn(`— AI Plugin: Not activated. Please verify your environment keys.`)\n return\n }\n\n if (!pluginConfig.disableSponsorMessage) {\n setTimeout(() => {\n payload.logger.info(securityMessage)\n }, 1000)\n setTimeout(() => {\n payload.logger.info(sponsorMessage)\n }, 3000)\n }\n\n // Inject AI capabilities with the abstraction layer\n ;(payload as any).ai = {\n // Core generation methods\n generateObject: async (args: any) => {\n const { generateObject } = await import('./ai/core/index.js')\n return generateObject({ ...args, payload })\n },\n\n generateText: async (args: any) => {\n const { generateText } = await import('./ai/core/index.js')\n return generateText({ ...args, payload })\n },\n\n generateMedia: async (args: any) => {\n const { generateMedia } = await import('./ai/core/index.js')\n return generateMedia({ ...args, payload })\n },\n\n // Streaming variants\n streamObject: async (args: any) => {\n const { streamObject } = await import('./ai/core/index.js')\n const result = await streamObject({ ...args, payload })\n return result.toTextStreamResponse()\n },\n\n streamText: async (args: any) => {\n const { streamText } = await import('./ai/core/index.js')\n return streamText({ ...args, payload })\n },\n\n // Helper utilities\n getModel: async (provider: string, modelId: string, type?: 'image' | 'text' | 'tts') => {\n const { getImageModel, getLanguageModel, getTTSModel } = await import(\n './ai/providers/registry.js'\n )\n if (type === 'image') {\n return getImageModel(payload, provider, modelId)\n }\n if (type === 'tts') {\n return getTTSModel(payload, provider, modelId)\n }\n return getLanguageModel(payload, provider, modelId)\n },\n\n getRegistry: async () => {\n const { getProviderRegistry } = await import('./ai/providers/registry.js')\n return getProviderRegistry(payload)\n },\n\n // Legacy method for backward compatibility\n /** @deprecated Use generateObject or generateText instead */\n generate: async (args: any) => {\n const { generate } = await import('./ai/index.js')\n return generate({ ...args, payload })\n },\n }\n }\n\n return updatedConfig\n }\n\nexport { payloadAiPlugin }\n"],"names":["deepMerge","lexicalJsonSchema","aiJobsCollection","aiSettingsGlobal","instructionsCollection","PLUGIN_NAME","fetchFields","fetchVoices","endpoints","translations","isPluginActivated","updateFieldsConfig","defaultPluginConfig","access","generate","req","user","settings","disableSponsorMessage","sponsorMessage","securityMessage","payloadAiPlugin","pluginConfig","incomingConfig","isActivated","updatedConfig","Instructions","AIJobs","lexicalSchema","editorConfig","nodes","admin","debugging","hidden","custom","schema","collections","globals","globalsSlugs","components","providers","updatedProviders","path","pluginEndpoints","map","collection","updatedCollectionConfig","textarea","upload","videogenWebhook","global","slug","i18n","onInit","payload","logger","warn","setTimeout","info","ai","generateObject","args","generateText","generateMedia","streamObject","result","toTextStreamResponse","streamText","getModel","provider","modelId","type","getImageModel","getLanguageModel","getTTSModel","getRegistry","getProviderRegistry"],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -2,6 +2,7 @@ import type { JSONSchema } from 'openai/lib/jsonschema';
2
2
  import type { ImageGenerateParams } from 'openai/resources/images';
3
3
  import type { CollectionConfig, CollectionSlug, DataFromCollectionSlug, Endpoint, Field, File, GlobalConfig, GroupField, PayloadRequest, TypedCollection } from 'payload';
4
4
  import type { CSSProperties, MouseEventHandler } from 'react';
5
+ import type { GenerateObjectResult, ModelMessage } from 'ai';
5
6
  import type { MediaResult } from './ai/core/index.js';
6
7
  import type { PLUGIN_INSTRUCTIONS_TABLE } from "./defaults.js";
7
8
  export interface PluginConfigAccess {
@@ -165,3 +166,35 @@ export type PromptFieldGetterContext = {
165
166
  export type PromptField = {
166
167
  getter?: (data: object, ctx: PromptFieldGetterContext) => Promise<string> | string;
167
168
  } & SerializedPromptField;
169
+ export interface BeforeGenerateArgs<T = any> {
170
+ doc: T;
171
+ field: Field;
172
+ headers: Record<string, string>;
173
+ instructions: Record<string, unknown>;
174
+ messages?: ModelMessage[];
175
+ payload: PayloadRequest['payload'];
176
+ prompt: string;
177
+ req: PayloadRequest;
178
+ system: string;
179
+ }
180
+ export type BeforeGenerateResult = {
181
+ messages?: ModelMessage[];
182
+ prompt?: string;
183
+ system?: string;
184
+ } | void;
185
+ export type BeforeGenerateHook<T = any> = (args: BeforeGenerateArgs<T>) => BeforeGenerateResult | Promise<BeforeGenerateResult>;
186
+ export interface AfterGenerateArgs<T = any> {
187
+ doc: T;
188
+ field: Field;
189
+ headers: Record<string, string>;
190
+ instructions: Record<string, unknown>;
191
+ payload: PayloadRequest['payload'];
192
+ req: PayloadRequest;
193
+ result: GenerateObjectResult<any> | MediaResult | string;
194
+ }
195
+ export type AfterGenerateHook<T = any> = (args: AfterGenerateArgs<T>) => Promise<void> | void;
196
+ export interface AIFieldConfig {
197
+ afterGenerate?: AfterGenerateHook[];
198
+ beforeGenerate?: BeforeGenerateHook[];
199
+ [key: string]: unknown;
200
+ }
package/dist/types.js CHANGED
@@ -1,3 +1,4 @@
1
+ // Add to PluginConfig or a new interface if accessed via custom.ai
1
2
  export { };
2
3
 
3
4
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { JSONSchema } from 'openai/lib/jsonschema'\nimport type { ImageGenerateParams } from 'openai/resources/images'\nimport type {\n CollectionConfig,\n CollectionSlug,\n DataFromCollectionSlug,\n Endpoint,\n Field,\n File,\n GlobalConfig,\n GroupField,\n PayloadRequest,\n TypedCollection,\n} from 'payload'\nimport type { CSSProperties, MouseEventHandler } from 'react'\n\nimport type { MediaResult } from './ai/core/index.js'\nimport type {PLUGIN_INSTRUCTIONS_TABLE} from \"./defaults.js\";\n\nexport interface PluginConfigAccess {\n /**\n * Control access to AI generation features (generate text, images, audio)\n * @default () => !!req.user (requires authentication)\n */\n generate?: ({ req }: { req: PayloadRequest }) => boolean | Promise<boolean>\n /**\n * Control access to AI settings/configuration\n * @default () => !!req.user (requires authentication)\n */\n settings?: ({ req }: { req: PayloadRequest }) => boolean | Promise<boolean>\n}\n\nexport interface PluginOptions {\n /**\n * Provide local tags to filter language options from the Translate Menu\n * Check for the available local tags,\n * visit: https://www.npmjs.com/package/locale-codes\n * Example: [\"en-US\", \"zh-SG\", \"zh-CN\", \"en\"]\n */\n enabledLanguages?: string[]\n}\n\nexport type PluginConfigMediaUploadFunction = (\n result: MediaResult,\n {\n collection,\n request,\n }: {\n collection: CollectionSlug\n request: PayloadRequest\n },\n) => Promise<DataFromCollectionSlug<CollectionSlug>>\n\nexport interface PluginConfig {\n /**\n * Access control configuration for AI features\n * By default, all AI features require authentication\n */\n access?: PluginConfigAccess\n debugging?: boolean\n disableSponsorMessage?: boolean\n editorConfig?: { nodes: JSONSchema[] }\n fields?: Field[]\n generatePromptOnInit?: boolean\n generationModels?: ((defaultModels: GenerationModel[]) => GenerationModel[]) | GenerationModel[]\n globals?: {\n [key: GlobalConfig['slug']]: boolean\n }\n interfaceName?: string\n mediaUpload?: PluginConfigMediaUploadFunction\n options?: PluginOptions\n // Override the instructions collection config\n overrideInstructions?: Partial<CollectionConfig>\n promptFields?: PromptField[]\n /**\n * Custom action prompts for AI text generation\n * If not provided, uses default prompts\n * You can access default prompts by importing { defaultPrompts } from '@ai-stack/payloadcms'\n */\n prompts?: ActionPrompt[]\n /**\n * Custom seed prompt function for generating field-specific prompts\n * If not provided, fields will have empty prompts by default\n */\n seedPrompts?: SeedPromptFunction\n uploadCollectionSlug?: CollectionSlug\n}\n\nexport interface GenerationModel {\n fields: string[]\n generateText?: (prompt: string, system: string) => Promise<string>\n handler?: (prompt: string, options: any) => File | Promise<any> | Response\n id: string\n name: string\n output: 'audio' | 'file' | 'image' | 'json' | 'text' | 'video'\n settings?: GroupField\n supportsPromptOptimization?: boolean\n}\n\nexport interface GenerationConfig {\n models: GenerationModel[]\n provider: string\n}\n\nexport type GenerateTextarea<T = any> = (args: {\n collectionSlug: CollectionSlug\n doc: T\n documentId?: number | string\n locale?: string\n options?: any\n}) => Promise<string> | string\n\nexport interface Endpoints {\n fetchVoices?: Omit<Endpoint, 'root'>\n textarea: Omit<Endpoint, 'root'>\n upload: Omit<Endpoint, 'root'>\n videogenWebhook?: Omit<Endpoint, 'root'>\n}\n\nexport type ActionMenuItems =\n | 'Compose'\n | 'Expand'\n | 'Proofread'\n | 'Rephrase'\n | 'Settings'\n | 'Simplify'\n | 'Summarize'\n | 'Tone'\n | 'Translate'\n\nexport type ActionPromptOptions = {\n layout?: string\n locale?: string\n prompt?: string\n systemPrompt?: string\n}\n\nexport type ActionPrompt = {\n layout?: (options?: ActionPromptOptions) => string\n name: ActionMenuItems\n system: (options: ActionPromptOptions) => string\n}\n\nexport type SeedPromptOptions = {\n fieldLabel: string\n fieldSchemaPaths: Record<string, any>\n fieldType: string\n path: string\n}\n\nexport type SeedPromptData = Omit<\n TypedCollection[typeof PLUGIN_INSTRUCTIONS_TABLE],\n 'createdAt' | 'id' | 'updatedAt'\n>\n\nexport type SeedPromptResult =\n | {\n data?: SeedPromptData\n }\n | {\n data?: SeedPromptData\n prompt: string\n system: string\n }\n | false\n | undefined\n | void\n\nexport type SeedPromptFunction = (\n options: SeedPromptOptions,\n) => Promise<SeedPromptResult> | SeedPromptResult\n\nexport type ActionMenuEvents =\n | 'onCompose'\n | 'onExpand'\n | 'onProofread'\n | 'onRephrase'\n | 'onSettings'\n | 'onSimplify'\n | 'onSummarize'\n | 'onTone'\n | 'onTranslate'\n\nexport type UseMenuEvents = {\n [key in ActionMenuEvents]?: (data?: unknown) => void\n}\n\nexport type UseMenuOptions = {\n isConfigAllowed: boolean\n}\n\nexport type BaseItemProps<T = any> = {\n children?: React.ReactNode\n disabled?: boolean\n hideIcon?: boolean\n isActive?: boolean\n isMenu?: boolean\n onClick: (data?: unknown) => void\n onMouseEnter?: MouseEventHandler<T> | undefined\n onMouseLeave?: MouseEventHandler<T> | undefined\n style?: CSSProperties | undefined\n title?: string\n}\n\nexport type ImageReference = {\n data: Blob\n name: string\n size: number\n type: string\n url: string\n}\n\nexport type GenerateImageParams = {\n images?: ImageReference[]\n size?: ImageGenerateParams['size']\n style?: ImageGenerateParams['style']\n version?: ImageGenerateParams['model']\n}\n\nexport type SerializedPromptField = {\n collections?: CollectionSlug[]\n name: string\n}\n\nexport type PromptFieldGetterContext = {\n collection: CollectionSlug\n type: string\n}\n\nexport type PromptField = {\n // If not provided, the value will be returned from the data object as-is\n getter?: (data: object, ctx: PromptFieldGetterContext) => Promise<string> | string\n} & SerializedPromptField\n"],"names":[],"mappings":"AAqOA,WAGyB"}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { JSONSchema } from 'openai/lib/jsonschema'\nimport type { ImageGenerateParams } from 'openai/resources/images'\nimport type {\n CollectionConfig,\n CollectionSlug,\n DataFromCollectionSlug,\n Endpoint,\n Field,\n File,\n GlobalConfig,\n GroupField,\n PayloadRequest,\n TypedCollection,\n} from 'payload'\nimport type { CSSProperties, MouseEventHandler } from 'react'\n\nimport type { GenerateObjectResult, ModelMessage } from 'ai'\nimport type { MediaResult } from './ai/core/index.js'\nimport type {PLUGIN_INSTRUCTIONS_TABLE} from \"./defaults.js\";\n\nexport interface PluginConfigAccess {\n /**\n * Control access to AI generation features (generate text, images, audio)\n * @default () => !!req.user (requires authentication)\n */\n generate?: ({ req }: { req: PayloadRequest }) => boolean | Promise<boolean>\n /**\n * Control access to AI settings/configuration\n * @default () => !!req.user (requires authentication)\n */\n settings?: ({ req }: { req: PayloadRequest }) => boolean | Promise<boolean>\n}\n\nexport interface PluginOptions {\n /**\n * Provide local tags to filter language options from the Translate Menu\n * Check for the available local tags,\n * visit: https://www.npmjs.com/package/locale-codes\n * Example: [\"en-US\", \"zh-SG\", \"zh-CN\", \"en\"]\n */\n enabledLanguages?: string[]\n}\n\nexport type PluginConfigMediaUploadFunction = (\n result: MediaResult,\n {\n collection,\n request,\n }: {\n collection: CollectionSlug\n request: PayloadRequest\n },\n) => Promise<DataFromCollectionSlug<CollectionSlug>>\n\nexport interface PluginConfig {\n /**\n * Access control configuration for AI features\n * By default, all AI features require authentication\n */\n access?: PluginConfigAccess\n debugging?: boolean\n disableSponsorMessage?: boolean\n editorConfig?: { nodes: JSONSchema[] }\n fields?: Field[]\n generatePromptOnInit?: boolean\n generationModels?: ((defaultModels: GenerationModel[]) => GenerationModel[]) | GenerationModel[]\n globals?: {\n [key: GlobalConfig['slug']]: boolean\n }\n interfaceName?: string\n mediaUpload?: PluginConfigMediaUploadFunction\n options?: PluginOptions\n // Override the instructions collection config\n overrideInstructions?: Partial<CollectionConfig>\n promptFields?: PromptField[]\n /**\n * Custom action prompts for AI text generation\n * If not provided, uses default prompts\n * You can access default prompts by importing { defaultPrompts } from '@ai-stack/payloadcms'\n */\n prompts?: ActionPrompt[]\n /**\n * Custom seed prompt function for generating field-specific prompts\n * If not provided, fields will have empty prompts by default\n */\n seedPrompts?: SeedPromptFunction\n uploadCollectionSlug?: CollectionSlug\n}\n\nexport interface GenerationModel {\n fields: string[]\n generateText?: (prompt: string, system: string) => Promise<string>\n handler?: (prompt: string, options: any) => File | Promise<any> | Response\n id: string\n name: string\n output: 'audio' | 'file' | 'image' | 'json' | 'text' | 'video'\n settings?: GroupField\n supportsPromptOptimization?: boolean\n}\n\nexport interface GenerationConfig {\n models: GenerationModel[]\n provider: string\n}\n\nexport type GenerateTextarea<T = any> = (args: {\n collectionSlug: CollectionSlug\n doc: T\n documentId?: number | string\n locale?: string\n options?: any\n}) => Promise<string> | string\n\nexport interface Endpoints {\n fetchVoices?: Omit<Endpoint, 'root'>\n textarea: Omit<Endpoint, 'root'>\n upload: Omit<Endpoint, 'root'>\n videogenWebhook?: Omit<Endpoint, 'root'>\n}\n\nexport type ActionMenuItems =\n | 'Compose'\n | 'Expand'\n | 'Proofread'\n | 'Rephrase'\n | 'Settings'\n | 'Simplify'\n | 'Summarize'\n | 'Tone'\n | 'Translate'\n\nexport type ActionPromptOptions = {\n layout?: string\n locale?: string\n prompt?: string\n systemPrompt?: string\n}\n\nexport type ActionPrompt = {\n layout?: (options?: ActionPromptOptions) => string\n name: ActionMenuItems\n system: (options: ActionPromptOptions) => string\n}\n\nexport type SeedPromptOptions = {\n fieldLabel: string\n fieldSchemaPaths: Record<string, any>\n fieldType: string\n path: string\n}\n\nexport type SeedPromptData = Omit<\n TypedCollection[typeof PLUGIN_INSTRUCTIONS_TABLE],\n 'createdAt' | 'id' | 'updatedAt'\n>\n\nexport type SeedPromptResult =\n | {\n data?: SeedPromptData\n }\n | {\n data?: SeedPromptData\n prompt: string\n system: string\n }\n | false\n | undefined\n | void\n\nexport type SeedPromptFunction = (\n options: SeedPromptOptions,\n) => Promise<SeedPromptResult> | SeedPromptResult\n\nexport type ActionMenuEvents =\n | 'onCompose'\n | 'onExpand'\n | 'onProofread'\n | 'onRephrase'\n | 'onSettings'\n | 'onSimplify'\n | 'onSummarize'\n | 'onTone'\n | 'onTranslate'\n\nexport type UseMenuEvents = {\n [key in ActionMenuEvents]?: (data?: unknown) => void\n}\n\nexport type UseMenuOptions = {\n isConfigAllowed: boolean\n}\n\nexport type BaseItemProps<T = any> = {\n children?: React.ReactNode\n disabled?: boolean\n hideIcon?: boolean\n isActive?: boolean\n isMenu?: boolean\n onClick: (data?: unknown) => void\n onMouseEnter?: MouseEventHandler<T> | undefined\n onMouseLeave?: MouseEventHandler<T> | undefined\n style?: CSSProperties | undefined\n title?: string\n}\n\nexport type ImageReference = {\n data: Blob\n name: string\n size: number\n type: string\n url: string\n}\n\nexport type GenerateImageParams = {\n images?: ImageReference[]\n size?: ImageGenerateParams['size']\n style?: ImageGenerateParams['style']\n version?: ImageGenerateParams['model']\n}\n\nexport type SerializedPromptField = {\n collections?: CollectionSlug[]\n name: string\n}\n\nexport type PromptFieldGetterContext = {\n collection: CollectionSlug\n type: string\n}\n\nexport type PromptField = {\n // If not provided, the value will be returned from the data object as-is\n getter?: (data: object, ctx: PromptFieldGetterContext) => Promise<string> | string\n} & SerializedPromptField\n\nexport interface BeforeGenerateArgs<T = any> {\n doc: T\n field: Field\n headers: Record<string, string>\n instructions: Record<string, unknown> // The instruction document\n messages?: ModelMessage[]\n payload: PayloadRequest['payload']\n prompt: string\n req: PayloadRequest\n system: string\n}\n\nexport type BeforeGenerateResult =\n | {\n messages?: ModelMessage[]\n prompt?: string\n system?: string\n }\n | void\n\nexport type BeforeGenerateHook<T = any> = (\n args: BeforeGenerateArgs<T>,\n) => BeforeGenerateResult | Promise<BeforeGenerateResult>\n\nexport interface AfterGenerateArgs<T = any> {\n doc: T\n field: Field\n headers: Record<string, string>\n instructions: Record<string, unknown>\n payload: PayloadRequest['payload']\n req: PayloadRequest\n result: GenerateObjectResult<any> | MediaResult | string // depends on context\n}\n\nexport type AfterGenerateHook<T = any> = (args: AfterGenerateArgs<T>) => Promise<void> | void\n\n// Add to PluginConfig or a new interface if accessed via custom.ai\nexport interface AIFieldConfig {\n afterGenerate?: AfterGenerateHook[]\n beforeGenerate?: BeforeGenerateHook[]\n [key: string]: unknown\n}\n"],"names":[],"mappings":"AA+QA,mEAAmE;AACnE,WAIC"}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Button, useForm, useFormFields } from '@payloadcms/ui';
3
+ import { Button, toast, useField } from '@payloadcms/ui';
4
4
  import React, { useCallback, useState } from 'react';
5
5
  import { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js';
6
6
  /**
@@ -9,87 +9,58 @@ import { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js';
9
9
  * SECURE: API key is never exposed to the client
10
10
  */ export const VoicesFetcher = ({ path })=>{
11
11
  const [loading, setLoading] = useState(false);
12
- const { dispatchFields } = useForm();
13
12
  // Get the parent path (the block path)
14
13
  const fieldPath = path || '';
15
14
  const blockPath = fieldPath.split('.').slice(0, -1).join('.');
16
15
  const voicesPath = `${blockPath}.voices`;
17
- // Get the current voices array to know how many rows to remove
18
- const voicesField = useFormFields(([fields])=>fields[voicesPath]);
19
- const currentRowCount = voicesField && 'rows' in voicesField && Array.isArray(voicesField.rows) ? voicesField.rows.length : 0;
16
+ const { setValue } = useField({
17
+ path: voicesPath
18
+ });
20
19
  const fetchVoices = useCallback(async ()=>{
21
20
  setLoading(true);
21
+ const controller = new AbortController();
22
+ const timeoutId = setTimeout(()=>controller.abort(), 30000) // 30s timeout
23
+ ;
22
24
  try {
23
25
  // Call server endpoint - it will read the API key from the database
24
26
  const response = await fetch(`/api${PLUGIN_API_ENDPOINT_FETCH_VOICES}`, {
25
27
  headers: {
26
28
  'Content-Type': 'application/json'
27
29
  },
28
- method: 'POST'
30
+ method: 'POST',
31
+ signal: controller.signal
29
32
  });
33
+ clearTimeout(timeoutId);
30
34
  if (!response.ok) {
31
- const error = await response.json();
32
- throw new Error(error.message || 'Failed to fetch voices');
35
+ let errorMessage = 'Failed to fetch voices';
36
+ try {
37
+ const error = await response.json();
38
+ errorMessage = error.message || errorMessage;
39
+ } catch (e) {
40
+ // If response is not JSON (e.g. 504 Gateway Timeout HTML), use status text
41
+ errorMessage = `Error ${response.status}: ${response.statusText}`;
42
+ }
43
+ throw new Error(errorMessage);
33
44
  }
34
45
  const data = await response.json();
35
46
  const voices = data.voices || [];
36
- // Remove existing rows first (in reverse order to maintain indices)
37
- for(let i = currentRowCount - 1; i >= 0; i--){
38
- dispatchFields({
39
- type: 'REMOVE_ROW',
40
- path: voicesPath,
41
- rowIndex: i
42
- });
43
- }
44
- // Add new rows for each voice using ADD_ROW action
45
- for (const voice of voices){
46
- dispatchFields({
47
- type: 'ADD_ROW',
48
- path: voicesPath,
49
- subFieldState: {
50
- id: {
51
- initialValue: voice.id,
52
- valid: true,
53
- value: voice.id
54
- },
55
- name: {
56
- initialValue: voice.name,
57
- valid: true,
58
- value: voice.name
59
- },
60
- category: {
61
- initialValue: voice.category || 'premade',
62
- valid: true,
63
- value: voice.category || 'premade'
64
- },
65
- enabled: {
66
- initialValue: voice.enabled !== false,
67
- valid: true,
68
- value: voice.enabled !== false
69
- },
70
- labels: {
71
- initialValue: voice.labels || {},
72
- valid: true,
73
- value: voice.labels || {}
74
- },
75
- preview_url: {
76
- initialValue: voice.preview_url || '',
77
- valid: true,
78
- value: voice.preview_url || ''
79
- }
80
- }
81
- });
82
- }
83
- alert(`Successfully fetched ${voices.length} voices!`);
47
+ // Replace the entire array value at once
48
+ // This is much more performant than dispatching ADD_ROW actions in a loop
49
+ setValue(voices);
50
+ toast.success(`Successfully fetched ${voices.length} voices!`);
84
51
  } catch (error) {
85
- alert(`Error: ${error instanceof Error ? error.message : 'Failed to fetch voices'}`);
52
+ const msg = error instanceof Error ? error.message : 'Failed to fetch voices';
53
+ if (error instanceof Error && error.name === 'AbortError') {
54
+ toast.error('Request timed out. Please try again.');
55
+ } else {
56
+ toast.error(`Error: ${msg}`);
57
+ }
86
58
  } finally{
87
59
  setLoading(false);
60
+ clearTimeout(timeoutId);
88
61
  }
89
62
  }, [
90
- currentRowCount,
91
- dispatchFields,
92
- voicesPath
63
+ setValue
93
64
  ]);
94
65
  return /*#__PURE__*/ _jsxs("div", {
95
66
  style: {
@@ -99,9 +70,10 @@ import { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js';
99
70
  /*#__PURE__*/ _jsx(Button, {
100
71
  buttonStyle: "secondary",
101
72
  disabled: loading,
73
+ margin: false,
102
74
  onClick: fetchVoices,
103
75
  size: "medium",
104
- children: loading ? 'Fetching Voices...' : 'Fetch Voices from ElevenLabs'
76
+ children: loading ? 'Fetching Voices...' : 'Fetch Voices'
105
77
  }),
106
78
  /*#__PURE__*/ _jsx("p", {
107
79
  style: {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/VoicesFetcher/index.tsx"],"sourcesContent":["'use client'\n\nimport type { FieldClientComponent } from 'payload'\n\nimport { Button, useForm, useFormFields } from '@payloadcms/ui'\nimport React, { useCallback, useState } from 'react'\n\nimport { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js'\n\ninterface Voice {\n category?: string\n enabled?: boolean\n id: string\n labels?: Record<string, unknown>\n name: string\n preview_url?: string\n}\n\n/**\n * VoicesFetcher Component\n * Fetches voices from ElevenLabs API (server-side) and populates the voices array field\n * SECURE: API key is never exposed to the client\n */\nexport const VoicesFetcher: FieldClientComponent = ({ path }) => {\n const [loading, setLoading] = useState(false)\n const { dispatchFields } = useForm()\n\n // Get the parent path (the block path)\n const fieldPath = (path as string) || ''\n const blockPath = fieldPath.split('.').slice(0, -1).join('.')\n const voicesPath = `${blockPath}.voices`\n\n // Get the current voices array to know how many rows to remove\n const voicesField = useFormFields(([fields]) => fields[voicesPath])\n const currentRowCount =\n voicesField && 'rows' in voicesField && Array.isArray(voicesField.rows)\n ? voicesField.rows.length\n : 0\n\n const fetchVoices = useCallback(async () => {\n setLoading(true)\n\n try {\n // Call server endpoint - it will read the API key from the database\n const response = await fetch(`/api${PLUGIN_API_ENDPOINT_FETCH_VOICES}`, {\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n if (!response.ok) {\n const error = await response.json()\n throw new Error(error.message || 'Failed to fetch voices')\n }\n\n const data = await response.json()\n const voices: Voice[] = data.voices || []\n\n // Remove existing rows first (in reverse order to maintain indices)\n for (let i = currentRowCount - 1; i >= 0; i--) {\n dispatchFields({\n type: 'REMOVE_ROW',\n path: voicesPath,\n rowIndex: i,\n })\n }\n\n // Add new rows for each voice using ADD_ROW action\n for (const voice of voices) {\n dispatchFields({\n type: 'ADD_ROW',\n path: voicesPath,\n subFieldState: {\n id: { initialValue: voice.id, valid: true, value: voice.id },\n name: { initialValue: voice.name, valid: true, value: voice.name },\n category: { initialValue: voice.category || 'premade', valid: true, value: voice.category || 'premade' },\n enabled: { initialValue: voice.enabled !== false, valid: true, value: voice.enabled !== false },\n labels: { initialValue: voice.labels || {}, valid: true, value: voice.labels || {} },\n preview_url: { initialValue: voice.preview_url || '', valid: true, value: voice.preview_url || '' },\n },\n })\n }\n\n alert(`Successfully fetched ${voices.length} voices!`)\n } catch (error) {\n alert(`Error: ${error instanceof Error ? error.message : 'Failed to fetch voices'}`)\n } finally {\n setLoading(false)\n }\n }, [currentRowCount, dispatchFields, voicesPath])\n\n return (\n <div style={{ marginBottom: '20px' }}>\n <Button buttonStyle=\"secondary\" disabled={loading} onClick={fetchVoices} size=\"medium\">\n {loading ? 'Fetching Voices...' : 'Fetch Voices from ElevenLabs'}\n </Button>\n <p style={{ color: 'var(--theme-elevation-600)', fontSize: '13px', marginTop: '8px' }}>\n This will fetch all available voices from your ElevenLabs account. Make sure you have saved\n your API key in the Setup tab first.\n </p>\n </div>\n )\n}\n\n\n\n"],"names":["Button","useForm","useFormFields","React","useCallback","useState","PLUGIN_API_ENDPOINT_FETCH_VOICES","VoicesFetcher","path","loading","setLoading","dispatchFields","fieldPath","blockPath","split","slice","join","voicesPath","voicesField","fields","currentRowCount","Array","isArray","rows","length","fetchVoices","response","fetch","headers","method","ok","error","json","Error","message","data","voices","i","type","rowIndex","voice","subFieldState","id","initialValue","valid","value","name","category","enabled","labels","preview_url","alert","div","style","marginBottom","buttonStyle","disabled","onClick","size","p","color","fontSize","marginTop"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/ui/VoicesFetcher/index.tsx"],"sourcesContent":["'use client'\n\nimport type { FieldClientComponent } from 'payload'\n\nimport { Button, toast, useField, useFormFields } from '@payloadcms/ui'\nimport React, { useCallback, useState } from 'react'\n\nimport { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js'\n\ninterface Voice {\n category?: string\n enabled?: boolean\n id: string\n labels?: Record<string, unknown>\n name: string\n preview_url?: string\n}\n\n/**\n * VoicesFetcher Component\n * Fetches voices from ElevenLabs API (server-side) and populates the voices array field\n * SECURE: API key is never exposed to the client\n */\nexport const VoicesFetcher: FieldClientComponent = ({ path }) => {\n const [loading, setLoading] = useState(false)\n\n // Get the parent path (the block path)\n const fieldPath = (path as string) || ''\n const blockPath = fieldPath.split('.').slice(0, -1).join('.')\n const voicesPath = `${blockPath}.voices`\n\n const { setValue } = useField<Voice[]>({ path: voicesPath })\n\n const fetchVoices = useCallback(async () => {\n setLoading(true)\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), 30000) // 30s timeout\n\n try {\n // Call server endpoint - it will read the API key from the database\n const response = await fetch(`/api${PLUGIN_API_ENDPOINT_FETCH_VOICES}`, {\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n signal: controller.signal,\n })\n\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n let errorMessage = 'Failed to fetch voices'\n try {\n const error = await response.json()\n errorMessage = error.message || errorMessage\n } catch (e) {\n // If response is not JSON (e.g. 504 Gateway Timeout HTML), use status text\n errorMessage = `Error ${response.status}: ${response.statusText}`\n }\n throw new Error(errorMessage)\n }\n\n const data = await response.json()\n const voices: Voice[] = data.voices || []\n\n // Replace the entire array value at once\n // This is much more performant than dispatching ADD_ROW actions in a loop\n setValue(voices)\n\n toast.success(`Successfully fetched ${voices.length} voices!`)\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : 'Failed to fetch voices'\n if (error instanceof Error && error.name === 'AbortError') {\n toast.error('Request timed out. Please try again.')\n } else {\n toast.error(`Error: ${msg}`)\n }\n } finally {\n setLoading(false)\n clearTimeout(timeoutId)\n }\n }, [setValue])\n\n return (\n <div style={{ marginBottom: '20px' }}>\n <Button buttonStyle=\"secondary\" disabled={loading} margin={false} onClick={fetchVoices} size=\"medium\">\n {loading ? 'Fetching Voices...' : 'Fetch Voices'}\n </Button>\n <p style={{ color: 'var(--theme-elevation-600)', fontSize: '13px', marginTop: '8px' }}>\n This will fetch all available voices from your ElevenLabs account. Make sure you have saved\n your API key in the Setup tab first.\n </p>\n </div>\n )\n}\n"],"names":["Button","toast","useField","React","useCallback","useState","PLUGIN_API_ENDPOINT_FETCH_VOICES","VoicesFetcher","path","loading","setLoading","fieldPath","blockPath","split","slice","join","voicesPath","setValue","fetchVoices","controller","AbortController","timeoutId","setTimeout","abort","response","fetch","headers","method","signal","clearTimeout","ok","errorMessage","error","json","message","e","status","statusText","Error","data","voices","success","length","msg","name","div","style","marginBottom","buttonStyle","disabled","margin","onClick","size","p","color","fontSize","marginTop"],"mappings":"AAAA;;AAIA,SAASA,MAAM,EAAEC,KAAK,EAAEC,QAAQ,QAAuB,iBAAgB;AACvE,OAAOC,SAASC,WAAW,EAAEC,QAAQ,QAAQ,QAAO;AAEpD,SAASC,gCAAgC,QAAQ,oBAAmB;AAWpE;;;;CAIC,GACD,OAAO,MAAMC,gBAAsC,CAAC,EAAEC,IAAI,EAAE;IAC1D,MAAM,CAACC,SAASC,WAAW,GAAGL,SAAS;IAEvC,uCAAuC;IACvC,MAAMM,YAAY,AAACH,QAAmB;IACtC,MAAMI,YAAYD,UAAUE,KAAK,CAAC,KAAKC,KAAK,CAAC,GAAG,CAAC,GAAGC,IAAI,CAAC;IACzD,MAAMC,aAAa,CAAC,EAAEJ,UAAU,OAAO,CAAC;IAExC,MAAM,EAAEK,QAAQ,EAAE,GAAGf,SAAkB;QAAEM,MAAMQ;IAAW;IAE1D,MAAME,cAAcd,YAAY;QAC9BM,WAAW;QAEX,MAAMS,aAAa,IAAIC;QACvB,MAAMC,YAAYC,WAAW,IAAMH,WAAWI,KAAK,IAAI,OAAO,cAAc;;QAE5E,IAAI;YACF,oEAAoE;YACpE,MAAMC,WAAW,MAAMC,MAAM,CAAC,IAAI,EAAEnB,iCAAiC,CAAC,EAAE;gBACtEoB,SAAS;oBACP,gBAAgB;gBAClB;gBACAC,QAAQ;gBACRC,QAAQT,WAAWS,MAAM;YAC3B;YAEAC,aAAaR;YAEb,IAAI,CAACG,SAASM,EAAE,EAAE;gBAChB,IAAIC,eAAe;gBACnB,IAAI;oBACF,MAAMC,QAAQ,MAAMR,SAASS,IAAI;oBACjCF,eAAeC,MAAME,OAAO,IAAIH;gBAClC,EAAE,OAAOI,GAAG;oBACV,2EAA2E;oBAC3EJ,eAAe,CAAC,MAAM,EAAEP,SAASY,MAAM,CAAC,EAAE,EAAEZ,SAASa,UAAU,CAAC,CAAC;gBACnE;gBACA,MAAM,IAAIC,MAAMP;YAClB;YAEA,MAAMQ,OAAO,MAAMf,SAASS,IAAI;YAChC,MAAMO,SAAkBD,KAAKC,MAAM,IAAI,EAAE;YAEzC,yCAAyC;YACzC,0EAA0E;YAC1EvB,SAASuB;YAETvC,MAAMwC,OAAO,CAAC,CAAC,qBAAqB,EAAED,OAAOE,MAAM,CAAC,QAAQ,CAAC;QAC/D,EAAE,OAAOV,OAAgB;YACvB,MAAMW,MAAMX,iBAAiBM,QAAQN,MAAME,OAAO,GAAG;YACrD,IAAIF,iBAAiBM,SAASN,MAAMY,IAAI,KAAK,cAAc;gBACzD3C,MAAM+B,KAAK,CAAC;YACd,OAAO;gBACL/B,MAAM+B,KAAK,CAAC,CAAC,OAAO,EAAEW,IAAI,CAAC;YAC7B;QACF,SAAU;YACRjC,WAAW;YACXmB,aAAaR;QACf;IACF,GAAG;QAACJ;KAAS;IAEb,qBACE,MAAC4B;QAAIC,OAAO;YAAEC,cAAc;QAAO;;0BACjC,KAAC/C;gBAAOgD,aAAY;gBAAYC,UAAUxC;gBAASyC,QAAQ;gBAAOC,SAASjC;gBAAakC,MAAK;0BAC1F3C,UAAU,uBAAuB;;0BAEpC,KAAC4C;gBAAEP,OAAO;oBAAEQ,OAAO;oBAA8BC,UAAU;oBAAQC,WAAW;gBAAM;0BAAG;;;;AAM7F,EAAC"}
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { Button, useForm, useFormFields } from '@payloadcms/ui';
2
+ import { Button, toast, useField } from '@payloadcms/ui';
3
3
  import React, { useCallback, useState } from 'react';
4
4
  import { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js';
5
5
  /**
@@ -9,18 +9,15 @@ import { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js';
9
9
  */
10
10
  export const VoicesFetcher = ({ path }) => {
11
11
  const [loading, setLoading] = useState(false);
12
- const { dispatchFields } = useForm();
13
12
  // Get the parent path (the block path)
14
13
  const fieldPath = path || '';
15
14
  const blockPath = fieldPath.split('.').slice(0, -1).join('.');
16
15
  const voicesPath = `${blockPath}.voices`;
17
- // Get the current voices array to know how many rows to remove
18
- const voicesField = useFormFields(([fields]) => fields[voicesPath]);
19
- const currentRowCount = voicesField && 'rows' in voicesField && Array.isArray(voicesField.rows)
20
- ? voicesField.rows.length
21
- : 0;
16
+ const { setValue } = useField({ path: voicesPath });
22
17
  const fetchVoices = useCallback(async () => {
23
18
  setLoading(true);
19
+ const controller = new AbortController();
20
+ const timeoutId = setTimeout(() => controller.abort(), 30000); // 30s timeout
24
21
  try {
25
22
  // Call server endpoint - it will read the API key from the database
26
23
  const response = await fetch(`/api${PLUGIN_API_ENDPOINT_FETCH_VOICES}`, {
@@ -28,48 +25,45 @@ export const VoicesFetcher = ({ path }) => {
28
25
  'Content-Type': 'application/json',
29
26
  },
30
27
  method: 'POST',
28
+ signal: controller.signal,
31
29
  });
30
+ clearTimeout(timeoutId);
32
31
  if (!response.ok) {
33
- const error = await response.json();
34
- throw new Error(error.message || 'Failed to fetch voices');
32
+ let errorMessage = 'Failed to fetch voices';
33
+ try {
34
+ const error = await response.json();
35
+ errorMessage = error.message || errorMessage;
36
+ }
37
+ catch (e) {
38
+ // If response is not JSON (e.g. 504 Gateway Timeout HTML), use status text
39
+ errorMessage = `Error ${response.status}: ${response.statusText}`;
40
+ }
41
+ throw new Error(errorMessage);
35
42
  }
36
43
  const data = await response.json();
37
44
  const voices = data.voices || [];
38
- // Remove existing rows first (in reverse order to maintain indices)
39
- for (let i = currentRowCount - 1; i >= 0; i--) {
40
- dispatchFields({
41
- type: 'REMOVE_ROW',
42
- path: voicesPath,
43
- rowIndex: i,
44
- });
45
- }
46
- // Add new rows for each voice using ADD_ROW action
47
- for (const voice of voices) {
48
- dispatchFields({
49
- type: 'ADD_ROW',
50
- path: voicesPath,
51
- subFieldState: {
52
- id: { initialValue: voice.id, valid: true, value: voice.id },
53
- name: { initialValue: voice.name, valid: true, value: voice.name },
54
- category: { initialValue: voice.category || 'premade', valid: true, value: voice.category || 'premade' },
55
- enabled: { initialValue: voice.enabled !== false, valid: true, value: voice.enabled !== false },
56
- labels: { initialValue: voice.labels || {}, valid: true, value: voice.labels || {} },
57
- preview_url: { initialValue: voice.preview_url || '', valid: true, value: voice.preview_url || '' },
58
- },
59
- });
60
- }
61
- alert(`Successfully fetched ${voices.length} voices!`);
45
+ // Replace the entire array value at once
46
+ // This is much more performant than dispatching ADD_ROW actions in a loop
47
+ setValue(voices);
48
+ toast.success(`Successfully fetched ${voices.length} voices!`);
62
49
  }
63
50
  catch (error) {
64
- alert(`Error: ${error instanceof Error ? error.message : 'Failed to fetch voices'}`);
51
+ const msg = error instanceof Error ? error.message : 'Failed to fetch voices';
52
+ if (error instanceof Error && error.name === 'AbortError') {
53
+ toast.error('Request timed out. Please try again.');
54
+ }
55
+ else {
56
+ toast.error(`Error: ${msg}`);
57
+ }
65
58
  }
66
59
  finally {
67
60
  setLoading(false);
61
+ clearTimeout(timeoutId);
68
62
  }
69
- }, [currentRowCount, dispatchFields, voicesPath]);
63
+ }, [setValue]);
70
64
  return (<div style={{ marginBottom: '20px' }}>
71
- <Button buttonStyle="secondary" disabled={loading} onClick={fetchVoices} size="medium">
72
- {loading ? 'Fetching Voices...' : 'Fetch Voices from ElevenLabs'}
65
+ <Button buttonStyle="secondary" disabled={loading} margin={false} onClick={fetchVoices} size="medium">
66
+ {loading ? 'Fetching Voices...' : 'Fetch Voices'}
73
67
  </Button>
74
68
  <p style={{ color: 'var(--theme-elevation-600)', fontSize: '13px', marginTop: '8px' }}>
75
69
  This will fetch all available voices from your ElevenLabs account. Make sure you have saved
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-stack/payloadcms",
3
- "version": "3.68.0-beta.4",
3
+ "version": "3.68.0-beta.5",
4
4
  "private": false,
5
5
  "bugs": "https://github.com/ashbuilds/payload-ai/issues",
6
6
  "repository": "https://github.com/ashbuilds/payload-ai",