graphrag_sdk 0.1.0__tar.gz

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.
@@ -0,0 +1,155 @@
1
+ Metadata-Version: 2.1
2
+ Name: graphrag_sdk
3
+ Version: 0.1.0
4
+ Summary:
5
+ Home-page: http://RAG-SDK.readthedocs.io
6
+ License: MIT
7
+ Keywords: RAG,Graph-RAG,KnowledgeGraph
8
+ Author: FalkorDB
9
+ Author-email: info@falkordb.com
10
+ Requires-Python: >=3.12,<4.0
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Requires-Dist: bs4 (>=0.0.2,<0.0.3)
15
+ Requires-Dist: falkordb (>=1.0.4,<2.0.0)
16
+ Requires-Dist: openai (>=1.30.3,<2.0.0)
17
+ Requires-Dist: sqlalchemy (>=2.0.30,<3.0.0)
18
+ Requires-Dist: typing-extensions (>=4.12.1,<5.0.0)
19
+ Project-URL: Repository, https://github.com/FalkorDB/GraphRAG-SDK
20
+ Description-Content-Type: text/markdown
21
+
22
+ # GraphRAG-SDK
23
+
24
+ ## Example
25
+
26
+ ### Prerequisites
27
+ GraphRAG-SDK relies on [FalkorDB](http://falkordb.com) as its graph engine and works with OpenAI.
28
+
29
+ Start FalkorDB locally:
30
+ ```sh
31
+ docker run -p 6379:6379 -it --rm -v ./data:/data falkordb/falkordb:edge
32
+ ```
33
+
34
+ Export your OpenAI API KEY:
35
+ ```sh
36
+ export OPENAI_API_KEY=<YOUR_OPENAI_KEY>
37
+ ```
38
+
39
+ ```python
40
+ from graphrag_sdk.schema import Schema
41
+ from graphrag_sdk import KnowledgeGraph, Source
42
+
43
+ # Auto generate graph schema from unstructured data
44
+ sources = [Source("./data/the_matrix.txt")]
45
+ s = Schema.auto_detect(sources)
46
+
47
+ # Create a knowledge graph based on schema
48
+ g = KnowledgeGraph("IMDB", schema=s)
49
+ g.process_sources(sources)
50
+
51
+ # Query your data
52
+ question = "Name a few actors who've played in 'The Matrix'"
53
+ answer, messages = g.ask(question)
54
+ print(f"Answer: {answer}")
55
+
56
+ # Output:
57
+ # Answer: A few actors who've played in 'The Matrix' are:
58
+ # - Keanu Reeves
59
+ # - Laurence Fishburne
60
+ # - Carrie-Anne Moss
61
+ # - Hugo Weaving
62
+ ```
63
+
64
+ ## Introduction
65
+ GraphRAG-SDK provides easy-to-use tooling to get you up and running with your own
66
+ Graph-RAG solution.
67
+
68
+ There are two main components:
69
+ ### Schema
70
+ A `schema` represents the types of entities and relationships within your data.
71
+ For example, the main entities in your data are: Movies, Actors, and Directors.
72
+ These are interconnected via `ACT` and `DIRECTED` edges.
73
+
74
+ Two approaches to schema creation are available:
75
+
76
+ #### Manual schema creation
77
+ Use this method when you know exactly how your data should be structured.
78
+
79
+ ```python
80
+ s = Schema()
81
+ s.add_entity('Actor').add_attribute('name', str, unique=True)
82
+ s.add_entity('Movie').add_attribute('title', str, unique=True)
83
+ s.add_relation("ACTED", 'Actor', 'Movie')
84
+
85
+ print(f"Schema: {s.to_JSON()}")
86
+
87
+ # Output:
88
+ # Schema: {"entities": [
89
+ # {"name": "Actor",
90
+ # "attributes": [
91
+ # {"name": "name", "type": "str", "desc": "Actor's name", "unique": true, "mandatory": false}]},
92
+ # {"name": "Movie",
93
+ # "attributes": [
94
+ # {"name": "title", "type": "str", "desc": "Movie's title", "unique": true, "mandatory": false}]}],
95
+ # "relations": [{"name": "ACTED", "src": "Actor", "dest": "Movie"}]}
96
+ ```
97
+
98
+ #### Automatic schema creation
99
+ Use this method to discover the main entities and relationships within your data.
100
+ Once the schema is discovered, you can adjust it to your liking.
101
+
102
+ ```python
103
+ sources = [Source("./data/madoff.txt")]
104
+ s = Schema.auto_detect(sources)
105
+ json_schema = s.to_JSON()
106
+ print(f"Schema: {json_schema}")
107
+
108
+ # Adjust json to your liking and reload the schema from JSON
109
+ json_schema['entities'].append({"name": "Movie", "attributes": [ {"name": "Title", "type": "str", "desc": "Movie's title", "unique": true, "mandatory": true}])
110
+
111
+ # Recreate refined schema
112
+ s = Schema.from_JSON(json_schema)
113
+
114
+ # Output:
115
+ # Schema: {"entities": [
116
+ # {"name": "Actor", "attributes": [
117
+ # {"name": "Name", "type": "str", "desc": "Actor's Name", "unique": true, "mandatory": true}]},
118
+ # {"name": "Critic", "attributes": [
119
+ # {"name": "TopCritic", "type": "bool", "desc": "Critic's TopCritic", "unique": false, "mandatory": false},
120
+ # {"name": "Name", "type": "str", "desc": "Critic's Name", "unique": true, "mandatory": true},
121
+ # {"name": "Publication", "type": "str", "desc": "Critic's Publication", "unique": false, "mandatory": false}]}...
122
+ ```
123
+
124
+ ### KnowledgeGraph
125
+ A `KnowledgeGraph` holds the actual entities and relationships within your data.
126
+ Once constructed, it serves as the backbone of your RAG solution. A Large Language Model will query your knowledge-graph to build a precise context on which its answer will be based.
127
+
128
+ ```python
129
+ # Create a knowledge graph based on a schema.
130
+ g = KnowledgeGraph("IMDB", host="127.0.0.1", port=6379, schema=s)
131
+ g.process_sources([Source("./data/the_matrix.txt"), Source("./data/wework.txt")])
132
+
133
+ # Query your data
134
+ question = "Name a few actors who've played in 'The Matrix'"
135
+ answer, messages = g.ask(question)
136
+ print(f"Answer: {answer}")
137
+
138
+ question = "List additional actors"
139
+ answer, messages = g.ask(question, messages)
140
+ print(f"Answer: {answer}")
141
+
142
+ # Outputs:
143
+ # Answer: A few actors who've played in 'The Matrix' are:
144
+ # - Keanu Reeves
145
+ # - Laurence Fishburne
146
+ # - Carrie-Anne Moss
147
+ # - Hugo Weaving
148
+ #
149
+ # Answer: A few additional actors who've played in 'The Matrix' are:
150
+ # - Joe Pantoliano
151
+ # - Marcus Chong
152
+ # - Paul Goddard
153
+ # - Gloria Foster
154
+ ```
155
+
@@ -0,0 +1,133 @@
1
+ # GraphRAG-SDK
2
+
3
+ ## Example
4
+
5
+ ### Prerequisites
6
+ GraphRAG-SDK relies on [FalkorDB](http://falkordb.com) as its graph engine and works with OpenAI.
7
+
8
+ Start FalkorDB locally:
9
+ ```sh
10
+ docker run -p 6379:6379 -it --rm -v ./data:/data falkordb/falkordb:edge
11
+ ```
12
+
13
+ Export your OpenAI API KEY:
14
+ ```sh
15
+ export OPENAI_API_KEY=<YOUR_OPENAI_KEY>
16
+ ```
17
+
18
+ ```python
19
+ from graphrag_sdk.schema import Schema
20
+ from graphrag_sdk import KnowledgeGraph, Source
21
+
22
+ # Auto generate graph schema from unstructured data
23
+ sources = [Source("./data/the_matrix.txt")]
24
+ s = Schema.auto_detect(sources)
25
+
26
+ # Create a knowledge graph based on schema
27
+ g = KnowledgeGraph("IMDB", schema=s)
28
+ g.process_sources(sources)
29
+
30
+ # Query your data
31
+ question = "Name a few actors who've played in 'The Matrix'"
32
+ answer, messages = g.ask(question)
33
+ print(f"Answer: {answer}")
34
+
35
+ # Output:
36
+ # Answer: A few actors who've played in 'The Matrix' are:
37
+ # - Keanu Reeves
38
+ # - Laurence Fishburne
39
+ # - Carrie-Anne Moss
40
+ # - Hugo Weaving
41
+ ```
42
+
43
+ ## Introduction
44
+ GraphRAG-SDK provides easy-to-use tooling to get you up and running with your own
45
+ Graph-RAG solution.
46
+
47
+ There are two main components:
48
+ ### Schema
49
+ A `schema` represents the types of entities and relationships within your data.
50
+ For example, the main entities in your data are: Movies, Actors, and Directors.
51
+ These are interconnected via `ACT` and `DIRECTED` edges.
52
+
53
+ Two approaches to schema creation are available:
54
+
55
+ #### Manual schema creation
56
+ Use this method when you know exactly how your data should be structured.
57
+
58
+ ```python
59
+ s = Schema()
60
+ s.add_entity('Actor').add_attribute('name', str, unique=True)
61
+ s.add_entity('Movie').add_attribute('title', str, unique=True)
62
+ s.add_relation("ACTED", 'Actor', 'Movie')
63
+
64
+ print(f"Schema: {s.to_JSON()}")
65
+
66
+ # Output:
67
+ # Schema: {"entities": [
68
+ # {"name": "Actor",
69
+ # "attributes": [
70
+ # {"name": "name", "type": "str", "desc": "Actor's name", "unique": true, "mandatory": false}]},
71
+ # {"name": "Movie",
72
+ # "attributes": [
73
+ # {"name": "title", "type": "str", "desc": "Movie's title", "unique": true, "mandatory": false}]}],
74
+ # "relations": [{"name": "ACTED", "src": "Actor", "dest": "Movie"}]}
75
+ ```
76
+
77
+ #### Automatic schema creation
78
+ Use this method to discover the main entities and relationships within your data.
79
+ Once the schema is discovered, you can adjust it to your liking.
80
+
81
+ ```python
82
+ sources = [Source("./data/madoff.txt")]
83
+ s = Schema.auto_detect(sources)
84
+ json_schema = s.to_JSON()
85
+ print(f"Schema: {json_schema}")
86
+
87
+ # Adjust json to your liking and reload the schema from JSON
88
+ json_schema['entities'].append({"name": "Movie", "attributes": [ {"name": "Title", "type": "str", "desc": "Movie's title", "unique": true, "mandatory": true}])
89
+
90
+ # Recreate refined schema
91
+ s = Schema.from_JSON(json_schema)
92
+
93
+ # Output:
94
+ # Schema: {"entities": [
95
+ # {"name": "Actor", "attributes": [
96
+ # {"name": "Name", "type": "str", "desc": "Actor's Name", "unique": true, "mandatory": true}]},
97
+ # {"name": "Critic", "attributes": [
98
+ # {"name": "TopCritic", "type": "bool", "desc": "Critic's TopCritic", "unique": false, "mandatory": false},
99
+ # {"name": "Name", "type": "str", "desc": "Critic's Name", "unique": true, "mandatory": true},
100
+ # {"name": "Publication", "type": "str", "desc": "Critic's Publication", "unique": false, "mandatory": false}]}...
101
+ ```
102
+
103
+ ### KnowledgeGraph
104
+ A `KnowledgeGraph` holds the actual entities and relationships within your data.
105
+ Once constructed, it serves as the backbone of your RAG solution. A Large Language Model will query your knowledge-graph to build a precise context on which its answer will be based.
106
+
107
+ ```python
108
+ # Create a knowledge graph based on a schema.
109
+ g = KnowledgeGraph("IMDB", host="127.0.0.1", port=6379, schema=s)
110
+ g.process_sources([Source("./data/the_matrix.txt"), Source("./data/wework.txt")])
111
+
112
+ # Query your data
113
+ question = "Name a few actors who've played in 'The Matrix'"
114
+ answer, messages = g.ask(question)
115
+ print(f"Answer: {answer}")
116
+
117
+ question = "List additional actors"
118
+ answer, messages = g.ask(question, messages)
119
+ print(f"Answer: {answer}")
120
+
121
+ # Outputs:
122
+ # Answer: A few actors who've played in 'The Matrix' are:
123
+ # - Keanu Reeves
124
+ # - Laurence Fishburne
125
+ # - Carrie-Anne Moss
126
+ # - Hugo Weaving
127
+ #
128
+ # Answer: A few additional actors who've played in 'The Matrix' are:
129
+ # - Joe Pantoliano
130
+ # - Marcus Chong
131
+ # - Paul Goddard
132
+ # - Gloria Foster
133
+ ```
@@ -0,0 +1,2 @@
1
+ from .kg import KnowledgeGraph
2
+ from .source import Source
@@ -0,0 +1,155 @@
1
+ import os
2
+ import queue
3
+ import concurrent.futures
4
+ from typing_extensions import AbstractSet
5
+
6
+ from .prompts import *
7
+ from .template import TEMPLATE
8
+ from openai import OpenAI
9
+ from falkordb import FalkorDB
10
+ from .query_graph import query
11
+ from .schema.schema import Schema
12
+ from .source import Source, AbstractSource
13
+ from .schema.tools_gen import schema_to_tools
14
+ from .schema.functions_gen import schema_to_functions
15
+
16
+ class KnowledgeGraph(object):
17
+ """Knowledge Graph model data as a network of entities and relations
18
+ To create one it is best to provide a schema which will define the graph's ontology
19
+ In addition to a set of sources from which entities and relations will be extracted.
20
+ """
21
+
22
+ def __init__(self, name:str, host:str="127.0.0.1", port:int=6379, username:str|None=None, password:str|None=None,
23
+ model:str="gpt-4-1106-preview", schema:Schema|None=None):
24
+ """
25
+ Initialize Knowledge Graph
26
+
27
+ Parameters:
28
+ name (str): Knowledge graph name.
29
+ host (str): FalkorDB hostname.
30
+ port (int): FalkorDB port number.
31
+ username (str|None): FalkorDB username.
32
+ password (str|None): FalkorDB password.
33
+ model (str): OpenAI model to use.
34
+ """
35
+
36
+ if not isinstance(name, str) or name == "":
37
+ raise Exception("name should be a non empty string")
38
+
39
+ # connect to database
40
+ self.db = FalkorDB(host=host, port=port, username=username, password=password)
41
+ self.graph = self.db.select_graph(name)
42
+
43
+ self.name = name
44
+ self.model = model
45
+ self.schema = schema
46
+ self.client = None # OpenAI client
47
+ self.sources = set([])
48
+ self.ontolegy_ratio = 0.1 # Sampaling ratio for ontolegy detection
49
+
50
+ # in case schema is None
51
+ # try to load schema from FalkorDB
52
+ if schema is None:
53
+ schema_name = self._schema_name()
54
+ if schema_name in self.db.list_graphs():
55
+ schema_graph = self.db.select_graph()
56
+ self.schema = self.schema.from_graph(schema_graph)
57
+
58
+ def list_sources(self) -> list[AbstractSource]:
59
+ """
60
+ List of sources associated with knowledge graph
61
+
62
+ Returns:
63
+ list[AbstractSource]: sources
64
+ """
65
+
66
+ return [s.source for s in self.sources]
67
+
68
+ def process_sources(self, sources: list[AbstractSource]) -> None:
69
+ """
70
+ Add entities and relations found in sources into the knowledge-graph
71
+
72
+ Parameters:
73
+ sources (list[AbstractSource]): list of sources to extract knowledge from
74
+ """
75
+
76
+ # Make sure knowledge graph is created.
77
+ if not 'build_graph_from_sources' in globals() or self.client is None:
78
+ self._create()
79
+
80
+ # Run assistant
81
+ result = build_graph_from_sources(self, self.client, sources)
82
+
83
+ # Add processed sources
84
+ for src in sources:
85
+ self.sources.add(src)
86
+
87
+ def _schema_name(self) -> str:
88
+ """
89
+ Generate a name for the schema based on the Knowledge Graph name
90
+ """
91
+
92
+ return f"{self.name}_schema"
93
+
94
+ def _createKGWithSchema(self):
95
+ # Save schema as an ontology graph
96
+ schema_graph = self.db.select_graph(self._schema_name())
97
+ self.schema.save_graph(schema_graph)
98
+
99
+ # Create Knowledge Graph construction assistant
100
+
101
+ # Generate assistant's toolings
102
+ tools = schema_to_tools(self.schema)
103
+
104
+ # Generate functions from schema
105
+ funcs = schema_to_functions(self.schema)
106
+ functions_names = [func[0] for func in funcs]
107
+ functions_impl = [func[1] for func in funcs]
108
+
109
+ # Generate assistant
110
+ # Generate code from template
111
+ code = TEMPLATE
112
+ code = code.replace("<TOOLS>", ",\n".join([str(tool) for tool in tools]))
113
+ code = code.replace("<GRAPH_ID>", "\"" + self.name + "\"")
114
+ code = code.replace("<FUNCTIONS>", "\n".join(functions_impl))
115
+ code = code.replace("<AVAILABLE_FUNCTIONS>", '{' + ",\n".join([f"'{f}': {f}" for f in functions_names]) +'}')
116
+
117
+ # Execute the code string in the global namespace
118
+ # Introduces the 'build_graph_from_sources' function
119
+ # TODO: note multiple calls to _createKGWithSchema will overide one another.
120
+ exec(code, globals())
121
+
122
+ def _createKGWithoutSchema(self):
123
+ raise Exception("Can not create knowledge graph, schema missing")
124
+
125
+ def _create(self) -> None:
126
+ """
127
+ Create knowledge graph
128
+ Extract entities and relations from sources
129
+ """
130
+ self.client = OpenAI()
131
+
132
+ if self.schema is not None:
133
+ self._createKGWithSchema()
134
+ else:
135
+ self._createKGWithoutSchema()
136
+
137
+ def ask(self, question:str, history:list|None=None) -> tuple[str, list]:
138
+ """
139
+ Query the knowledge graph using natural language
140
+ if the query is asked as part of a longer conversation make sure to
141
+ include past history.
142
+
143
+ Rreturns:
144
+ tuple[str, list]: answer, conversation history
145
+
146
+ Example:
147
+ >>> ans, history = kg.ask("Which actor has the most oscars")
148
+ >>> ans, history = kg.ask("List a few movies in which that actored played in", history)
149
+ """
150
+
151
+ if history is None:
152
+ history = []
153
+
154
+ answer = query(self, question, history, self.model)
155
+ return (answer, history)
@@ -0,0 +1,76 @@
1
+ ONTOLOGY_DETECTION_SYSTEM_PROMPT = """Create SQL statement which will capture the different entities and the relations between them in the provided text.
2
+ Each table should represent either a single entity type with the entity's attributes as the table columns or a relation between two entities, using foreign keys.
3
+ For example an IMDB document is likely to produce 5 tables: Movie, Actor and Director each with its own columns and ACTED and DIRECTED tables.
4
+ Do not create an ID primary key column but set the most compelling attribute as the entity's primary key
5
+ Make sure to call the run_sql function provided in your tools set for each sql statement generated."""
6
+
7
+ KNOWLEDGE_EXTRACTION_SYSTEM_PROMPT = """You are a helpful assistant with the goal of extracting
8
+ entities and relationships from text"""
9
+
10
+ SQL_FOLDING_PROMPT = """The following two CREATE TABLE SQL statments are
11
+ creating the same table name, consolidate the two into a single
12
+ CREATE TABLE SQL statment.
13
+ Remove all duplicated columns.
14
+ Make sure the number of PRIMARY KEYs and FOREIGN KEYs is maintained.
15
+ Reply only with the result SQL statment, the reply should start with:
16
+ CREATE TABLE and end with );
17
+ For example: given the two CREATE TABLE SQL:
18
+ CREATE TABLE Person (FirstName string, LastName string, Age int);
19
+ CREATE TABLE Person (SurName string, ForeName string, Height int);
20
+ The response should be:
21
+ CREATE TABLE Person (FirstName string, LastName string, Age int, Height int);
22
+ """
23
+
24
+ SQL_FOCUS_PROMPT = """Given a CREATE TABLE SQL statment the table name
25
+ represents some entity, in case the number of columns in the table is
26
+ greater than 5, remove columns which are less descriptive of the entity.
27
+ Do NOT remove columns which are either a PRIMARY KEY or a FOREIGN KEY.
28
+ Make sure to leave at least 5 columns.
29
+ Do NOT rename the table name.
30
+ Reply only with the result SQL statment, the reply should start with:
31
+ CREATE TABLE and end with );
32
+ For example: given the CREATE TABLE SQL:
33
+ CREATE TABLE Movie (
34
+ Title string PRIMARY KEY,
35
+ Genre string,
36
+ ReleaseYear int,
37
+ Rating string,
38
+ Language string,
39
+ BoxOffice string,
40
+ streaming_date STRING,
41
+ runtime STRING
42
+ );
43
+ The response should be:
44
+ CREATE TABLE Movie (
45
+ Title string PRIMARY KEY,
46
+ Genre string,
47
+ ReleaseYear int,
48
+ Rating string,
49
+ runtime STRING
50
+ );
51
+ Another example: given the CREATE TABLE SQL:
52
+ CREATE TABLE Episode (
53
+ show_title VARCHAR(255),
54
+ show_year VARCHAR(4),
55
+ season INT,
56
+ episode_number INT,
57
+ episode_title VARCHAR(255),
58
+ air_date DATE,
59
+ episode_rating DECIMAL(2, 1),
60
+ FOREIGN KEY (show_title, show_year) REFERENCES TVShow(title, year)
61
+ );
62
+ You are not allowed to remove either show_title or show_year as these are part of the table's
63
+ foreign key.
64
+ """
65
+
66
+ RELATIONSHIP_NAMING_PROMPT = """"You are a Cypher expert, help suggest names for
67
+ relationship types connecting source node to destination node.
68
+ Respond only with the suggested name.
69
+ For example given source node of type 'Actor' and destination node of type 'Movie'
70
+ Replay with: ACTED"""
71
+
72
+ FIND_UNIQUE_ATTRIBUTE_PROMPT = """From the list of attributes describing a graph node
73
+ pick the most suitble attribute to act as a unique identifier of the node.
74
+ Respond only with the suggested attribute name
75
+ For example given a list of attributes: series_title, air_date, season_number, episode_number, title
76
+ Describing the graph entity "Episode" Reply with: title"""
@@ -0,0 +1,144 @@
1
+ import json
2
+ from openai import OpenAI
3
+
4
+ # Equipet assistant with tools:
5
+ tools = [
6
+ {
7
+ "type": "function",
8
+ "function": {
9
+ "name": "run_cypher_query",
10
+ "description": "Run a Cypher query against the graph",
11
+ "parameters": {
12
+ "type": "object",
13
+ "properties": {
14
+ "query": {
15
+ "type": "string",
16
+ "description": "query to execute",
17
+ },
18
+ },
19
+ "required": ["query"],
20
+ },
21
+ },
22
+ }
23
+ ]
24
+
25
+ def run_cypher_query(g, q):
26
+ return g.query(q).result_set
27
+
28
+ # convert a schema into a string representation
29
+ def _graph_schema_to_prompt(s) -> str:
30
+ # Build a string description of graph schema
31
+ desc = "The knowladge graph schema is as follows:\n"
32
+
33
+ #--------------------------------------------------------------------------
34
+ # Describe labels
35
+ #--------------------------------------------------------------------------
36
+
37
+ # list labels
38
+ desc = desc + "The graph contains the following node labels:\n";
39
+ for e in s.entities:
40
+ desc += e + "\n"
41
+
42
+ # specify attributes associated with each entity
43
+ for e in s.entities:
44
+ e = s.entities[e]
45
+
46
+ lbl_name = e.name
47
+ attributes = e.attributes
48
+ attr_count = len(attributes)
49
+
50
+ if (attr_count == 0):
51
+ desc += f"The {lbl_name} label has no attributes\n"
52
+ else:
53
+ desc += f"The {lbl_name} label is associated with the following attribute(s):\n"
54
+ for attr in attributes:
55
+ attr_name = attr.name
56
+ attr_type = attr.type.__name__
57
+ desc += f"{attr_name} which is of type {attr_type}\n"
58
+
59
+ desc += "The graph contains the following relationship types:\n"
60
+
61
+ #--------------------------------------------------------------------------
62
+ # Describe relationships
63
+ #--------------------------------------------------------------------------
64
+
65
+ # list relations
66
+ for rel in s.relations:
67
+ rel = s.relations[rel]
68
+
69
+ src = rel.src.name
70
+ relationship_type = rel.name
71
+ dest = rel.dest.name
72
+ desc += f"{relationship_type}\n"
73
+ desc += f"The {relationship_type} relationship connects the following labels:\n"
74
+ desc += f"{src} is connected via {relationship_type} to {dest}\n"
75
+
76
+ desc += "This is the end of the knowladge graph schema description.\n"
77
+
78
+ return desc;
79
+
80
+ def run_conversation(g, question, messages, model) -> str:
81
+ client = OpenAI()
82
+
83
+ # Step 1: send the conversation and available functions to the model
84
+ messages.append({"role": "user", "content": question})
85
+
86
+ response = client.chat.completions.create(
87
+ model=model,
88
+ messages=messages,
89
+ tools=tools,
90
+ tool_choice="auto", # auto is default, but we'll be explicit
91
+ )
92
+
93
+ response_message = response.choices[0].message
94
+ tool_calls = response_message.tool_calls
95
+
96
+ # Step 2: check if the model wanted to call a function
97
+ if tool_calls:
98
+ # Step 3: call the function
99
+ # Note: the JSON response may not always be valid; be sure to handle errors
100
+ available_functions = {
101
+ "run_cypher_query": run_cypher_query,
102
+ } # only one function in this example, but you can have multiple
103
+ messages.append(response_message) # extend conversation with assistant's reply
104
+ # Step 4: send the info for each function call and function response to the model
105
+ for tool_call in tool_calls:
106
+ function_name = tool_call.function.name
107
+ function_to_call = available_functions[function_name]
108
+ function_args = json.loads(tool_call.function.arguments)
109
+ q = function_args.get("query")
110
+ print(f"OpenAI suggested query: {q}")
111
+ function_response = function_to_call(g, q)
112
+ messages.append(
113
+ {
114
+ "tool_call_id": tool_call.id,
115
+ "role": "tool",
116
+ "name": function_name,
117
+ "content": str(function_response),
118
+ }
119
+ ) # extend conversation with function response
120
+ second_response = client.chat.completions.create(
121
+ model=model,
122
+ messages=messages,
123
+ ) # get a new response from the model where it can see the function response
124
+
125
+ return second_response.choices[0].message.content
126
+
127
+ return None
128
+
129
+ def query(kg, question:str, messages:list, model="gpt-4-1106-preview") -> str:
130
+ # Build system message
131
+ if len(messages) == 0:
132
+ # graph schema
133
+ schema_desc = _graph_schema_to_prompt(kg.schema)
134
+ messages.append(
135
+ {"role": "system", "content": f"""You are a graph database expert fluent in the Cypher query language
136
+ with access to the following knowledge graph: {schema_desc}"""})
137
+
138
+ # Get an answer
139
+ answer = run_conversation(kg.graph, question, messages, model)
140
+
141
+ # Append answer to conversation
142
+ messages.append({"role": "assistant", "content": answer})
143
+
144
+ return answer
@@ -0,0 +1 @@
1
+ from .schema import Schema