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,57 @@
1
+ import base64
2
+ from io import StringIO
3
+ from typing import Any, Dict
4
+
5
+ from dotenv import dotenv_values
6
+ from naas_abi_core.services.secret.SecretPorts import ISecretAdapter
7
+
8
+
9
+ class Base64Secret(ISecretAdapter):
10
+ def __init__(self, secret_adapter: ISecretAdapter, base64_secret_key: str):
11
+ self.secret_adapter = secret_adapter
12
+ self.base64_secret_key = base64_secret_key
13
+
14
+ def __get_base64_secret(self) -> str:
15
+ return str(self.secret_adapter.get(self.base64_secret_key, ""))
16
+
17
+ def __get_decoded_secrets(self) -> Dict[str, str | None]:
18
+ base64_secret = self.__get_base64_secret()
19
+
20
+ if base64_secret == "":
21
+ return {}
22
+
23
+ base64_secret_content = base64.b64decode(base64_secret).decode("utf-8")
24
+ decoded_secrets = dotenv_values(stream=StringIO(base64_secret_content))
25
+
26
+ for key, value in decoded_secrets.items():
27
+ decoded_secrets[key] = str(value)
28
+
29
+ return decoded_secrets
30
+
31
+ def __encode_secrets(self, secrets: Dict[str, str | None]) -> str:
32
+ secret_string = ""
33
+ for key, value in secrets.items():
34
+ secret_string += f'{key}="{value}"\n'
35
+ return base64.b64encode(secret_string.encode("utf-8")).decode("utf-8")
36
+
37
+ def get(self, key: str, default: Any = None) -> str | Any | None:
38
+ return self.__get_decoded_secrets().get(key, default)
39
+
40
+ def set(self, key: str, value: str):
41
+ decoded_secrets = self.__get_decoded_secrets()
42
+
43
+ decoded_secrets[str(key)] = str(value)
44
+
45
+ self.secret_adapter.set(
46
+ self.base64_secret_key, self.__encode_secrets(decoded_secrets)
47
+ )
48
+
49
+ def remove(self, key: str):
50
+ decoded_secrets = self.__get_decoded_secrets()
51
+ decoded_secrets.pop(str(key), None)
52
+ self.secret_adapter.set(
53
+ self.base64_secret_key, self.__encode_secrets(decoded_secrets)
54
+ )
55
+
56
+ def list(self) -> Dict[str, str | None]:
57
+ return self.__get_decoded_secrets()
@@ -0,0 +1,39 @@
1
+ import os
2
+ import uuid
3
+
4
+ import pytest
5
+ from dotenv import load_dotenv
6
+
7
+
8
+ @pytest.fixture
9
+ def base64_secret_naas():
10
+ from naas_abi_core.services.secret.adaptors.secondary.Base64Secret import (
11
+ Base64Secret,
12
+ )
13
+ from naas_abi_core.services.secret.adaptors.secondary.NaasSecret import NaasSecret
14
+
15
+ load_dotenv()
16
+
17
+ naas_api_key = os.getenv("NAAS_API_KEY")
18
+ if naas_api_key is None:
19
+ pytest.skip("NAAS_API_KEY is not set")
20
+
21
+ naas_secret = NaasSecret(naas_api_key, os.getenv("NAAS_API_URL"))
22
+ base64_secret_key = str(uuid.uuid4())
23
+ return Base64Secret(naas_secret, base64_secret_key), naas_secret, base64_secret_key
24
+
25
+
26
+ def test_base64_secret(base64_secret_naas):
27
+ base64_secret_naas, naas_secret, base64_secret_key = base64_secret_naas
28
+ base64_secret_naas.set("test", "test")
29
+ assert base64_secret_naas.get("test") == "test"
30
+ assert base64_secret_naas.list() == {"test": "test"}
31
+ base64_secret_naas.remove("test")
32
+ assert base64_secret_naas.get("test") is None
33
+ assert base64_secret_naas.list() == {}
34
+ base64_secret_naas.set("test", "test")
35
+ assert base64_secret_naas.get("test") == "test"
36
+ assert base64_secret_naas.list() == {"test": "test"}
37
+ base64_secret_naas.remove("test")
38
+
39
+ naas_secret.remove(base64_secret_key)
@@ -0,0 +1,88 @@
1
+ from typing import Any, Dict
2
+
3
+ import requests
4
+ from naas_abi_core import logger
5
+ from naas_abi_core.services.secret.SecretPorts import (
6
+ ISecretAdapter,
7
+ SecretAuthenticationError,
8
+ )
9
+
10
+ NAAS_API_URL = "https://api.naas.ai"
11
+
12
+
13
+ class NaasSecret(ISecretAdapter):
14
+ def __init__(self, naas_api_key: str, naas_api_url: str | None = None):
15
+ self.naas_api_key = naas_api_key
16
+
17
+ if naas_api_url is None:
18
+ self.naas_api_url = NAAS_API_URL
19
+ else:
20
+ self.naas_api_url = naas_api_url
21
+
22
+ def get(self, key: str, default: Any = None) -> str | Any | None:
23
+ response = requests.get(
24
+ f"{self.naas_api_url}/secret/{key}",
25
+ headers={"Authorization": f"Bearer {self.naas_api_key}"},
26
+ )
27
+ try:
28
+ response.raise_for_status()
29
+ return str(response.json()["secret"]["value"])
30
+ except requests.exceptions.HTTPError as _:
31
+ if response.status_code == 404:
32
+ logger.debug(f"Secret {key} not found")
33
+ elif response.status_code == 401:
34
+ error_message = f"Authentication error on '{NAAS_API_URL}' with NAAS_API_KEY '{self.naas_api_key}'"
35
+ logger.error(error_message)
36
+ raise SecretAuthenticationError(error_message)
37
+ else:
38
+ logger.error(f"Error getting secret {key}: {response.status_code}")
39
+ return default
40
+
41
+ def set(self, key: str, value: str):
42
+ response = requests.post(
43
+ f"{self.naas_api_url}/secret/",
44
+ headers={"Authorization": f"Bearer {self.naas_api_key}"},
45
+ json={"secret": {"name": key, "value": value}},
46
+ )
47
+
48
+ try:
49
+ response.raise_for_status()
50
+ except requests.exceptions.HTTPError as _:
51
+ logger.error(f"Error setting secret {key}: {response.status_code}")
52
+ return
53
+
54
+ def remove(self, key: str):
55
+ response = requests.delete(
56
+ f"{self.naas_api_url}/secret/{key}",
57
+ headers={"Authorization": f"Bearer {self.naas_api_key}"},
58
+ )
59
+
60
+ try:
61
+ response.raise_for_status()
62
+ except requests.exceptions.HTTPError as _:
63
+ logger.error(f"Error removing secret {key}: {response.status_code}")
64
+ return
65
+
66
+ def list(self) -> Dict[str, str | None]:
67
+ response = requests.get(
68
+ f"{self.naas_api_url}/secret/",
69
+ headers={"Authorization": f"Bearer {self.naas_api_key}"},
70
+ json={
71
+ "page_size": 1000,
72
+ "page_number": 1,
73
+ },
74
+ )
75
+
76
+ try:
77
+ response.raise_for_status()
78
+ except requests.exceptions.HTTPError as _:
79
+ if response.status_code == 404:
80
+ logger.debug("No secrets found or workspace not accessible")
81
+ else:
82
+ logger.error(f"Error listing secrets: {response.status_code}")
83
+ return {}
84
+
85
+ return {
86
+ str(secret["name"]): str(secret["value"])
87
+ for secret in response.json()["secrets"]
88
+ }
@@ -0,0 +1,25 @@
1
+ import pytest
2
+
3
+
4
+ @pytest.fixture
5
+ def naas_secret():
6
+ import os
7
+
8
+ from dotenv import load_dotenv
9
+ from naas_abi_core.services.secret.adaptors.secondary.NaasSecret import NaasSecret
10
+
11
+ load_dotenv()
12
+
13
+ naas_api_key = os.getenv("NAAS_API_KEY")
14
+ if naas_api_key is None:
15
+ pytest.skip("NAAS_API_KEY is not set")
16
+
17
+ return NaasSecret(naas_api_key, os.getenv("NAAS_API_URL"))
18
+
19
+
20
+ def test_naas_secret(naas_secret):
21
+ naas_secret.set("test", "test")
22
+ assert naas_secret.get("test") == "test"
23
+ assert "test" in naas_secret.list()
24
+ naas_secret.remove("test")
25
+ assert naas_secret.get("test") is None
@@ -0,0 +1,29 @@
1
+ import os
2
+ from typing import Any, Dict
3
+
4
+ from dotenv import dotenv_values, find_dotenv, set_key
5
+ from naas_abi_core.services.secret.SecretPorts import ISecretAdapter
6
+ from naas_abi_core.utils.Logger import logger
7
+
8
+
9
+ class DotenvSecretSecondaryAdaptor(ISecretAdapter):
10
+ def __init__(self):
11
+ logger.debug(f"dotenv is using the file: {find_dotenv()}")
12
+
13
+ self.secrets = dotenv_values()
14
+
15
+ def get(self, key: str, default: Any = None) -> str:
16
+ return self.secrets.get(key, os.environ.get(key, default))
17
+
18
+ def set(self, key: str, value: str):
19
+ os.environ[key] = value
20
+ set_key(".env", key, value)
21
+
22
+ def remove(self, key: str):
23
+ os.environ.pop(key, None)
24
+
25
+ def list(self) -> Dict[str, str | None]:
26
+ secrets: Dict[str, str | None] = {}
27
+ for key in self.secrets.keys():
28
+ secrets[str(key)] = str(self.secrets.get(key))
29
+ return secrets
@@ -0,0 +1,116 @@
1
+ import os
2
+
3
+ from naas_abi_core.services.object_storage.ObjectStorageFactory import (
4
+ ObjectStorageFactory,
5
+ )
6
+ from naas_abi_core.services.triple_store.adaptors.secondary.AWSNeptune import (
7
+ AWSNeptuneSSHTunnel,
8
+ )
9
+ from naas_abi_core.services.triple_store.adaptors.secondary.Oxigraph import Oxigraph
10
+ from naas_abi_core.services.triple_store.adaptors.secondary.TripleStoreService__SecondaryAdaptor__Filesystem import (
11
+ TripleStoreService__SecondaryAdaptor__Filesystem,
12
+ )
13
+ from naas_abi_core.services.triple_store.adaptors.secondary.TripleStoreService__SecondaryAdaptor__ObjectStorage import (
14
+ TripleStoreService__SecondaryAdaptor__ObjectStorage,
15
+ )
16
+ from naas_abi_core.services.triple_store.TripleStoreService import TripleStoreService
17
+
18
+
19
+ class TripleStoreFactory:
20
+ @staticmethod
21
+ def TripleStoreServiceNaas(
22
+ naas_api_key: str,
23
+ workspace_id: str,
24
+ storage_name: str,
25
+ base_prefix: str = "ontologies",
26
+ ) -> TripleStoreService:
27
+ """Creates an TripleStoreService using Naas object storage.
28
+
29
+ Args:
30
+ naas_api_key (str): API key for Naas service
31
+ workspace_id (str): Workspace ID for Naas storage
32
+ storage_name (str): Name of storage to use
33
+ base_prefix (str): Base prefix for object paths. Defaults to "ontologies"
34
+
35
+ Returns:
36
+ TripleStoreService: Configured ontology store service using Naas storage
37
+ """
38
+ object_service = ObjectStorageFactory.ObjectStorageServiceNaas(
39
+ naas_api_key=naas_api_key,
40
+ workspace_id=workspace_id,
41
+ storage_name=storage_name,
42
+ base_prefix=base_prefix,
43
+ )
44
+ return TripleStoreService(
45
+ TripleStoreService__SecondaryAdaptor__ObjectStorage(object_service)
46
+ )
47
+
48
+ @staticmethod
49
+ def TripleStoreServiceFilesystem(path: str) -> TripleStoreService:
50
+ return TripleStoreService(
51
+ TripleStoreService__SecondaryAdaptor__Filesystem(path)
52
+ )
53
+
54
+ @staticmethod
55
+ def TripleStoreServiceAWSNeptuneSSHTunnel(
56
+ aws_region_name: str | None = None,
57
+ aws_access_key_id: str | None = None,
58
+ aws_secret_access_key: str | None = None,
59
+ db_instance_identifier: str | None = None,
60
+ bastion_host: str | None = None,
61
+ bastion_port: int | None = None,
62
+ bastion_user: str | None = None,
63
+ bastion_private_key: str | None = None,
64
+ ) -> TripleStoreService:
65
+ aws_region_name = os.environ.get("AWS_REGION")
66
+ aws_access_key_id = os.environ.get("AWS_ACCESS_KEY_ID")
67
+ aws_secret_access_key = os.environ.get("AWS_SECRET_ACCESS_KEY")
68
+ db_instance_identifier = os.environ.get("AWS_NEPTUNE_DB_INSTANCE_IDENTIFIER")
69
+ bastion_host = os.environ.get("AWS_BASTION_HOST")
70
+ bastion_port = int(os.environ.get("AWS_BASTION_PORT", -42))
71
+ bastion_user = os.environ.get("AWS_BASTION_USER")
72
+ bastion_private_key = os.environ.get("AWS_BASTION_PRIVATE_KEY")
73
+
74
+ assert aws_region_name is not None
75
+ assert aws_access_key_id is not None
76
+ assert aws_secret_access_key is not None
77
+ assert db_instance_identifier is not None
78
+ assert bastion_host is not None
79
+ assert bastion_port is not None and bastion_port != -42
80
+ assert bastion_user is not None
81
+ assert bastion_private_key is not None
82
+
83
+ return TripleStoreService(
84
+ AWSNeptuneSSHTunnel(
85
+ aws_region_name=aws_region_name,
86
+ aws_access_key_id=aws_access_key_id,
87
+ aws_secret_access_key=aws_secret_access_key,
88
+ db_instance_identifier=db_instance_identifier,
89
+ bastion_host=bastion_host,
90
+ bastion_port=bastion_port,
91
+ bastion_user=bastion_user,
92
+ bastion_private_key=bastion_private_key,
93
+ )
94
+ )
95
+
96
+ @staticmethod
97
+ def TripleStoreServiceOxigraph(
98
+ oxigraph_url: str | None = None,
99
+ ) -> TripleStoreService:
100
+ """Creates a TripleStoreService using Oxigraph.
101
+
102
+ Args:
103
+ oxigraph_url (str, optional): URL of the Oxigraph instance.
104
+ Defaults to OXIGRAPH_URL env var or http://localhost:7878
105
+
106
+ Returns:
107
+ TripleStoreService: Configured triple store service using Oxigraph
108
+ """
109
+ if oxigraph_url is None:
110
+ oxigraph_url = os.environ.get("OXIGRAPH_URL", "http://localhost:7878")
111
+
112
+ return TripleStoreService(
113
+ Oxigraph(
114
+ oxigraph_url=oxigraph_url,
115
+ )
116
+ )
@@ -0,0 +1,223 @@
1
+ from abc import ABC, abstractmethod
2
+ from rdflib import Graph, URIRef
3
+ import rdflib
4
+ from typing import List, Callable, Dict, Tuple
5
+ from enum import Enum
6
+
7
+
8
+ class Exceptions:
9
+ class SubjectNotFoundError(Exception):
10
+ pass
11
+
12
+ class SubscriptionNotFoundError(Exception):
13
+ pass
14
+
15
+ class ViewNotFoundError(Exception):
16
+ pass
17
+
18
+
19
+ class OntologyEvent(Enum):
20
+ INSERT = "INSERT"
21
+ DELETE = "DELETE"
22
+
23
+
24
+ class ITripleStorePort(ABC):
25
+ @abstractmethod
26
+ def insert(self, triples: Graph):
27
+ pass
28
+
29
+ @abstractmethod
30
+ def remove(self, triples: Graph):
31
+ pass
32
+
33
+ @abstractmethod
34
+ def get(self) -> Graph:
35
+ pass
36
+
37
+ @abstractmethod
38
+ def handle_view_event(
39
+ self,
40
+ view: Tuple[URIRef | None, URIRef | None, URIRef | None],
41
+ event: OntologyEvent,
42
+ triple: Tuple[URIRef | None, URIRef | None, URIRef | None],
43
+ ):
44
+ pass
45
+
46
+ @abstractmethod
47
+ def query(self, query: str) -> rdflib.query.Result:
48
+ pass
49
+
50
+ @abstractmethod
51
+ def query_view(self, view: str, query: str) -> rdflib.query.Result:
52
+ pass
53
+
54
+ @abstractmethod
55
+ def get_subject_graph(self, subject: URIRef) -> Graph:
56
+ pass
57
+
58
+
59
+ class ITripleStoreService(ABC):
60
+ __ontology_adaptor: ITripleStorePort
61
+
62
+ __event_listeners: Dict[
63
+ tuple, Dict[OntologyEvent, List[tuple[str, Callable, bool]]]
64
+ ]
65
+
66
+ __views: List[Tuple[URIRef | None, URIRef | None, URIRef | None]]
67
+
68
+ @abstractmethod
69
+ def subscribe(
70
+ self, topic: tuple, event_type: OntologyEvent, callback: Callable
71
+ ) -> str:
72
+ """Subscribe to events for a specific topic pattern.
73
+
74
+ This method allows subscribing to INSERT or DELETE events that match a specific subject-predicate-object
75
+ pattern. When matching triples are inserted or deleted, the provided callback will be executed.
76
+
77
+ Args:
78
+ topic (tuple): A (subject, predicate, object) tuple specifying the pattern to match.
79
+ Each element can be None to match any value in that position.
80
+ event_type (OntologyEvent): The type of event to subscribe to (INSERT or DELETE)
81
+ callback (Callable): Function to call when matching events occur. Will be called with:
82
+ - event_type: The OntologyEvent that occurred
83
+ - triple: The (subject, predicate, object) triple that matched
84
+
85
+ Returns:
86
+ str: A unique subscription ID that can be used to unsubscribe later
87
+ """
88
+ pass
89
+
90
+ @abstractmethod
91
+ def unsubscribe(self, subscription_id: str):
92
+ """Unsubscribe from events using a subscription ID.
93
+
94
+ This method removes a subscription based on its ID, stopping any further callbacks
95
+ for that subscription.
96
+
97
+ Args:
98
+ subscription_id (str): The subscription ID returned from a previous subscribe() call
99
+
100
+ Raises:
101
+ SubscriptionNotFoundError: If no subscription exists with the provided ID
102
+
103
+ Returns:
104
+ None
105
+ """
106
+ pass
107
+
108
+ @abstractmethod
109
+ def insert(self, triples: Graph):
110
+ """Insert triples from the provided graph into the store.
111
+
112
+ This method takes a graph of triples and inserts them into the triple store. The triples
113
+ are partitioned and stored by subject to enable efficient subject-based querying.
114
+
115
+ Args:
116
+ triples (Graph): The RDF graph containing triples to insert
117
+
118
+ Returns:
119
+ None
120
+ """
121
+ pass
122
+
123
+ @abstractmethod
124
+ def remove(self, triples: Graph):
125
+ """Remove triples from the provided graph from the store.
126
+
127
+ This method takes a graph of triples and removes them from the triple store. The triples
128
+ are matched against existing triples and removed where matches are found.
129
+
130
+ Args:
131
+ triples (Graph): The RDF graph containing triples to remove
132
+
133
+ Returns:
134
+ None
135
+ """
136
+ pass
137
+
138
+ @abstractmethod
139
+ def get(self) -> Graph:
140
+ """Get the complete RDF graph from the triple store.
141
+
142
+ This method retrieves and returns the full RDF graph containing all triples
143
+ stored in the triple store.
144
+
145
+ Returns:
146
+ Graph: The complete RDF graph containing all stored triples
147
+ """
148
+ pass
149
+
150
+ @abstractmethod
151
+ def query(self, query: str) -> rdflib.query.Result:
152
+ """Execute a SPARQL query against the triple store.
153
+
154
+ This method executes the provided SPARQL query string against all triples in the store
155
+ and returns the results as a new RDF graph.
156
+
157
+ Args:
158
+ query (str): The SPARQL query string to execute against the triple store
159
+
160
+ Returns:
161
+ Graph: A new RDF graph containing the query results
162
+
163
+ Example:
164
+ >>> store.query("SELECT ?s WHERE { ?s a owl:Class }")
165
+ """
166
+ pass
167
+
168
+ @abstractmethod
169
+ def query_view(self, view: str, query: str) -> rdflib.query.Result:
170
+ pass
171
+
172
+ @abstractmethod
173
+ def get_subject_graph(self, subject: str) -> Graph:
174
+ """Get the RDF graph containing all triples for a specific subject.
175
+
176
+ This method retrieves and returns an RDFlib Graph containing all triples
177
+ that have the specified subject.
178
+
179
+ Args:
180
+ subject (str): The subject to retrieve triples for
181
+
182
+ Returns:
183
+ Graph: An RDFlib Graph containing all triples for the specified subject
184
+
185
+ Raises:
186
+ SubjectNotFoundError: If no triples exist with the specified subject
187
+ """
188
+ pass
189
+
190
+ @abstractmethod
191
+ def load_schema(self, filepath: str):
192
+ """Load an RDF/OWL schema file into the triple store.
193
+
194
+ This method takes a file path string pointing to an RDF/OWL schema file,
195
+ loads it into an RDFlib Graph, and inserts all schema triples into the triple store.
196
+ The schema defines the ontology structure including classes, properties and restrictions.
197
+
198
+ Args:
199
+ filepath (str): Path to the RDF/OWL schema file to load
200
+
201
+ Returns:
202
+ None
203
+
204
+ Example:
205
+ >>> store.load_schema("path/to/schema.ttl")
206
+ """
207
+ pass
208
+
209
+ @abstractmethod
210
+ def get_schema_graph(self) -> Graph:
211
+ """Get the RDF graph containing just the schema/ontology triples.
212
+
213
+ This method returns an RDFlib Graph containing only the schema/ontology triples
214
+ that define classes, properties, and other structural elements. It excludes
215
+ instance data triples.
216
+
217
+ Returns:
218
+ Graph: An RDF graph containing only the schema/ontology triples
219
+
220
+ Example:
221
+ >>> schema = store.get_schema_graph()
222
+ """
223
+ pass