@genkit-ai/firebase
Version:
Genkit AI framework plugin for Firebase including Firestore trace/state store and deployment helpers for Cloud Functions for Firebase.
1 lines • 8.65 kB
Source Map (JSON)
{"version":3,"sources":["../../src/beta/data-connect.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 {\n getApp,\n initializeServerApp,\n type FirebaseApp,\n type FirebaseOptions,\n type FirebaseServerApp,\n} from 'firebase/app';\nimport {\n executeMutation,\n executeQuery,\n getDataConnect,\n mutationRef,\n queryRef,\n} from 'firebase/data-connect';\nimport { readFileSync } from 'fs';\nimport { GenkitError, type Genkit, type JSONSchema7 } from 'genkit';\nimport { logger } from 'genkit/logging';\nimport { genkitPlugin, type GenkitPlugin } from 'genkit/plugin';\n\nexport interface DataConnectTool {\n name: string;\n type: 'query' | 'mutation';\n description?: string;\n parameters: JSONSchema7;\n}\n\nexport interface ToolsConfig {\n connector: string;\n location: string;\n service: string;\n tools: DataConnectTool[];\n}\n\nexport interface DataConnectToolsOptions {\n /** Provide a name for the plugin. All tools will have this prefix, e.g. `myname/myTool` */\n name: string;\n /** Pass in tool definitions as generated from Data Connect's `llmTools` generator. */\n config?: ToolsConfig;\n /** Path to the file output by the `llmTools` generator. */\n configFile: string;\n /**\n * Your Firebase client SDK config or a FirebaseApp or a FirebaseServerApp. If not provided\n * the plugin will attempt to use `context.firebaseApp` for an in-context app or will fall\n * back the default app.\n */\n firebaseApp?: FirebaseOptions | FirebaseApp;\n /**\n * How to handle errors coming from Data Connect tools:\n *\n * - `return`: (default) return the error to the model to allow it to decide how to proceed\n * - `throw`: throw an error, halting execution\n *\n * You may also supply a custom error handler. Whatever is returned from the handler will\n * be returned to the LLM. If you throw an exception from the handler, execution will be\n * halted with an error.\n */\n onError?:\n | 'return'\n | 'throw'\n | ((tool: DataConnectTool, error: Error) => any | Promise<any>);\n}\n\nexport function serverAppFromContext(\n context: Record<string, any>,\n config?: FirebaseOptions | FirebaseApp\n): FirebaseServerApp | FirebaseApp {\n if ('firebaseApp' in context)\n return context.firebaseApp as FirebaseApp | FirebaseServerApp;\n try {\n if (!config) config = getApp();\n } catch (e) {\n throw new GenkitError({\n status: 'FAILED_PRECONDITION',\n message: `Must either supply a 'firebaseApp' option or have already initialized a default FirebaseApp when calling Data Connect tools.`,\n });\n }\n\n return initializeServerApp(config, {\n authIdToken: context.auth?.rawToken,\n appCheckToken: context.app?.rawToken,\n });\n}\n\n/**\n * dataConnectTools connects Genkit to your Data Connect operations by creating tools from\n * your connector's queries and mutations. This plugin is driven by the generated JSON file\n * from the `llmTools` option. You can generate this file using the `llmTools` option in\n * `connector.yaml`, for example:\n *\n * ```yaml\n * connectorId: tools\n * generate:\n * llmTools:\n * outputFile: ../../tools.json\n * ```\n *\n * Once you have the tools file, you can use this function to register the tools as a Genkit\n * plugin:\n *\n * ```ts\n * const app = initializeFirebase({...});\n *\n * const ai = genkit({\n * plugins: [..., dataConnectTool({\n * name: 'myTools',\n * configFile: 'tools.json',\n * sdk: app,\n * })]\n * })\n * ```\n *\n * **IMPORTANT:** This plugin relies on the *client SDKs* for Firebase and does not have\n * administrative access to Data Connect. To authenticate this plugin requires a `firebaseApp`\n * instance on `context` with appropriate privileges or the use of the `firebaseContext()`\n * context provider.\n *\n * @param options Configuration options for the plugin.\n * @returns A Genkit plugin.\n */\nexport function dataConnectTools(\n options: DataConnectToolsOptions\n): GenkitPlugin {\n if (!options.config && !options.configFile)\n throw new GenkitError({\n status: 'INVALID_ARGUMENT',\n message:\n 'Must supply `config` or `configFile` when initializing a Data Connect tools plugin.',\n });\n\n if (!options.config) {\n try {\n options.config = JSON.parse(\n readFileSync(options.configFile, 'utf8')\n ) as ToolsConfig;\n } catch (e) {\n throw new GenkitError({\n status: 'INVALID_ARGUMENT',\n message: `Could not parse Data Connect tools config from ${options.configFile}: ${(e as any).message}`,\n });\n }\n }\n\n return genkitPlugin(options.name, (ai: Genkit) => {\n const config = options.config!;\n for (const tool of config.tools) {\n ai.defineTool(\n {\n name: `${options.name}/${tool.name}`,\n description: tool.description || '',\n inputJsonSchema: tool.parameters,\n },\n async (input, { context }) => {\n const serverApp = serverAppFromContext(context, options.firebaseApp);\n const dc = getDataConnect(serverApp, {\n connector: config.connector,\n location: config.location,\n service: config.service,\n });\n\n try {\n if (tool.type === 'query') {\n const { data } = await executeQuery(\n queryRef(dc, tool.name, input)\n );\n return data;\n }\n\n const { data } = await executeMutation(\n mutationRef(dc, tool.name, input)\n );\n logger.debug(\n `[dataConnectTools] ${tool.name}(${JSON.stringify(input)}) -> ${JSON.stringify(data)}`\n );\n return data;\n } catch (e) {\n logger.info('[dataConnectTools] error on tool call:', e);\n if (options.onError === 'throw') throw e;\n if (typeof options.onError === 'function')\n return Promise.resolve(options.onError(tool, e as Error));\n if (options.onError === 'return' || !options.onError)\n return { error: (e as any).message };\n }\n }\n );\n }\n });\n}\n"],"mappings":"AAgBA;AAAA,EACE;AAAA,EACA;AAAA,OAIK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAC7B,SAAS,mBAAkD;AAC3D,SAAS,cAAc;AACvB,SAAS,oBAAuC;AA6CzC,SAAS,qBACd,SACA,QACiC;AACjC,MAAI,iBAAiB;AACnB,WAAO,QAAQ;AACjB,MAAI;AACF,QAAI,CAAC,OAAQ,UAAS,OAAO;AAAA,EAC/B,SAAS,GAAG;AACV,UAAM,IAAI,YAAY;AAAA,MACpB,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,SAAO,oBAAoB,QAAQ;AAAA,IACjC,aAAa,QAAQ,MAAM;AAAA,IAC3B,eAAe,QAAQ,KAAK;AAAA,EAC9B,CAAC;AACH;AAsCO,SAAS,iBACd,SACc;AACd,MAAI,CAAC,QAAQ,UAAU,CAAC,QAAQ;AAC9B,UAAM,IAAI,YAAY;AAAA,MACpB,QAAQ;AAAA,MACR,SACE;AAAA,IACJ,CAAC;AAEH,MAAI,CAAC,QAAQ,QAAQ;AACnB,QAAI;AACF,cAAQ,SAAS,KAAK;AAAA,QACpB,aAAa,QAAQ,YAAY,MAAM;AAAA,MACzC;AAAA,IACF,SAAS,GAAG;AACV,YAAM,IAAI,YAAY;AAAA,QACpB,QAAQ;AAAA,QACR,SAAS,kDAAkD,QAAQ,UAAU,KAAM,EAAU,OAAO;AAAA,MACtG,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,aAAa,QAAQ,MAAM,CAAC,OAAe;AAChD,UAAM,SAAS,QAAQ;AACvB,eAAW,QAAQ,OAAO,OAAO;AAC/B,SAAG;AAAA,QACD;AAAA,UACE,MAAM,GAAG,QAAQ,IAAI,IAAI,KAAK,IAAI;AAAA,UAClC,aAAa,KAAK,eAAe;AAAA,UACjC,iBAAiB,KAAK;AAAA,QACxB;AAAA,QACA,OAAO,OAAO,EAAE,QAAQ,MAAM;AAC5B,gBAAM,YAAY,qBAAqB,SAAS,QAAQ,WAAW;AACnE,gBAAM,KAAK,eAAe,WAAW;AAAA,YACnC,WAAW,OAAO;AAAA,YAClB,UAAU,OAAO;AAAA,YACjB,SAAS,OAAO;AAAA,UAClB,CAAC;AAED,cAAI;AACF,gBAAI,KAAK,SAAS,SAAS;AACzB,oBAAM,EAAE,MAAAA,MAAK,IAAI,MAAM;AAAA,gBACrB,SAAS,IAAI,KAAK,MAAM,KAAK;AAAA,cAC/B;AACA,qBAAOA;AAAA,YACT;AAEA,kBAAM,EAAE,KAAK,IAAI,MAAM;AAAA,cACrB,YAAY,IAAI,KAAK,MAAM,KAAK;AAAA,YAClC;AACA,mBAAO;AAAA,cACL,sBAAsB,KAAK,IAAI,IAAI,KAAK,UAAU,KAAK,CAAC,QAAQ,KAAK,UAAU,IAAI,CAAC;AAAA,YACtF;AACA,mBAAO;AAAA,UACT,SAAS,GAAG;AACV,mBAAO,KAAK,0CAA0C,CAAC;AACvD,gBAAI,QAAQ,YAAY,QAAS,OAAM;AACvC,gBAAI,OAAO,QAAQ,YAAY;AAC7B,qBAAO,QAAQ,QAAQ,QAAQ,QAAQ,MAAM,CAAU,CAAC;AAC1D,gBAAI,QAAQ,YAAY,YAAY,CAAC,QAAQ;AAC3C,qBAAO,EAAE,OAAQ,EAAU,QAAQ;AAAA,UACvC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;","names":["data"]}