@genkit-ai/core
Version:
Genkit AI framework core libraries.
1 lines • 10.9 kB
Source Map (JSON)
{"version":3,"sources":["../src/dynamic-action-provider.ts"],"sourcesContent":["/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as z from 'zod';\nimport {\n Action,\n ActionMetadata,\n ActionMetadataSchema,\n defineAction,\n} from './action.mjs';\nimport { GenkitError } from './error.mjs';\nimport { ActionMetadataRecord, ActionType, Registry } from './registry.mjs';\n\ntype DapValue = {\n [K in ActionType]?: Action<z.ZodTypeAny, z.ZodTypeAny, z.ZodTypeAny>[];\n};\n\nclass SimpleCache {\n private value: DapValue | undefined;\n private expiresAt: number | undefined;\n private ttlMillis: number;\n private dap: DynamicActionProviderAction | undefined;\n private dapFn: DapFn;\n private fetchPromise: Promise<DapValue> | null = null;\n\n constructor(config: DapConfig, dapFn: DapFn) {\n this.dapFn = dapFn;\n this.ttlMillis = !config.cacheConfig?.ttlMillis\n ? 3 * 1000\n : config.cacheConfig?.ttlMillis;\n }\n\n setDap(dap: DynamicActionProviderAction) {\n this.dap = dap;\n }\n\n setValue(value: DapValue) {\n const dapId = this.dap?.__action?.name;\n if (dapId) {\n Object.entries(value).forEach(([actionType, actionList]) => {\n actionList?.forEach((action) => {\n action.__action.key = `/dynamic-action-provider/${dapId}:${actionType}/${action.__action.name}`;\n });\n });\n }\n this.value = value;\n this.expiresAt = Date.now() + this.ttlMillis;\n }\n\n /**\n * Gets or fetches the DAP data.\n * @param skipTrace Don't run the action. i.e. don't create a trace log.\n * @returns The DAP data\n */\n async getOrFetch(params?: { skipTrace?: boolean }): Promise<DapValue> {\n const isStale =\n !this.value ||\n !this.expiresAt ||\n this.ttlMillis < 0 ||\n Date.now() > this.expiresAt;\n if (!isStale) {\n return this.value!;\n }\n\n if (!this.fetchPromise) {\n this.fetchPromise = (async () => {\n try {\n if (this.dap && !params?.skipTrace) {\n await this.dap.run(); // calls setValue\n } else {\n this.setValue(await this.dapFn());\n }\n if (!this.value) {\n throw new Error('value is undefined');\n }\n return this.value;\n } catch (error) {\n console.error('Error fetching Dynamic Action Provider value:', error);\n this.invalidate();\n throw error; // Rethrow to reject the fetchPromise\n } finally {\n // Allow new fetches after this one completes or fails.\n this.fetchPromise = null;\n }\n })();\n }\n return await this.fetchPromise;\n }\n\n invalidate() {\n this.value = undefined;\n }\n}\n\nexport interface DynamicRegistry {\n __cache: SimpleCache;\n invalidateCache(): void;\n getAction(\n actionType: string,\n actionName: string\n ): Promise<Action<z.ZodTypeAny, z.ZodTypeAny, z.ZodTypeAny> | undefined>;\n listActionMetadata(\n actionType: string,\n actionName: string\n ): Promise<ActionMetadata[]>;\n getActionMetadataRecord(dapPrefix: string): Promise<ActionMetadataRecord>;\n}\n\nexport type DynamicActionProviderAction = Action<\n z.ZodVoid,\n z.ZodArray<typeof ActionMetadataSchema>,\n z.ZodTypeAny\n> &\n DynamicRegistry & {\n __action: {\n metadata: {\n type: 'dynamic-action-provider';\n };\n };\n };\n\nexport function isDynamicActionProvider(\n obj: Action<z.ZodTypeAny, z.ZodTypeAny>\n): obj is DynamicActionProviderAction {\n return obj.__action?.metadata?.type == 'dynamic-action-provider';\n}\n\nexport interface DapConfig {\n name: string;\n description?: string;\n cacheConfig?: {\n // Negative = no caching\n // Zero or undefined = default (3000 milliseconds)\n // Positive number = how many milliseconds the cache is valid for\n ttlMillis: number | undefined;\n };\n metadata?: Record<string, any>;\n}\n\nexport type DapFn = () => Promise<DapValue>;\nexport type DapMetadata = {\n [K in ActionType]?: ActionMetadata[];\n};\n\nfunction transformDapValue(value: DapValue): ActionMetadata[] {\n return Object.values(value).flatMap(\n (actions) => actions?.map((a) => a.__action) || []\n );\n}\n\nexport function defineDynamicActionProvider(\n registry: Registry,\n config: DapConfig | string,\n fn: DapFn\n): DynamicActionProviderAction {\n let cfg: DapConfig;\n if (typeof config == 'string') {\n cfg = { name: config };\n } else {\n cfg = { ...config };\n }\n const cache = new SimpleCache(cfg, fn);\n const a = defineAction(\n registry,\n {\n ...cfg,\n inputSchema: z.void(),\n outputSchema: z.array(ActionMetadataSchema),\n actionType: 'dynamic-action-provider',\n metadata: { ...(cfg.metadata || {}), type: 'dynamic-action-provider' },\n },\n async (_options) => {\n const dapValue = await fn();\n cache.setValue(dapValue);\n return transformDapValue(dapValue);\n }\n );\n implementDap(a as DynamicActionProviderAction, cache);\n return a as DynamicActionProviderAction;\n}\n\nfunction implementDap(dap: DynamicActionProviderAction, cache: SimpleCache) {\n cache.setDap(dap);\n dap.__cache = cache;\n dap.invalidateCache = () => {\n dap.__cache.invalidate();\n };\n\n dap.getAction = async (actionType: string, actionName: string) => {\n const result = await dap.__cache.getOrFetch();\n if (result[actionType]) {\n return result[actionType].find((t) => t.__action.name == actionName);\n }\n return undefined;\n };\n\n dap.listActionMetadata = async (actionType: string, actionName: string) => {\n const result = await dap.__cache.getOrFetch();\n if (!result[actionType]) {\n return [];\n }\n\n // Match everything in the actionType\n const metadata = result[actionType].map((a) => a.__action);\n if (actionName == '*') {\n return metadata;\n }\n\n // Prefix matching\n if (actionName.endsWith('*')) {\n const prefix = actionName.slice(0, -1);\n return metadata.filter((m) => m.name.startsWith(prefix));\n }\n\n // Single match or empty array\n return metadata.filter((m) => m.name == actionName);\n };\n\n // This is called by listResolvableActions which is used by the\n // reflection API.\n dap.getActionMetadataRecord = async (dapPrefix: string) => {\n const dapActions = {} as ActionMetadataRecord;\n // We want to skip traces so we don't get a new action trace\n // every time the DevUI requests the list of actions.\n // This is ok, because the DevUI will show the actions, so\n // not having them in the trace is fine.\n const result = await dap.__cache.getOrFetch({ skipTrace: true });\n for (const actions of Object.values(result)) {\n const metadataList = actions.map((a) => a.__action);\n for (const metadata of metadataList) {\n if (!metadata.name || !metadata.key) {\n throw new GenkitError({\n status: 'INVALID_ARGUMENT',\n message: `Invalid metadata when listing dynamic actions from ${dapPrefix} - name required`,\n });\n }\n dapActions[metadata.key] = metadata;\n }\n }\n return dapActions;\n };\n}\n"],"mappings":"AAgBA,YAAY,OAAO;AACnB;AAAA,EAGE;AAAA,EACA;AAAA,OACK;AACP,SAAS,mBAAmB;AAO5B,MAAM,YAAY;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAyC;AAAA,EAEjD,YAAY,QAAmB,OAAc;AAC3C,SAAK,QAAQ;AACb,SAAK,YAAY,CAAC,OAAO,aAAa,YAClC,IAAI,MACJ,OAAO,aAAa;AAAA,EAC1B;AAAA,EAEA,OAAO,KAAkC;AACvC,SAAK,MAAM;AAAA,EACb;AAAA,EAEA,SAAS,OAAiB;AACxB,UAAM,QAAQ,KAAK,KAAK,UAAU;AAClC,QAAI,OAAO;AACT,aAAO,QAAQ,KAAK,EAAE,QAAQ,CAAC,CAAC,YAAY,UAAU,MAAM;AAC1D,oBAAY,QAAQ,CAAC,WAAW;AAC9B,iBAAO,SAAS,MAAM,4BAA4B,KAAK,IAAI,UAAU,IAAI,OAAO,SAAS,IAAI;AAAA,QAC/F,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AACA,SAAK,QAAQ;AACb,SAAK,YAAY,KAAK,IAAI,IAAI,KAAK;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAW,QAAqD;AACpE,UAAM,UACJ,CAAC,KAAK,SACN,CAAC,KAAK,aACN,KAAK,YAAY,KACjB,KAAK,IAAI,IAAI,KAAK;AACpB,QAAI,CAAC,SAAS;AACZ,aAAO,KAAK;AAAA,IACd;AAEA,QAAI,CAAC,KAAK,cAAc;AACtB,WAAK,gBAAgB,YAAY;AAC/B,YAAI;AACF,cAAI,KAAK,OAAO,CAAC,QAAQ,WAAW;AAClC,kBAAM,KAAK,IAAI,IAAI;AAAA,UACrB,OAAO;AACL,iBAAK,SAAS,MAAM,KAAK,MAAM,CAAC;AAAA,UAClC;AACA,cAAI,CAAC,KAAK,OAAO;AACf,kBAAM,IAAI,MAAM,oBAAoB;AAAA,UACtC;AACA,iBAAO,KAAK;AAAA,QACd,SAAS,OAAO;AACd,kBAAQ,MAAM,iDAAiD,KAAK;AACpE,eAAK,WAAW;AAChB,gBAAM;AAAA,QACR,UAAE;AAEA,eAAK,eAAe;AAAA,QACtB;AAAA,MACF,GAAG;AAAA,IACL;AACA,WAAO,MAAM,KAAK;AAAA,EACpB;AAAA,EAEA,aAAa;AACX,SAAK,QAAQ;AAAA,EACf;AACF;AA6BO,SAAS,wBACd,KACoC;AACpC,SAAO,IAAI,UAAU,UAAU,QAAQ;AACzC;AAmBA,SAAS,kBAAkB,OAAmC;AAC5D,SAAO,OAAO,OAAO,KAAK,EAAE;AAAA,IAC1B,CAAC,YAAY,SAAS,IAAI,CAAC,MAAM,EAAE,QAAQ,KAAK,CAAC;AAAA,EACnD;AACF;AAEO,SAAS,4BACd,UACA,QACA,IAC6B;AAC7B,MAAI;AACJ,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,EAAE,MAAM,OAAO;AAAA,EACvB,OAAO;AACL,UAAM,EAAE,GAAG,OAAO;AAAA,EACpB;AACA,QAAM,QAAQ,IAAI,YAAY,KAAK,EAAE;AACrC,QAAM,IAAI;AAAA,IACR;AAAA,IACA;AAAA,MACE,GAAG;AAAA,MACH,aAAa,EAAE,KAAK;AAAA,MACpB,cAAc,EAAE,MAAM,oBAAoB;AAAA,MAC1C,YAAY;AAAA,MACZ,UAAU,EAAE,GAAI,IAAI,YAAY,CAAC,GAAI,MAAM,0BAA0B;AAAA,IACvE;AAAA,IACA,OAAO,aAAa;AAClB,YAAM,WAAW,MAAM,GAAG;AAC1B,YAAM,SAAS,QAAQ;AACvB,aAAO,kBAAkB,QAAQ;AAAA,IACnC;AAAA,EACF;AACA,eAAa,GAAkC,KAAK;AACpD,SAAO;AACT;AAEA,SAAS,aAAa,KAAkC,OAAoB;AAC1E,QAAM,OAAO,GAAG;AAChB,MAAI,UAAU;AACd,MAAI,kBAAkB,MAAM;AAC1B,QAAI,QAAQ,WAAW;AAAA,EACzB;AAEA,MAAI,YAAY,OAAO,YAAoB,eAAuB;AAChE,UAAM,SAAS,MAAM,IAAI,QAAQ,WAAW;AAC5C,QAAI,OAAO,UAAU,GAAG;AACtB,aAAO,OAAO,UAAU,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ,UAAU;AAAA,IACrE;AACA,WAAO;AAAA,EACT;AAEA,MAAI,qBAAqB,OAAO,YAAoB,eAAuB;AACzE,UAAM,SAAS,MAAM,IAAI,QAAQ,WAAW;AAC5C,QAAI,CAAC,OAAO,UAAU,GAAG;AACvB,aAAO,CAAC;AAAA,IACV;AAGA,UAAM,WAAW,OAAO,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ;AACzD,QAAI,cAAc,KAAK;AACrB,aAAO;AAAA,IACT;AAGA,QAAI,WAAW,SAAS,GAAG,GAAG;AAC5B,YAAM,SAAS,WAAW,MAAM,GAAG,EAAE;AACrC,aAAO,SAAS,OAAO,CAAC,MAAM,EAAE,KAAK,WAAW,MAAM,CAAC;AAAA,IACzD;AAGA,WAAO,SAAS,OAAO,CAAC,MAAM,EAAE,QAAQ,UAAU;AAAA,EACpD;AAIA,MAAI,0BAA0B,OAAO,cAAsB;AACzD,UAAM,aAAa,CAAC;AAKpB,UAAM,SAAS,MAAM,IAAI,QAAQ,WAAW,EAAE,WAAW,KAAK,CAAC;AAC/D,eAAW,WAAW,OAAO,OAAO,MAAM,GAAG;AAC3C,YAAM,eAAe,QAAQ,IAAI,CAAC,MAAM,EAAE,QAAQ;AAClD,iBAAW,YAAY,cAAc;AACnC,YAAI,CAAC,SAAS,QAAQ,CAAC,SAAS,KAAK;AACnC,gBAAM,IAAI,YAAY;AAAA,YACpB,QAAQ;AAAA,YACR,SAAS,sDAAsD,SAAS;AAAA,UAC1E,CAAC;AAAA,QACH;AACA,mBAAW,SAAS,GAAG,IAAI;AAAA,MAC7B;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;","names":[]}