naas-abi-core 1.4.1__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 (124) hide show
  1. assets/favicon.ico +0 -0
  2. assets/logo.png +0 -0
  3. naas_abi_core/__init__.py +1 -0
  4. naas_abi_core/apps/api/api.py +245 -0
  5. naas_abi_core/apps/api/api_test.py +281 -0
  6. naas_abi_core/apps/api/openapi_doc.py +144 -0
  7. naas_abi_core/apps/mcp/Dockerfile.mcp +35 -0
  8. naas_abi_core/apps/mcp/mcp_server.py +243 -0
  9. naas_abi_core/apps/mcp/mcp_server_test.py +163 -0
  10. naas_abi_core/apps/terminal_agent/main.py +555 -0
  11. naas_abi_core/apps/terminal_agent/terminal_style.py +175 -0
  12. naas_abi_core/engine/Engine.py +87 -0
  13. naas_abi_core/engine/EngineProxy.py +109 -0
  14. naas_abi_core/engine/Engine_test.py +6 -0
  15. naas_abi_core/engine/IEngine.py +91 -0
  16. naas_abi_core/engine/conftest.py +45 -0
  17. naas_abi_core/engine/engine_configuration/EngineConfiguration.py +216 -0
  18. naas_abi_core/engine/engine_configuration/EngineConfiguration_Deploy.py +7 -0
  19. naas_abi_core/engine/engine_configuration/EngineConfiguration_GenericLoader.py +49 -0
  20. naas_abi_core/engine/engine_configuration/EngineConfiguration_ObjectStorageService.py +159 -0
  21. naas_abi_core/engine/engine_configuration/EngineConfiguration_ObjectStorageService_test.py +26 -0
  22. naas_abi_core/engine/engine_configuration/EngineConfiguration_SecretService.py +138 -0
  23. naas_abi_core/engine/engine_configuration/EngineConfiguration_SecretService_test.py +74 -0
  24. naas_abi_core/engine/engine_configuration/EngineConfiguration_TripleStoreService.py +224 -0
  25. naas_abi_core/engine/engine_configuration/EngineConfiguration_TripleStoreService_test.py +109 -0
  26. naas_abi_core/engine/engine_configuration/EngineConfiguration_VectorStoreService.py +76 -0
  27. naas_abi_core/engine/engine_configuration/EngineConfiguration_VectorStoreService_test.py +33 -0
  28. naas_abi_core/engine/engine_configuration/EngineConfiguration_test.py +9 -0
  29. naas_abi_core/engine/engine_configuration/utils/PydanticModelValidator.py +15 -0
  30. naas_abi_core/engine/engine_loaders/EngineModuleLoader.py +302 -0
  31. naas_abi_core/engine/engine_loaders/EngineOntologyLoader.py +16 -0
  32. naas_abi_core/engine/engine_loaders/EngineServiceLoader.py +47 -0
  33. naas_abi_core/integration/__init__.py +7 -0
  34. naas_abi_core/integration/integration.py +28 -0
  35. naas_abi_core/models/Model.py +198 -0
  36. naas_abi_core/models/OpenRouter.py +18 -0
  37. naas_abi_core/models/OpenRouter_test.py +36 -0
  38. naas_abi_core/module/Module.py +252 -0
  39. naas_abi_core/module/ModuleAgentLoader.py +50 -0
  40. naas_abi_core/module/ModuleUtils.py +20 -0
  41. naas_abi_core/modules/templatablesparqlquery/README.md +196 -0
  42. naas_abi_core/modules/templatablesparqlquery/__init__.py +39 -0
  43. naas_abi_core/modules/templatablesparqlquery/ontologies/TemplatableSparqlQueryOntology.ttl +116 -0
  44. naas_abi_core/modules/templatablesparqlquery/workflows/GenericWorkflow.py +48 -0
  45. naas_abi_core/modules/templatablesparqlquery/workflows/TemplatableSparqlQueryLoader.py +192 -0
  46. naas_abi_core/pipeline/__init__.py +6 -0
  47. naas_abi_core/pipeline/pipeline.py +70 -0
  48. naas_abi_core/services/__init__.py +0 -0
  49. naas_abi_core/services/agent/Agent.py +1619 -0
  50. naas_abi_core/services/agent/AgentMemory_test.py +28 -0
  51. naas_abi_core/services/agent/Agent_test.py +214 -0
  52. naas_abi_core/services/agent/IntentAgent.py +1179 -0
  53. naas_abi_core/services/agent/IntentAgent_test.py +139 -0
  54. naas_abi_core/services/agent/beta/Embeddings.py +181 -0
  55. naas_abi_core/services/agent/beta/IntentMapper.py +120 -0
  56. naas_abi_core/services/agent/beta/LocalModel.py +88 -0
  57. naas_abi_core/services/agent/beta/VectorStore.py +89 -0
  58. naas_abi_core/services/agent/test_agent_memory.py +278 -0
  59. naas_abi_core/services/agent/test_postgres_integration.py +145 -0
  60. naas_abi_core/services/cache/CacheFactory.py +31 -0
  61. naas_abi_core/services/cache/CachePort.py +63 -0
  62. naas_abi_core/services/cache/CacheService.py +246 -0
  63. naas_abi_core/services/cache/CacheService_test.py +85 -0
  64. naas_abi_core/services/cache/adapters/secondary/CacheFSAdapter.py +39 -0
  65. naas_abi_core/services/object_storage/ObjectStorageFactory.py +57 -0
  66. naas_abi_core/services/object_storage/ObjectStoragePort.py +47 -0
  67. naas_abi_core/services/object_storage/ObjectStorageService.py +41 -0
  68. naas_abi_core/services/object_storage/adapters/secondary/ObjectStorageSecondaryAdapterFS.py +52 -0
  69. naas_abi_core/services/object_storage/adapters/secondary/ObjectStorageSecondaryAdapterNaas.py +131 -0
  70. naas_abi_core/services/object_storage/adapters/secondary/ObjectStorageSecondaryAdapterS3.py +171 -0
  71. naas_abi_core/services/ontology/OntologyPorts.py +36 -0
  72. naas_abi_core/services/ontology/OntologyService.py +17 -0
  73. naas_abi_core/services/ontology/adaptors/secondary/OntologyService_SecondaryAdaptor_NERPort.py +37 -0
  74. naas_abi_core/services/secret/Secret.py +138 -0
  75. naas_abi_core/services/secret/SecretPorts.py +45 -0
  76. naas_abi_core/services/secret/Secret_test.py +65 -0
  77. naas_abi_core/services/secret/adaptors/secondary/Base64Secret.py +57 -0
  78. naas_abi_core/services/secret/adaptors/secondary/Base64Secret_test.py +39 -0
  79. naas_abi_core/services/secret/adaptors/secondary/NaasSecret.py +88 -0
  80. naas_abi_core/services/secret/adaptors/secondary/NaasSecret_test.py +25 -0
  81. naas_abi_core/services/secret/adaptors/secondary/dotenv_secret_secondaryadaptor.py +29 -0
  82. naas_abi_core/services/triple_store/TripleStoreFactory.py +116 -0
  83. naas_abi_core/services/triple_store/TripleStorePorts.py +223 -0
  84. naas_abi_core/services/triple_store/TripleStoreService.py +419 -0
  85. naas_abi_core/services/triple_store/adaptors/secondary/AWSNeptune.py +1300 -0
  86. naas_abi_core/services/triple_store/adaptors/secondary/AWSNeptune_test.py +284 -0
  87. naas_abi_core/services/triple_store/adaptors/secondary/Oxigraph.py +597 -0
  88. naas_abi_core/services/triple_store/adaptors/secondary/Oxigraph_test.py +1474 -0
  89. naas_abi_core/services/triple_store/adaptors/secondary/TripleStoreService__SecondaryAdaptor__Filesystem.py +223 -0
  90. naas_abi_core/services/triple_store/adaptors/secondary/TripleStoreService__SecondaryAdaptor__ObjectStorage.py +234 -0
  91. naas_abi_core/services/triple_store/adaptors/secondary/base/TripleStoreService__SecondaryAdaptor__FileBase.py +18 -0
  92. naas_abi_core/services/vector_store/IVectorStorePort.py +101 -0
  93. naas_abi_core/services/vector_store/IVectorStorePort_test.py +189 -0
  94. naas_abi_core/services/vector_store/VectorStoreFactory.py +47 -0
  95. naas_abi_core/services/vector_store/VectorStoreService.py +171 -0
  96. naas_abi_core/services/vector_store/VectorStoreService_test.py +185 -0
  97. naas_abi_core/services/vector_store/__init__.py +13 -0
  98. naas_abi_core/services/vector_store/adapters/QdrantAdapter.py +251 -0
  99. naas_abi_core/services/vector_store/adapters/QdrantAdapter_test.py +57 -0
  100. naas_abi_core/tests/test_services_imports.py +69 -0
  101. naas_abi_core/utils/Expose.py +55 -0
  102. naas_abi_core/utils/Graph.py +182 -0
  103. naas_abi_core/utils/JSON.py +49 -0
  104. naas_abi_core/utils/LazyLoader.py +44 -0
  105. naas_abi_core/utils/Logger.py +12 -0
  106. naas_abi_core/utils/OntologyReasoner.py +141 -0
  107. naas_abi_core/utils/OntologyYaml.py +681 -0
  108. naas_abi_core/utils/SPARQL.py +256 -0
  109. naas_abi_core/utils/Storage.py +33 -0
  110. naas_abi_core/utils/StorageUtils.py +398 -0
  111. naas_abi_core/utils/String.py +52 -0
  112. naas_abi_core/utils/Workers.py +114 -0
  113. naas_abi_core/utils/__init__.py +0 -0
  114. naas_abi_core/utils/onto2py/README.md +0 -0
  115. naas_abi_core/utils/onto2py/__init__.py +10 -0
  116. naas_abi_core/utils/onto2py/__main__.py +29 -0
  117. naas_abi_core/utils/onto2py/onto2py.py +611 -0
  118. naas_abi_core/utils/onto2py/tests/ttl2py_test.py +271 -0
  119. naas_abi_core/workflow/__init__.py +5 -0
  120. naas_abi_core/workflow/workflow.py +48 -0
  121. naas_abi_core-1.4.1.dist-info/METADATA +630 -0
  122. naas_abi_core-1.4.1.dist-info/RECORD +124 -0
  123. naas_abi_core-1.4.1.dist-info/WHEEL +4 -0
  124. naas_abi_core-1.4.1.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,252 @@
