UNPKG

genkitx-neo4j

Version:

Genkit AI framework plugin for Neo4j Graph database

1 lines 12.9 kB
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Copyright 2024 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 neo4j_driver from \"neo4j-driver\";\nimport { Genkit, z } from \"genkit\";\nimport { GenkitPlugin, genkitPlugin } from \"genkit/plugin\";\n\nimport { EmbedderArgument, Embedding } from \"genkit/embedder\";\nimport {\n CommonRetrieverOptionsSchema,\n Document,\n indexerRef,\n retrieverRef,\n} from \"genkit/retriever\";\n\nconst Neo4jRetrieverOptionsSchema = CommonRetrieverOptionsSchema.extend({\n k: z.number().max(1000),\n // filter: z.record(z.string(), z.any()).optional(), later for metadata filtering\n});\n\nconst Neo4jIndexerOptionsSchema = z.object({\n namespace: z.string().optional(),\n});\n\nexport interface Neo4jGraphConfig {\n url: string;\n username: string;\n password: string;\n database?: string;\n}\n\n/**\n * neo4jRetrieverRef function creates a retriever for Neo4j.\n * @param params The params for the new Neo4j retriever\n * @param params.indexId The indexId for the Neo4j retriever\n * @param params.displayName A display name for the retriever.\nIf not specified, the default label will be `Neo4j - <indexId>`\n * @returns A reference to a Neo4j retriever.\n */\nexport const neo4jRetrieverRef = (params: {\n indexId: string;\n displayName?: string;\n}) => {\n return retrieverRef({\n name: `neo4j/${params.indexId}`,\n info: {\n label: params.displayName ?? `Neo4j - ${params.indexId}`,\n },\n configSchema: Neo4jRetrieverOptionsSchema,\n });\n};\n\n/**\n * neo4jIndexerRef function creates an indexer for Neo4j.\n * @param params The params for the new Neo4j indexer.\n * @param params.indexId The indexId for the Neo4j indexer.\n * @param params.displayName A display name for the indexer.\nIf not specified, the default label will be `Neo4j - <indexId>`\n * @returns A reference to a Neo4j indexer.\n */\nexport const neo4jIndexerRef = (params: {\n indexId: string;\n displayName?: string;\n}) => {\n return indexerRef({\n name: `neo4j/${params.indexId}`,\n info: {\n label: params.displayName ?? `Neo4j - ${params.indexId}`,\n },\n //configSchema: Neo4jIndexerOptionsSchema.optional(),\n });\n};\n\n/**\n * Neo4j plugin that provides a Neo4j retriever and indexer\n * @param params An array of params to set up Neo4j retrievers and indexers\n * @param params.clientParams Neo4jConfiguration containing the\nusername, password, and url. If not set, the NEO4J_URI, NEO4J_USERNAME,\nand NEO4J_PASSWORD environment variable will be used instead.\n * @param params.indexId The name of the index\n * @param params.embedder The embedder to use for the indexer and retriever\n * @param params.embedderOptions Options to customize the embedder\n * @returns The Neo4j Genkit plugin\n */\nexport function neo4j<EmbedderCustomOptions extends z.ZodTypeAny>(\n params: {\n clientParams?: Neo4jGraphConfig;\n indexId: string;\n embedder: EmbedderArgument<EmbedderCustomOptions>;\n embedderOptions?: z.infer<EmbedderCustomOptions>;\n }[],\n): GenkitPlugin {\n return genkitPlugin(\"neo4j\", async (ai: Genkit) => {\n params.map((i) => configureNeo4jRetriever(ai, i));\n params.map((i) => configureNeo4jIndexer(ai, i));\n });\n}\n\nexport default neo4j;\n\n/**\n * Configures a Neo4j retriever.\n * @param ai A Genkit instance\n * @param params The params for the retriever\n * @param params.indexId The name of the retriever\n * @param params.clientParams PNeo4jConfiguration containing the\nusername, password, and url. If not set, the NEO4J_URI, NEO4J_USERNAME,\nand NEO4J_PASSWORD environment variable will be used instead.\n * @param params.embedder The embedder to use for the retriever\n * @param params.embedderOptions Options to customize the embedder\n * @returns A Pinecone retriever\n */\nexport function configureNeo4jRetriever<\n EmbedderCustomOptions extends z.ZodTypeAny,\n>(\n ai: Genkit,\n params: {\n indexId: string;\n clientParams?: Neo4jGraphConfig;\n embedder: EmbedderArgument<EmbedderCustomOptions>;\n embedderOptions?: z.infer<EmbedderCustomOptions>;\n },\n) {\n const { indexId, embedder, embedderOptions } = {\n ...params,\n };\n const neo4jConfig = params.clientParams ?? getDefaultConfig();\n const neo4j_instance = neo4j_driver.driver(\n neo4jConfig.url, // URL (protocol://host:port)\n neo4j_driver.auth.basic(neo4jConfig.username, neo4jConfig.password), // Authentication\n );\n return ai.defineRetriever(\n {\n name: `neo4j/${params.indexId}`,\n configSchema: Neo4jRetrieverOptionsSchema,\n },\n async (content, options) => {\n const queryEmbeddings = await ai.embed({\n embedder,\n content,\n options: embedderOptions,\n });\n\n const retriever_query = `\n CALL db.index.vector.queryNodes($index, $k, $embedding) YIELD node, score\n RETURN node.text AS text, node {.*, text: Null,\n embedding: Null, id: Null } AS metadata\n `;\n const response = await neo4j_instance.executeQuery(\n retriever_query,\n {\n k: options.k,\n embedding: queryEmbeddings[0].embedding,\n index: indexId,\n },\n {\n database: neo4jConfig.database,\n },\n );\n // Create documents properly by returning the result from map\n const documents = response.records.map((el) => {\n return Document.fromText(\n el.get(\"text\"),\n Object.fromEntries(\n Object.entries(el.get(\"metadata\")).filter(\n ([_, value]) => value !== null,\n ),\n ),\n );\n });\n neo4j_instance.close();\n return { documents: documents };\n },\n );\n}\n\n/**\n * Configures a Neo4j indexer.\n * @param ai A Genkit instance\n * @param params The params for the indexer\n * @param params.indexId The name of the indexer\n * @param params.clientParams Neo4jConfiguration containing the\nusername, password, and url. If not set, the NEO4J_URI, NEO4J_USERNAME,\nand NEO4J_PASSWORD environment variable will be used instead.\n * @param params.embedder The embedder to use for the retriever\n * @param params.embedderOptions Options to customize the embedder\n * @returns A Genkit indexer\n */\nexport function configureNeo4jIndexer<\n EmbedderCustomOptions extends z.ZodTypeAny,\n>(\n ai: Genkit,\n params: {\n indexId: string;\n clientParams?: Neo4jGraphConfig;\n embedder: EmbedderArgument<EmbedderCustomOptions>;\n embedderOptions?: z.infer<EmbedderCustomOptions>;\n },\n) {\n const { indexId, embedder, embedderOptions } = {\n ...params,\n };\n const neo4jConfig = params.clientParams ?? getDefaultConfig();\n const neo4j_instance = neo4j_driver.driver(\n neo4jConfig.url, // URL (protocol://host:port)\n neo4j_driver.auth.basic(neo4jConfig.username, neo4jConfig.password), // Authentication\n );\n\n return ai.defineIndexer(\n {\n name: `neo4j/${params.indexId}`,\n //configSchema: neo4jIndexerOptionsSchema.optional(),\n },\n async (docs, options) => {\n const embeddings = await Promise.all(\n docs.map((doc) =>\n ai.embed({\n embedder,\n content: doc,\n options: embedderOptions,\n }),\n ),\n );\n\n const BATCH_SIZE = 1000;\n\n for (let i = 0; i < docs.length; i += BATCH_SIZE) {\n const batchDocs = docs.slice(i, i + BATCH_SIZE);\n const batchEmbeddings = embeddings.slice(i, i + BATCH_SIZE);\n\n const batchParams = batchDocs.map((el, j) => ({\n text: el.content[0][\"text\"],\n metadata: el.metadata ?? {},\n embedding: batchEmbeddings[j][0][\"embedding\"],\n }));\n\n await neo4j_instance.executeQuery(\n `\n UNWIND $data AS row\n CREATE (t:\\`${indexId}\\`)\n SET t.text = row.text,\n t += row.metadata\n WITH t, row.embedding AS embedding\n CALL db.create.setNodeVectorProperty(t, 'embedding', embedding)\n `,\n { data: batchParams },\n { database: neo4jConfig.database },\n );\n }\n\n await neo4j_instance.executeQuery(\n `\n CREATE VECTOR INDEX $indexName IF NOT EXISTS\n FOR (n:\\`${indexId}\\`) ON n.embedding\n `,\n { indexName: indexId },\n { database: neo4jConfig.database },\n );\n neo4j_instance.close();\n },\n );\n}\n\nfunction getDefaultConfig() {\n const {\n NEO4J_URI: url,\n NEO4J_USERNAME: username,\n NEO4J_PASSWORD: password,\n NEO4J_DATABASE: database,\n } = process.env;\n\n if (!url || !username || !password) {\n throw new Error(\n \"Please provide Neo4j connection details through environment variables: NEO4J_URI, NEO4J_USERNAME, and NEO4J_PASSWORD are required.\\n\" +\n \"For more details see https://neo4j.com/docs/api/javascript-driver/current/\",\n );\n }\n\n return {\n url,\n username,\n password,\n ...(database && { database }),\n };\n}\n"],"mappings":"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","names":["_a"]}