naas-abi-core 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.
- naas_abi_core/__init__.py +1 -0
- naas_abi_core/apps/api/api.py +242 -0
- naas_abi_core/apps/api/api_test.py +281 -0
- naas_abi_core/apps/api/openapi_doc.py +307 -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/cli/__init__.py +53 -0
- naas_abi_core/cli/agent.py +30 -0
- naas_abi_core/cli/chat.py +26 -0
- naas_abi_core/cli/config.py +49 -0
- naas_abi_core/cli/init.py +13 -0
- naas_abi_core/cli/module.py +28 -0
- naas_abi_core/cli/new.py +13 -0
- naas_abi_core/cli/secret.py +79 -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 +160 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_GenericLoader.py +49 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_ObjectStorageService.py +131 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_ObjectStorageService_test.py +26 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_SecretService.py +116 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_TripleStoreService.py +171 -0
- naas_abi_core/engine/engine_configuration/EngineConfiguration_VectorStoreService.py +65 -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 +15 -0
- naas_abi_core/models/OpenRouter_test.py +36 -0
- naas_abi_core/module/Module.py +245 -0
- naas_abi_core/module/ModuleAgentLoader.py +49 -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 +1171 -0
- naas_abi_core/services/agent/IntentAgent_test.py +139 -0
- naas_abi_core/services/agent/beta/Embeddings.py +180 -0
- naas_abi_core/services/agent/beta/IntentMapper.py +119 -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 +40 -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 +81 -0
- naas_abi_core/services/secret/adaptors/secondary/NaasSecret_test.py +25 -0
- naas_abi_core/services/secret/adaptors/secondary/dotenv_secret_secondaryadaptor.py +26 -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 +1284 -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/utils/Expose.py +53 -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.disabled.py +679 -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.0.0.dist-info/METADATA +75 -0
- naas_abi_core-1.0.0.dist-info/RECORD +124 -0
- naas_abi_core-1.0.0.dist-info/WHEEL +4 -0
- naas_abi_core-1.0.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Dict, List, Union
|
|
4
|
+
|
|
5
|
+
from naas_abi_core.services.object_storage.ObjectStorageService import (
|
|
6
|
+
ObjectStorageService,
|
|
7
|
+
)
|
|
8
|
+
from naas_abi_core.services.secret.Secret import Secret
|
|
9
|
+
from naas_abi_core.services.triple_store.TripleStoreService import TripleStoreService
|
|
10
|
+
from naas_abi_core.services.vector_store.VectorStoreService import VectorStoreService
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from naas_abi_core.module.Module import BaseModule
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class IEngine:
|
|
17
|
+
class Services:
|
|
18
|
+
__object_storage: ObjectStorageService | None
|
|
19
|
+
__triple_store: TripleStoreService | None
|
|
20
|
+
__vector_store: VectorStoreService | None
|
|
21
|
+
__secret: Secret | None
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
object_storage: ObjectStorageService | None = None,
|
|
26
|
+
triple_store: TripleStoreService | None = None,
|
|
27
|
+
vector_store: VectorStoreService | None = None,
|
|
28
|
+
secret: Secret | None = None,
|
|
29
|
+
):
|
|
30
|
+
self.__object_storage = object_storage
|
|
31
|
+
self.__triple_store = triple_store
|
|
32
|
+
self.__vector_store = vector_store
|
|
33
|
+
self.__secret = secret
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def object_storage(self) -> ObjectStorageService:
|
|
37
|
+
assert self.__object_storage is not None, (
|
|
38
|
+
"Object storage service is not initialized"
|
|
39
|
+
)
|
|
40
|
+
return self.__object_storage
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def triple_store(self) -> TripleStoreService:
|
|
44
|
+
assert self.__triple_store is not None, (
|
|
45
|
+
"Triple store service is not initialized"
|
|
46
|
+
)
|
|
47
|
+
return self.__triple_store
|
|
48
|
+
|
|
49
|
+
def triple_store_available(self) -> bool:
|
|
50
|
+
return self.__triple_store is not None
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def vector_store(self) -> VectorStoreService:
|
|
54
|
+
assert self.__vector_store is not None, (
|
|
55
|
+
"Vector store service is not initialized"
|
|
56
|
+
)
|
|
57
|
+
return self.__vector_store
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def secret(self) -> Secret:
|
|
61
|
+
assert self.__secret is not None, "Secret service is not initialized"
|
|
62
|
+
return self.__secret
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def all(
|
|
66
|
+
self,
|
|
67
|
+
) -> List[
|
|
68
|
+
Union[
|
|
69
|
+
ObjectStorageService | None,
|
|
70
|
+
TripleStoreService | None,
|
|
71
|
+
VectorStoreService | None,
|
|
72
|
+
Secret | None,
|
|
73
|
+
]
|
|
74
|
+
]:
|
|
75
|
+
return [
|
|
76
|
+
self.__object_storage,
|
|
77
|
+
self.__triple_store,
|
|
78
|
+
self.__vector_store,
|
|
79
|
+
self.__secret,
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
__services: Services
|
|
83
|
+
__modules: Dict[str, BaseModule]
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def services(self) -> Services:
|
|
87
|
+
return self.__services
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def modules(self) -> Dict[str, BaseModule]:
|
|
91
|
+
return self.__modules
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from pytest import fixture
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@fixture
|
|
5
|
+
def test_configuration():
|
|
6
|
+
return """
|
|
7
|
+
workspace_id: "1234567890"
|
|
8
|
+
storage_name: "test"
|
|
9
|
+
github_repository: "test"
|
|
10
|
+
github_project_id: 1234567890
|
|
11
|
+
triple_store_path: "test"
|
|
12
|
+
api_title: "test"
|
|
13
|
+
api_description: "test"
|
|
14
|
+
logo_path: "test"
|
|
15
|
+
favicon_path: "test"
|
|
16
|
+
space_name: "test"
|
|
17
|
+
cors_origins:
|
|
18
|
+
- "http://localhost:9879"
|
|
19
|
+
|
|
20
|
+
services:
|
|
21
|
+
object_storage: &object_storage_service
|
|
22
|
+
object_storage_adapter:
|
|
23
|
+
adapter: "fs"
|
|
24
|
+
config:
|
|
25
|
+
base_path: "test"
|
|
26
|
+
test: "{{ secret.OXIGRAPH_URL }}"
|
|
27
|
+
|
|
28
|
+
triple_store:
|
|
29
|
+
triple_store_adapter:
|
|
30
|
+
adapter: "object_storage"
|
|
31
|
+
config:
|
|
32
|
+
triples_prefix: "test"
|
|
33
|
+
object_storage_service: *object_storage_service
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
vector_store:
|
|
37
|
+
vector_store_adapter:
|
|
38
|
+
adapter: "qdrant"
|
|
39
|
+
config: {}
|
|
40
|
+
|
|
41
|
+
secret:
|
|
42
|
+
secret_adapters:
|
|
43
|
+
- adapter: "dotenv"
|
|
44
|
+
config: {}
|
|
45
|
+
"""
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from io import StringIO
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
import yaml
|
|
6
|
+
from jinja2 import Template
|
|
7
|
+
from naas_abi_core import logger
|
|
8
|
+
from naas_abi_core.engine.engine_configuration.EngineConfiguration_ObjectStorageService import (
|
|
9
|
+
ObjectStorageServiceConfiguration,
|
|
10
|
+
)
|
|
11
|
+
from naas_abi_core.engine.engine_configuration.EngineConfiguration_SecretService import (
|
|
12
|
+
SecretServiceConfiguration,
|
|
13
|
+
)
|
|
14
|
+
from naas_abi_core.engine.engine_configuration.EngineConfiguration_TripleStoreService import (
|
|
15
|
+
TripleStoreServiceConfiguration,
|
|
16
|
+
)
|
|
17
|
+
from naas_abi_core.engine.engine_configuration.EngineConfiguration_VectorStoreService import (
|
|
18
|
+
VectorStoreServiceConfiguration,
|
|
19
|
+
)
|
|
20
|
+
from naas_abi_core.services.secret.Secret import Secret
|
|
21
|
+
from pydantic import BaseModel, model_validator
|
|
22
|
+
from typing_extensions import Literal
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ServicesConfiguration(BaseModel):
|
|
26
|
+
object_storage: ObjectStorageServiceConfiguration
|
|
27
|
+
triple_store: TripleStoreServiceConfiguration
|
|
28
|
+
vector_store: VectorStoreServiceConfiguration
|
|
29
|
+
secret: SecretServiceConfiguration
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ApiConfiguration(BaseModel):
|
|
33
|
+
title: str = "ABI API"
|
|
34
|
+
description: str = "API for ABI, your Artifical Business Intelligence"
|
|
35
|
+
logo_path: str = "assets/logo.png"
|
|
36
|
+
favicon_path: str = "assets/favicon.ico"
|
|
37
|
+
cors_origins: List[str] = ["http://localhost:9879"]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class FirstPassConfiguration(BaseModel):
|
|
41
|
+
"""This is a first pass configuration that is used to load the secret service.
|
|
42
|
+
|
|
43
|
+
It is used to load the secret service before the other services are loaded.
|
|
44
|
+
This is because the secret service needs to be loaded before the other services
|
|
45
|
+
are loaded to be able to resolve the secrets.
|
|
46
|
+
This is a first pass configuration that is used to load the secret service.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
class FirstPassServicesConfiguration(BaseModel):
|
|
50
|
+
secret: SecretServiceConfiguration
|
|
51
|
+
|
|
52
|
+
services: FirstPassServicesConfiguration
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ModuleConfig(BaseModel):
|
|
56
|
+
path: str | None = None
|
|
57
|
+
module: str | None = None
|
|
58
|
+
enabled: bool
|
|
59
|
+
config: dict = {}
|
|
60
|
+
|
|
61
|
+
@model_validator(mode="after")
|
|
62
|
+
def validate_path_or_module(self):
|
|
63
|
+
if self.path is None and self.module is None:
|
|
64
|
+
raise ValueError("Either path or module must be provided")
|
|
65
|
+
|
|
66
|
+
if self.path is None:
|
|
67
|
+
assert self.module is not None, (
|
|
68
|
+
"module must be provided if path is not provided"
|
|
69
|
+
)
|
|
70
|
+
if self.module is None:
|
|
71
|
+
assert self.path is not None, (
|
|
72
|
+
"path must be provided if module is not provided"
|
|
73
|
+
)
|
|
74
|
+
return self
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class GlobalConfig(BaseModel):
|
|
78
|
+
ai_mode: Literal["cloud", "local", "airgap"]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class EngineConfiguration(BaseModel):
|
|
82
|
+
api: ApiConfiguration
|
|
83
|
+
|
|
84
|
+
services: ServicesConfiguration
|
|
85
|
+
|
|
86
|
+
global_config: GlobalConfig
|
|
87
|
+
|
|
88
|
+
modules: List[ModuleConfig]
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
def from_yaml(cls, yaml_path: str) -> "EngineConfiguration":
|
|
92
|
+
with open(yaml_path, "r") as file:
|
|
93
|
+
return cls.from_yaml_content(file.read())
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def from_yaml_content(cls, yaml_content: str) -> "EngineConfiguration":
|
|
97
|
+
# First we do a pass with the minimal configuration to load the secret service.
|
|
98
|
+
class SecretServiceWrapper:
|
|
99
|
+
secret_service: Secret | None = None
|
|
100
|
+
|
|
101
|
+
def __init__(self, secret_service: Secret | None = None):
|
|
102
|
+
self.secret_service = secret_service
|
|
103
|
+
|
|
104
|
+
def __getattr__(self, name):
|
|
105
|
+
if self.secret_service is None:
|
|
106
|
+
return 0
|
|
107
|
+
secret = self.secret_service.get(name)
|
|
108
|
+
if secret is None:
|
|
109
|
+
raise ValueError(f"Secret {name} not found")
|
|
110
|
+
return secret
|
|
111
|
+
|
|
112
|
+
def get(self, key, default=None):
|
|
113
|
+
return 0
|
|
114
|
+
|
|
115
|
+
first_pass_data = yaml.safe_load(
|
|
116
|
+
StringIO(Template(yaml_content).render(secret=SecretServiceWrapper()))
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
first_pass_configuration = FirstPassConfiguration(**first_pass_data)
|
|
120
|
+
secret_service = first_pass_configuration.services.secret.load()
|
|
121
|
+
|
|
122
|
+
# Here we can now template the yaml by using `yaml_content` and the secret service.
|
|
123
|
+
# Using Jinja2 template engine.
|
|
124
|
+
|
|
125
|
+
logger.debug("Yaml content: {yaml_content}")
|
|
126
|
+
|
|
127
|
+
template = Template(yaml_content)
|
|
128
|
+
templated_yaml = template.render(secret=SecretServiceWrapper(secret_service))
|
|
129
|
+
|
|
130
|
+
logger.debug(f"Templated yaml: {templated_yaml}")
|
|
131
|
+
|
|
132
|
+
data = yaml.safe_load(StringIO(templated_yaml))
|
|
133
|
+
|
|
134
|
+
logger.debug(f"Data: {data}")
|
|
135
|
+
|
|
136
|
+
return cls(**data)
|
|
137
|
+
|
|
138
|
+
@classmethod
|
|
139
|
+
def load_configuration(
|
|
140
|
+
cls, configuration_yaml: str | None = None
|
|
141
|
+
) -> "EngineConfiguration":
|
|
142
|
+
# This is useful for testing.
|
|
143
|
+
if configuration_yaml is not None:
|
|
144
|
+
return cls.from_yaml_content(configuration_yaml)
|
|
145
|
+
|
|
146
|
+
logger.debug(f"Loading configuration from {os.getenv('ENV')}")
|
|
147
|
+
|
|
148
|
+
if os.path.exists(f"config.{os.getenv('ENV')}.yaml"):
|
|
149
|
+
return cls.from_yaml(f"config.{os.getenv('ENV')}.yaml")
|
|
150
|
+
elif os.path.exists("config.yaml"):
|
|
151
|
+
return cls.from_yaml("config.yaml")
|
|
152
|
+
else:
|
|
153
|
+
raise FileNotFoundError(
|
|
154
|
+
"Configuration file not found. Please create a config.yaml file or config.{env}.yaml file."
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
if __name__ == "__main__":
|
|
159
|
+
config = EngineConfiguration.load_configuration()
|
|
160
|
+
print(config)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
from typing import Any, Dict
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class GenericLoader(BaseModel):
|
|
8
|
+
"""
|
|
9
|
+
Generic loader for dynamically importing and instantiating Python callables.
|
|
10
|
+
|
|
11
|
+
This class enables dynamic loading of Python classes or functions by specifying
|
|
12
|
+
the module path and callable name. The callable is invoked with the provided
|
|
13
|
+
configuration dictionary unpacked as keyword arguments.
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
1. Use importlib.import_module(python_module) to import the target module
|
|
17
|
+
2. Use getattr(module, module_callable) to retrieve the callable
|
|
18
|
+
3. Invoke the callable with the custom_config as **custom_config
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
loader = GenericLoader(
|
|
22
|
+
python_module="my.package.module",
|
|
23
|
+
module_callable="MyClass",
|
|
24
|
+
custom_config={"param1": "value1", "param2": 42}
|
|
25
|
+
)
|
|
26
|
+
# This would effectively do:
|
|
27
|
+
# import importlib
|
|
28
|
+
# module = importlib.import_module("my.package.module")
|
|
29
|
+
# callable_obj = getattr(module, "MyClass")
|
|
30
|
+
# instance = callable_obj(param1="value1", param2=42)
|
|
31
|
+
|
|
32
|
+
Attributes:
|
|
33
|
+
python_module: The fully qualified Python module path (e.g., "package.subpackage.module")
|
|
34
|
+
module_callable: The name of the callable (class or function) to retrieve from the module
|
|
35
|
+
custom_config: Dictionary of configuration parameters to pass as keyword arguments to the callable
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
python_module: str | None = None
|
|
39
|
+
module_callable: str | None = None
|
|
40
|
+
custom_config: Dict[str, Any] | None = None
|
|
41
|
+
|
|
42
|
+
def load(self) -> Any:
|
|
43
|
+
assert self.python_module is not None, "python_module is required"
|
|
44
|
+
assert self.module_callable is not None, "module_callable is required"
|
|
45
|
+
assert self.custom_config is not None, "custom_config is required"
|
|
46
|
+
|
|
47
|
+
module = importlib.import_module(self.python_module)
|
|
48
|
+
callable_obj = getattr(module, self.module_callable)
|
|
49
|
+
return callable_obj(**self.custom_config)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
from typing import Dict, Literal, Tuple, Union
|
|
2
|
+
|
|
3
|
+
from naas_abi_core.engine.engine_configuration.EngineConfiguration_GenericLoader import (
|
|
4
|
+
GenericLoader,
|
|
5
|
+
)
|
|
6
|
+
from naas_abi_core.engine.engine_configuration.utils.PydanticModelValidator import (
|
|
7
|
+
pydantic_model_validator,
|
|
8
|
+
)
|
|
9
|
+
from naas_abi_core.services.object_storage.ObjectStoragePort import (
|
|
10
|
+
IObjectStorageAdapter,
|
|
11
|
+
)
|
|
12
|
+
from naas_abi_core.services.object_storage.ObjectStorageService import (
|
|
13
|
+
ObjectStorageService,
|
|
14
|
+
)
|
|
15
|
+
from pydantic import BaseModel, model_validator
|
|
16
|
+
from typing_extensions import Self
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ObjectStorageAdapterFSConfiguration(BaseModel):
|
|
20
|
+
base_path: str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ObjectStorageAdapterS3Configuration(BaseModel):
|
|
24
|
+
bucket_name: str
|
|
25
|
+
access_key_id: str
|
|
26
|
+
secret_access_key: str
|
|
27
|
+
base_prefix: str
|
|
28
|
+
session_token: str | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ObjectStorageAdapterNaasConfiguration(BaseModel):
|
|
32
|
+
naas_api_key: str
|
|
33
|
+
workspace_id: str
|
|
34
|
+
storage_name: str
|
|
35
|
+
base_prefix: str = ""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ObjectStorageAdapterConfiguration(GenericLoader):
|
|
39
|
+
adapter: Literal["fs", "s3", "naas", "custom"]
|
|
40
|
+
config: (
|
|
41
|
+
Union[
|
|
42
|
+
ObjectStorageAdapterFSConfiguration,
|
|
43
|
+
ObjectStorageAdapterS3Configuration,
|
|
44
|
+
ObjectStorageAdapterNaasConfiguration,
|
|
45
|
+
]
|
|
46
|
+
| None
|
|
47
|
+
) = None
|
|
48
|
+
|
|
49
|
+
__MAPPING: Dict[Literal["fs", "s3", "naas"], Tuple[str, str]] = {
|
|
50
|
+
"fs": (
|
|
51
|
+
"abi.services.object_storage.adapters.secondary.ObjectStorageSecondaryAdapterFS",
|
|
52
|
+
"ObjectStorageSecondaryAdapterFS",
|
|
53
|
+
),
|
|
54
|
+
"s3": (
|
|
55
|
+
"abi.services.object_storage.adapters.secondary.ObjectStorageSecondaryAdapterS3",
|
|
56
|
+
"ObjectStorageSecondaryAdapterS3",
|
|
57
|
+
),
|
|
58
|
+
"naas": (
|
|
59
|
+
"abi.services.object_storage.adapters.secondary.ObjectStorageSecondaryAdapterNaas",
|
|
60
|
+
"ObjectStorageSecondaryAdapterNaas",
|
|
61
|
+
),
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@model_validator(mode="after")
|
|
65
|
+
def validate_adapter(self) -> Self:
|
|
66
|
+
if self.adapter != "custom":
|
|
67
|
+
assert self.config is not None, (
|
|
68
|
+
"config is required if adapter is not custom"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if self.adapter == "fs":
|
|
72
|
+
pydantic_model_validator(
|
|
73
|
+
ObjectStorageAdapterFSConfiguration,
|
|
74
|
+
self.config,
|
|
75
|
+
"Invalid configuration for services.object_storage.object_storage_adapter 'fs' adapter",
|
|
76
|
+
)
|
|
77
|
+
if self.adapter == "s3":
|
|
78
|
+
pydantic_model_validator(
|
|
79
|
+
ObjectStorageAdapterS3Configuration,
|
|
80
|
+
self.config,
|
|
81
|
+
"Invalid configuration for services.object_storage.object_storage_adapter 's3' adapter",
|
|
82
|
+
)
|
|
83
|
+
if self.adapter == "naas":
|
|
84
|
+
pydantic_model_validator(
|
|
85
|
+
ObjectStorageAdapterNaasConfiguration,
|
|
86
|
+
self.config,
|
|
87
|
+
"Invalid configuration for services.object_storage.object_storage_adapter 'naas' adapter",
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
return self
|
|
91
|
+
|
|
92
|
+
def load(self) -> IObjectStorageAdapter:
|
|
93
|
+
if self.adapter != "custom":
|
|
94
|
+
assert self.config is not None, (
|
|
95
|
+
"config is required if adapter is not custom"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if self.adapter == "fs":
|
|
99
|
+
from naas_abi_core.services.object_storage.adapters.secondary.ObjectStorageSecondaryAdapterFS import (
|
|
100
|
+
ObjectStorageSecondaryAdapterFS,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return ObjectStorageSecondaryAdapterFS(**self.config.model_dump())
|
|
104
|
+
elif self.adapter == "s3":
|
|
105
|
+
from naas_abi_core.services.object_storage.adapters.secondary.ObjectStorageSecondaryAdapterS3 import (
|
|
106
|
+
ObjectStorageSecondaryAdapterS3,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return ObjectStorageSecondaryAdapterS3(**self.config.model_dump())
|
|
110
|
+
elif self.adapter == "naas":
|
|
111
|
+
from naas_abi_core.services.object_storage.adapters.secondary.ObjectStorageSecondaryAdapterNaas import (
|
|
112
|
+
ObjectStorageSecondaryAdapterNaas,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return ObjectStorageSecondaryAdapterNaas(**self.config.model_dump())
|
|
116
|
+
else:
|
|
117
|
+
raise ValueError(f"Unknown adapter: {self.adapter}")
|
|
118
|
+
# return GenericLoader(
|
|
119
|
+
# python_module=self.__MAPPING[self.adapter][0],
|
|
120
|
+
# module_callable=self.__MAPPING[self.adapter][1],
|
|
121
|
+
# custom_config=self.config.model_dump(),
|
|
122
|
+
# ).load()
|
|
123
|
+
else:
|
|
124
|
+
return super().load()
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class ObjectStorageServiceConfiguration(BaseModel):
|
|
128
|
+
object_storage_adapter: ObjectStorageAdapterConfiguration
|
|
129
|
+
|
|
130
|
+
def load(self) -> ObjectStorageService:
|
|
131
|
+
return ObjectStorageService(adapter=self.object_storage_adapter.load())
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from naas_abi_core.engine.engine_configuration.EngineConfiguration_ObjectStorageService import (
|
|
2
|
+
ObjectStorageAdapterConfiguration,
|
|
3
|
+
ObjectStorageAdapterFSConfiguration,
|
|
4
|
+
ObjectStorageServiceConfiguration,
|
|
5
|
+
)
|
|
6
|
+
from naas_abi_core.services.object_storage.adapters.secondary.ObjectStorageSecondaryAdapterFS import (
|
|
7
|
+
ObjectStorageSecondaryAdapterFS,
|
|
8
|
+
)
|
|
9
|
+
from naas_abi_core.services.object_storage.ObjectStoragePort import (
|
|
10
|
+
IObjectStorageAdapter,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_object_storage_service_configuration():
|
|
15
|
+
configuration = ObjectStorageServiceConfiguration(
|
|
16
|
+
object_storage_adapter=ObjectStorageAdapterConfiguration(
|
|
17
|
+
adapter="fs", config=ObjectStorageAdapterFSConfiguration(base_path="test")
|
|
18
|
+
)
|
|
19
|
+
)
|
|
20
|
+
assert configuration.object_storage_adapter is not None
|
|
21
|
+
|
|
22
|
+
object_storage_adapter = configuration.object_storage_adapter.load()
|
|
23
|
+
|
|
24
|
+
assert object_storage_adapter is not None
|
|
25
|
+
assert isinstance(object_storage_adapter, IObjectStorageAdapter)
|
|
26
|
+
assert isinstance(object_storage_adapter, ObjectStorageSecondaryAdapterFS)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, List, Literal, Union
|
|
2
|
+
|
|
3
|
+
from naas_abi_core.engine.engine_configuration.EngineConfiguration_GenericLoader import (
|
|
4
|
+
GenericLoader,
|
|
5
|
+
)
|
|
6
|
+
from naas_abi_core.engine.engine_configuration.utils.PydanticModelValidator import (
|
|
7
|
+
pydantic_model_validator,
|
|
8
|
+
)
|
|
9
|
+
from naas_abi_core.services.secret.Secret import Secret
|
|
10
|
+
from naas_abi_core.services.secret.SecretPorts import ISecretAdapter
|
|
11
|
+
from pydantic import BaseModel, model_validator
|
|
12
|
+
from typing_extensions import Self
|
|
13
|
+
|
|
14
|
+
# Only import for type checking, not at runtime
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from naas_abi_core.services.secret.adaptors.secondary.Base64Secret import (
|
|
17
|
+
Base64Secret,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Base64SecretConfiguration(BaseModel):
|
|
22
|
+
secret_adapter: "SecretAdapterConfiguration"
|
|
23
|
+
base64_secret_key: str
|
|
24
|
+
|
|
25
|
+
def load(self) -> "Base64Secret":
|
|
26
|
+
from naas_abi_core.services.secret.adaptors.secondary.Base64Secret import (
|
|
27
|
+
Base64Secret,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
return Base64Secret(self.secret_adapter.load(), self.base64_secret_key)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class NaasSecretConfiguration(BaseModel):
|
|
34
|
+
naas_api_key: str
|
|
35
|
+
naas_api_url: str
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class DotenvSecretConfiguration(BaseModel):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class SecretAdapterConfiguration(GenericLoader):
|
|
43
|
+
adapter: Literal["dotenv", "naas", "base64", "custom"]
|
|
44
|
+
config: (
|
|
45
|
+
Union[
|
|
46
|
+
DotenvSecretConfiguration,
|
|
47
|
+
NaasSecretConfiguration,
|
|
48
|
+
Base64SecretConfiguration,
|
|
49
|
+
]
|
|
50
|
+
| None
|
|
51
|
+
) = None
|
|
52
|
+
|
|
53
|
+
@model_validator(mode="after")
|
|
54
|
+
def validate_adapter(self) -> Self:
|
|
55
|
+
if self.adapter != "custom":
|
|
56
|
+
assert self.config is not None, (
|
|
57
|
+
"config is required if adapter is not custom"
|
|
58
|
+
)
|
|
59
|
+
if self.adapter == "base64":
|
|
60
|
+
pydantic_model_validator(
|
|
61
|
+
Base64SecretConfiguration,
|
|
62
|
+
self.config,
|
|
63
|
+
"Invalid configuration for services.secret.secret_adapters 'base64' adapter",
|
|
64
|
+
)
|
|
65
|
+
if self.adapter == "dotenv":
|
|
66
|
+
pydantic_model_validator(
|
|
67
|
+
DotenvSecretConfiguration,
|
|
68
|
+
self.config,
|
|
69
|
+
"Invalid configuration for services.secret.secret_adapters 'dotenv' adapter",
|
|
70
|
+
)
|
|
71
|
+
if self.adapter == "naas":
|
|
72
|
+
pydantic_model_validator(
|
|
73
|
+
NaasSecretConfiguration,
|
|
74
|
+
self.config,
|
|
75
|
+
"Invalid configuration for services.secret.secret_adapters 'naas' adapter",
|
|
76
|
+
)
|
|
77
|
+
return self
|
|
78
|
+
|
|
79
|
+
def load(self) -> ISecretAdapter:
|
|
80
|
+
if self.adapter != "custom":
|
|
81
|
+
assert self.config is not None, (
|
|
82
|
+
"config is required if adapter is not custom"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Lazy import: only import the adapter that's actually configured
|
|
86
|
+
if self.adapter == "base64":
|
|
87
|
+
assert isinstance(self.config, Base64SecretConfiguration), (
|
|
88
|
+
"config must be a Base64SecretConfiguration if adapter is base64"
|
|
89
|
+
)
|
|
90
|
+
assert hasattr(self.config, "load"), (
|
|
91
|
+
"config must have a load method if adapter is base64"
|
|
92
|
+
)
|
|
93
|
+
return self.config.load()
|
|
94
|
+
elif self.adapter == "dotenv":
|
|
95
|
+
from naas_abi_core.services.secret.adaptors.secondary.dotenv_secret_secondaryadaptor import (
|
|
96
|
+
DotenvSecretSecondaryAdaptor,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
return DotenvSecretSecondaryAdaptor(**self.config.model_dump())
|
|
100
|
+
elif self.adapter == "naas":
|
|
101
|
+
from naas_abi_core.services.secret.adaptors.secondary.NaasSecret import (
|
|
102
|
+
NaasSecret,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return NaasSecret(**self.config.model_dump())
|
|
106
|
+
else:
|
|
107
|
+
raise ValueError(f"Unknown adapter: {self.adapter}")
|
|
108
|
+
else:
|
|
109
|
+
return super().load()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class SecretServiceConfiguration(BaseModel):
|
|
113
|
+
secret_adapters: List[SecretAdapterConfiguration]
|
|
114
|
+
|
|
115
|
+
def load(self) -> Secret:
|
|
116
|
+
return Secret(adapters=[adapter.load() for adapter in self.secret_adapters])
|