graphrag_sdk 0.3.0__tar.gz → 0.3.2__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.
Files changed (50) hide show
  1. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/PKG-INFO +46 -16
  2. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/README.md +44 -15
  3. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/agents/kg_agent.py +1 -1
  4. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/chat_session.py +36 -10
  5. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/fixtures/prompts.py +5 -4
  6. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/kg.py +58 -10
  7. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/models/azure_openai.py +32 -0
  8. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/models/gemini.py +23 -0
  9. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/models/ollama.py +32 -0
  10. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/models/openai.py +32 -0
  11. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/orchestrator/orchestrator.py +5 -5
  12. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/steps/graph_query_step.py +12 -21
  13. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/steps/qa_step.py +5 -6
  14. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/pyproject.toml +2 -1
  15. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/LICENSE +0 -0
  16. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/__init__.py +0 -0
  17. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/agents/__init__.py +0 -0
  18. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/agents/agent.py +0 -0
  19. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/attribute.py +0 -0
  20. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/document.py +0 -0
  21. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/document_loaders/__init__.py +0 -0
  22. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/document_loaders/csv.py +0 -0
  23. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/document_loaders/html.py +0 -0
  24. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/document_loaders/jsonl.py +0 -0
  25. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/document_loaders/pdf.py +0 -0
  26. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/document_loaders/text.py +0 -0
  27. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/document_loaders/url.py +0 -0
  28. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/entity.py +0 -0
  29. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/fixtures/regex.py +0 -0
  30. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/helpers.py +0 -0
  31. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/model_config.py +0 -0
  32. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/models/__init__.py +0 -0
  33. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/models/model.py +0 -0
  34. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/ontology.py +0 -0
  35. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/orchestrator/__init__.py +0 -0
  36. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/orchestrator/execution_plan.py +0 -0
  37. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/orchestrator/orchestrator_decision.py +0 -0
  38. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/orchestrator/orchestrator_runner.py +0 -0
  39. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/orchestrator/step.py +0 -0
  40. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/orchestrator/step_result.py +0 -0
  41. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/orchestrator/steps/__init__.py +0 -0
  42. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/orchestrator/steps/agent.py +0 -0
  43. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/orchestrator/steps/parallel.py +0 -0
  44. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/orchestrator/steps/summary.py +0 -0
  45. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/orchestrator/steps/user_input.py +0 -0
  46. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/relation.py +0 -0
  47. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/source.py +0 -0
  48. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/steps/Step.py +0 -0
  49. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/steps/create_ontology_step.py +0 -0
  50. {graphrag_sdk-0.3.0 → graphrag_sdk-0.3.2}/graphrag_sdk/steps/extract_data_step.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: graphrag_sdk
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary:
5
5
  Home-page: http://RAG-SDK.readthedocs.io
6
6
  License: MIT
@@ -32,6 +32,7 @@ Requires-Dist: pypdf (>=4.2.0,<5.0.0)
32
32
  Requires-Dist: python-abc (>=0.2.0,<0.3.0)
33
33
  Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
34
34
  Requires-Dist: ratelimit (>=2.2.1,<3.0.0)
35
+ Requires-Dist: requests (>=2.32.3,<3.0.0)
35
36
  Requires-Dist: typing-extensions (>=4.12.1,<5.0.0)
36
37
  Requires-Dist: vertexai (>=1.49.0,<2.0.0) ; extra == "all" or extra == "vertexai"
37
38
  Project-URL: Repository, https://github.com/FalkorDB/GraphRAG-SDK
@@ -86,19 +87,9 @@ docker run -p 6379:6379 -p 3000:3000 -it --rm -v ./data:/data falkordb/falkordb
86
87
  Currently, this SDK supports the following LLMs API:
87
88
 
