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.
- assets/favicon.ico +0 -0
- assets/logo.png +0 -0
- naas_abi_core/__init__.py +1 -0
- naas_abi_core/apps/api/api.py +245 -0
- naas_abi_core/apps/api/api_test.py +281 -0
- naas_abi_core/apps/api/openapi_doc.py +144 -0
- naas_abi_core/apps/mcp/Dockerfile.mcp +35 -0
- naas_abi_core/apps/mcp/mcp_server.py +243 -0
- naas_abi_core/apps/mcp/mcp_server_test.py +163 -0
- naas_abi_core/apps/terminal_agent/main.py +555 -0
- naas_abi_core/apps/terminal_agent/terminal_style.py +175 -0
- naas_abi_core/engine/Engine.py +87 -0
- naas_abi_core/engine/EngineProxy.py +109 -0
- naas_abi_core/engine/Engine_test.py +6 -0
- naas_abi_core/engine/IEngine.py +91 -0
- naas_abi_core/engine/conftest.py +45 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration.py +216 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_Deploy.py +7 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_GenericLoader.py +49 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_ObjectStorageService.py +159 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_ObjectStorageService_test.py +26 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_SecretService.py +138 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_SecretService_test.py +74 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_TripleStoreService.py +224 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_TripleStoreService_test.py +109 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_VectorStoreService.py +76 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_VectorStoreService_test.py +33 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_test.py +9 -0
- naas_abi_core/engine/engine_configuration/utils/PydanticModelValidator.py +15 -0
- naas_abi_core/engine/engine_loaders/EngineModuleLoader.py +302 -0
- naas_abi_core/engine/engine_loaders/EngineOntologyLoader.py +16 -0
- naas_abi_core/engine/engine_loaders/EngineServiceLoader.py +47 -0
- naas_abi_core/integration/__init__.py +7 -0
- naas_abi_core/integration/integration.py +28 -0
- naas_abi_core/models/Model.py +198 -0
- naas_abi_core/models/OpenRouter.py +18 -0
- naas_abi_core/models/OpenRouter_test.py +36 -0
- naas_abi_core/module/Module.py +252 -0
- naas_abi_core/module/ModuleAgentLoader.py +50 -0
- naas_abi_core/module/ModuleUtils.py +20 -0
- naas_abi_core/modules/templatablesparqlquery/README.md +196 -0
- naas_abi_core/modules/templatablesparqlquery/__init__.py +39 -0
- naas_abi_core/modules/templatablesparqlquery/ontologies/TemplatableSparqlQueryOntology.ttl +116 -0
- naas_abi_core/modules/templatablesparqlquery/workflows/GenericWorkflow.py +48 -0
- naas_abi_core/modules/templatablesparqlquery/workflows/TemplatableSparqlQueryLoader.py +192 -0
- naas_abi_core/pipeline/__init__.py +6 -0
- naas_abi_core/pipeline/pipeline.py +70 -0
- naas_abi_core/services/__init__.py +0 -0
- naas_abi_core/services/agent/Agent.py +1619 -0
- naas_abi_core/services/agent/AgentMemory_test.py +28 -0
- naas_abi_core/services/agent/Agent_test.py +214 -0
- naas_abi_core/services/agent/IntentAgent.py +1179 -0
- naas_abi_core/services/agent/IntentAgent_test.py +139 -0
- naas_abi_core/services/agent/beta/Embeddings.py +181 -0
- naas_abi_core/services/agent/beta/IntentMapper.py +120 -0
- naas_abi_core/services/agent/beta/LocalModel.py +88 -0
- naas_abi_core/services/agent/beta/VectorStore.py +89 -0
- naas_abi_core/services/agent/test_agent_memory.py +278 -0
- naas_abi_core/services/agent/test_postgres_integration.py +145 -0
- naas_abi_core/services/cache/CacheFactory.py +31 -0
- naas_abi_core/services/cache/CachePort.py +63 -0
- naas_abi_core/services/cache/CacheService.py +246 -0
- naas_abi_core/services/cache/CacheService_test.py +85 -0
- naas_abi_core/services/cache/adapters/secondary/CacheFSAdapter.py +39 -0
- naas_abi_core/services/object_storage/ObjectStorageFactory.py +57 -0
- naas_abi_core/services/object_storage/ObjectStoragePort.py +47 -0
- naas_abi_core/services/object_storage/ObjectStorageService.py +41 -0
- naas_abi_core/services/object_storage/adapters/secondary/ObjectStorageSecondaryAdapterFS.py +52 -0
- naas_abi_core/services/object_storage/adapters/secondary/ObjectStorageSecondaryAdapterNaas.py +131 -0
- naas_abi_core/services/object_storage/adapters/secondary/ObjectStorageSecondaryAdapterS3.py +171 -0
- naas_abi_core/services/ontology/OntologyPorts.py +36 -0
- naas_abi_core/services/ontology/OntologyService.py +17 -0
- naas_abi_core/services/ontology/adaptors/secondary/OntologyService_SecondaryAdaptor_NERPort.py +37 -0
- naas_abi_core/services/secret/Secret.py +138 -0
- naas_abi_core/services/secret/SecretPorts.py +45 -0
- naas_abi_core/services/secret/Secret_test.py +65 -0
- naas_abi_core/services/secret/adaptors/secondary/Base64Secret.py +57 -0
- naas_abi_core/services/secret/adaptors/secondary/Base64Secret_test.py +39 -0
- naas_abi_core/services/secret/adaptors/secondary/NaasSecret.py +88 -0
- naas_abi_core/services/secret/adaptors/secondary/NaasSecret_test.py +25 -0
- naas_abi_core/services/secret/adaptors/secondary/dotenv_secret_secondaryadaptor.py +29 -0
- naas_abi_core/services/triple_store/TripleStoreFactory.py +116 -0
- naas_abi_core/services/triple_store/TripleStorePorts.py +223 -0
- naas_abi_core/services/triple_store/TripleStoreService.py +419 -0
- naas_abi_core/services/triple_store/adaptors/secondary/AWSNeptune.py +1300 -0
- naas_abi_core/services/triple_store/adaptors/secondary/AWSNeptune_test.py +284 -0
- naas_abi_core/services/triple_store/adaptors/secondary/Oxigraph.py +597 -0
- naas_abi_core/services/triple_store/adaptors/secondary/Oxigraph_test.py +1474 -0
- naas_abi_core/services/triple_store/adaptors/secondary/TripleStoreService__SecondaryAdaptor__Filesystem.py +223 -0
- naas_abi_core/services/triple_store/adaptors/secondary/TripleStoreService__SecondaryAdaptor__ObjectStorage.py +234 -0
- naas_abi_core/services/triple_store/adaptors/secondary/base/TripleStoreService__SecondaryAdaptor__FileBase.py +18 -0
- naas_abi_core/services/vector_store/IVectorStorePort.py +101 -0
- naas_abi_core/services/vector_store/IVectorStorePort_test.py +189 -0
- naas_abi_core/services/vector_store/VectorStoreFactory.py +47 -0
- naas_abi_core/services/vector_store/VectorStoreService.py +171 -0
- naas_abi_core/services/vector_store/VectorStoreService_test.py +185 -0
- naas_abi_core/services/vector_store/__init__.py +13 -0
- naas_abi_core/services/vector_store/adapters/QdrantAdapter.py +251 -0
- naas_abi_core/services/vector_store/adapters/QdrantAdapter_test.py +57 -0
- naas_abi_core/tests/test_services_imports.py +69 -0
- naas_abi_core/utils/Expose.py +55 -0
- naas_abi_core/utils/Graph.py +182 -0
- naas_abi_core/utils/JSON.py +49 -0
- naas_abi_core/utils/LazyLoader.py +44 -0
- naas_abi_core/utils/Logger.py +12 -0
- naas_abi_core/utils/OntologyReasoner.py +141 -0
- naas_abi_core/utils/OntologyYaml.py +681 -0
- naas_abi_core/utils/SPARQL.py +256 -0
- naas_abi_core/utils/Storage.py +33 -0
- naas_abi_core/utils/StorageUtils.py +398 -0
- naas_abi_core/utils/String.py +52 -0
- naas_abi_core/utils/Workers.py +114 -0
- naas_abi_core/utils/__init__.py +0 -0
- naas_abi_core/utils/onto2py/README.md +0 -0
- naas_abi_core/utils/onto2py/__init__.py +10 -0
- naas_abi_core/utils/onto2py/__main__.py +29 -0
- naas_abi_core/utils/onto2py/onto2py.py +611 -0
- naas_abi_core/utils/onto2py/tests/ttl2py_test.py +271 -0
- naas_abi_core/workflow/__init__.py +5 -0
- naas_abi_core/workflow/workflow.py +48 -0
- naas_abi_core-1.4.1.dist-info/METADATA +630 -0
- naas_abi_core-1.4.1.dist-info/RECORD +124 -0
- naas_abi_core-1.4.1.dist-info/WHEEL +4 -0
- 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
|