@langchain/neo4j 0.0.1

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.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +281 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +23 -0
  4. package/dist/chains/graph_qa/cypher.cjs +117 -0
  5. package/dist/chains/graph_qa/cypher.cjs.map +1 -0
  6. package/dist/chains/graph_qa/cypher.d.cts +73 -0
  7. package/dist/chains/graph_qa/cypher.d.cts.map +1 -0
  8. package/dist/chains/graph_qa/cypher.d.ts +73 -0
  9. package/dist/chains/graph_qa/cypher.d.ts.map +1 -0
  10. package/dist/chains/graph_qa/cypher.js +115 -0
  11. package/dist/chains/graph_qa/cypher.js.map +1 -0
  12. package/dist/chains/graph_qa/index.cjs +2 -0
  13. package/dist/chains/graph_qa/index.d.ts +2 -0
  14. package/dist/chains/graph_qa/index.js +3 -0
  15. package/dist/chains/graph_qa/prompts.cjs +42 -0
  16. package/dist/chains/graph_qa/prompts.cjs.map +1 -0
  17. package/dist/chains/graph_qa/prompts.d.cts +14 -0
  18. package/dist/chains/graph_qa/prompts.d.cts.map +1 -0
  19. package/dist/chains/graph_qa/prompts.d.ts +14 -0
  20. package/dist/chains/graph_qa/prompts.d.ts.map +1 -0
  21. package/dist/chains/graph_qa/prompts.js +40 -0
  22. package/dist/chains/graph_qa/prompts.js.map +1 -0
  23. package/dist/graphs/document.cjs +63 -0
  24. package/dist/graphs/document.cjs.map +1 -0
  25. package/dist/graphs/document.d.cts +56 -0
  26. package/dist/graphs/document.d.cts.map +1 -0
  27. package/dist/graphs/document.d.ts +56 -0
  28. package/dist/graphs/document.d.ts.map +1 -0
  29. package/dist/graphs/document.js +60 -0
  30. package/dist/graphs/document.js.map +1 -0
  31. package/dist/graphs/index.cjs +3 -0
  32. package/dist/graphs/index.d.ts +3 -0
  33. package/dist/graphs/index.js +4 -0
  34. package/dist/graphs/memgraph_graph.cjs +74 -0
  35. package/dist/graphs/memgraph_graph.cjs.map +1 -0
  36. package/dist/graphs/memgraph_graph.d.cts +36 -0
  37. package/dist/graphs/memgraph_graph.d.cts.map +1 -0
  38. package/dist/graphs/memgraph_graph.d.ts +36 -0
  39. package/dist/graphs/memgraph_graph.d.ts.map +1 -0
  40. package/dist/graphs/memgraph_graph.js +74 -0
  41. package/dist/graphs/memgraph_graph.js.map +1 -0
  42. package/dist/graphs/neo4j_graph.cjs +429 -0
  43. package/dist/graphs/neo4j_graph.cjs.map +1 -0
  44. package/dist/graphs/neo4j_graph.d.cts +96 -0
  45. package/dist/graphs/neo4j_graph.d.cts.map +1 -0
  46. package/dist/graphs/neo4j_graph.d.ts +96 -0
  47. package/dist/graphs/neo4j_graph.d.ts.map +1 -0
  48. package/dist/graphs/neo4j_graph.js +425 -0
  49. package/dist/graphs/neo4j_graph.js.map +1 -0
  50. package/dist/index.cjs +28 -0
  51. package/dist/index.d.cts +8 -0
  52. package/dist/index.d.ts +8 -0
  53. package/dist/index.js +12 -0
  54. package/dist/stores/message/index.cjs +1 -0
  55. package/dist/stores/message/index.d.ts +1 -0
  56. package/dist/stores/message/index.js +2 -0
  57. package/dist/stores/message/neo4j.cjs +114 -0
  58. package/dist/stores/message/neo4j.cjs.map +1 -0
  59. package/dist/stores/message/neo4j.d.cts +40 -0
  60. package/dist/stores/message/neo4j.d.cts.map +1 -0
  61. package/dist/stores/message/neo4j.d.ts +40 -0
  62. package/dist/stores/message/neo4j.d.ts.map +1 -0
  63. package/dist/stores/message/neo4j.js +112 -0
  64. package/dist/stores/message/neo4j.js.map +1 -0
  65. package/dist/vectorstores/index.cjs +1 -0
  66. package/dist/vectorstores/index.d.ts +1 -0
  67. package/dist/vectorstores/index.js +2 -0
  68. package/dist/vectorstores/neo4j_vector.cjs +622 -0
  69. package/dist/vectorstores/neo4j_vector.cjs.map +1 -0
  70. package/dist/vectorstores/neo4j_vector.d.cts +96 -0
  71. package/dist/vectorstores/neo4j_vector.d.cts.map +1 -0
  72. package/dist/vectorstores/neo4j_vector.d.ts +96 -0
  73. package/dist/vectorstores/neo4j_vector.d.ts.map +1 -0
  74. package/dist/vectorstores/neo4j_vector.js +616 -0
  75. package/dist/vectorstores/neo4j_vector.js.map +1 -0
  76. package/package.json +98 -0