88
89
  * [OpenAI](https://openai.com/index/openai-api) Recommended model:`gpt-4o`
89
- * [google](https://makersuite.google.com/app/apikey) Recommended model:`gemini-1.5-flash-001`
90
-
91
- Make sure that a `.env` file is present with all required credentials.
92
-
93
- <details>
94
- <summary>.env</summary>
95
-
96
- ```
97
- OPENAI_API_KEY="OPENAI_API_KEY"
98
- GOOGLE_API_KEY="GOOGLE_API_KEY"
99
- ```
100
-
101
- </details>
90
+ * [Google](https://makersuite.google.com/app/apikey) Recommended model:`gemini-1.5-flash-001`
91
+ * [Azure-OpenAI](https://ai.azure.com) Recommended model:`gpt-4o`
92
+ * [Ollama](https://ollama.com/) Available only to the Q&A step. Recommended models: `llama3`. Ollama models are suitable for the Q&A step only (after the knowledge graph (KG) created).
102
93
 
103
94
  ## Basic Usage
104
95
 
@@ -219,9 +210,9 @@ After generating the initial ontology, you can review it and make any necessary
219
210
 
220
211
  Once you are satisfied with the ontology, you can proceed to use it for creating and managing your Knowledge Graph (KG).
221
212
 
222
- ### Knowledge Graph
213
+ ### Knowledge Graph Agent
223
214
 
224
- Now, you can use the SDK to create a Knowledge Graph (KG) from your sources and ontology.
215
+ Now, you can use the SDK to create a Knowledge Graph (KG) from your sources and ontology for Q&A.
225
216
 
226
217
  ```python
227
218
  # After approving the ontology, load it from disk.
@@ -240,6 +231,45 @@ kg.process_sources(sources)
240
231
 
241
232
  You can update the KG at any time by processing more sources with the `process_sources` method.
242
233
 
234
+ #### Configurable Prompts
235
+ When creating your Knowledge Graph (KG) agent, you can customize the prompts to tailor its behavior. This step is optional but can enhance functionality.
236
+
237
+ There are five types of prompts:
238
+
239
+ 1. **`cypher_system_instruction`**
240
+ - System instructions for the Cypher generation step.
241
+ - **Note:** Ensure your prompt includes `{ontology}`.
242
+
243
+ 2. **`qa_system_instruction`**
244
+ - System instructions for the Q&A step.
245
+
246
+ 3. **`cypher_gen_prompt`**
247
+ - The prompt used during the Cypher generation step.
248
+ - **Note:** Include `{question}` in your prompt.
249
+
250
+ 4. **`cypher_gen_prompt_history`**
251
+ - The prompt for Cypher generation when history needs to be considered.
252
+ - **Note:** Include `{question}` and `{last_answer}` in your prompt.
253
+
254
+ 5. **`qa_prompt`**
255
+ - The prompt used during the Q&A step.
256
+ - **Note:** Include `{question}`, `{context}`, and `{cypher}` in your prompt.
257
+
258
+ Here’s an example configuration:
259
+
260
+ ```python
261
+ kg = KnowledgeGraph(
262
+ name="kg_name",
263
+ model_config=KnowledgeGraphModelConfig.with_model(model),
264
+ ontology=ontology,
265
+ cypher_system_instruction=cypher_system_instruction,
266
+ qa_system_instruction=qa_system_instruction,
267
+ cypher_gen_prompt=cypher_gen_prompt,
268
+ cypher_gen_prompt_history=cypher_gen_prompt_history,
269
+ qa_prompt=qa_prompt
270
+ )
271
+ ```
272
+
243
273
  ### Graph RAG
244
274
 
245
275
  At this point, you have a Knowledge Graph that can be queried using this SDK. Use the method `chat_session` for start a conversation.
@@ -47,19 +47,9 @@ docker run -p 6379:6379 -p 3000:3000 -it --rm -v ./data:/data falkordb/falkordb
47
47
  Currently, this SDK supports the following LLMs API:
48
48
 
49
49
  * [OpenAI](https://openai.com/index/openai-api) Recommended model:`gpt-4o`
50
- * [google](https://makersuite.google.com/app/apikey) Recommended model:`gemini-1.5-flash-001`
51
-
52
- Make sure that a `.env` file is present with all required credentials.
53
-
54
- <details>
55
- <summary>.env</summary>
56
-
57
- ```
58
- OPENAI_API_KEY="OPENAI_API_KEY"
59
- GOOGLE_API_KEY="GOOGLE_API_KEY"
60
- ```
61
-
62
- </details>
50
+ * [Google](https://makersuite.google.com/app/apikey) Recommended model:`gemini-1.5-flash-001`
51
+ * [Azure-OpenAI](https://ai.azure.com) Recommended model:`gpt-4o`
52
+ * [Ollama](https://ollama.com/) Available only to the Q&A step. Recommended models: `llama3`. Ollama models are suitable for the Q&A step only (after the knowledge graph (KG) created).
63
53
 
64
54
  ## Basic Usage
65
55
 
@@ -180,9 +170,9 @@ After generating the initial ontology, you can review it and make any necessary
180
170
 
181
171
  Once you are satisfied with the ontology, you can proceed to use it for creating and managing your Knowledge Graph (KG).
182
172
 
183
- ### Knowledge Graph
173
+ ### Knowledge Graph Agent
184
174
 
185
- Now, you can use the SDK to create a Knowledge Graph (KG) from your sources and ontology.
175
+ Now, you can use the SDK to create a Knowledge Graph (KG) from your sources and ontology for Q&A.
186
176
 
187
177
  ```python
188
178
  # After approving the ontology, load it from disk.
@@ -201,6 +191,45 @@ kg.process_sources(sources)
201
191
 
202
192
  You can update the KG at any time by processing more sources with the `process_sources` method.
203
193
 
194
+ #### Configurable Prompts
195
+ When creating your Knowledge Graph (KG) agent, you can customize the prompts to tailor its behavior. This step is optional but can enhance functionality.
196
+
197
+ There are five types of prompts:
198
+
199
+ 1. **`cypher_system_instruction`**
200
+ - System instructions for the Cypher generation step.
201
+ - **Note:** Ensure your prompt includes `{ontology}`.
202
+
203
+ 2. **`qa_system_instruction`**
204
+ - System instructions for the Q&A step.
205
+
206
+ 3. **`cypher_gen_prompt`**
207
+ - The prompt used during the Cypher generation step.
208
+ - **Note:** Include `{question}` in your prompt.
209
+
210
+ 4. **`cypher_gen_prompt_history`**
211
+ - The prompt for Cypher generation when history needs to be considered.
212
+ - **Note:** Include `{question}` and `{last_answer}` in your prompt.
213
+
214
+ 5. **`qa_prompt`**
215
+ - The prompt used during the Q&A step.
216
+ - **Note:** Include `{question}`, `{context}`, and `{cypher}` in your prompt.
217
+
218
+ Here’s an example configuration:
219
+
220
+ ```python
221
+ kg = KnowledgeGraph(
222
+ name="kg_name",
223
+ model_config=KnowledgeGraphModelConfig.with_model(model),
224
+ ontology=ontology,
225
+ cypher_system_instruction=cypher_system_instruction,
226
+ qa_system_instruction=qa_system_instruction,
227
+ cypher_gen_prompt=cypher_gen_prompt,
228
+ cypher_gen_prompt_history=cypher_gen_prompt_history,
229
+ qa_prompt=qa_prompt
230
+ )
231
+ ```
232
+
204
233
  ### Graph RAG
205
234
 
206
235
  At this point, you have a Knowledge Graph that can be queried using this SDK. Use the method `chat_session` for start a conversation.
@@ -136,7 +136,7 @@ class KGAgent(Agent):
136
136
 
137
137
  """
138
138
  output = self.chat_session.send_message(params["prompt"])
139
- return output
139
+ return output['response']
140
140
 
141
141
  def __repr__(self):
142
142
  """
@@ -1,9 +1,8 @@
1
+ from falkordb import Graph
1
2
  from graphrag_sdk.ontology import Ontology
3
+ from graphrag_sdk.steps.qa_step import QAStep
2
4
  from graphrag_sdk.model_config import KnowledgeGraphModelConfig
3
5
  from graphrag_sdk.steps.graph_query_step import GraphQueryGenerationStep
4
- from graphrag_sdk.steps.qa_step import QAStep
5
- from graphrag_sdk.fixtures.prompts import GRAPH_QA_SYSTEM, CYPHER_GEN_SYSTEM
6
- from falkordb import Graph
7
6
 
8
7
 
9
8
  class ChatSession:
@@ -25,7 +24,9 @@ class ChatSession:
25
24
  >>> chat_session.send_message("What is the capital of France?")
26
25
  """
27
26
 
28
- def __init__(self, model_config: KnowledgeGraphModelConfig, ontology: Ontology, graph: Graph):
27
+ def __init__(self, model_config: KnowledgeGraphModelConfig, ontology: Ontology, graph: Graph,
28
+ cypher_system_instruction: str, qa_system_instruction: str,
29
+ cypher_gen_prompt: str, qa_prompt: str, cypher_gen_prompt_history: str):
29
30
  """
30
31
  Initializes a new ChatSession object.
31
32
 
@@ -44,14 +45,21 @@ class ChatSession:
44
45
  self.model_config = model_config
45
46
  self.graph = graph
46
47
  self.ontology = ontology
48
+ cypher_system_instruction = cypher_system_instruction.format(ontology=str(ontology.to_json()))
49
+
50
+
51
+ self.cypher_prompt = cypher_gen_prompt
52
+ self.qa_prompt = qa_prompt
53
+ self.cypher_prompt_with_history = cypher_gen_prompt_history
54
+
47
55
  self.cypher_chat_session = (
48
56
  model_config.cypher_generation.with_system_instruction(
49
- CYPHER_GEN_SYSTEM.replace("#ONTOLOGY", str(ontology.to_json()))
57
+ cypher_system_instruction
50
58
  ).start_chat()
51
59
  )
52
60
  self.qa_chat_session = model_config.qa.with_system_instruction(
53
- GRAPH_QA_SYSTEM
54
- ).start_chat()
61
+ qa_system_instruction
62
+ ).start_chat()
55
63
  self.last_answer = None
56
64
 
57
65
  def send_message(self, message: str):
@@ -62,24 +70,42 @@ class ChatSession:
62
70
  message (str): The message to send.
63
71
 
64
72
  Returns:
65
- str: The response to the message.
73
+ dict: The response to the message in the following format:
74
+ {"question": message,
75
+ "response": answer,
76
+ "context": context,
77
+ "cypher": cypher}
66
78
  """
67
79
  cypher_step = GraphQueryGenerationStep(
68
80
  graph=self.graph,
69
81
  chat_session=self.cypher_chat_session,
70
82
  ontology=self.ontology,
71
83
  last_answer=self.last_answer,
84
+ cypher_prompt=self.cypher_prompt,
85
+ cypher_prompt_with_history=self.cypher_prompt_with_history
72
86
  )
73
87
 
74
88
  (context, cypher) = cypher_step.run(message)
75
89
 
76
90
  if not cypher or len(cypher) == 0:
77
- return "I am sorry, I could not find the answer to your question"
91
+ return {
92
+ "question": message,
93
+ "response": "I am sorry, I could not find the answer to your question",
94
+ "context": None,
95
+ "cypher": None
96
+ }
78
97
 
79
98
  qa_step = QAStep(
80
99
  chat_session=self.qa_chat_session,
100
+ qa_prompt=self.qa_prompt,
81
101
  )
82
102
 
83
103
  answer = qa_step.run(message, cypher, context)
84
104
  self.last_answer = answer
85
- return answer
105
+
106
+ return {
107
+ "question": message,
108
+ "response": answer,
109
+ "context": context,
110
+ "cypher": cypher
111
+ }
@@ -429,7 +429,7 @@ Single-Pair minimal-weight paths: Find minimal-weight paths between a pair of en
429
429
  Single-Source minimal-weight paths: Find minimal-weight paths from a given source entity using algo.SSpaths().
430
430
 
431
431
  Ontology:
432
- #ONTOLOGY
432
+ {ontology}
433
433
 
434
434
 
435
435
  For example, given the question "Which managers own Neo4j stocks?", the OpenCypher statement should look like this:
@@ -441,9 +441,10 @@ RETURN m, s
441
441
  """
442
442
 
443
443
  CYPHER_GEN_PROMPT = """
444
- Using the ontology provided, generate an OpenCypher statement to query the graph database returning all relevant entities, relationships, and attributes to answer the question below:
445
- If you cannot generate a OpenCypher statement for any reason, return an empty string.
444
+ Using the ontology provided, generate an OpenCypher statement to query the graph database returning all relevant entities, relationships, and attributes to answer the question below.
445
+ If you cannot generate a OpenCypher statement for any reason, return an empty response.
446
446
  Respect the order of the relationships, the arrows should always point from the "source" to the "target".
447
+ Please think if your answer is a valid Cypher query, and correct it if it is not.
447
448
 
448
449
  Question: {question}
449
450
  """
@@ -481,8 +482,8 @@ The information part contains the provided information that you must use to cons
481
482
  The provided information is authoritative, you must never doubt it or try to use your internal knowledge to correct it.
482
483
  Make the answer sound as a response to the question. Do not mention that you based the result on the given information.
483
484
  Do not answer more than the question asks for.
484
- Here is an example:
485
485
 
486
+ Here is an example:
486
487
  Question: Which managers own Neo4j stocks?
487
488
  Context:[manager:CTL LLC, manager:JANE STREET GROUP LLC]
488
489
  Helpful Answer: CTL LLC, JANE STREET GROUP LLC owns Neo4j stocks.
@@ -1,16 +1,15 @@
1
1
  import logging
2
- from graphrag_sdk.ontology import Ontology
2
+ import warnings
3
3
  from falkordb import FalkorDB
4
+ from graphrag_sdk.ontology import Ontology
4
5
  from graphrag_sdk.source import AbstractSource
5
- from graphrag_sdk.model_config import KnowledgeGraphModelConfig
6
- from graphrag_sdk.steps.extract_data_step import ExtractDataStep
7
- from graphrag_sdk.steps.graph_query_step import GraphQueryGenerationStep
8
- from graphrag_sdk.fixtures.prompts import GRAPH_QA_SYSTEM, CYPHER_GEN_SYSTEM
9
- from graphrag_sdk.steps.qa_step import QAStep
10
6
  from graphrag_sdk.chat_session import ChatSession
11
- from graphrag_sdk.helpers import map_dict_to_cypher_properties
12
7
  from graphrag_sdk.attribute import AttributeType, Attribute
13
- from graphrag_sdk.models import GenerativeModelChatSession
8
+ from graphrag_sdk.helpers import map_dict_to_cypher_properties
9
+ from graphrag_sdk.model_config import KnowledgeGraphModelConfig
10
+ from graphrag_sdk.steps.extract_data_step import ExtractDataStep
11
+ from graphrag_sdk.fixtures.prompts import (GRAPH_QA_SYSTEM, CYPHER_GEN_SYSTEM,
12
+ CYPHER_GEN_PROMPT, GRAPH_QA_PROMPT, CYPHER_GEN_PROMPT_WITH_HISTORY)
14
13
 
15
14
  logger = logging.getLogger(__name__)
16
15
  logger.setLevel(logging.DEBUG)
@@ -31,6 +30,11 @@ class KnowledgeGraph:
31
30
  port: int = 6379,
32
31
  username: str | None = None,
33
32
  password: str | None = None,
33
+ cypher_system_instruction: str = None,
34
+ qa_system_instruction: str = None,
35
+ cypher_gen_prompt: str = None,
36
+ qa_prompt: str = None,
37
+ cypher_gen_prompt_history: str = None,
34
38
  ):
35
39
  """
36
40
  Initialize Knowledge Graph
@@ -43,6 +47,11 @@ class KnowledgeGraph:
43
47
  username (str|None): FalkorDB username.
44
48
  password (str|None): FalkorDB password.
45
49
  ontology (Ontology|None): Ontology to use.
50
+ cypher_system_instruction (str|None): Cypher system instruction. Make sure you have {ontology} in the instruction.
51
+ qa_system_instruction (str|None): QA system instruction.
52
+ cypher_gen_prompt (str|None): Cypher generation prompt. Make sure you have {question} in the prompt.
53
+ qa_prompt (str|None): QA prompt. Make sure you have {question}, {context} and {cypher} in the prompt.
54
+ cypher_gen_prompt_history (str|None): Cypher generation prompt with history. Make sure you have {question} and {last_answer} in the prompt.
46
55
  """
47
56
 
48
57
  if not isinstance(name, str) or name == "":
@@ -57,6 +66,44 @@ class KnowledgeGraph:
57
66
  self._model_config = model_config
58
67
  self.sources = set([])
59
68
 
69
+ if cypher_system_instruction is None:
70
+ cypher_system_instruction = CYPHER_GEN_SYSTEM
71
+ else:
72
+ if "{ontology}" not in cypher_system_instruction:
73
+ warnings.warn("Cypher system instruction should contain {ontology}", category=UserWarning)
74
+
75
+ if qa_system_instruction is None:
76
+ qa_system_instruction = GRAPH_QA_SYSTEM
77
+
78
+ if cypher_gen_prompt is None:
79
+ cypher_gen_prompt = CYPHER_GEN_PROMPT
80
+ else:
81
+ if "{question}" not in cypher_gen_prompt:
82
+ raise Exception("Cypher generation prompt should contain {question}")
83
+
84
+ if qa_prompt is None:
85
+ qa_prompt = GRAPH_QA_PROMPT
86
+ else:
87
+ if "{question}" not in qa_prompt or "{context}" not in qa_prompt:
88
+ raise Exception("QA prompt should contain {question} and {context}")
89
+ if "{cypher}" not in qa_prompt:
90
+ warnings.warn("QA prompt should contain {cypher}", category=UserWarning)
91
+
92
+ if cypher_gen_prompt_history is None:
93
+ cypher_gen_prompt_history = CYPHER_GEN_PROMPT_WITH_HISTORY
94
+ else:
95
+ if "{question}" not in cypher_gen_prompt_history:
96
+ raise Exception("Cypher generation prompt with history should contain {question}")
97
+ if "{last_answer}" not in cypher_gen_prompt_history:
98
+ warnings.warn("Cypher generation prompt with history should contain {last_answer}", category=UserWarning)
99
+
100
+ # Assign the validated values
101
+ self.cypher_system_instruction = cypher_system_instruction
102
+ self.qa_system_instruction = qa_system_instruction
103
+ self.cypher_gen_prompt = cypher_gen_prompt
104
+ self.qa_prompt = qa_prompt
105
+ self.cypher_gen_prompt_history = cypher_gen_prompt_history
106
+
60
107
  # Attributes
61
108
 
62
109
  @property
@@ -135,8 +182,9 @@ class KnowledgeGraph:
135
182
  setattr(self, key, None)
136
183
 
137
184
  def chat_session(self) -> ChatSession:
138
- return ChatSession(self._model_config, self.ontology, self.graph)
139
-
185
+ chat_session = ChatSession(self._model_config, self.ontology, self.graph, self.cypher_system_instruction,
186
+ self.qa_system_instruction, self.cypher_gen_prompt, self.qa_prompt, self.cypher_gen_prompt_history)
187
+ return chat_session
140
188
  def add_node(self, entity: str, attributes: dict):
141
189
  """
142
190
  Add a node to the knowledge graph, checking if it matches the ontology
@@ -227,3 +227,35 @@ class AzureOpenAiChatSession(GenerativeModelChatSession):
227
227
  config['response_format'] = { "type": "json_object" }
228
228
 
229
229
  return config
230
+
231
+ def delete_last_message(self):
232
+ """
233
+ Deletes the last message exchange (user message and assistant response) from the chat history.
234
+ Preserves the system message if present.
235
+
236
+ Example:
237
+ Before:
238
+ [
239
+ {"role": "system", "content": "System message"},
240
+ {"role": "user", "content": "User message"},
241
+ {"role": "assistant", "content": "Assistant response"},
242
+ ]
243
+ After:
244
+ [
245
+ {"role": "system", "content": "System message"},
246
+ ]
247
+
248
+ Note: Does nothing if the chat history is empty or contains only a system message.
249
+ """
250
+ # Keep at least the system message if present
251
+ min_length = 1 if self._model.system_instruction else 0
252
+ if len(self._history) - 2 >= min_length:
253
+ self._history.pop()
254
+ self._history.pop()
255
+ else:
256
+ # Reset to initial state with just system message if present
257
+ self._history = (
258
+ [{"role": "system", "content": self._model.system_instruction}]
259
+ if self._model.system_instruction is not None
260
+ else []
261
+ )
@@ -117,3 +117,26 @@ class GeminiChatSession(GenerativeModelChatSession):
117
117
  "temperature": 0
118
118
  }
119
119
  return self._model._generation_config
120
+
121
+ def delete_last_message(self):
122
+ """
123
+ Deletes the last message exchange (user message and assistant response) from the chat history.
124
+ Preserves the system message if present.
125
+
126
+ Example:
127
+ Before:
128
+ [
129
+ {"role": "user", "content": "User message"},
130
+ {"role": "assistant", "content": "Assistant response"},
131
+ ]
132
+ After:
133
+ []
134
+
135
+ Note: Does nothing if the chat history is empty or contains only a system message.
136
+ """
137
+ if len(self._chat_session.history) >= 2:
138
+ self._chat_session.history.pop()
139
+ self._chat_session.history.pop()
140
+ else:
141
+ self._chat_session.history = []
142
+
@@ -216,3 +216,35 @@ class OllamaChatSession(GenerativeModelChatSession):
216
216
  config['format'] = 'json'
217
217
 
218
218
  return config
219
+
220
+ def delete_last_message(self):
221
+ """
222
+ Deletes the last message exchange (user message and assistant response) from the chat history.
223
+ Preserves the system message if present.
224
+
225
+ Example:
226
+ Before:
227
+ [
228
+ {"role": "system", "content": "System message"},
229
+ {"role": "user", "content": "User message"},
230
+ {"role": "assistant", "content": "Assistant response"},
231
+ ]
232
+ After:
233
+ [
234
+ {"role": "system", "content": "System message"},
235
+ ]
236
+
237
+ Note: Does nothing if the chat history is empty or contains only a system message.
238
+ """
239
+ # Keep at least the system message if present
240
+ min_length = 1 if self._model.system_instruction else 0
241
+ if len(self._chat_history) - 2 >= min_length:
242
+ self._chat_history.pop()
243
+ self._chat_history.pop()
244
+ else:
245
+ # Reset to initial state with just system message if present
246
+ self._chat_history = (
247
+ [{"role": "system", "content": self._model.system_instruction}]
248
+ if self._model.system_instruction is not None
249
+ else []
250
+ )
@@ -106,3 +106,35 @@ class OpenAiChatSession(GenerativeModelChatSession):
106
106
  config['response_format'] = { "type": "json_object" }
107
107
 
108
108
  return config
109
+
110
+ def delete_last_message(self):
111
+ """
112
+ Deletes the last message exchange (user message and assistant response) from the chat history.
113
+ Preserves the system message if present.
114
+
115
+ Example:
116
+ Before:
117
+ [
118
+ {"role": "system", "content": "System message"},
119
+ {"role": "user", "content": "User message"},
120
+ {"role": "assistant", "content": "Assistant response"},
121
+ ]
122
+ After:
123
+ [
124
+ {"role": "system", "content": "System message"},
125
+ ]
126
+
127
+ Note: Does nothing if the chat history is empty or contains only a system message.
128
+ """
129
+ # Keep at least the system message if present
130
+ min_length = 1 if self._model.system_instruction else 0
131
+ if len(self._history) - 2 >= min_length:
132
+ self._history.pop()
133
+ self._history.pop()
134
+ else:
135
+ # Reset to initial state with just system message if present
136
+ self._history = (
137
+ [{"role": "system", "content": self._model.system_instruction}]
138
+ if self._model.system_instruction is not None
139
+ else []
140
+ )
@@ -1,5 +1,6 @@
1
- from graphrag_sdk.models import GenerativeModel
2
1
  from graphrag_sdk.agents import Agent
2
+ from graphrag_sdk.models import GenerativeModel
3
+ from graphrag_sdk.models.model import OutputMethod
3
4
  from .orchestrator_runner import OrchestratorRunner
4
5
  from graphrag_sdk.fixtures.prompts import (
5
6
  ORCHESTRATOR_SYSTEM,
@@ -16,13 +17,12 @@ logger = logging.getLogger(__name__)
16
17
 
17
18
  class Orchestrator:
18
19
 
19
- _agents = []
20
- _chat = None
21
-
22
20
  def __init__(self, model: GenerativeModel, backstory: str = ""):
23
21
  self._model = model
24
22
  self._backstory = backstory
25
-
23
+ self._agents = []
24
+ self._chat = None
25
+
26
26
  def _get_chat(self):
27
27
  if self._chat is None:
28
28
  self._chat = self._model.with_system_instruction(
@@ -1,21 +1,15 @@
1
+ import logging
2
+ from falkordb import Graph
1
3
  from graphrag_sdk.steps.Step import Step
2
4
  from graphrag_sdk.ontology import Ontology
3
5
  from graphrag_sdk.models import (
4
6
  GenerativeModelChatSession,
5
7
  )
6
- from graphrag_sdk.fixtures.prompts import (
7
- CYPHER_GEN_SYSTEM,
8
- CYPHER_GEN_PROMPT,
9
- CYPHER_GEN_PROMPT_WITH_ERROR,
10
- CYPHER_GEN_PROMPT_WITH_HISTORY,
11
- )
12
- import logging
13
8
  from graphrag_sdk.helpers import (
14
9
  extract_cypher,
15
10
  validate_cypher,
16
11
  stringify_falkordb_response,
17
12
  )
18
- from falkordb import Graph
19
13
 
20
14
  logger = logging.getLogger(__name__)
21
15
  logger.setLevel(logging.DEBUG)
@@ -33,28 +27,26 @@ class GraphQueryGenerationStep(Step):
33
27
  chat_session: GenerativeModelChatSession,
34
28
  config: dict = None,
35
29
  last_answer: str = None,
30
+ cypher_prompt: str = None,
31
+ cypher_prompt_with_history: str = None,
36
32
  ) -> None:
37
33
  self.ontology = ontology
38
34
  self.config = config or {}
39
35
  self.graph = graph
40
36
  self.chat_session = chat_session
41
37
  self.last_answer = last_answer
38
+ self.cypher_prompt = cypher_prompt
39
+ self.cypher_prompt_with_history = cypher_prompt_with_history
42
40
 
43
- def run(self, question: str, retries: int = 5):
44
- error = False
45
-
41
+ def run(self, question: str, retries: int = 10):
46
42
  cypher = ""
47
- while error is not None and retries > 0:
43
+ for i in range(retries):
48
44
  try:
49
45
  cypher_prompt = (
50
- (CYPHER_GEN_PROMPT.format(question=question)
46
+ (self.cypher_prompt.format(question=question)
51
47
  if self.last_answer is None
52
- else CYPHER_GEN_PROMPT_WITH_HISTORY.format(question=question, last_answer=self.last_answer))
53
- if error is False
54
- else CYPHER_GEN_PROMPT_WITH_ERROR.format(
55
- question=question, error=error
56
- )
57
- )
48
+ else self.cypher_prompt_with_history.format(question=question, last_answer=self.last_answer))
49
+ )
58
50
  logger.debug(f"Cypher Prompt: {cypher_prompt}")
59
51
  cypher_statement_response = self.chat_session.send_message(
60
52
  cypher_prompt,
@@ -67,7 +59,6 @@ class GraphQueryGenerationStep(Step):
67
59
  return (None, None)
68
60
 
69
61
  validation_errors = validate_cypher(cypher, self.ontology)
70
- # print(f"Is valid: {is_valid}")
71
62
  if validation_errors is not None:
72
63
  raise Exception("\n".join(validation_errors))
73
64
 
@@ -82,6 +73,6 @@ class GraphQueryGenerationStep(Step):
82
73
  except Exception as e:
83
74
  logger.debug(f"Error: {e}")
84
75
  error = e
85
- retries -= 1
76
+ self.chat_session.delete_last_message()
86
77
 
87
78
  raise Exception("Failed to generate Cypher query: " + str(error))
@@ -1,9 +1,7 @@
1
+ import logging
1
2
  from graphrag_sdk.steps.Step import Step
2
3
  from graphrag_sdk.models import GenerativeModelChatSession
3
4
 
4
- from graphrag_sdk.fixtures.prompts import GRAPH_QA_SYSTEM, GRAPH_QA_PROMPT
5
- import logging
6
-
7
5
  logger = logging.getLogger(__name__)
8
6
  logger.setLevel(logging.DEBUG)
9
7
 
@@ -17,17 +15,18 @@ class QAStep(Step):
17
15
  self,
18
16
  chat_session: GenerativeModelChatSession,
19
17
  config: dict = None,
18
+ qa_prompt: str = None,
20
19
  ) -> None:
21
20
  self.config = config or {}
22
21
  self.chat_session = chat_session
22
+ self.qa_prompt = qa_prompt
23
23
 
24
24
  def run(self, question: str, cypher: str, context: str):
25
-
26
- qa_prompt = GRAPH_QA_PROMPT.format(
25
+ qa_prompt = self.qa_prompt.format(
27
26
  context=context, cypher=cypher, question=question
28
27
  )
29
28
 
30
- # logger.debug(f"QA Prompt: {qa_prompt}")
29
+ logger.debug(f"QA Prompt: {qa_prompt}")
31
30
  qa_response = self.chat_session.send_message(qa_prompt)
32
31
 
33
32
  return qa_response.text
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "graphrag_sdk"
3
- version = "0.3.0"
3
+ version = "0.3.2"
4
4
  license = "MIT"
5
5
  description = ""
6
6
  authors = ["FalkorDB <info@falkordb.com>"]
@@ -16,6 +16,7 @@ typing-extensions = "^4.12.1"
16
16
  bs4 = "^0.0.2"
17
17
  pypdf = "^4.2.0"
18
18
  backoff = "^2.2.1"
19
+ requests = "^2.32.3"
19
20
  python-abc = "^0.2.0"
20
21
  ratelimit = "^2.2.1"
21
22
  python-dotenv = "^1.0.1"
File without changes