-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add FalkorDB support #7367
Open
Naseem77
wants to merge
6
commits into
langchain-ai:main
Choose a base branch
from
FalkorDB:falkordb_graph
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+253
−0
Open
add FalkorDB support #7367
Changes from 2 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
4ff411d
add FalkorDB support
Naseem77 08a70d9
Merge branch 'main' into falkordb_graph
gkorland 9aad38e
resolve conflicts and update missing files
Naseem77 dc1c509
remove unnecessary FalkorDB from dependencies
Naseem77 67e82f0
fix lint errors
Naseem77 8044927
Merge branch 'main' into falkordb_graph
Naseem77 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { FalkorDBGraph } from "@langchain/community/graphs/falkordb_graph"; | ||
import { OpenAI } from "@langchain/llms/openai"; | ||
import { GraphCypherQAChain } from "langchain/chains/graph_qa/cypher"; | ||
|
||
/** | ||
* This example uses FalkorDB database, which is native graph database. | ||
* To set it up follow the instructions on https://docs.falkordb.com/. | ||
*/ | ||
|
||
const url = "bolt://localhost:6379"; | ||
|
||
const graph = await FalkorDBGraph.initialize({ url }); | ||
const model = new OpenAI({ temperature: 0 }); | ||
|
||
// Populate the database with two nodes and a relationship | ||
await graph.query( | ||
"CREATE (a:Actor {name:'Bruce Willis'})" + | ||
"-[:ACTED_IN]->(:Movie {title: 'Pulp Fiction'})" | ||
); | ||
|
||
const chain = GraphCypherQAChain.fromLLM({ | ||
llm: model, | ||
graph, | ||
}); | ||
|
||
const res = await chain.run("Who played in Pulp Fiction?"); | ||
console.log(res); | ||
// Bruce Willis played in Pulp Fiction. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Naseem77 marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import { createClient } from "redis"; | ||
import { Graph } from "redisgraph.js"; | ||
Naseem77 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
|
||
interface FalkorDBGraphConfig { | ||
url: string; | ||
graph?: string; | ||
enhancedSchema?: boolean; | ||
} | ||
|
||
interface StructuredSchema { | ||
nodeProps: { [key: string]: string[] }; | ||
relProps: { [key: string]: string[] }; | ||
relationships: { start: string; type: string; end: string }[]; | ||
} | ||
|
||
export class FalkorDBGraph { | ||
private driver; | ||
private graph: Graph; | ||
private schema = ""; | ||
private structuredSchema: StructuredSchema = { | ||
nodeProps: {}, | ||
relProps: {}, | ||
relationships: [], | ||
}; | ||
private enhancedSchema: boolean; | ||
|
||
constructor({ url, graph = "falkordb", enhancedSchema = false }: FalkorDBGraphConfig) { | ||
try { | ||
this.driver = createClient({ url }); | ||
this.graph = new Graph(graph); // Initialize the Graph instance | ||
this.enhancedSchema = enhancedSchema; | ||
} catch (error) { | ||
throw new Error( | ||
"Could not create a FalkorDB driver instance. Please check the connection details." | ||
); | ||
} | ||
} | ||
|
||
static async initialize(config: FalkorDBGraphConfig): Promise<FalkorDBGraph> { | ||
const graph = new FalkorDBGraph(config); | ||
await graph.verifyConnectivity(); | ||
await graph.refreshSchema(); | ||
return graph; | ||
} | ||
|
||
getSchema(): string { | ||
return this.schema; | ||
} | ||
|
||
getStructuredSchema(): StructuredSchema { | ||
return this.structuredSchema; | ||
} | ||
|
||
async query(query: string): Promise<any[]> { | ||
const resultSet = await this.graph.query(query); // Run the query | ||
const rows = []; | ||
|
||
// Iterate through the ResultSet | ||
while (resultSet.hasNext()) { | ||
const record = resultSet.next(); // Get the next record | ||
const keys = record.keys(); // Get column names | ||
const values = record.values(); // Get values | ||
const obj = Object.fromEntries(keys.map((key, i) => [key, values[i]])); // Map keys to values | ||
rows.push(obj); // Add the object to rows | ||
} | ||
|
||
return rows; | ||
} | ||
|
||
async verifyConnectivity(): Promise<void> { | ||
await this.driver.connect(); // Ensure the Redis client is connected | ||
} | ||
|
||
async refreshSchema(): Promise<void> { | ||
const nodePropertiesQuery = ` | ||
MATCH (n) | ||
WITH keys(n) as keys, labels(n) AS labels | ||
UNWIND labels AS label | ||
UNWIND keys AS key | ||
WITH label, collect(DISTINCT key) AS properties | ||
RETURN {label: label, properties: properties} AS output | ||
`; | ||
|
||
const relPropertiesQuery = ` | ||
MATCH ()-[r]->() | ||
WITH keys(r) as keys, type(r) AS type | ||
UNWIND keys AS key | ||
WITH type, collect(DISTINCT key) AS properties | ||
RETURN {type: type, properties: properties} AS output | ||
`; | ||
|
||
const relQuery = ` | ||
MATCH (n)-[r]->(m) | ||
UNWIND labels(n) as src_label | ||
UNWIND labels(m) as dst_label | ||
RETURN DISTINCT {start: src_label, type: type(r), end: dst_label} AS output | ||
`; | ||
|
||
const nodeProperties = await this.query(nodePropertiesQuery); | ||
const relationshipsProperties = await this.query(relPropertiesQuery); | ||
const relationships = await this.query(relQuery); | ||
|
||
this.structuredSchema = { | ||
nodeProps: Object.fromEntries( | ||
nodeProperties.map((el: { output: { label: string; properties: string[] } }) => [el.output.label, el.output.properties]) | ||
), | ||
relProps: Object.fromEntries( | ||
relationshipsProperties.map((el: { output: { type: string; properties: string[] } }) => [el.output.type, el.output.properties]) | ||
), | ||
relationships: relationships.map((el: { output: { start: string; type: string; end: string } }) => el.output), | ||
}; | ||
|
||
if (this.enhancedSchema) { | ||
this.enhanceSchemaDetails(); | ||
} | ||
|
||
this.schema = this.formatSchema(); | ||
} | ||
|
||
private async enhanceSchemaDetails(): Promise<void> { | ||
console.log("Enhanced schema details not yet implemented for FalkorDB."); | ||
} | ||
|
||
private formatSchema(): string { | ||
const { nodeProps, relProps, relationships } = this.structuredSchema; | ||
|
||
const formattedNodeProps = Object.entries(nodeProps) | ||
.map(([label, props]) => `${label}: {${props.join(", ")}}`) | ||
.join("\n"); | ||
|
||
const formattedRelProps = Object.entries(relProps) | ||
.map(([type, props]) => `${type}: {${props.join(", ")}}`) | ||
.join("\n"); | ||
|
||
const formattedRelationships = relationships | ||
.map((rel) => `(:${rel.start}) -[:${rel.type}]-> (:${rel.end})`) | ||
.join("\n"); | ||
|
||
return [ | ||
"Node properties are the following:", | ||
formattedNodeProps, | ||
"Relationship properties are the following:", | ||
formattedRelProps, | ||
"The relationships are the following:", | ||
formattedRelationships, | ||
].join("\n"); | ||
} | ||
|
||
async close(): Promise<void> { | ||
await this.driver.quit(); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
libs/langchain-community/src/graphs/tests/falkordb_graph.int.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* eslint-disable no-process-env */ | ||
|
||
import { test } from "@jest/globals"; | ||
import { FalkorDBGraph } from "../falkordb_graph.js"; | ||
|
||
describe("FalkorDB Graph Tests", () => { | ||
const url = process.env.FALKORDB_URI as string; | ||
let graph: FalkorDBGraph; | ||
|
||
beforeEach(async () => { | ||
graph = await FalkorDBGraph.initialize({ url }); | ||
await graph.query("MATCH (n) DETACH DELETE n"); | ||
}); | ||
|
||
afterEach(async () => { | ||
await graph.close(); | ||
}); | ||
|
||
test("Test that FalkorDN database is correctly instantiated and connected", async () => { | ||
expect(url).toBeDefined(); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
return graph.query('RETURN "test" AS output').then((output: any) => { | ||
const expectedOutput = [{ output: "test" }]; | ||
expect(output).toEqual(expectedOutput); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe we'll need to change the
GraphCypherQAChain
typing here?