@@ -0,0 +1,36 @@
1
+ import { Neo4jGraph } from "./neo4j_graph.cjs";
2
+
3
+ //#region src/graphs/memgraph_graph.d.ts
4
+ interface MemgraphGraphConfig {
5
+ url: string;
6
+ username: string;
7
+ password: string;
8
+ database?: string;
9
+ }
10
+ /**
11
+ * @security *Security note*: Make sure that the database connection uses credentials
12
+ * that are narrowly-scoped to only include necessary permissions.
13
+ * Failure to do so may result in data corruption or loss, since the calling
14
+ * code may attempt commands that would result in deletion, mutation
15
+ * of data if appropriately prompted or reading sensitive data if such
16
+ * data is present in the database.
17
+ * The best way to guard against such negative outcomes is to (as appropriate)
18
+ * limit the permissions granted to the credentials used with this tool.
19
+ * For example, creating read only users for the database is a good way to
20
+ * ensure that the calling code cannot mutate or delete data.
21
+ *
22
+ * @link See https://js.langchain.com/docs/security for more information.
23
+ */
24
+ declare class MemgraphGraph extends Neo4jGraph {
25
+ constructor({
26
+ url,
27
+ username,
28
+ password,
29
+ database
30
+ }: MemgraphGraphConfig);
31
+ static initialize(config: MemgraphGraphConfig): Promise<MemgraphGraph>;
32
+ refreshSchema(): Promise<void>;
33
+ }
34
+ //#endregion
35
+ export { MemgraphGraph, MemgraphGraphConfig };
36
+ //# sourceMappingURL=memgraph_graph.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memgraph_graph.d.cts","names":[],"sources":["../../src/graphs/memgraph_graph.ts"],"mappings":";;;UAEiB,mBAAA;EACf,GAAA;EACA,QAAA;EACA,QAAA;EACA,QAAA;AAAA;;;;;;;AAuBF;;;;;;;;cAAa,aAAA,SAAsB,UAAA;EACjC,WAAA,CAAA;IACE,GAAA;IACA,QAAA;IACA,QAAA;IACA;EAAA,GACC,mBAAA;EAAA,OAIU,UAAA,CAAW,MAAA,EAAQ,mBAAA,GAAsB,OAAA,CAAQ,aAAA;EAoBxD,aAAA,CAAA,GAAa,OAAA;AAAA"}
@@ -0,0 +1,36 @@
1
+ import { Neo4jGraph } from "./neo4j_graph.js";
2
+
3
+ //#region src/graphs/memgraph_graph.d.ts
4
+ interface MemgraphGraphConfig {
5
+ url: string;
6
+ username: string;
7
+ password: string;
8
+ database?: string;
9
+ }
10
+ /**
11
+ * @security *Security note*: Make sure that the database connection uses credentials
12
+ * that are narrowly-scoped to only include necessary permissions.
13
+ * Failure to do so may result in data corruption or loss, since the calling
14
+ * code may attempt commands that would result in deletion, mutation
15
+ * of data if appropriately prompted or reading sensitive data if such
16
+ * data is present in the database.
17
+ * The best way to guard against such negative outcomes is to (as appropriate)
18
+ * limit the permissions granted to the credentials used with this tool.
19
+ * For example, creating read only users for the database is a good way to
20
+ * ensure that the calling code cannot mutate or delete data.
21
+ *
22
+ * @link See https://js.langchain.com/docs/security for more information.
23
+ */
24
+ declare class MemgraphGraph extends Neo4jGraph {
25
+ constructor({
26
+ url,
27
+ username,
28
+ password,
29
+ database
30
+ }: MemgraphGraphConfig);
31
+ static initialize(config: MemgraphGraphConfig): Promise<MemgraphGraph>;
32
+ refreshSchema(): Promise<void>;
33
+ }
34
+ //#endregion
35
+ export { MemgraphGraph, MemgraphGraphConfig };
36
+ //# sourceMappingURL=memgraph_graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memgraph_graph.d.ts","names":[],"sources":["../../src/graphs/memgraph_graph.ts"],"mappings":";;;UAEiB,mBAAA;EACf,GAAA;EACA,QAAA;EACA,QAAA;EACA,QAAA;AAAA;;;;;;;AAuBF;;;;;;;;cAAa,aAAA,SAAsB,UAAA;EACjC,WAAA,CAAA;IACE,GAAA;IACA,QAAA;IACA,QAAA;IACA;EAAA,GACC,mBAAA;EAAA,OAIU,UAAA,CAAW,MAAA,EAAQ,mBAAA,GAAsB,OAAA,CAAQ,aAAA;EAoBxD,aAAA,CAAA,GAAa,OAAA;AAAA"}
@@ -0,0 +1,74 @@
1
+ import { Neo4jGraph } from "./neo4j_graph.js";
2
+ //#region src/graphs/memgraph_graph.ts
3
+ const rawSchemaQuery = `
4
+ CALL llm_util.schema("raw")
5
+ YIELD *
6
+ RETURN *
7
+ `;
8
+ /**
9
+ * @security *Security note*: Make sure that the database connection uses credentials
10
+ * that are narrowly-scoped to only include necessary permissions.
11
+ * Failure to do so may result in data corruption or loss, since the calling
12
+ * code may attempt commands that would result in deletion, mutation
13
+ * of data if appropriately prompted or reading sensitive data if such
14
+ * data is present in the database.
15
+ * The best way to guard against such negative outcomes is to (as appropriate)
16
+ * limit the permissions granted to the credentials used with this tool.
17
+ * For example, creating read only users for the database is a good way to
18
+ * ensure that the calling code cannot mutate or delete data.
19
+ *
20
+ * @link See https://js.langchain.com/docs/security for more information.
21
+ */
22
+ var MemgraphGraph = class MemgraphGraph extends Neo4jGraph {
23
+ constructor({ url, username, password, database = "memgraph" }) {
24
+ super({
25
+ url,
26
+ username,
27
+ password,
28
+ database
29
+ });
30
+ }
31
+ static async initialize(config) {
32
+ const graph = new MemgraphGraph(config);
33
+ try {
34
+ await graph.verifyConnectivity();
35
+ } catch {
36
+ console.error("Failed to verify connection.");
37
+ }
38
+ try {
39
+ await graph.refreshSchema();
40
+ console.debug("Schema refreshed successfully.");
41
+ } catch (e) {
42
+ const error = e;
43
+ throw new Error(error.message);
44
+ }
45
+ return graph;
46
+ }
47
+ async refreshSchema() {
48
+ const rawSchemaQueryResult = await this.query(rawSchemaQuery);
49
+ if (rawSchemaQueryResult?.[0]?.schema) {
50
+ const rawSchema = rawSchemaQueryResult?.[0]?.schema;
51
+ this.structuredSchema = {
52
+ nodeProps: rawSchema.node_props,
53
+ relProps: rawSchema.rel_props,
54
+ relationships: rawSchema.relationships
55
+ };
56
+ this.schema = [
57
+ "Node properties are the following:",
58
+ Object.entries(rawSchema.node_props).map(([nodeName, properties]) => {
59
+ return `Node name: '${nodeName}', Node properties: ${JSON.stringify(properties)}`;
60
+ }).join("\n"),
61
+ "Relationship properties are the following:",
62
+ Object.entries(rawSchema.rel_props).map(([relationshipName, properties]) => {
63
+ return `Relationship name: '${relationshipName}', Relationship properties: ${JSON.stringify(properties)}`;
64
+ }).join("\n"),
65
+ "The relationships are the following:",
66
+ rawSchema.relationships?.map((el) => `(:${el.start})-[:${el.type}]->(:${el.end})`).join("\n")
67
+ ].join("\n");
68
+ }
69
+ }
70
+ };
71
+ //#endregion
72
+ export { MemgraphGraph };
73
+
74
+ //# sourceMappingURL=memgraph_graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memgraph_graph.js","names":[],"sources":["../../src/graphs/memgraph_graph.ts"],"sourcesContent":["import { Neo4jGraph } from \"./neo4j_graph.js\";\n\nexport interface MemgraphGraphConfig {\n url: string;\n username: string;\n password: string;\n database?: string;\n}\n\nconst rawSchemaQuery = `\nCALL llm_util.schema(\"raw\")\nYIELD *\nRETURN *\n`;\n\n/**\n * @security *Security note*: Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling\n * code may attempt commands that would result in deletion, mutation\n * of data if appropriately prompted or reading sensitive data if such\n * data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate)\n * limit the permissions granted to the credentials used with this tool.\n * For example, creating read only users for the database is a good way to\n * ensure that the calling code cannot mutate or delete data.\n *\n * @link See https://js.langchain.com/docs/security for more information.\n */\nexport class MemgraphGraph extends Neo4jGraph {\n constructor({\n url,\n username,\n password,\n database = \"memgraph\",\n }: MemgraphGraphConfig) {\n super({ url, username, password, database });\n }\n\n static async initialize(config: MemgraphGraphConfig): Promise<MemgraphGraph> {\n const graph = new MemgraphGraph(config);\n\n try {\n await graph.verifyConnectivity();\n } catch {\n console.error(\"Failed to verify connection.\");\n }\n\n try {\n await graph.refreshSchema();\n console.debug(\"Schema refreshed successfully.\");\n } catch (e) {\n const error = e as Error;\n throw new Error(error.message);\n }\n\n return graph;\n }\n\n async refreshSchema() {\n const rawSchemaQueryResult = await this.query(rawSchemaQuery);\n if (rawSchemaQueryResult?.[0]?.schema) {\n const rawSchema = rawSchemaQueryResult?.[0]?.schema;\n\n this.structuredSchema = {\n nodeProps: rawSchema.node_props,\n relProps: rawSchema.rel_props,\n relationships: rawSchema.relationships,\n };\n\n const formattedNodeProps = Object.entries(rawSchema.node_props)\n .map(([nodeName, properties]) => {\n const propertiesStr = JSON.stringify(properties);\n return `Node name: '${nodeName}', Node properties: ${propertiesStr}`;\n })\n .join(\"\\n\");\n\n const formattedRelProps = Object.entries(rawSchema.rel_props)\n .map(([relationshipName, properties]) => {\n const propertiesStr = JSON.stringify(properties);\n return `Relationship name: '${relationshipName}', Relationship properties: ${propertiesStr}`;\n })\n .join(\"\\n\");\n\n const formattedRels = rawSchema.relationships\n ?.map(\n (el: { end: string; start: string; type: string }) =>\n `(:${el.start})-[:${el.type}]->(:${el.end})`\n )\n .join(\"\\n\");\n\n this.schema = [\n \"Node properties are the following:\",\n formattedNodeProps,\n \"Relationship properties are the following:\",\n formattedRelProps,\n \"The relationships are the following:\",\n formattedRels,\n ].join(\"\\n\");\n }\n }\n}\n"],"mappings":";;AASA,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;AAoBvB,IAAa,gBAAb,MAAa,sBAAsB,WAAW;CAC5C,YAAY,EACV,KACA,UACA,UACA,WAAW,cACW;AACtB,QAAM;GAAE;GAAK;GAAU;GAAU;GAAU,CAAC;;CAG9C,aAAa,WAAW,QAAqD;EAC3E,MAAM,QAAQ,IAAI,cAAc,OAAO;AAEvC,MAAI;AACF,SAAM,MAAM,oBAAoB;UAC1B;AACN,WAAQ,MAAM,+BAA+B;;AAG/C,MAAI;AACF,SAAM,MAAM,eAAe;AAC3B,WAAQ,MAAM,iCAAiC;WACxC,GAAG;GACV,MAAM,QAAQ;AACd,SAAM,IAAI,MAAM,MAAM,QAAQ;;AAGhC,SAAO;;CAGT,MAAM,gBAAgB;EACpB,MAAM,uBAAuB,MAAM,KAAK,MAAM,eAAe;AAC7D,MAAI,uBAAuB,IAAI,QAAQ;GACrC,MAAM,YAAY,uBAAuB,IAAI;AAE7C,QAAK,mBAAmB;IACtB,WAAW,UAAU;IACrB,UAAU,UAAU;IACpB,eAAe,UAAU;IAC1B;AAuBD,QAAK,SAAS;IACZ;IAtByB,OAAO,QAAQ,UAAU,WAAW,CAC5D,KAAK,CAAC,UAAU,gBAAgB;AAE/B,YAAO,eAAe,SAAS,sBADT,KAAK,UAAU,WAAW;MAEhD,CACD,KAAK,KAAK;IAmBX;IAjBwB,OAAO,QAAQ,UAAU,UAAU,CAC1D,KAAK,CAAC,kBAAkB,gBAAgB;AAEvC,YAAO,uBAAuB,iBAAiB,8BADzB,KAAK,UAAU,WAAW;MAEhD,CACD,KAAK,KAAK;IAcX;IAZoB,UAAU,eAC5B,KACC,OACC,KAAK,GAAG,MAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI,GAC7C,CACA,KAAK,KAAK;IASZ,CAAC,KAAK,KAAK"}
@@ -0,0 +1,429 @@
1
+ const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
2
+ let neo4j_driver = require("neo4j-driver");
3
+ neo4j_driver = require_runtime.__toESM(neo4j_driver);
4
+ let _langchain_core_utils_hash = require("@langchain/core/utils/hash");
5
+ //#region src/graphs/neo4j_graph.ts
6
+ const BASE_ENTITY_LABEL = "__Entity__";
7
+ const DISTINCT_VALUE_LIMIT = 10;
8
+ const LIST_LIMIT = 128;
9
+ const EXHAUSTIVE_SEARCH_LIMIT = 1e4;
10
+ const EXCLUDED_LABELS = ["Bloom_Perspective", "Bloom_Scene"];
11
+ const EXCLUDED_RELS = ["Bloom_HAS_SCENE"];
12
+ const INCLUDE_DOCS_QUERY = `
13
+ MERGE (d:Document {id:$document.metadata.id})
14
+ SET d.text = $document.pageContent
15
+ SET d += $document.metadata
16
+ WITH d
17
+ `;
18
+ const NODE_PROPERTIES_QUERY = `
19
+ CALL apoc.meta.data()
20
+ YIELD label, other, elementType, type, property
21
+ WHERE NOT type = "RELATIONSHIP" AND elementType = "node"
22
+ AND NOT label IN $EXCLUDED_LABELS
23
+ WITH label AS nodeLabels, collect({property:property, type:type}) AS properties
24
+ RETURN {labels: nodeLabels, properties: properties} AS output
25
+ `;
26
+ const REL_PROPERTIES_QUERY = `
27
+ CALL apoc.meta.data()
28
+ YIELD label, other, elementType, type, property
29
+ WHERE NOT type = "RELATIONSHIP" AND elementType = "relationship"
30
+ AND NOT label in $EXCLUDED_LABELS
31
+ WITH label AS nodeLabels, collect({property:property, type:type}) AS properties
32
+ RETURN {type: nodeLabels, properties: properties} AS output
33
+ `;
34
+ const REL_QUERY = `
35
+ CALL apoc.meta.data()
36
+ YIELD label, other, elementType, type, property
37
+ WHERE type = "RELATIONSHIP" AND elementType = "node"
38
+ UNWIND other AS other_node
39
+ WITH * WHERE NOT label IN $EXCLUDED_LABELS
40
+ AND NOT other_node IN $EXCLUDED_LABELS
41
+ RETURN {start: label, type: property, end: toString(other_node)} AS output
42
+ `;
43
+ function isDistinctMoreThanLimit(distinct_count = 11, limit = DISTINCT_VALUE_LIMIT) {
44
+ return distinct_count !== void 0 && distinct_count > limit;
45
+ }
46
+ function cleanStringValues(text) {
47
+ return text.replace(/\n/g, " ").replace(/\r/g, " ");
48
+ }
49
+ function formatSchema(schema, isEnhanced) {
50
+ let formattedNodeProps = [];
51
+ let formattedRelProps = [];
52
+ if (isEnhanced) {
53
+ for (const [nodeType, properties] of Object.entries(schema.nodeProps)) {
54
+ formattedNodeProps.push(`- **${nodeType}**`);
55
+ for (const prop of properties) {
56
+ let example = "";
57
+ if (prop.type === "STRING") {
58
+ if (prop.values.length > 0) if (isDistinctMoreThanLimit(prop.distinct_count)) example = `Example: ${cleanStringValues(prop.values[0])}`;
59
+ else example = `Available options: ${prop.values.map(cleanStringValues).join(", ")}`;
60
+ } else if ([
61
+ "INTEGER",
62
+ "FLOAT",
63
+ "DATE",
64
+ "DATE_TIME",
65
+ "LOCAL_DATE_TIME"
66
+ ].includes(prop.type)) {
67
+ if (prop.min !== void 0) example = `Min: ${prop.min}, Max: ${prop.max}`;
68
+ else if (prop.values.length > 0) example = `Example: ${prop.values[0]}`;
69
+ } else if (prop.type === "LIST") {
70
+ if (!prop.min_size || prop.min_size > LIST_LIMIT) continue;
71
+ example = `Min Size: ${prop.min_size}, Max Size: ${prop.max_size}`;
72
+ }
73
+ formattedNodeProps.push(` - \`${prop.property}\`: ${prop.type} ${example}`);
74
+ }
75
+ }
76
+ for (const [relType, properties] of Object.entries(schema.relProps)) {
77
+ formattedRelProps.push(`- **${relType}**`);
78
+ for (const prop of properties) {
79
+ let example = "";
80
+ if (prop.type === "STRING") {
81
+ if (prop.values.length > 0) if (isDistinctMoreThanLimit(prop.distinct_count)) example = `Example: ${cleanStringValues(prop.values[0])}`;
82
+ else example = `Available options: ${prop.values.map(cleanStringValues).join(", ")}`;
83
+ } else if ([
84
+ "INTEGER",
85
+ "FLOAT",
86
+ "DATE",
87
+ "DATE_TIME",
88
+ "LOCAL_DATE_TIME"
89
+ ].includes(prop.type)) {
90
+ if (prop.min) example = `Min: ${prop.min}, Max: ${prop.max}`;
91
+ else if (prop.values) example = `Example: ${prop.values[0]}`;
92
+ } else if (prop.type === "LIST") {
93
+ if (prop.min_size > LIST_LIMIT) continue;
94
+ example = `Min Size: ${prop.min_size}, Max Size: ${prop.max_size}`;
95
+ }
96
+ formattedRelProps.push(` - \`${prop.property}\`: ${prop.type} ${example}`);
97
+ }
98
+ }
99
+ } else {
100
+ formattedNodeProps = Object.entries(schema.nodeProps).map(([key, value]) => {
101
+ return `${key} {${value.map((prop) => `${prop.property}: ${prop.type}`).join(", ")}}`;
102
+ });
103
+ formattedRelProps = Object.entries(schema.relProps).map(([key, value]) => {
104
+ return `${key} {${value.map((prop) => `${prop.property}: ${prop.type} `).join(", ")} } `;
105
+ });
106
+ }
107
+ const formattedRels = schema.relationships.map((el) => `(: ${el.start}) - [: ${el.type}] -> (:${el.end})`);
108
+ return [
109
+ "Node properties are the following:",
110
+ formattedNodeProps?.join(", "),
111
+ "Relationship properties are the following:",
112
+ formattedRelProps?.join(", "),
113
+ "The relationships are the following:",
114
+ formattedRels?.join(", ")
115
+ ].join("\n");
116
+ }
117
+ /**
118
+ * @security *Security note*: Make sure that the database connection uses credentials
119
+ * that are narrowly-scoped to only include necessary permissions.
120
+ * Failure to do so may result in data corruption or loss, since the calling
121
+ * code may attempt commands that would result in deletion, mutation
122
+ * of data if appropriately prompted or reading sensitive data if such
123
+ * data is present in the database.
124
+ * The best way to guard against such negative outcomes is to (as appropriate)
125
+ * limit the permissions granted to the credentials used with this tool.
126
+ * For example, creating read only users for the database is a good way to
127
+ * ensure that the calling code cannot mutate or delete data.
128
+ *
129
+ * @link See https://js.langchain.com/docs/security for more information.
130
+ */
131
+ var Neo4jGraph = class Neo4jGraph {
132
+ driver;
133
+ database;
134
+ timeoutMs;
135
+ enhancedSchema;
136
+ schema = "";
137
+ structuredSchema = {
138
+ nodeProps: {},
139
+ relProps: {},
140
+ relationships: [],
141
+ metadata: {
142
+ constraint: {},
143
+ index: {}
144
+ }
145
+ };
146
+ constructor({ url, username, password, database = "neo4j", timeoutMs, enhancedSchema = false }) {
147
+ try {
148
+ this.driver = neo4j_driver.default.driver(url, neo4j_driver.default.auth.basic(username, password));
149
+ this.database = database;
150
+ this.timeoutMs = timeoutMs;
151
+ this.enhancedSchema = enhancedSchema;
152
+ } catch {
153
+ throw new Error("Could not create a Neo4j driver instance. Please check the connection details.");
154
+ }
155
+ }
156
+ static async initialize(config) {
157
+ const graph = new Neo4jGraph(config);
158
+ await graph.verifyConnectivity();
159
+ try {
160
+ await graph.refreshSchema();
161
+ } catch (error) {
162
+ if (error.code === "Neo.ClientError.Procedure.ProcedureNotFound") throw new Error("Could not use APOC procedures. Please ensure the APOC plugin is installed in Neo4j and that 'apoc.meta.data()' is allowed in Neo4j configuration.");
163
+ throw error;
164
+ } finally {
165
+ console.log("Schema refreshed successfully.");
166
+ }
167
+ return graph;
168
+ }
169
+ getSchema() {
170
+ return this.schema;
171
+ }
172
+ getStructuredSchema() {
173
+ return this.structuredSchema;
174
+ }
175
+ async query(query, params = {}, routing = neo4j_driver.default.routing.WRITE) {
176
+ return toObjects((await this.driver.executeQuery(query, params, {
177
+ database: this.database,
178
+ routing,
179
+ transactionConfig: { timeout: this.timeoutMs }
180
+ })).records);
181
+ }
182
+ async verifyConnectivity() {
183
+ await this.driver.getServerInfo();
184
+ }
185
+ async refreshSchema() {
186
+ const nodeProperties = (await this.query(NODE_PROPERTIES_QUERY, { EXCLUDED_LABELS: EXCLUDED_LABELS.concat([BASE_ENTITY_LABEL]) }))?.map((el) => el.output);
187
+ const relationshipsProperties = (await this.query(REL_PROPERTIES_QUERY, { EXCLUDED_LABELS: EXCLUDED_RELS }))?.map((el) => el.output);
188
+ const relationships = (await this.query(REL_QUERY, { EXCLUDED_LABELS: EXCLUDED_LABELS.concat([BASE_ENTITY_LABEL]) }))?.map((el) => el.output);
189
+ const constraint = await this.query("SHOW CONSTRAINTS");
190
+ const index = await this.query("SHOW INDEXES YIELD *");
191
+ this.structuredSchema = {
192
+ nodeProps: Object.fromEntries(nodeProperties?.map((el) => [el.labels, el.properties]) || []),
193
+ relProps: Object.fromEntries(relationshipsProperties?.map((el) => [el.type, el.properties]) || []),
194
+ relationships: relationships || [],
195
+ metadata: {
196
+ constraint,
197
+ index
198
+ }
199
+ };
200
+ if (this.enhancedSchema) {
201
+ const schemaCounts = await this.query("CALL apoc.meta.graphSample() YIELD nodes, relationships RETURN nodes, [rel in relationships | {name: apoc.any.property(rel, 'type'), count: apoc.any.property(rel, 'count')}] AS relationships");
202
+ for (const node of schemaCounts[0].nodes) {
203
+ if (EXCLUDED_LABELS.includes(node.name)) continue;
204
+ const nodeProps = this.structuredSchema.nodeProps[node.name];
205
+ if (!nodeProps) continue;
206
+ const enhancedCypher = await this.enhancedSchemaCypher(node.name, nodeProps, node.count < EXHAUSTIVE_SEARCH_LIMIT);
207
+ const enhancedInfo = (await this.query(enhancedCypher))[0].output;
208
+ for (const prop of nodeProps) if (enhancedInfo[prop.property]) Object.assign(prop, enhancedInfo[prop.property]);
209
+ }
210
+ for (const rel of schemaCounts[0].relationships) {
211
+ if (EXCLUDED_RELS.includes(rel.name)) continue;
212
+ const relProps = this.structuredSchema.relProps[rel.name];
213
+ if (!relProps) continue;
214
+ const enhancedCypher = await this.enhancedSchemaCypher(rel.name, relProps, rel.count < EXHAUSTIVE_SEARCH_LIMIT, true);
215
+ const enhancedInfo = (await this.query(enhancedCypher))[0].output;
216
+ for (const prop of relProps) if (prop.property in enhancedInfo) Object.assign(prop, enhancedInfo[prop.property]);
217
+ }
218
+ }
219
+ this.schema = formatSchema(this.structuredSchema, this.enhancedSchema);
220
+ }
221
+ async enhancedSchemaCypher(labelOrType, properties, exhaustive, isRelationship = false) {
222
+ let matchClause = isRelationship ? `MATCH ()-[n:\`${labelOrType}\`]->()` : `MATCH (n:\`${labelOrType}\`)`;
223
+ const withClauses = [];
224
+ const returnClauses = [];
225
+ const outputDict = {};
226
+ if (exhaustive) for (const prop of properties) {
227
+ const propName = prop.property;
228
+ const propType = prop.type;
229
+ if (propType === "STRING") {
230
+ withClauses.push(`collect(distinct substring(n.\`${propName}\`, 0, 50)) AS \`${propName}_values\``);
231
+ returnClauses.push(`values: \`${propName}_values\`[..${DISTINCT_VALUE_LIMIT}], distinct_count: size(\`${propName}_values\`)`);
232
+ } else if ([
233
+ "INTEGER",
234
+ "FLOAT",
235
+ "DATE",
236
+ "DATE_TIME",
237
+ "LOCAL_DATE_TIME"
238
+ ].includes(propType)) {
239
+ withClauses.push(`min(n.\`${propName}\`) AS \`${propName}_min\``);
240
+ withClauses.push(`max(n.\`${propName}\`) AS \`${propName}_max\``);
241
+ withClauses.push(`count(distinct n.\`${propName}\`) AS \`${propName}_distinct\``);
242
+ returnClauses.push(`min: toString(\`${propName}_min\`), max: toString(\`${propName}_max\`), distinct_count: \`${propName}_distinct\``);
243
+ } else if (propType === "LIST") {
244
+ withClauses.push(`min(size(n.\`${propName}\`)) AS \`${propName}_size_min\`, max(size(n.\`${propName}\`)) AS \`${propName}_size_max\``);
245
+ returnClauses.push(`min_size: \`${propName}_size_min\`, max_size: \`${propName}_size_max\``);
246
+ } else if ([
247
+ "BOOLEAN",
248
+ "POINT",
249
+ "DURATION"
250
+ ].includes(propType)) continue;
251
+ outputDict[propName] = `{${returnClauses.pop()}}`;
252
+ }
253
+ else {
254
+ matchClause += ` WITH n LIMIT 5`;
255
+ for (const prop of properties) {
256
+ const propName = prop.property;
257
+ const propType = prop.type;
258
+ const propIndex = this.structuredSchema?.metadata?.index.filter((el) => el.label === labelOrType && el.properties[0] === propName && el.type === "RANGE");
259
+ if (propType === "STRING") if (propIndex.length > 0 && propIndex[0].size > 0 && propIndex[0].distinctValues <= DISTINCT_VALUE_LIMIT) {
260
+ const distinctValues = (await this.query(`CALL apoc.schema.properties.distinct('${labelOrType}', '${propName}') YIELD value`))[0].value;
261
+ returnClauses.push(`values: ${distinctValues}, distinct_count: ${distinctValues.length}`);
262
+ } else {
263
+ withClauses.push(`collect(distinct substring(n.\`${propName}\`, 0, 50)) AS \`${propName}_values\``);
264
+ returnClauses.push(`values: ${propName}_values`);
265
+ }
266
+ else if ([
267
+ "INTEGER",
268
+ "FLOAT",
269
+ "DATE",
270
+ "DATE_TIME",
271
+ "LOCAL_DATE_TIME"
272
+ ].includes(propType)) if (!propIndex) {
273
+ withClauses.push(`collect(distinct toString(n.\`${propName}\`)) AS \`${propName}_values\``);
274
+ returnClauses.push(`values: ${propName}_values`);
275
+ } else {
276
+ withClauses.push(`min(n.\`${propName}\`) AS \`${propName}_min\``);
277
+ withClauses.push(`max(n.\`${propName}\`) AS \`${propName}_max\``);
278
+ withClauses.push(`count(distinct n.\`${propName}\`) AS \`${propName}_distinct\``);
279
+ returnClauses.push(`min: toString(\`${propName}_min\`), max: toString(\`${propName}_max\`), distinct_count: \`${propName}_distinct\``);
280
+ }
281
+ else if (propType === "LIST") {
282
+ withClauses.push(`min(size(n.\`${propName}\`)) AS \`${propName}_size_min\`, max(size(n.\`${propName}\`)) AS \`${propName}_size_max\``);
283
+ returnClauses.push(`min_size: \`${propName}_size_min\`, max_size: \`${propName}_size_max\``);
284
+ } else if ([
285
+ "BOOLEAN",
286
+ "POINT",
287
+ "DURATION"
288
+ ].includes(propType)) continue;
289
+ outputDict[propName] = `{${returnClauses.pop()}}`;
290
+ }
291
+ }
292
+ const withClause = `WITH ${withClauses.join(", ")}`;
293
+ const returnClause = `RETURN {${Object.entries(outputDict).map(([k, v]) => `\`${k}\`: ${v}`).join(", ")}} AS output`;
294
+ return [
295
+ matchClause,
296
+ withClause,
297
+ returnClause
298
+ ].join("\n");
299
+ }
300
+ async addGraphDocuments(graphDocuments, config = {}) {
301
+ const { baseEntityLabel } = config;
302
+ if (baseEntityLabel) {
303
+ if (!(this.structuredSchema?.metadata?.constraint?.some((el) => JSON.stringify(el.labelsOrTypes) === JSON.stringify(["__Entity__"]) && JSON.stringify(el.properties) === JSON.stringify(["id"])) ?? false)) {
304
+ await this.query(`
305
+ CREATE CONSTRAINT IF NOT EXISTS FOR (b:${BASE_ENTITY_LABEL})
306
+ REQUIRE b.id IS UNIQUE;
307
+ `);
308
+ await this.refreshSchema();
309
+ }
310
+ }
311
+ const nodeImportQuery = getNodeImportQuery(config);
312
+ const relImportQuery = getRelImportQuery(config);
313
+ for (const document of graphDocuments) {
314
+ if (!document.source.metadata.id) document.source.metadata.id = (0, _langchain_core_utils_hash.sha256)(document.source.pageContent);
315
+ await this.query(nodeImportQuery, {
316
+ data: document.nodes.map((el) => ({ ...el })),
317
+ document: { ...document.source }
318
+ });
319
+ await this.query(relImportQuery, { data: document.relationships.map((el) => ({
320
+ source: el.source.id,
321
+ source_label: el.source.type,
322
+ target: el.target.id,
323
+ target_label: el.target.type,
324
+ type: el.type.replace(/ /g, "_").toUpperCase(),
325
+ properties: el.properties
326
+ })) });
327
+ }
328
+ }
329
+ async close() {
330
+ await this.driver.close();
331
+ }
332
+ };
333
+ function getNodeImportQuery({ baseEntityLabel, includeSource }) {
334
+ if (baseEntityLabel) return `
335
+ ${includeSource ? INCLUDE_DOCS_QUERY : ""}
336
+ UNWIND $data AS row
337
+ MERGE(source: \`${BASE_ENTITY_LABEL}\` {id: row.id})
338
+ SET source += row.properties
339
+ ${includeSource ? "MERGE (d)-[:MENTIONS]->(source)" : ""}
340
+ WITH source, row
341
+ CALL apoc.create.addLabels(source, [row.type]) YIELD node
342
+ RETURN distinct 'done' AS result
343
+ `;
344
+ else return `
345
+ ${includeSource ? INCLUDE_DOCS_QUERY : ""}
346
+ UNWIND $data AS row
347
+ CALL apoc.merge.node([row.type], {id: row.id},
348
+ row.properties, {}) YIELD node
349
+ ${includeSource ? "MERGE (d)-[:MENTIONS]->(node)" : ""}
350
+ RETURN distinct 'done' AS result
351
+ `;
352
+ }
353
+ function getRelImportQuery({ baseEntityLabel }) {
354
+ if (baseEntityLabel) return `
355
+ UNWIND $data AS row
356
+ MERGE (source:\`${BASE_ENTITY_LABEL}\` {id: row.source})
357
+ MERGE (target:\`${BASE_ENTITY_LABEL}\` {id: row.target})
358
+ WITH source, target, row
359
+ CALL apoc.merge.relationship(source, row.type,
360
+ {}, row.properties, target) YIELD rel
361
+ RETURN distinct 'done'
362
+ `;
363
+ else return `
364
+ UNWIND $data AS row
365
+ CALL apoc.merge.node([row.source_label], {id: row.source},
366
+ {}, {}) YIELD node as source
367
+ CALL apoc.merge.node([row.target_label], {id: row.target},
368
+ {}, {}) YIELD node as target
369
+ CALL apoc.merge.relationship(source, row.type,
370
+ {}, row.properties, target) YIELD rel
371
+ RETURN distinct 'done'
372
+ `;
373
+ }
374
+ function toObjects(records) {
375
+ return records.map((record) => {
376
+ const rObj = record.toObject();
377
+ const out = {};
378
+ Object.keys(rObj).forEach((key) => {
379
+ out[key] = itemIntToString(rObj[key]);
380
+ });
381
+ return out;
382
+ });
383
+ }
384
+ function itemIntToString(item) {
385
+ if (neo4j_driver.default.isInt(item)) return item.toString();
386
+ if (Array.isArray(item)) return item.map((ii) => itemIntToString(ii));
387
+ if ([
388
+ "number",
389
+ "string",
390
+ "boolean"
391
+ ].indexOf(typeof item) !== -1) return item;
392
+ if (item === null) return item;
393
+ if (typeof item === "object") return objIntToString(item);
394
+ }
395
+ function objIntToString(obj) {
396
+ const entry = extractFromNeoObjects(obj);
397
+ let newObj = null;
398
+ if (Array.isArray(entry)) newObj = entry.map((item) => itemIntToString(item));
399
+ else if (entry !== null && typeof entry === "object") {
400
+ newObj = {};
401
+ Object.keys(entry).forEach((key) => {
402
+ newObj[key] = itemIntToString(entry[key]);
403
+ });
404
+ }
405
+ return newObj;
406
+ }
407
+ function extractFromNeoObjects(obj) {
408
+ if (obj instanceof neo4j_driver.default.types.Node || obj instanceof neo4j_driver.default.types.Relationship) return obj.properties;
409
+ else if (obj instanceof neo4j_driver.default.types.Path) return [].concat.apply([], extractPathForRows(obj));
410
+ return obj;
411
+ }
412
+ const extractPathForRows = (path) => {
413
+ let { segments } = path;
414
+ if (!Array.isArray(path.segments) || path.segments.length < 1) segments = [{
415
+ ...path,
416
+ end: null
417
+ }];
418
+ return segments.map((segment) => [
419
+ objIntToString(segment.start),
420
+ objIntToString(segment.relationship),
421
+ objIntToString(segment.end)
422
+ ].filter((part) => part !== null));
423
+ };
424
+ //#endregion
425
+ exports.BASE_ENTITY_LABEL = BASE_ENTITY_LABEL;
426
+ exports.Neo4jGraph = Neo4jGraph;
427
+ exports.formatSchema = formatSchema;
428
+
429
+ //# sourceMappingURL=neo4j_graph.cjs.map