graphrag_sdk 0.2.0__tar.gz → 0.2.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 (51) hide show
  1. graphrag_sdk-0.2.2/LICENSE +21 -0
  2. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/PKG-INFO +17 -13
  3. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/README.md +9 -9
  4. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/agents/agent.py +2 -5
  5. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/agents/kg_agent.py +5 -7
  6. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/chat_session.py +4 -2
  7. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/fixtures/prompts.py +61 -13
  8. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/kg.py +0 -49
  9. graphrag_sdk-0.2.2/graphrag_sdk/models/azure_openai.py +229 -0
  10. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/models/gemini.py +12 -6
  11. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/models/model.py +16 -13
  12. graphrag_sdk-0.2.2/graphrag_sdk/models/ollama.py +218 -0
  13. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/models/openai.py +13 -37
  14. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/orchestrator/orchestrator_runner.py +3 -3
  15. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/orchestrator/step.py +0 -1
  16. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/orchestrator/steps/agent.py +2 -8
  17. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/orchestrator/steps/summary.py +0 -1
  18. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/orchestrator/steps/user_input.py +0 -1
  19. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/steps/extract_data_step.py +14 -8
  20. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/steps/graph_query_step.py +6 -1
  21. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/pyproject.toml +4 -4
  22. graphrag_sdk-0.2.0/graphrag_sdk/models/ollama.py +0 -119
  23. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/__init__.py +0 -0
  24. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/agents/__init__.py +0 -0
  25. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/attribute.py +0 -0
  26. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/document.py +0 -0
  27. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/document_loaders/__init__.py +0 -0
  28. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/document_loaders/csv.py +0 -0
  29. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/document_loaders/html.py +0 -0
  30. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/document_loaders/jsonl.py +0 -0
  31. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/document_loaders/pdf.py +0 -0
  32. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/document_loaders/text.py +0 -0
  33. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/document_loaders/url.py +0 -0
  34. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/entity.py +0 -0
  35. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/fixtures/regex.py +0 -0
  36. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/helpers.py +0 -0
  37. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/model_config.py +0 -0
  38. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/models/__init__.py +0 -0
  39. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/ontology.py +0 -0
  40. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/orchestrator/__init__.py +0 -0
  41. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/orchestrator/execution_plan.py +0 -0
  42. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/orchestrator/orchestrator.py +0 -0
  43. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/orchestrator/orchestrator_decision.py +0 -0
  44. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/orchestrator/step_result.py +0 -0
  45. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/orchestrator/steps/__init__.py +0 -0
  46. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/orchestrator/steps/parallel.py +0 -0
  47. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/relation.py +0 -0
  48. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/source.py +0 -0
  49. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/steps/Step.py +0 -0
  50. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/steps/create_ontology_step.py +0 -0
  51. {graphrag_sdk-0.2.0 → graphrag_sdk-0.2.2}/graphrag_sdk/steps/qa_step.py +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 FalkorDB
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,19 +1,23 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: graphrag_sdk
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary:
5
5
  Home-page: http://RAG-SDK.readthedocs.io
6
6
  License: MIT
7
7
  Keywords: RAG,Graph-RAG,KnowledgeGraph
8
8
  Author: FalkorDB
9
9
  Author-email: info@falkordb.com
10
- Requires-Python: >=3.11.4,<4.0.0
10
+ Requires-Python: >=3.9.0,<4.0.0
11
11
  Classifier: License :: OSI Approved :: MIT License
12
12
  Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
13
16
  Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
14
18
  Requires-Dist: backoff (>=2.2.1,<3.0.0)
15
19
  Requires-Dist: bs4 (>=0.0.2,<0.0.3)
16
- Requires-Dist: falkordb (>=1.0.4,<2.0.0)
20
+ Requires-Dist: falkordb (>=1.0.9,<2.0.0)
17
21
  Requires-Dist: fix-busted-json (>=0.0.18,<0.0.19)
