naas-abi 1.0.0__py3-none-any.whl

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 (62) hide show
  1. naas_abi/__init__.py +35 -0
  2. naas_abi/agents/AbiAgent.py +442 -0
  3. naas_abi/agents/AbiAgent_test.py +157 -0
  4. naas_abi/agents/EntitytoSPARQLAgent.py +952 -0
  5. naas_abi/agents/EntitytoSPARQLAgent_test.py +66 -0
  6. naas_abi/agents/KnowledgeGraphBuilderAgent.py +321 -0
  7. naas_abi/agents/KnowledgeGraphBuilderAgent_test.py +86 -0
  8. naas_abi/agents/OntologyEngineerAgent.py +115 -0
  9. naas_abi/agents/OntologyEngineerAgent_test.py +42 -0
  10. naas_abi/apps/oxigraph_admin/main.py +392 -0
  11. naas_abi/apps/oxigraph_admin/terminal_style.py +151 -0
  12. naas_abi/apps/sparql_terminal/main.py +68 -0
  13. naas_abi/apps/sparql_terminal/terminal_style.py +236 -0
  14. naas_abi/apps/terminal_agent/main.py +553 -0
  15. naas_abi/apps/terminal_agent/terminal_style.py +175 -0
  16. naas_abi/cli.py +714 -0
  17. naas_abi/mappings.py +83 -0
  18. naas_abi/models/airgap_gemma.py +220 -0
  19. naas_abi/models/airgap_qwen.py +24 -0
  20. naas_abi/models/default.py +23 -0
  21. naas_abi/models/gpt_4_1.py +25 -0
  22. naas_abi/pipelines/AIAgentOntologyGenerationPipeline.py +635 -0
  23. naas_abi/pipelines/AIAgentOntologyGenerationPipeline_test.py +133 -0
  24. naas_abi/pipelines/AddIndividualPipeline.py +215 -0
  25. naas_abi/pipelines/AddIndividualPipeline_test.py +66 -0
  26. naas_abi/pipelines/InsertDataSPARQLPipeline.py +197 -0
  27. naas_abi/pipelines/InsertDataSPARQLPipeline_test.py +96 -0
  28. naas_abi/pipelines/MergeIndividualsPipeline.py +245 -0
  29. naas_abi/pipelines/MergeIndividualsPipeline_test.py +98 -0
  30. naas_abi/pipelines/RemoveIndividualPipeline.py +166 -0
  31. naas_abi/pipelines/RemoveIndividualPipeline_test.py +58 -0
  32. naas_abi/pipelines/UpdateCommercialOrganizationPipeline.py +198 -0
  33. naas_abi/pipelines/UpdateDataPropertyPipeline.py +175 -0
  34. naas_abi/pipelines/UpdateLegalNamePipeline.py +107 -0
  35. naas_abi/pipelines/UpdateLinkedInPagePipeline.py +179 -0
  36. naas_abi/pipelines/UpdatePersonPipeline.py +184 -0
  37. naas_abi/pipelines/UpdateSkillPipeline.py +118 -0
  38. naas_abi/pipelines/UpdateTickerPipeline.py +104 -0
  39. naas_abi/pipelines/UpdateWebsitePipeline.py +106 -0
  40. naas_abi/triggers.py +131 -0
  41. naas_abi/workflows/AgentRecommendationWorkflow.py +321 -0
  42. naas_abi/workflows/AgentRecommendationWorkflow_test.py +160 -0
  43. naas_abi/workflows/ArtificialAnalysisWorkflow.py +337 -0
  44. naas_abi/workflows/ArtificialAnalysisWorkflow_test.py +57 -0
  45. naas_abi/workflows/ConvertOntologyGraphToYamlWorkflow.py +210 -0
  46. naas_abi/workflows/ConvertOntologyGraphToYamlWorkflow_test.py +78 -0
  47. naas_abi/workflows/CreateClassOntologyYamlWorkflow.py +208 -0
  48. naas_abi/workflows/CreateClassOntologyYamlWorkflow_test.py +65 -0
  49. naas_abi/workflows/CreateIndividualOntologyYamlWorkflow.py +183 -0
  50. naas_abi/workflows/CreateIndividualOntologyYamlWorkflow_test.py +86 -0
  51. naas_abi/workflows/ExportGraphInstancesToExcelWorkflow.py +450 -0
  52. naas_abi/workflows/ExportGraphInstancesToExcelWorkflow_test.py +33 -0
  53. naas_abi/workflows/GetObjectPropertiesFromClassWorkflow.py +385 -0
  54. naas_abi/workflows/GetObjectPropertiesFromClassWorkflow_test.py +57 -0
  55. naas_abi/workflows/GetSubjectGraphWorkflow.py +84 -0
  56. naas_abi/workflows/GetSubjectGraphWorkflow_test.py +71 -0
  57. naas_abi/workflows/SearchIndividualWorkflow.py +190 -0
  58. naas_abi/workflows/SearchIndividualWorkflow_test.py +98 -0
  59. naas_abi-1.0.0.dist-info/METADATA +9 -0
  60. naas_abi-1.0.0.dist-info/RECORD +62 -0
  61. naas_abi-1.0.0.dist-info/WHEEL +5 -0
  62. naas_abi-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,66 @@