1
+ from __future__ import annotations
2
+
3
+ import glob
4
+ import os
5
+ from typing import Dict, List, cast
6
+
7
+ from naas_abi_core import logger
8
+ from naas_abi_core.engine.engine_configuration.EngineConfiguration import GlobalConfig
9
+ from naas_abi_core.engine.EngineProxy import EngineProxy
10
+ from naas_abi_core.integration.integration import Integration
11
+ from naas_abi_core.module.ModuleAgentLoader import ModuleAgentLoader
12
+ from naas_abi_core.pipeline.pipeline import Pipeline
13
+ from naas_abi_core.services.agent.Agent import Agent
14
+ from naas_abi_core.workflow.workflow import Workflow
15
+ from pydantic import BaseModel, ConfigDict
16
+ from typing_extensions import Generic, Self, TypeVar
17
+
18
+
19
+ class ModuleDependencies:
20
+ __modules: List[str]
21
+ __services: List[type]
22
+
23
+ def __init__(self, modules: List[str], services: List[type]):
24
+ self.__modules = modules
25
+ self.__services = services
26
+
27
+ def _get_modules(self) -> List[str]:
28
+ return self.__modules
29
+
30
+ def _set_modules(self, modules: List[str]) -> None:
31
+ self.__modules = modules
32
+
33
+ modules = property(_get_modules, _set_modules)
34
+
35
+ def _get_services(self) -> List[type]:
36
+ return self.__services
37
+
38
+ def _set_services(self, services: List[type]) -> None:
39
+ self.__services = services
40
+
41
+ services = property(_get_services, _set_services)
42
+
43
+
44
+ class ModuleConfiguration(BaseModel):
45
+ model_config = ConfigDict(extra="forbid")
46
+
47
+ global_config: GlobalConfig
48
+
49
+
50
+ TConfig = TypeVar("TConfig", bound=ModuleConfiguration)
51
+
52
+
53
+ class BaseModule(Generic[TConfig]):
54
+ """Base interface class for ABI modules."""
55
+
56
+ _instances: Dict[type, Self] = {}
57
+
58
+ _engine: EngineProxy
59
+ _configuration: TConfig
60
+ dependencies: ModuleDependencies = ModuleDependencies(modules=[], services=[])
61
+
62
+ __ontologies: List[str] = []
63
+ __agents: List[type[Agent]] = []
64
+ __integrations: List[Integration] = []
65
+ __workflows: List[Workflow] = []
66
+ __pipelines: List[Pipeline] = []
67
+
68
+ def __init__(self, engine: EngineProxy, configuration: TConfig):
69
+ assert isinstance(configuration, ModuleConfiguration), (
70
+ "configuration must be an instance of ModuleConfiguration"
71
+ )
72
+ logger.debug(f"Initializing module {self.__module__.split('.')[0]}")
73
+ self._engine = engine
74
+ self._configuration = configuration
75
+
76
+ assert hasattr(self.__class__, "Configuration"), (
77
+ "BaseModule must have a Configuration class"
78
+ )
79
+ assert type(self.__class__.Configuration) is type(ModuleConfiguration), (
80
+ "BaseModule.Configuration must be a subclass of ModuleConfiguration"
81
+ )
82
+
83
+ self_instance: Self = cast(Self, self)
84
+
85
+ self._instances[self.__class__] = self_instance
86
+
87
+ @classmethod
88
+ def get_dependencies(cls) -> List[str]:
89
+ """Return the list of module dependencies."""
90
+ return getattr(cls, "dependencies", [])
91
+
92
+ @classmethod
93
+ def get_instance(cls) -> Self:
94
+ if cls not in cls._instances:
95
+ raise ValueError(f"Module {cls} not initialized")
96
+ return cls._instances[cls]
97
+
98
+ @property
99
+ def engine(self) -> EngineProxy:
100
+ return self._engine
101
+
102
+ @property
103
+ def configuration(self) -> TConfig:
104
+ return self._configuration
105
+
106
+ @property
107
+ def ontologies(self) -> List[str]:
108
+ return self.__ontologies
109
+
110
+ @property
111
+ def agents(self) -> List[type[Agent]]:
112
+ return self.__agents
113
+
114
+ @property
115
+ def integrations(self) -> List[Integration]:
116
+ return self.__integrations
117
+
118
+ @property
119
+ def workflows(self) -> List[Workflow]:
120
+ return self.__workflows
121
+
122
+ @property
123
+ def pipelines(self) -> List[Pipeline]:
124
+ return self.__pipelines
125
+
126
+ def on_load(self):
127
+ logger.debug(f"on_load for module {self.__module__.split('.')[0]}")
128
+ self.__load_ontologies()
129
+
130
+ self.__agents = ModuleAgentLoader.load_agents(self.__class__)
131
+
132
+ def on_initialized(self):
133
+ """
134
+ Called after all modules have been loaded and the engine is fully initialized.
135
+
136
+ Use this method to perform post-initialization steps that require other modules,
137
+ services, or ontologies to be available and loaded.
138
+ """
139
+ logger.debug(f"on_initialized for module {self.__module__.split('.')[0]}")
140
+
141
+ def on_unloaded(self):
142
+ pass
143
+
144
+ def __load_ontologies(self):
145
+ if os.path.exists(os.path.join(self.__module__.split(".")[0], "ontologies")):
146
+ for file in glob.glob(
147
+ os.path.join(self.module_path, "ontologies", "**", "*.ttl"),
148
+ recursive=True,
149
+ ):
150
+ self.ontologies.append(file)
151
+
152
+
153
+ # class IModule(ABC):
154
+ # """Base interface class for ABI modules.
155
+
156
+ # This class serves as the base interface for all modules in the src/modules directory.
157
+
158
+ # The IModule class provides a default loading mechanism.
159
+
160
+
161
+ # The default loading mechanism will:
162
+ # 1. Load all agents from the module's 'agents' directory
163
+ # 2. Initialize module configuration and state
164
+
165
+ # Attributes:
166
+ # agents (List[Agent]): List of agents loaded from the module
167
+ # """
168
+
169
+ # agents: List[Agent]
170
+ # triggers: List[tuple[tuple[Any, Any, Any], OntologyEvent, Callable]]
171
+ # ontologies: List[str]
172
+
173
+ # def __init__(self, module_path: str, module_import_path: str, imported_module: Any):
174
+ # self.module_path = module_path
175
+ # self.module_import_path = module_import_path
176
+ # self.imported_module = imported_module
177
+ # self.triggers = []
178
+ # self.ontologies = []
179
+ # self.agents = []
180
+
181
+ # def check_requirements(self):
182
+ # if hasattr(self.imported_module, "requirements"):
183
+ # if not self.imported_module.requirements():
184
+ # logger.error(f"❌ Module {self.module_import_path} failed to meet requirements.")
185
+ # raise SystemExit(f"Application crashed due to module loading failure: {self.module_import_path}")
186
+
187
+ # def load(self):
188
+ # try:
189
+ # self.check_requirements()
190
+ # # self.__load_agents()
191
+ # self.__load_triggers()
192
+ # self.__load_ontologies()
193
+ # except Exception as e:
194
+ # logger.error(f"❌ Critical error loading module {self.module_import_path}: {e}")
195
+ # raise SystemExit(f"Application crashed due to module loading failure: {self.module_import_path}")
196
+
197
+ # def load_agents(self):
198
+ # try:
199
+ # self.__load_agents()
200
+ # except Exception as e:
201
+ # import traceback
202
+ # logger.error(f"❌ Critical error loading agents for module {self.module_import_path}: {e}")
203
+ # traceback.print_exc()
204
+ # raise SystemExit(f"Application crashed due to agent loading failure: {self.module_import_path}")
205
+
206
+ # def __load_agents(self):
207
+ # # Load agents
208
+ # self.agents = []
209
+ # loaded_agent_names = set()
210
+
211
+ # # Find all agent files recursively
212
+ # for root, _, files in os.walk(self.module_path):
213
+ # for file in files:
214
+ # if file.endswith("Agent.py") and not file.endswith("Agent_test.py"):
215
+ # # Get relative path from module root to agent file
216
+ # rel_path = os.path.relpath(root, self.module_path)
217
+ # # Convert path to import format
218
+ # import_path = rel_path.replace(os.sep, ".")
219
+ # if import_path == ".":
220
+ # agent_path = self.module_import_path + "." + file[:-3]
221
+ # else:
222
+ # agent_path = self.module_import_path + "." + import_path + "." + file[:-3]
223
+
224
+ # module = importlib.import_module(agent_path)
225
+ # if hasattr(module, "create_agent"):
226
+ # agent = module.create_agent()
227
+ # if agent is not None:
228
+ # agent_name = getattr(agent, "name", None)
229
+ # if agent_name and agent_name not in loaded_agent_names:
230
+ # self.agents.append(agent)
231
+ # loaded_agent_names.add(agent_name)
232
+ # else:
233
+ # logger.warning(f"Skipping duplicate agent: {agent_name}")
234
+
235
+ # def __load_triggers(self):
236
+ # if os.path.exists(os.path.join(self.module_path, "triggers.py")):
237
+ # module = importlib.import_module(self.module_import_path + ".triggers")
238
+ # if hasattr(module, "triggers"):
239
+ # self.triggers = module.triggers
240
+
241
+ # def __load_ontologies(self):
242
+ # if os.path.exists(os.path.join(self.module_path, "ontologies")):
243
+ # for file in glob.glob(
244
+ # os.path.join(self.module_path, "ontologies", "**", "*.ttl"),
245
+ # recursive=True,
246
+ # ):
247
+ # self.ontologies.append(file)
248
+
249
+ # def on_initialized(self):
250
+ # if hasattr(self.imported_module, "on_initialized"):
251
+ # self.imported_module.on_initialized()
252
+ # self.imported_module.on_initialized()
@@ -0,0 +1,50 @@
1
+ import importlib
2
+ import os
3
+ from typing import List
4
+
5
+ from naas_abi_core.module.ModuleUtils import find_class_module_root_path
6
+ from naas_abi_core.services.agent.Agent import Agent
7
+ from naas_abi_core.utils.Logger import logger
8
+
9
+
10
+ class ModuleAgentLoader:
11
+ @classmethod
12
+ def load_agents(cls, class_: type) -> List[type[Agent]]:
13
+ agents: List[type[Agent]] = []
14
+ module_root_path = find_class_module_root_path(class_)
15
+
16
+ agents_path = module_root_path / "agents"
17
+
18
+ logger.debug(f"Loading agents from {agents_path}")
19
+
20
+ if os.path.exists(agents_path):
21
+ for file in os.listdir(agents_path):
22
+ if file.endswith(".py") and not file.endswith("test.py"):
23
+ agent_module_path = (
24
+ f"{class_.__module__}.agents.{file.replace('.py', '')}"
25
+ )
26
+ logger.debug(f"Importing agent module from {agent_module_path}")
27
+ agent_module = importlib.import_module(agent_module_path)
28
+ for key, value in agent_module.__dict__.items():
29
+ if (
30
+ isinstance(value, type)
31
+ and issubclass(value, Agent)
32
+ and value.__module__.split(".")[0]
33
+ == class_.__module__.split(".")[
34
+ 0
35
+ ] # This makes sure we only load agents from the same module.
36
+ ):
37
+ if not hasattr(key, "New") and hasattr(
38
+ agent_module, "create_agent"
39
+ ):
40
+ setattr(
41
+ getattr(agent_module, key),
42
+ "New",
43
+ getattr(agent_module, "create_agent"),
44
+ )
45
+
46
+ agents.append(getattr(agent_module, key))
47
+
48
+ logger.debug(f"Agents: {agents}")
49
+
50
+ return agents
@@ -0,0 +1,20 @@
1
+ import inspect
2
+ from pathlib import Path
3
+
4
+
5
+ def find_class_module_root_path(class_: type) -> Path:
6
+ class_file_path = inspect.getfile(class_)
7
+ class_path = Path(class_file_path)
8
+ class_directory = str(class_path.parent)
9
+ return Path(class_directory)
10
+ # logger.debug(f"Class directory: {class_.__module__}")
11
+ # logger.debug(f"Class file path: {class_directory}")
12
+ # module_name = class_.__module__.split(".")[0]
13
+
14
+ # # find last occurrence of module_name in class_directory
15
+ # module_root_path = class_directory[
16
+ # : class_directory.rfind(module_name) + len(module_name)
17
+ # ]
18
+
19
+ # logger.debug(f"Module root path: {module_root_path}")
20
+ # return Path(module_root_path)
@@ -0,0 +1,196 @@
1
+ # Templatable SPARQL Query Module
2
+
3
+ ## Overview
4
+
5
+ ### Description
6
+
7
+ The Templatable SPARQL Query Module provides a dynamic system for creating reusable SPARQL queries through ontology-based definitions. This module enables developers to define SPARQL query templates in RDF/TTL format with arguments and validation patterns, which are then automatically converted into executable workflows and tools at runtime.
8
+
9
+ This module enables:
10
+ - Dynamic SPARQL query generation from ontology definitions
11
+ - Automatic argument validation using regex patterns and formats
12
+ - Runtime tool creation for AI agents without writing Python boilerplate
13
+ - Template-based query execution using Jinja2 syntax
14
+ - Integration with knowledge graphs and triple stores
15
+
16
+ ### Requirements
17
+
18
+ Triple Store Setup:
19
+ 1. Ensure you have a running triple store service configured in your ABI instance
20
+ 2. The module uses `services.triple_store_service` to query ontology definitions
21
+
22
+ ### TL;DR
23
+
24
+ To get started with the Templatable SPARQL Query module:
25
+
26
+ 1. Define your SPARQL queries in TTL format using the provided ontology
27
+ 2. Load the TTL file into your triple store
28
+ 3. The module automatically creates tools from your query definitions
29
+
30
+ Access tools using:
31
+ ```python
32
+ from src.core.modules.templatablesparqlquery import get_tools
33
+ tools = get_tools()
34
+ ```
35
+
36
+ ### Structure
37
+
38
+ ```
39
+ src/core/templatablesparqlquery/
40
+
41
+ ├── ontologies/
42
+ │ └── TemplatableSparqlQueryOntology.ttl
43
+ ├── workflows/
44
+ │ ├── GenericWorkflow.py
45
+ │ └── TemplatableSparqlQuery.py
46
+ └── README.md
47
+ ```
48
+
49
+ ## Core Components
50
+
51
+ The module provides a declarative approach to SPARQL query definition and execution through ontology-based configuration.
52
+
53
+ ### Workflows
54
+
55
+ #### Templatable SPARQL Query Workflow
56
+ Dynamically loads query definitions from the triple store and creates executable workflows with proper argument validation and Jinja2 template rendering.
57
+
58
+ **Capabilities:**
59
+ - Loads query definitions from triple store using SPARQL
60
+ - Creates Pydantic models for argument validation
61
+ - Renders SPARQL templates with user parameters
62
+ - Executes queries against the triple store
63
+ - Returns formatted results
64
+
65
+ **Use Cases:**
66
+ - Dynamic query generation without hardcoded Python workflows
67
+ - Reusable query templates with parameter validation
68
+ - AI agent tool creation from ontology definitions
69
+
70
+ #### Generic Workflow
71
+ A generic wrapper class that handles the execution of templated SPARQL queries with type-safe argument validation.
72
+
73
+ **Configuration:**
74
+
75
+ ```python
76
+ from src.core.modules.templatablesparqlquery.workflows.GenericWorkflow import GenericWorkflow
77
+ from pydantic import BaseModel, Field
78
+
79
+ # Define argument model
80
+ class MyQueryArguments(BaseModel):
81
+ department_id: str = Field(..., description="Department identifier", pattern="^[A-Z0-9]{3,}$")
82
+ employee_name: str = Field(..., description="Employee name filter")
83
+
84
+ # Create workflow
85
+ workflow = GenericWorkflow[MyQueryArguments](
86
+ name="find_employees",
87
+ description="Find employees in a department",
88
+ sparql_template=sparql_query_template,
89
+ arguments_model=MyQueryArguments
90
+ )
91
+ ```
92
+
93
+ #### Run
94
+ Execute workflows by calling the module's tool loading functions:
95
+ ```python
96
+ from src.core.modules.templatablesparqlquery import get_tools
97
+ tools = get_tools()
98
+ # Tools are automatically created from ontology definitions
99
+ ```
100
+
101
+ #### Testing
102
+ Currently no dedicated test files are present in the module structure.
103
+
104
+ ### Ontologies
105
+
106
+ #### Templatable SPARQL Query Ontology
107
+
108
+ The core ontology defining the structure for templatable SPARQL queries and their arguments:
109
+
110
+ **Key Classes:**
111
+ - `abi:TemplatableSparqlQuery`: Represents a SPARQL query template with intent information
112
+ - `abi:QueryArgument`: Represents an argument for query templating
113
+
114
+ **Key Properties:**
115
+ - `abi:intentDescription`: Natural language description of query purpose
116
+ - `abi:sparqlTemplate`: The SPARQL query template with Jinja2 variables
117
+ - `abi:hasArgument`: Links queries to their arguments
118
+ - `abi:argumentName`: Argument name used in templates
119
+ - `abi:validationPattern`: Regex pattern for argument validation
120
+ - `abi:validationFormat`: Expected format description
121
+
122
+ #### Example Query Definition
123
+
124
+ ```turtle
125
+ @prefix abi: <http://ontology.naas.ai/abi/> .
126
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
127
+
128
+ abi:findEmployeesQuery a abi:TemplatableSparqlQuery ;
129
+ rdfs:label "Find Employees"@en ;
130
+ abi:intentDescription "Find all employees working in a specific department" ;
131
+ abi:sparqlTemplate """
132
+ SELECT ?employee ?name
133
+ WHERE {
134
+ ?employee :worksIn :{{ department_id }} ;
135
+ :hasName ?name .
136
+ {% if employee_name %}
137
+ FILTER(CONTAINS(LCASE(?name), LCASE("{{ employee_name }}")))
138
+ {% endif %}
139
+ }
140
+ """ ;
141
+ abi:hasArgument abi:departmentArg .
142
+
143
+ abi:departmentArg a abi:QueryArgument ;
144
+ abi:argumentName "department_id" ;
145
+ abi:argumentDescription "The unique identifier of the department" ;
146
+ abi:validationPattern "^[A-Z0-9]{3,}$" ;
147
+ abi:validationFormat "department_id" .
148
+ ```
149
+
150
+ ## How to Add New Queries
151
+
152
+ ### Step 1: Define Query in TTL Format
153
+ Create or update a TTL file with your query definition using the templatable SPARQL query ontology.
154
+
155
+ ### Step 2: Define Arguments
156
+ Specify all required arguments with proper validation patterns and descriptions.
157
+
158
+ ### Step 3: Load into Triple Store
159
+ Ensure your TTL file is loaded into the triple store during application initialization.
160
+
161
+ ### Step 4: Access Generated Tools
162
+ The module automatically creates tools from your definitions:
163
+
164
+ ```python
165
+ from src.core.modules.templatablesparqlquery import get_tools
166
+ tools = get_tools()
167
+ ```
168
+
169
+ ## Dependencies
170
+
171
+ ### Python Libraries
172
+ - `pydantic`: Data validation and serialization for argument models
173
+ - `rdflib`: RDF graph processing and SPARQL query execution
174
+ - `jinja2`: Template rendering for SPARQL queries
175
+ - `langchain_core`: Tool integration for AI agents
176
+ - `asyncio`: Asynchronous processing support
177
+
178
+ ### Internal Modules
179
+ - `src.utils.SPARQL`: SPARQL result processing utilities
180
+ - `src.services`: Triple store service integration
181
+
182
+ ### External Services
183
+ - **Triple Store**: Required for storing and querying ontology definitions
184
+ - **Knowledge Graph**: Source data for SPARQL query execution
185
+
186
+ ## Technical Implementation
187
+
188
+ The module uses a sophisticated runtime generation process:
189
+
190
+ 1. **Query Discovery**: Scans the triple store for `TemplatableSparqlQuery` instances
191
+ 2. **Argument Resolution**: Retrieves associated `QueryArgument` definitions
192
+ 3. **Model Generation**: Creates Pydantic models with validation patterns
193
+ 4. **Tool Creation**: Generates LangChain tools for AI agent integration
194
+ 5. **Template Execution**: Uses Jinja2 to render SPARQL templates with parameters
195
+
196
+ This approach eliminates the need for writing repetitive Python code for similar query patterns, allowing developers to focus on query logic and ontology design.
@@ -0,0 +1,39 @@
1
+ from naas_abi_core.module.Module import (
2
+ BaseModule,
3
+ ModuleConfiguration,
4
+ ModuleDependencies,
5
+ )
6
+ from naas_abi_core.modules.templatablesparqlquery.workflows.TemplatableSparqlQueryLoader import (
7
+ TemplatableSparqlQueryLoader,
8
+ )
9
+ from naas_abi_core.services.triple_store.TripleStoreService import TripleStoreService
10
+
11
+
12
+ class ABIModule(BaseModule):
13
+ __workflows: list = []
14
+ __tools: list = []
15
+
16
+ dependencies: ModuleDependencies = ModuleDependencies(
17
+ modules=[], services=[TripleStoreService]
18
+ )
19
+
20
+ class Configuration(ModuleConfiguration):
21
+ pass
22
+
23
+ def on_initialized(self):
24
+ self.__templatable_sparql_query_loader = TemplatableSparqlQueryLoader(
25
+ self.engine.services.triple_store
26
+ )
27
+ self.__workflows = self.__templatable_sparql_query_loader.load_workflows()
28
+ self.__tools = [
29
+ tool for workflow in self.__workflows for tool in workflow.as_tools()
30
+ ]
31
+
32
+ def get_workflows(self):
33
+ return self.__workflows
34
+
35
+ def get_tools(self, tool_names: list[str] = []):
36
+ if len(tool_names) == 0:
37
+ return self.__tools
38
+ else:
39
+ return [tool for tool in self.__tools if tool.name in tool_names]
@@ -0,0 +1,116 @@
1
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
2
+ @prefix dc11: <http://purl.org/dc/elements/1.1/> .
3
+ @prefix dc: <http://purl.org/dc/terms/> .
4
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
5
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
6
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
7
+ @prefix skos: <http://www.w3.org/2004/02/skos/core#> .
8
+ @prefix bfo: <http://purl.obolibrary.org/obo/> .
9
+ @prefix cco: <https://www.commoncoreontologies.org/> .
10
+ @prefix abi: <http://ontology.naas.ai/abi/> .
11
+
12
+ <http://ontology.naas.ai/abi/TemplatableSparqlQueryOntology> a owl:Ontology ;
13
+ owl:versionIRI <https://github.com/jupyter-naas/abi/tree/cli/src/core/modules/abi/ontologies/domain-level/TemplatableSparqlQueryOntology.ttl> ;
14
+ dc11:contributor "Jeremy Ravenel" , "Maxime Jublou" , "Florent Ravenel" ;
15
+ dc:description "Templatable Sparql Query Ontology."@en ;
16
+ dc:license "" ;
17
+ dc:title "Templatable Sparql Query Ontology" .
18
+
19
+ #################################################################
20
+ # Classes
21
+ #################################################################
22
+
23
+ abi:TemplatableSparqlQuery a owl:Class ;
24
+ rdfs:subClassOf bfo:BFO_0000031 ;
25
+ rdfs:label "Templatable SPARQL Query"@en ;
26
+ rdfs:comment "A class representing a SPARQL query that can be templated with variables and includes intent information."@en .
27
+
28
+ abi:QueryArgument a owl:Class ;
29
+ rdfs:subClassOf bfo:BFO_0000031 ;
30
+ rdfs:label "Query Argument"@en ;
31
+ rdfs:comment "A class representing an argument that can be used to template a SPARQL query."@en .
32
+
33
+ #################################################################
34
+ # Object Properties
35
+ #################################################################
36
+
37
+ abi:hasArgument a owl:ObjectProperty ;
38
+ rdfs:label "has argument"@en ;
39
+ rdfs:domain abi:TemplatableSparqlQuery ;
40
+ rdfs:range abi:QueryArgument ;
41
+ rdfs:comment "Links a templatable SPARQL query to its arguments."@en .
42
+
43
+ #################################################################
44
+ # Data Properties
45
+ #################################################################
46
+
47
+ abi:intentDescription a owl:DatatypeProperty ;
48
+ rdfs:label "intent description"@en ;
49
+ rdfs:domain abi:TemplatableSparqlQuery ;
50
+ rdfs:range xsd:string ;
51
+ rdfs:comment "A natural language description of the query's intent."@en .
52
+
53
+ abi:sparqlTemplate a owl:DatatypeProperty ;
54
+ rdfs:label "SPARQL template"@en ;
55
+ rdfs:domain abi:TemplatableSparqlQuery ;
56
+ rdfs:range xsd:string ;
57
+ rdfs:comment "The SPARQL query template with variable placeholders."@en .
58
+
59
+ abi:argumentName a owl:DatatypeProperty ;
60
+ rdfs:label "argument name"@en ;
61
+ rdfs:domain abi:QueryArgument ;
62
+ rdfs:range xsd:string ;
63
+ rdfs:comment "The name of the argument used in the template."@en .
64
+
65
+ abi:argumentDescription a owl:DatatypeProperty ;
66
+ rdfs:label "argument description"@en ;
67
+ rdfs:domain abi:QueryArgument ;
68
+ rdfs:range xsd:string ;
69
+ rdfs:comment "A description of what the argument represents."@en .
70
+
71
+ abi:validationPattern a owl:DatatypeProperty ;
72
+ rdfs:label "validation pattern"@en ;
73
+ rdfs:domain abi:QueryArgument ;
74
+ rdfs:range xsd:string ;
75
+ rdfs:comment "A regex pattern for validating the argument value."@en .
76
+
77
+ abi:validationFormat a owl:DatatypeProperty ;
78
+ rdfs:label "validation format"@en ;
79
+ rdfs:domain abi:QueryArgument ;
80
+ rdfs:range xsd:string ;
81
+ rdfs:comment "The expected format of the argument value (e.g., date, number, URI)."@en .
82
+
83
+ #################################################################
84
+ # Example Usage
85
+ #################################################################
86
+
87
+ # # Example of a query to find employees in a department with optional name filter:
88
+ # abi:findEmployeesQuery a abi:TemplatableSparqlQuery ;
89
+ # rdfs:label "findEmployeesQuery"@en ;
90
+ # abi:intentDescription "Find all employees working in a specific department, optionally filtered by name" ;
91
+ # abi:sparqlTemplate """
92
+ # SELECT ?employee ?name
93
+ # WHERE {
94
+ # ?employee :worksIn :{{ department_id }} ;
95
+ # :hasName ?name .
96
+ # {% if employee_name %}
97
+ # FILTER(CONTAINS(LCASE(?name), LCASE("{{ employee_name }}")))
98
+ # {% endif %}
99
+ # }
100
+ # """ ;
101
+ # abi:hasArgument abi:departmentArg, abi:nameArg .
102
+
103
+ # abi:departmentArg a abi:QueryArgument ;
104
+ # abi:argumentName "department_id" ;
105
+ # abi:argumentDescription "The unique identifier of the department" ;
106
+ # abi:validationPattern "^[A-Z0-9]{3,}$" ;
107
+ # abi:validationFormat "department_id" .
108
+
109
+ # abi:nameArg a abi:QueryArgument ;
110
+ # abi:argumentName "employee_name" ;
111
+ # abi:argumentDescription "Optional name to filter employees (case-insensitive partial match)" ;
112
+ # abi:validationPattern "^[a-zA-Z0-9\\s-]{2,50}$" ;
113
+ # abi:validationFormat "employee_name" .
114
+
115
+ # #################################################################
116
+