{"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  FirebaseApp,\n  FirebaseOptions,\n  FirebaseServerApp,\n  getApp,\n  initializeServerApp,\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 { Genkit, GenkitError, type JSONSchema7 } from 'genkit';\nimport { logger } from 'genkit/logging';\nimport { GenkitPlugin, 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,EAIE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAC7B,SAAiB,mBAAqC;AACtD,SAAS,cAAc;AACvB,SAAuB,oBAAoB;AA6CpC,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"]}