18
22
  Requires-Dist: google-generativeai (>=0.8.1,<0.9.0)
19
23
  Requires-Dist: ipykernel (>=6.29.5,<7.0.0)
@@ -25,14 +29,17 @@ Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
25
29
  Requires-Dist: ratelimit (>=2.2.1,<3.0.0)
26
30
  Requires-Dist: typing-extensions (>=4.12.1,<5.0.0)
27
31
  Requires-Dist: vertexai (>=1.49.0,<2.0.0)
28
- Project-URL: Repository, https://github.com/FalkorDB/falkordb-gemini-kg
32
+ Project-URL: Repository, https://github.com/FalkorDB/GraphRAG-SDK
29
33
  Description-Content-Type: text/markdown
30
34
 
35
+ [![Dockerhub](https://img.shields.io/docker/pulls/falkordb/falkordb?label=Docker)](https://hub.docker.com/r/falkordb/falkordb/)
36
+ [![pypi](https://badge.fury.io/py/graphrag_sdk.svg)](https://pypi.org/project/graphrag_sdk/)
37
+ [![Discord](https://img.shields.io/discord/1146782921294884966?style=flat-square)](https://discord.gg/6M4QwDXn2w)
38
+ [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](code_of_conduct.md)
39
+
31
40
  # GraphRAG-SDK
32
41
 
33
42
  [![Try Free](https://img.shields.io/badge/Try%20Free-FalkorDB%20Cloud-FF8101?labelColor=FDE900&style=for-the-badge&link=https://app.falkordb.cloud)](https://app.falkordb.cloud)
34
- [![Dockerhub](https://img.shields.io/docker/pulls/falkordb/falkordb?label=Docker)](https://hub.docker.com/r/falkordb/falkordb/)
35
- [![Discord](https://img.shields.io/discord/1146782921294884966?style=flat-square)](https://discord.gg/6M4QwDXn2w)
36
43
 
37
44
  GraphRAG-SDK is a comprehensive solution for building Graph Retrieval-Augmented Generation (GraphRAG) applications, leveraging [FalkorDB](https://www.falkordb.com/) for optimal performance.
38
45
 
@@ -91,10 +98,10 @@ The following example demonstrates the basic usage of this SDK to create a Graph
91
98
  ```python
92
99
  from dotenv import load_dotenv
93
100
 
94
- from graphrag_sdk.classes.source import URL
101
+ from graphrag_sdk.source import URL
95
102
  from graphrag_sdk import KnowledgeGraph, Ontology
96
103
  from graphrag_sdk.models.openai import OpenAiGenerativeModel
97
- from graphrag_sdk.classes.model_config import KnowledgeGraphModelConfig
104
+ from graphrag_sdk.model_config import KnowledgeGraphModelConfig
98
105
  load_dotenv()
99
106
 
100
107
  # Import Data
@@ -150,7 +157,7 @@ The SDK supports the following file formats:
150
157
 
151
158
  ```python
152
159
  import os
153
- from graphrag_sdk.classes.source import Source
160
+ from graphrag_sdk.source import Source
154
161
 
155
162
  src_files = "data_folder"
156
163
  sources = []
@@ -224,12 +231,9 @@ You can update the KG at any time by processing more sources with the `process_s
224
231
 
225
232
  ### Graph RAG
226
233
 
227
- At this point, you have a Knowledge Graph that can be queried using this SDK. You can use the `ask` method for single questions or `chat_session` for conversations.
234
+ At this point, you have a Knowledge Graph that can be queried using this SDK. Use the method `chat_session` for start a conversation.
228
235
 
229
236
  ```python
230
- # Single question.
231
- response = kg.ask("What were the last five fights? When were they? How many rounds did they have?")
232
- print(response)
233
237
 
234
238
  # Conversation.
235
239
  chat = kg.chat_session()
@@ -1,8 +1,11 @@
1
+ [![Dockerhub](https://img.shields.io/docker/pulls/falkordb/falkordb?label=Docker)](https://hub.docker.com/r/falkordb/falkordb/)
2
+ [![pypi](https://badge.fury.io/py/graphrag_sdk.svg)](https://pypi.org/project/graphrag_sdk/)
3
+ [![Discord](https://img.shields.io/discord/1146782921294884966?style=flat-square)](https://discord.gg/6M4QwDXn2w)
4
+ [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](code_of_conduct.md)
5
+
1
6
  # GraphRAG-SDK
2
7
 
3
8
  [![Try Free](https://img.shields.io/badge/Try%20Free-FalkorDB%20Cloud-FF8101?labelColor=FDE900&style=for-the-badge&link=https://app.falkordb.cloud)](https://app.falkordb.cloud)
4
- [![Dockerhub](https://img.shields.io/docker/pulls/falkordb/falkordb?label=Docker)](https://hub.docker.com/r/falkordb/falkordb/)
5
- [![Discord](https://img.shields.io/discord/1146782921294884966?style=flat-square)](https://discord.gg/6M4QwDXn2w)
6
9
 
7
10
  GraphRAG-SDK is a comprehensive solution for building Graph Retrieval-Augmented Generation (GraphRAG) applications, leveraging [FalkorDB](https://www.falkordb.com/) for optimal performance.
8
11
 
@@ -61,10 +64,10 @@ The following example demonstrates the basic usage of this SDK to create a Graph
61
64
  ```python
62
65
  from dotenv import load_dotenv
63
66
 
64
- from graphrag_sdk.classes.source import URL
67
+ from graphrag_sdk.source import URL
65
68
  from graphrag_sdk import KnowledgeGraph, Ontology
66
69
  from graphrag_sdk.models.openai import OpenAiGenerativeModel
67
- from graphrag_sdk.classes.model_config import KnowledgeGraphModelConfig
70
+ from graphrag_sdk.model_config import KnowledgeGraphModelConfig
68
71
  load_dotenv()
69
72
 
70
73
  # Import Data
@@ -120,7 +123,7 @@ The SDK supports the following file formats:
120
123
 
121
124
  ```python
122
125
  import os
123
- from graphrag_sdk.classes.source import Source
126
+ from graphrag_sdk.source import Source
124
127
 
125
128
  src_files = "data_folder"
126
129
  sources = []
@@ -194,12 +197,9 @@ You can update the KG at any time by processing more sources with the `process_s
194
197
 
195
198
  ### Graph RAG
196
199
 
197
- At this point, you have a Knowledge Graph that can be queried using this SDK. You can use the `ask` method for single questions or `chat_session` for conversations.
200
+ At this point, you have a Knowledge Graph that can be queried using this SDK. Use the method `chat_session` for start a conversation.
198
201
 
199
202
  ```python
200
- # Single question.
201
- response = kg.ask("What were the last five fights? When were they? How many rounds did they have?")
202
- print(response)
203
203
 
204
204
  # Conversation.
205
205
  chat = kg.chat_session()
@@ -144,18 +144,15 @@ class Agent(ABC):
144
144
  pass
145
145
 
146
146
  @abstractmethod
147
- def run(
148
- self, params: dict, session: GenerativeModelChatSession
149
- ) -> tuple[str, GenerativeModelChatSession]:
147
+ def run(self, params: dict) -> str:
150
148
  """
151
149
  Run the agent with the given parameters and chat session.
152
150
 
153
151
  Args:
154
152
  params (Dict): The parameters for the agent.
155
- session (GenerativeModelChatSession): The chat session.
156
153
 
157
154
  Returns:
158
- Tuple[str, GenerativeModelChatSession]: The agent's response and the updated chat session.
155
+ str: The agent's response.
159
156
  """
160
157
  pass
161
158
 
@@ -43,6 +43,7 @@ class KGAgent(Agent):
43
43
  self.agent_id = agent_id
44
44
  self.introduction = introduction
45
45
  self.kg = kg
46
+ self.chat_session = self._kg.chat_session()
46
47
 
47
48
  @property
48
49
  def agent_id(self) -> str:
@@ -123,22 +124,19 @@ class KGAgent(Agent):
123
124
  """
124
125
  self._kg = value
125
126
 
126
- def run(
127
- self, params: dict, session: GenerativeModelChatSession | None = None
128
- ) -> tuple[str, GenerativeModelChatSession]:
127
+ def run(self, params: dict) -> str:
129
128
  """
130
129
  Ask the agent a question.
131
130
 
132
131
  Args:
133
132
  params (dict): The parameters for the agent.
134
- session (GenerativeModelChatSession | None): The chat session to use for the agent. Defaults to None.
135
133
 
136
134
  Returns:
137
- tuple[str, GenerativeModelChatSession]: The agent's response and the updated chat session.
135
+ str: The agent's response.
138
136
 
139
137
  """
140
- (output, chat_session) = self._kg.ask(params["prompt"], session)
141
- return (output, chat_session)
138
+ output = self.chat_session.send_message(params["prompt"])
139
+ return output
142
140
 
143
141
  def __repr__(self):
144
142
  """
@@ -52,6 +52,7 @@ class ChatSession:
52
52
  self.qa_chat_session = model_config.qa.with_system_instruction(
53
53
  GRAPH_QA_SYSTEM
54
54
  ).start_chat()
55
+ self.last_answer = None
55
56
 
56
57
  def send_message(self, message: str):
57
58
  """
@@ -67,6 +68,7 @@ class ChatSession:
67
68
  graph=self.graph,
68
69
  chat_session=self.cypher_chat_session,
69
70
  ontology=self.ontology,
71
+ last_answer=self.last_answer,
70
72
  )
71
73
 
72
74
  (context, cypher) = cypher_step.run(message)
@@ -79,5 +81,5 @@ class ChatSession:
79
81
  )
80
82
 
81
83
  answer = qa_step.run(message, cypher, context)
82
-
83
- return answer
84
+ self.last_answer = answer
85
+ return answer
@@ -188,7 +188,7 @@ Raw text:
188
188
  """
189
189
 
190
190
  BOUNDARIES_PREFIX = """
191
- Use the following instructions as boundaries for the ontology extraction process.
191
+ Use the following instructions as boundaries for the ontology extraction process:
192
192
  {user_boundaries}
193
193
  """
194
194
 
@@ -330,7 +330,7 @@ Schema:
330
330
  }
331
331
  ```
332
332
 
333
- For example:
333
+ Output example:
334
334
  ```{"entities":[{"label":"Person","attributes":{"name":"John Doe","age":30}},{"label":"Movie","attributes":{"title":"Inception","releaseYear":2010}}],"relations":[{"label":"ACTED_IN","source":{"label":"Person","attributes":{"name":"JohnDoe"}},"target":{"label":"Movie","attributes":{"title":"Inception"}},"attributes":{"role":"Cobb"}}]}```
335
335
 
336
336
  Ontology:
@@ -338,18 +338,47 @@ Ontology:
338
338
  """
339
339
 
340
340
  EXTRACT_DATA_PROMPT = """
341
- Extract all possible entities and relations from the text below.
342
- Use the ontology provided in the system prompt.
343
- Assign textual IDs whenever required.
344
- Use double quotes for string values.
345
- It's imperative that string values are properly escaped.
346
- All formats should be consistent, for example, dates should be in the format "YYYY-MM-DD".
347
- If needed, add the correct spacing for text fields, where the text is not properly formatted.
348
-
349
- User instructions:
341
+ You are tasked with extracting entities and relations from the text below, using the ontology provided.
342
+
343
+ **Output Format:**
344
+
345
+ - Provide the extracted data as a JSON object with two keys: `"entities"` and `"relations"`.
346
+
347
+ - **Entities**: Represent entities and concepts. Each entity should have a `"label"` and `"attributes"` field.
348
+
349
+ - **Relations**: Represent relations between entities or concepts. Each relation should have a `"label"`, `"source"`, `"target"`, and `"attributes"` field.
350
+
351
+ **Guidelines:**
352
+ - **Extract all entities and relations**: Capture all entities and relations mentioned in the text.
353
+
354
+ - **Use Only the Provided Ontology**: Utilize only the types of entities, relations, and attributes defined in the ontology.
355
+
356
+ - **Assign IDs Where Required**: Assign textual IDs to entities and relations as specified.
357
+
358
+ - **Avoid Duplicates**: Ensure each entity and relation is unique; do not include duplicates.
359
+
360
+ - **Formatting**:
361
+ - Do not include any introduction or explanation in the response, only the JSON.
362
+
363
+ - Use double quotes for all string values.
364
+
365
+ - Properly escape any special characters.
366
+
367
+ - Dates should be in the format `"YYYY-MM-DD"`.
368
+
369
+ - Correct any spacing or formatting issues in text fields as necessary.
370
+
371
+ - **Precision**: Be concise and precise in your extraction.
372
+
373
+ - **Token Limit**: Ensure your response does not exceed **{max_tokens} tokens**.
374
+
375
+ **User Instructions**:
350
376
  {instructions}
351
377
 
352
- Raw Text:
378
+ **Ontology**:
379
+ {ontology}
380
+
381
+ **Raw Text**:
353
382
  {text}
354
383
  """
355
384
 
@@ -363,6 +392,12 @@ JSON:
363
392
  {json}
364
393
  """
365
394
 
395
+ # This constant is used as a follow-up prompt when the initial data extraction is incomplete or contains duplicates.
396
+ # It instructs the model to complete the answer and ensure uniqueness of entities and relations.
397
+ COMPLETE_DATA_EXTRACTION = """
398
+ Please complete your answer. Ensure that each entity and relations is unique. Do not include duplicates. Please be precise.
399
+ """
400
+
366
401
  CYPHER_GEN_SYSTEM = """
367
402
  Task: Generate OpenCypher statement to query a graph database.
368
403
 
@@ -426,6 +461,19 @@ Do not include any apologies or other texts, except the generated OpenCypher sta
426
461
  Question: {question}
427
462
  """
428
463
 
464
+ CYPHER_GEN_PROMPT_WITH_HISTORY = """
465
+ 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.
466
+
467
+ First, determine if the last answers provided to the user over the entire conversation is relevant to the current question. If it is relevant, you may consider incorporating information from it into the query. If it is not relevant, ignore it and generate the query based solely on the question.
468
+
469
+ If you cannot generate an OpenCypher statement for any reason, return an empty string.
470
+
471
+ Respect the order of the relationships; the arrows should always point from the "source" to the "target".
472
+
473
+ Last Answer: {last_answer}
474
+
475
+ Question: {question}
476
+ """
429
477
 
430
478
  GRAPH_QA_SYSTEM = """
431
479
  You are an assistant that helps to form nice and human understandable answers.
@@ -540,7 +588,7 @@ Choose between the following steps to create the execution plan:
540
588
 
541
589
 
542
590
  ORCHESTRATOR_SUMMARY_PROMPT = """
543
- Given the following execution log, generate the final answer to the user's question.
591
+ Given the following execution log and the history of this chat, generate the final answer to the user's question.
544
592
  Be very polite and detailed in your response, always providing the reasoning behind the answer.
545
593
 
546
594
  User question:
@@ -118,55 +118,6 @@ class KnowledgeGraph:
118
118
 
119
119
  step.run(instructions)
120
120
 
121
- def ask(
122
- self, question: str, qa_chat_session: GenerativeModelChatSession | None = None
123
- ) -> tuple[str, GenerativeModelChatSession]:
124
- """
125
- Query the knowledge graph using natural language.
126
- Optionally, you can provide a qa_chat_session to use for the query.
127
-
128
- Parameters:
129
- question (str): question to ask the knowledge graph
130
- qa_chat_session (GenerativeModelChatSession|None): qa_chat_session to use for the query
131
-
132
- Returns:
133
- tuple[str, GenerativeModelChatSession]: answer, qa_chat_session
134
-
135
- Example:
136
- >>> (ans, qa_chat_session) = kg.ask("List a few movies in which that actor played in")
137
- >>> print(ans)
138
- """
139
-
140
- cypher_chat_session = (
141
- self._model_config.cypher_generation.with_system_instruction(
142
- CYPHER_GEN_SYSTEM.replace("#ONTOLOGY", str(self.ontology.to_json())),
143
- ).start_chat()
144
- )
145
- cypher_step = GraphQueryGenerationStep(
146
- ontology=self.ontology,
147
- chat_session=cypher_chat_session,
148
- graph=self.graph,
149
- )
150
-
151
- (context, cypher) = cypher_step.run(question)
152
-
153
- if not cypher or len(cypher) == 0:
154
- return "I am sorry, I could not find the answer to your question"
155
-
156
- qa_chat_session = (
157
- qa_chat_session
158
- or self._model_config.qa.with_system_instruction(
159
- GRAPH_QA_SYSTEM
160
- ).start_chat()
161
- )
162
- qa_step = QAStep(
163
- chat_session=qa_chat_session,
164
- )
165
-
166
- answer = qa_step.run(question, cypher, context)
167
-
168
- return (answer, qa_chat_session)
169
-
170
121
  def delete(self) -> None:
171
122
  """
172
123
  Deletes the knowledge graph and any other related resource
@@ -0,0 +1,229 @@
1
+ import os
2
+ from typing import Optional
3
+ from openai import AzureOpenAI
4
+ from .model import (
5
+ OutputMethod,
6
+ GenerativeModel,
7
+ GenerativeModelConfig,
8
+ GenerationResponse,
9
+ FinishReason,
10
+ GenerativeModelChatSession,
11
+ )
12
+
13
+ class AzureOpenAiGenerativeModel(GenerativeModel):
14
+ """
15
+ A generative model that interfaces with Azure's OpenAI API for chat completions.
16
+ """
17
+
18
+ client: AzureOpenAI = None
19
+
20
+ def __init__(
21
+ self,
22
+ model_name: str,
23
+ generation_config: Optional[GenerativeModelConfig] = None,
24
+ system_instruction: Optional[str] = None,
25
+ ):
26
+ """
27
+ Initialize the AzureOpenAiGenerativeModel with required parameters.
28
+
29
+ Args:
30
+ model_name (str): Name of the Azure OpenAI model.
31
+ generation_config (Optional[GenerativeModelConfig]): Configuration settings for generation.
32
+ system_instruction (Optional[str]): System-level instruction for the model.
33
+ """
34
+ self.model_name = model_name
35
+ self.generation_config = generation_config or GenerativeModelConfig()
36
+ self.system_instruction = system_instruction
37
+
38
+ # Credentials
39
+ self.api_key = os.getenv("AZURE_OPENAI_API_KEY")
40
+ self.azure_endpoint = os.getenv("AZURE_ENDPOINT")
41
+ self.api_version = os.getenv("AZURE_API_VERSION")
42
+
43
+ if not self.api_key or not self.azure_endpoint or not self.api_version:
44
+ raise ValueError(
45
+ "Missing credentials in the environment: AZURE_OPENAI_API_KEY, AZURE_ENDPOINT, or AZURE_API_VERSION."
46
+ )
47
+
48
+
49
+ def _connect_to_model(self) -> None:
50
+ """
51
+ Establish a connection to the Azure OpenAI model by initializing the AzureOpenAI client.
52
+ """
53
+ self.client = AzureOpenAI(azure_endpoint=self.azure_endpoint,
54
+ api_version=self.api_version,
55
+ api_key=self.api_key,
56
+ )
57
+
58
+ def with_system_instruction(self, system_instruction: str) -> "GenerativeModel":
59
+ """
60
+ Set or update the system instruction and connect to the Azure OpenAI model.
61
+
62
+ Args:
63
+ system_instruction (str): System instructions for the model.
64
+
65
+ Returns:
66
+ GenerativeModel: The updated model instance.
67
+ """
68
+ self.system_instruction = system_instruction
69
+ self._connect_to_model()
70
+
71
+ return self
72
+
73
+ def start_chat(self, args: Optional[dict] = None) -> GenerativeModelChatSession:
74
+ """
75
+ Start a new chat session.
76
+
77
+ Args:
78
+ args (Optional[dict]): Additional arguments for the chat session.
79
+
80
+ Returns:
81
+ GenerativeModelChatSession: A new instance of the chat session.
82
+ """
83
+ return AzureOpenAiChatSession(self, args)
84
+
85
+ def ask(self, message: str) -> GenerationResponse:
86
+ """
87
+ Send a message to the model and receive a response.
88
+
89
+ Args:
90
+ message (str): The user's message input.
91
+
92
+ Returns:
93
+ GenerationResponse: The model's generated response.
94
+ """
95
+ response = self.client.chat.completions.create(
96
+ model=self.model_name,
97
+ messages=[
98
+ {"role": "system", "content": self.system_instruction},
99
+ {"role": "user", "content": message[:14385]},
100
+ ],
101
+ max_tokens=self.generation_config.max_output_tokens,
102
+ temperature=self.generation_config.temperature,
103
+ top_p=self.generation_config.top_p,
104
+ top_k=self.generation_config.top_k,
105
+ stop=self.generation_config.stop_sequences,
106
+ )
107
+ return self._parse_generate_content_response(response)
108
+
109
+ def _parse_generate_content_response(self, response: any) -> GenerationResponse:
110
+ """
111
+ Parse the model's response and extract content for the user.
112
+
113
+ Args:
114
+ response (any): The raw response from the model.
115
+
116
+ Returns:
117
+ GenerationResponse: Parsed response containing the generated text and finish reason.
118
+ """
119
+ return GenerationResponse(
120
+ text=response.choices[0].message.content,
121
+ finish_reason=(
122
+ FinishReason.STOP
123
+ if response.choices[0].finish_reason == "stop"
124
+ else (
125
+ FinishReason.MAX_TOKENS
126
+ if response.choices[0].finish_reason == "length"
127
+ else FinishReason.OTHER
128
+ )
129
+ ),
130
+ )
131
+
132
+ def to_json(self) -> dict:
133
+ """
134
+ Serialize the model's configuration and state to JSON format.
135
+
136
+ Returns:
137
+ dict: The serialized JSON data.
138
+ """
139
+ return {
140
+ "model_name": self.model_name,
141
+ "generation_config": self.generation_config.to_json(),
142
+ "system_instruction": self.system_instruction,
143
+ }
144
+
145
+ @staticmethod
146
+ def from_json(json: dict) -> "GenerativeModel":
147
+ """
148
+ Deserialize a JSON object to create an instance of AzureOpenAiGenerativeModel.
149
+
150
+ Args:
151
+ json (dict): The serialized JSON data.
152
+
153
+ Returns:
154
+ GenerativeModel: A new instance of the model.
155
+ """
156
+ return AzureOpenAiGenerativeModel(
157
+ json["model_name"],
158
+ generation_config=GenerativeModelConfig.from_json(
159
+ json["generation_config"]
160
+ ),
161
+ system_instruction=json["system_instruction"],
162
+ )
163
+
164
+
165
+ class AzureOpenAiChatSession(GenerativeModelChatSession):
166
+ """
167
+ A chat session for interacting with the Azure OpenAI model, maintaining conversation history.
168
+ """
169
+
170
+
171
+ _history = []
172
+
173
+ def __init__(self, model: AzureOpenAiGenerativeModel, args: Optional[dict] = None):
174
+ """
175
+ Initialize the chat session and set up the conversation history.
176
+
177
+ Args:
178
+ model (AzureOpenAiGenerativeModel): The model instance for the session.
179
+ args (Optional[dict]): Additional arguments for customization.
180
+ """
181
+ self._model = model
182
+ self._args = args
183
+ self._history = (
184
+ [{"role": "system", "content": self._model.system_instruction}]
185
+ if self._model.system_instruction is not None
186
+ else []
187
+ )
188
+
189
+ def send_message(self, message: str, output_method: OutputMethod = OutputMethod.DEFAULT) -> GenerationResponse:
190
+ """
191
+ Send a message in the chat session and receive the model's response.
192
+
193
+ Args:
194
+ message (str): The message to send.
195
+ output_method (OutputMethod): Format for the model's output.
196
+
197
+ Returns:
198
+ GenerationResponse: The generated response.
199
+ """
200
+ generation_config = self._get_generation_config(output_method)
201
+ prompt = []
202
+ prompt.extend(self._history)
203
+ prompt.append({"role": "user", "content": message[:14385]})
204
+ response = self._model.client.chat.completions.create(
205
+ model=self._model.model_name,
206
+ messages=prompt,
207
+ **generation_config
208
+ )
209
+ content = self._model._parse_generate_content_response(response)
210
+ self._history.append({"role": "user", "content": message})
211
+ self._history.append({"role": "assistant", "content": content.text})
212
+ return content
213
+
214
+ def _get_generation_config(self, output_method: OutputMethod):
215
+ """
216
+ Adjust the generation configuration based on the output method.
217
+
218
+ Args:
219
+ output_method (OutputMethod): The desired output method (e.g., default or JSON).
220
+
221
+ Returns:
222
+ dict: The configuration settings for generation.
223
+ """
224
+ config = self._model.generation_config.to_json()
225
+ if output_method == OutputMethod.JSON:
226
+ config['temperature'] = 0
227
+ config['response_format'] = { "type": "json_object" }
228
+
229
+ return config
@@ -1,5 +1,6 @@
1
1
  import os
2
2
  from .model import (
3
+ OutputMethod,
3
4
  GenerativeModel,
4
5
  GenerativeModelConfig,
5
6
  GenerationResponse,
@@ -61,10 +62,6 @@ class GeminiGenerativeModel(GenerativeModel):
61
62
  def start_chat(self, args: dict | None = None) -> GenerativeModelChatSession:
62
63
  return GeminiChatSession(self, args)
63
64
 
64
- def ask(self, message: str) -> GenerationResponse:
65
- response = self._model.generate_content(message)
66
- return self.parse_generate_content_response(response)
67
-
68
65
  def parse_generate_content_response(
69
66
  self, response: types.generation_types.GenerateContentResponse
70
67
  ) -> GenerationResponse:
@@ -108,6 +105,15 @@ class GeminiChatSession(GenerativeModelChatSession):
108
105
  history=args.get("history", []) if args is not None else [],
109
106
  )
110
107
 
111
- def send_message(self, message: str) -> GenerationResponse:
112
- response = self._chat_session.send_message(message)
108
+ def send_message(self, message: str, output_method: OutputMethod = OutputMethod.DEFAULT) -> GenerationResponse:
109
+ generation_config = self._get_generation_config(output_method)
110
+ response = self._chat_session.send_message(message, generation_config=generation_config)
113
111
  return self._model.parse_generate_content_response(response)
112
+
113
+ def _get_generation_config(self, output_method: OutputMethod):
114
+ if output_method == OutputMethod.JSON:
115
+ return {
116
+ "response_mime_type": "application/json",
117
+ "temperature": 0
118
+ }
119
+ return self._model._generation_config