1
+ import pytest
2
+ from naas_abi.agents.EntitytoSPARQLAgent import create_agent
3
+
4
+
5
+ @pytest.fixture
6
+ def agent():
7
+ return create_agent()
8
+
9
+
10
+ def test_basic_functionality_and_output_structure(agent):
11
+ """
12
+ Test 1: Basic Functionality and Output Structure
13
+
14
+ Verifies that the agent returns a response containing:
15
+ 1. Original text acknowledgment
16
+ 2. Entity extraction explanation with BFO reasoning
17
+ 3. Relationship analysis and justification
18
+ 4. Complete SPARQL INSERT DATA statement
19
+ """
20
+ from datetime import datetime
21
+
22
+ statement = """latest news on France today:
23
+ Major Wildfire in Southern France (Aude Region)
24
+ A devastating wildfire—France's largest in decades—has been brought under control after sweeping through over 16,000 hectares (160 km²), an area larger than Paris
25
+ Reuters
26
+ France 24
27
+ AP News
28
+ """
29
+
30
+ result = agent.invoke(statement)
31
+ result_str = str(result)
32
+
33
+ # Test that result is not empty
34
+ assert result_str is not None and result_str.strip() != "", (
35
+ "Agent should return non-empty result"
36
+ )
37
+
38
+ # Test for original text acknowledgment
39
+ assert "Entity Extraction Analysis" in result_str, (
40
+ "Should contain Entity Extraction Analysis section"
41
+ )
42
+ assert "original text" in result_str.lower(), "Should acknowledge original text"
43
+
44
+ # Test for entity extraction explanation
45
+ assert "Extracted Entities" in result_str, (
46
+ "Should contain Extracted Entities section"
47
+ )
48
+ assert "BFO" in result_str, "Should reference BFO ontology framework"
49
+ assert "Continuants" in result_str or "Occurrents" in result_str, (
50
+ "Should categorize entities as Continuants or Occurrents"
51
+ )
52
+
53
+ # Test for relationship analysis
54
+ assert "Relationships" in result_str, "Should contain relationship analysis"
55
+ assert "Justification" in result_str or "Reasoning" in result_str, (
56
+ "Should provide justification for relationships"
57
+ )
58
+
59
+ # Test for SPARQL statement
60
+ assert "```sparql" in result_str, "Should contain SPARQL statement"
61
+ assert "INSERT DATA" in result_str, "Should contain INSERT DATA clause"
62
+
63
+ # Test date
64
+ assert datetime.now().strftime("%Y-%m-%d") in result_str, (
65
+ "Should contain today's date"
66
+ )
@@ -0,0 +1,321 @@
1
+ # from naas_abi import secret
2
+ from typing import Optional
3
+
4
+ from langchain_openai import ChatOpenAI # noqa: F401
5
+ from naas_abi import ABIModule
6
+ from naas_abi_core.services.agent.Agent import (
7
+ Agent,
8
+ AgentConfiguration,
9
+ AgentSharedState,
10
+ )
11
+
12
+ MODULE: ABIModule = ABIModule.get_instance()
13
+
14
+ NAME: str = "Knowledge_Graph_Builder"
15
+ AVATAR_URL: str = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f3/Rdf_logo.svg/1200px-Rdf_logo.svg.png"
16
+ DESCRIPTION: str = (
17
+ "A Knowledge Graph Builder Agent that helps users to build Knowledge Graphs."
18
+ )
19
+ SYSTEM_PROMPT: str = """
20
+ # ROLE:
21
+ You are a friendly and helpful Knowledge Graph Builder Agent and your role is to help users interact with instances within a Knowledge Graph by getting, adding, updating, merging and removing them.
22
+
23
+ # OBJECTIVE:
24
+ - Execute the tasks provided by the user.
25
+ - Do not damage the triplestore by removing or merging individuals that are not related to the prompt.
26
+ - Do not add individuals that are not related to the prompt.
27
+ - Do not update data properties that are not related to the prompt.
28
+ - Do not merge individuals that are not related to the prompt.
29
+ - Do not remove individuals that are not related to the prompt.
30
+
31
+ # CONTEXT:
32
+ You will receive prompts from users or other agents.
33
+
34
+ # TOOLS:
35
+ - search_class: Search for ontology classes based on their labels, definitions, examples, and comments.
36
+ - get_individuals_from_class: Get all individuals that are instances of a specific class.
37
+ - search_individuals_from_class: Search for individuals that are instances of a specific class starting with a search string.
38
+ - get_subject_graph: Retrieve detailed information about an entity.
39
+ - add_individual: Add an individual from label and class URI to the triplestore.
40
+ - insert_data_sparql: Insert individual data into the triplestore using SPARQL statement INSERT DATA.
41
+ - update_data_property: Update a data property of an entity.
42
+ - merge_individuals: Merge two individuals in the triplestore by transferring all triples from one to another.
43
+ - remove_individuals: Remove individuals from the triplestore by deleting all their associated triples.
44
+
45
+ # TASKS:
46
+ 1. Search for ontology classes based on their labels, definitions, examples, and comments
47
+ 2. Get all individuals that are instances of a specific class
48
+ 3. Add new individuals to the triplestore
49
+ 4. Update data properties for a given individual
50
+ 5. Merge two individuals in the triplestore by transferring all triples from one to another
51
+ 6. Remove individuals from the triplestore by deleting all their associated triples
52
+
53
+ # OPERATING GUIDELINES:
54
+ 1. Search instances in triplestore using appropriate search tool to find the information you need
55
+
56
+ 2.1 Add Individual by using add_individual tool.
57
+ Before using the tool, use get_subject_graph tool to get the individual and validate the individual to add is related to the prompt and provide the correct URI and label like:
58
+ "We are going to add the following individual to the triplestore:
59
+ - Individual label (rdfs:label): Naas.ai
60
+ - Class URI: https://www.commoncoreontologies.org/ont00000443
61
+ Are you sure you want to add this individual? (y/n)"
62
+ Finish by using the tool get_subject_graph of the uri to add to check if the individual is added.
63
+
64
+ 2.2 Insert data into the triplestore using SPARQL statement INSERT DATA.
65
+ If you receive a sparql statement starting with ```sparql `` and with "INSERT DATA" in it, use the insert_data_sparql tool to insert the data into the triplestore.
66
+ Before using the tool, use extract_sparql_from_text tool to get the sparql statement and validate it with the user:
67
+ "We are going to insert data from the following sparql statement into the triplestore:
68
+ ```sparql
69
+ INSERT DATA {
70
+ <http://ontology.naas.ai/abi/69a231b9-e87a-4503-8f80-a530ed8eaa4b> <http://www.w3.org/2000/01/rdf-schema#label> "Naas.ai" .
71
+ }
72
+ ```
73
+ Are you sure you want to insert this data? (y/n)"
74
+ If the user confirms, use the insert_data_sparql tool to insert the data into the triplestore.
75
+
76
+ 3. Update Data Property of an Entity by getting all its triples first using get_subject_graph tool and then use update_data_property tool to update the data property:
77
+ "We are going to update the following data property of the following individual:
78
+ - Individual URI: http://ontology.naas.ai/abi/69a231b9-e87a-4503-8f80-a530ed8eaa4b
79
+ - Data property to update: "http://www.w3.org/2000/01/rdf-schema#label"
80
+ - Old value: Naas
81
+ - New value: Naas.ai
82
+ Are you sure you want to update this data property? (y/n)"
83
+ Finish by using the tool get_subject_graph of the uri to update to check if the data property is updated.
84
+
85
+ 4. Merge Individuals by using merge_individuals tool.
86
+ Before using the tool, use get_subject_graph tool to get the individuals and validate the individuals to merge are related to the prompt and provide the correct URIs and labels like:
87
+ "We are going to merge the following individuals:
88
+ - Instance to keep:
89
+ - Label: Naas.ai
90
+ - URI: http://ontology.naas.ai/abi/69a231b9-e87a-4503-8f80-a530ed8eaa4b
91
+ - Class URI: https://www.commoncoreontologies.org/ont00000443
92
+ - Instance to merge:
93
+ - Label: Naas.ai
94
+ - URI: http://ontology.naas.ai/abi/4f92bbdd-e710-4e43-9480-9b6cd6d9af80
95
+ - Class URI: https://www.commoncoreontologies.org/ont00000443
96
+ Are you sure you want to merge these individuals? (y/n)"
97
+ If class URI are not the same, you can NOT merge them.
98
+ If class URI are the same, finish by using the tool get_subject_graph of the uri to keep.
99
+
100
+ 5. Remove Individual by using remove_individuals tool.
101
+ Before using the tool, use get_subject_graph tool to get the individual and validate the individual to remove is related to the prompt and provide the correct URI and label like:
102
+ "We are going to remove the following individuals from the triplestore:
103
+ - URI: http://ontology.naas.ai/abi/69a231b9-e87a-4503-8f80-a530ed8eaa4b
104
+ - Label: Naas.ai
105
+ - Class URI: https://www.commoncoreontologies.org/ont00000443
106
+ Are you sure you want to remove this individual? (y/n)"
107
+ Finish by using the tool get_subject_graph of the uri to remove to check if the individual is removed.
108
+
109
+ # CONSTRAINTS:
110
+ - Never use internal knowledge for answers
111
+ - Always ask for confirmation before performing UPDATE, MERGE, ADD or REMOVE operations.
112
+ """
113
+
114
+ SUGGESTIONS: list = [
115
+ {
116
+ "label": "Search classes",
117
+ "value": "Search classes representing a ...",
118
+ },
119
+ {
120
+ "label": "Search individuals",
121
+ "value": "Search for individuals from a class ...",
122
+ },
123
+ {
124
+ "label": "Add individual",
125
+ "value": "Add an individual ...",
126
+ },
127
+ {
128
+ "label": "Update data property",
129
+ "value": "Update a data property of an individual ...",
130
+ },
131
+ {
132
+ "label": "Merge individuals",
133
+ "value": "Merge two individuals ...",
134
+ },
135
+ {
136
+ "label": "Remove individuals",
137
+ "value": "Remove individual from triplestore ...",
138
+ },
139
+ ]
140
+
141
+
142
+ def create_agent(
143
+ agent_shared_state: Optional[AgentSharedState] = None,
144
+ agent_configuration: Optional[AgentConfiguration] = None,
145
+ ) -> Optional[Agent]:
146
+ # Define model
147
+ from naas_abi.models.default import get_model
148
+
149
+ model = get_model()
150
+
151
+ # Use provided configuration or create default one
152
+ if agent_configuration is None:
153
+ agent_configuration = AgentConfiguration(system_prompt=SYSTEM_PROMPT)
154
+
155
+ # Use provided shared state or create new one
156
+ if agent_shared_state is None:
157
+ agent_shared_state = AgentSharedState()
158
+
159
+ # Init tools
160
+ tools: list = []
161
+ from naas_abi.pipelines.AddIndividualPipeline import (
162
+ AddIndividualPipeline,
163
+ AddIndividualPipelineConfiguration,
164
+ )
165
+
166
+ # Add Foundational Tools
167
+ from naas_abi.workflows.SearchIndividualWorkflow import (
168
+ SearchIndividualWorkflow,
169
+ SearchIndividualWorkflowConfiguration,
170
+ )
171
+
172
+ ## Initialize search workflow first since add pipeline depends on it
173
+ search_config = SearchIndividualWorkflowConfiguration(
174
+ MODULE.engine.services.triple_store
175
+ )
176
+ search_workflow = SearchIndividualWorkflow(search_config)
177
+ tools += search_workflow.as_tools()
178
+
179
+ ## Initialize add pipeline with search workflow config
180
+ add_config = AddIndividualPipelineConfiguration(
181
+ MODULE.engine.services.triple_store, search_config
182
+ )
183
+ add_pipeline = AddIndividualPipeline(add_config)
184
+ tools += add_pipeline.as_tools()
185
+
186
+ from naas_abi.pipelines.InsertDataSPARQLPipeline import (
187
+ InsertDataSPARQLPipeline,
188
+ InsertDataSPARQLPipelineConfiguration,
189
+ )
190
+
191
+ insert_data_spql_pipeline = InsertDataSPARQLPipeline(
192
+ InsertDataSPARQLPipelineConfiguration(
193
+ triple_store=MODULE.engine.services.triple_store
194
+ )
195
+ )
196
+ tools += insert_data_spql_pipeline.as_tools()
197
+
198
+ # Add GetSubjectGraphWorkflow
199
+ from naas_abi.workflows.GetSubjectGraphWorkflow import (
200
+ GetSubjectGraphWorkflow,
201
+ GetSubjectGraphWorkflowConfiguration,
202
+ )
203
+
204
+ get_subject_graph_config = GetSubjectGraphWorkflowConfiguration()
205
+ get_subject_graph_workflow = GetSubjectGraphWorkflow(get_subject_graph_config)
206
+ tools += get_subject_graph_workflow.as_tools()
207
+
208
+ # Add UpdateDataPropertyPipeline
209
+ from naas_abi.pipelines.UpdateDataPropertyPipeline import (
210
+ UpdateDataPropertyPipeline,
211
+ UpdateDataPropertyPipelineConfiguration,
212
+ )
213
+
214
+ update_data_property_pipeline = UpdateDataPropertyPipeline(
215
+ UpdateDataPropertyPipelineConfiguration(
216
+ triple_store=MODULE.engine.services.triple_store
217
+ )
218
+ )
219
+ tools += update_data_property_pipeline.as_tools()
220
+
221
+ # Add MergeIndividualsPipeline
222
+ from naas_abi.pipelines.MergeIndividualsPipeline import (
223
+ MergeIndividualsPipeline,
224
+ MergeIndividualsPipelineConfiguration,
225
+ )
226
+
227
+ merge_individuals_pipeline = MergeIndividualsPipeline(
228
+ MergeIndividualsPipelineConfiguration(
229
+ triple_store=MODULE.engine.services.triple_store
230
+ )
231
+ )
232
+ tools += merge_individuals_pipeline.as_tools()
233
+
234
+ # Add RemoveIndividualPipeline
235
+ from naas_abi.pipelines.RemoveIndividualPipeline import (
236
+ RemoveIndividualPipeline,
237
+ RemoveIndividualPipelineConfiguration,
238
+ )
239
+
240
+ remove_individuals_pipeline = RemoveIndividualPipeline(
241
+ RemoveIndividualPipelineConfiguration(
242
+ triple_store=MODULE.engine.services.triple_store
243
+ )
244
+ )
245
+ tools += remove_individuals_pipeline.as_tools()
246
+
247
+ # Add specialized pipelines
248
+ from naas_abi.pipelines.UpdateCommercialOrganizationPipeline import (
249
+ UpdateCommercialOrganizationPipeline,
250
+ UpdateCommercialOrganizationPipelineConfiguration,
251
+ )
252
+ from naas_abi.pipelines.UpdateLegalNamePipeline import (
253
+ UpdateLegalNamePipeline,
254
+ UpdateLegalNamePipelineConfiguration,
255
+ )
256
+ from naas_abi.pipelines.UpdateLinkedInPagePipeline import (
257
+ UpdateLinkedInPagePipeline,
258
+ UpdateLinkedInPagePipelineConfiguration,
259
+ )
260
+ from naas_abi.pipelines.UpdatePersonPipeline import (
261
+ UpdatePersonPipeline,
262
+ UpdatePersonPipelineConfiguration,
263
+ )
264
+ from naas_abi.pipelines.UpdateSkillPipeline import (
265
+ UpdateSkillPipeline,
266
+ UpdateSkillPipelineConfiguration,
267
+ )
268
+ from naas_abi.pipelines.UpdateTickerPipeline import (
269
+ UpdateTickerPipeline,
270
+ UpdateTickerPipelineConfiguration,
271
+ )
272
+ from naas_abi.pipelines.UpdateWebsitePipeline import (
273
+ UpdateWebsitePipeline,
274
+ UpdateWebsitePipelineConfiguration,
275
+ )
276
+
277
+ specialized_pipelines = [
278
+ (UpdatePersonPipeline, UpdatePersonPipelineConfiguration),
279
+ (UpdateSkillPipeline, UpdateSkillPipelineConfiguration),
280
+ (
281
+ UpdateCommercialOrganizationPipeline,
282
+ UpdateCommercialOrganizationPipelineConfiguration,
283
+ ),
284
+ (UpdateLinkedInPagePipeline, UpdateLinkedInPagePipelineConfiguration),
285
+ (UpdateWebsitePipeline, UpdateWebsitePipelineConfiguration),
286
+ (UpdateLegalNamePipeline, UpdateLegalNamePipelineConfiguration),
287
+ (UpdateTickerPipeline, UpdateTickerPipelineConfiguration),
288
+ ]
289
+ for Pipeline, Configuration in specialized_pipelines:
290
+ tools += Pipeline(Configuration(MODULE.engine.services.triple_store)).as_tools()
291
+
292
+ # Add search organizations tools
293
+ templatable_sparql_query_module = ABIModule.get_instance().engine.modules[
294
+ "naas_abi_core.modules.templatablesparqlquery"
295
+ ]
296
+
297
+ ontology_tools: list = [
298
+ "search_class",
299
+ "count_instances_by_class",
300
+ "get_individuals_from_class",
301
+ "search_individuals_from_class",
302
+ "add_individual",
303
+ "update_data_property",
304
+ "merge_individuals",
305
+ "remove_individuals",
306
+ ]
307
+ tools.extend(templatable_sparql_query_module.get_tools(ontology_tools))
308
+
309
+ return KnowledgeGraphBuilderAgent(
310
+ name=NAME,
311
+ description=DESCRIPTION,
312
+ chat_model=model,
313
+ tools=tools,
314
+ agents=[],
315
+ state=agent_shared_state,
316
+ configuration=agent_configuration,
317
+ )
318
+
319
+
320
+ class KnowledgeGraphBuilderAgent(Agent):
321
+ pass
@@ -0,0 +1,86 @@
1
+ from typing import Optional
2
+
3
+ import pytest
4
+ from naas_abi.agents.KnowledgeGraphBuilderAgent import create_agent
5
+ from naas_abi_core.services.agent.Agent import Agent
6
+
7
+
8
+ @pytest.fixture
9
+ def agent() -> Optional[Agent]:
10
+ return create_agent()
11
+
12
+
13
+ def test_add_individual(agent: Agent):
14
+ e = agent.invoke("Add individual Naas.ai as Organization")
15
+ assert "We are going to add the following individual" in e, e
16
+ assert "Individual label" in e, e
17
+ assert "Class URI" in e, e
18
+ assert "Are you sure you want to add this individual?" in e, e
19
+
20
+
21
+ def test_insert_data_sparql(agent: Agent):
22
+ e = agent.invoke("""
23
+ ```sparql
24
+ INSERT DATA {
25
+ <http://ontology.naas.ai/abi/test> <http://www.w3.org/2000/01/rdf-schema#label> "Test" .
26
+ }
27
+ ```
28
+ """)
29
+ assert (
30
+ "we are going to insert data from the following sparql statement" in e.lower()
31
+ or "multiple individuals" in e.lower()
32
+ or "multiple instances" in e.lower()
33
+ ), e
34
+ if (
35
+ "multiple individuals" not in e.lower()
36
+ and "multiple instances" not in e.lower()
37
+ ):
38
+ assert (
39
+ "Are you sure you want to insert this data?" in e
40
+ or "please confirm" in e.lower()
41
+ ), e
42
+
43
+
44
+ def test_update_data_property(agent: Agent):
45
+ e = agent.invoke("Update label of Naas.ai to Naas")
46
+ assert (
47
+ "We are going to update the following data property" in e
48
+ or "multiple individuals" in e.lower()
49
+ ), e
50
+ if "multiple individuals" not in e.lower():
51
+ assert "Individual URI" in e, e
52
+ assert "Data property to update" in e, e
53
+ assert "Old value" in e, e
54
+ assert "New value" in e, e
55
+ assert (
56
+ "Are you sure you want to update this data property?" in e
57
+ or "please confirm" in e.lower()
58
+ ), e
59
+
60
+
61
+ def test_merge_individuals(agent: Agent):
62
+ e = agent.invoke("Merge duplicate Naas.ai instances")
63
+ assert (
64
+ "We are going to merge the following individuals" in e
65
+ or "multiple individuals" in e.lower()
66
+ or "only one" in e.lower()
67
+ ), e
68
+ if "multiple individuals" not in e.lower() and "only one" not in e.lower():
69
+ assert "Instance to keep" in e, e
70
+ assert "Instance to merge" in e, e
71
+ assert (
72
+ "Are you sure you want to merge these individuals?" in e
73
+ or "please confirm" in e.lower()
74
+ ), e
75
+
76
+
77
+ def test_remove_individual(agent: Agent):
78
+ e = agent.invoke("Remove individual Naas.ai")
79
+ assert (
80
+ "We are going to remove the following individual" in e
81
+ or "multiple individuals" in e
82
+ ), e
83
+ assert (
84
+ "Are you sure you want to remove this individual?" in e
85
+ or "please confirm" in e.lower()
86
+ ), e
@@ -0,0 +1,115 @@
1
+ from typing import Optional
2
+
3
+ from langchain_openai import ChatOpenAI # noqa: F401
4
+ from naas_abi import ABIModule
5
+
6
+ # from naas_abi import secret
7
+ from naas_abi_core.services.agent.Agent import (
8
+ Agent,
9
+ AgentConfiguration,
10
+ AgentSharedState,
11
+ )
12
+
13
+ MODULE: ABIModule = ABIModule.get_instance()
14
+
15
+ NAME = "Ontology_Engineer_Agent"
16
+ DESCRIPTION = "A agent that helps users understand BFO Ontology and transform text into ontologies."
17
+ SYSTEM_PROMPT = """
18
+ # ROLE:
19
+ You are a BFO (Basic Formal Ontology) Expert and Ontology Engineering Specialist.
20
+ Your role involves both educational guidance and practical implementation.
21
+
22
+ # OBJECTIVE:
23
+ Your primary objective is to help users understand BFO Ontology and transform natural language text into structured, semantically accurate ontological representations.
24
+
25
+ # CONTEXT:
26
+ You will receive messages from users or the supervisor agent ABI.
27
+
28
+ # TOOLS/AGENTS:
29
+ - Entity_to_SPARQL: Extracts entities from text and generates SPARQL INSERT DATA statements with proper BFO mappings
30
+ - Knowledge_Graph_Builder: Manages triplestore operations including data insertion, querying, updating, and validation
31
+
32
+ # OPERATING GUIDELINES:
33
+
34
+ 1. EDUCATIONAL QUERIES ABOUT ONTOLOGY
35
+ When users ask about ontology engineering concepts, or theoretical questions, use your comprehensive internal knowledge of BFO 2.0.
36
+ Provide an answer first with the BFO classes with its URI representing the answer of the user's question and then an clear and concise explanation of the answer.
37
+ Answer expected for question 'What is a Person in BFO Ontology?' is:
38
+ "
39
+ The BFO class representing a Person is a material entity (bfo:BFO_0000040).
40
+
41
+ A Person in BFO is modeled as a material entity - a physical object made of matter that occupies space and has mass.
42
+ This classification reflects that humans are physical, material beings composed of cells, tissues and organs that form an integrated whole.
43
+ As material entities, persons can bear physical qualities, participate in processes, and maintain their material nature while undergoing changes over time.
44
+ "
45
+
46
+ 2. TEXT-TO-ONTOLOGY TRANSFORMATION WORKFLOW
47
+ If a user wants to transform text into ontological representation, use Entity_to_SPARQL agent.
48
+ Before delegating to agent, try to resolve ambiguities about:
49
+ - pronouns ("I", "you", "they") => must be a named entity. Example: "I" => "Florent Ravenel"
50
+ - dates ("today", "yesterday", "tomorrow") => must be a named entity. Example: "today" => "2025-08-12"
51
+ If there is no disambiguation, use the Entity_to_SPARQL agent to map the text to ontology.
52
+
53
+ 3. SPARQL INSERT DATA TO TRIPESTORE
54
+ If the user wants to insert data into the triplestore, use the Knowledge_Graph_Builder agent to insert the data into the triplestore.
55
+ Before delegating to agent, validate the SPARQL statement that will be added to the triplestore like:
56
+ "I am going to add the following SPARQL statement to the triplestore:
57
+ ```sparql
58
+ {SPARQL_STATEMENT}
59
+ ```
60
+ Are you sure you want to add this SPARQL statement to the triplestore?"
61
+ If the user confirms, delegate to Knowledge_Graph_Builder agent.
62
+
63
+ # CONSTRAINTS:
64
+ - Delegate all mapping to Entity_to_SPARQL agent, do not try to do it yourself.
65
+ """
66
+
67
+
68
+ def create_agent(
69
+ agent_shared_state: Optional[AgentSharedState] = None,
70
+ agent_configuration: Optional[AgentConfiguration] = None,
71
+ ) -> Optional[Agent]:
72
+ # Set model based on AI_MODE
73
+ ai_mode = MODULE.configuration.global_config.ai_mode
74
+
75
+ if ai_mode == "airgap":
76
+ from naas_abi.models.default import get_model
77
+
78
+ model = get_model()
79
+ else:
80
+ from naas_abi_marketplace.ai.chatgpt.models.o3_mini import model
81
+
82
+ # Use provided configuration or create default one
83
+ if agent_configuration is None:
84
+ agent_configuration = AgentConfiguration(system_prompt=SYSTEM_PROMPT)
85
+
86
+ # Use provided shared state or create new one
87
+ if agent_shared_state is None:
88
+ agent_shared_state = AgentSharedState()
89
+
90
+ tools: list = []
91
+
92
+ agents: list = []
93
+ from naas_abi.agents.EntitytoSPARQLAgent import (
94
+ create_agent as entity_to_sparql_agent,
95
+ )
96
+ from naas_abi.agents.KnowledgeGraphBuilderAgent import (
97
+ create_agent as knowledge_graph_builder_agent,
98
+ )
99
+
100
+ agents += [entity_to_sparql_agent(), knowledge_graph_builder_agent()]
101
+
102
+ return OntologyEngineerAgent(
103
+ name=NAME,
104
+ description=DESCRIPTION,
105
+ chat_model=model,
106
+ tools=tools,
107
+ agents=agents,
108
+ memory=None,
109
+ state=agent_shared_state,
110
+ configuration=agent_configuration,
111
+ )
112
+
113
+
114
+ class OntologyEngineerAgent(Agent):
115
+ pass
@@ -0,0 +1,42 @@
1
+ import pytest
2
+ from naas_abi.agents.OntologyEngineerAgent import create_agent
3
+
4
+
5
+ @pytest.fixture
6
+ def agent():
7
+ return create_agent()
8
+
9
+
10
+ def test_question_about_bfo_ontology(agent):
11
+ intent = "What's an Organization?"
12
+ result = agent.invoke(intent)
13
+ assert result is not None, result
14
+ assert "BFO_0000040" or "material entity" in result, result
15
+
16
+
17
+ def test_text_to_ontology_transformation(agent):
18
+ intent = "Map to ontology: 'Florent Ravenel is working for Naas.ai'"
19
+ result = agent.invoke(intent)
20
+ assert result is not None, result
21
+ assert "Florent Ravenel is working for Naas.ai" in result, result
22
+ assert "```sparql" in result, result
23
+ assert "INSERT DATA" in result, result
24
+
25
+
26
+ def test_add_to_triplestore_confirmation(agent):
27
+ intent = """Add to triplestore the following SPARQL statement:
28
+ ```sparql
29
+ INSERT DATA {
30
+ <http://ontology.naas.ai/abi/69a231b9-e87a-4503-8f80-a530ed8eaa4b> <http://www.w3.org/2000/01/rdf-schema#label> "Naas.ai" .
31
+ }
32
+ ```
33
+ """
34
+ result = agent.invoke(intent)
35
+ assert result is not None, result
36
+ assert (
37
+ "I am going to add the following SPARQL statement to the triplestore" in result
38
+ ), result
39
+ assert (
40
+ "Are you sure you want to add this SPARQL statement to the triplestore?"
41
+ in result
42
+ ), result