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,246 @@
1
+ import base64
2
+ import datetime
3
+ import inspect
4
+ import json
5
+ import pickle
6
+ from collections import OrderedDict
7
+ from typing import Any, Callable
8
+
9
+ from naas_abi_core import logger
10
+ from naas_abi_core.services.cache.CachePort import (
11
+ CachedData,
12
+ CacheExpiredError,
13
+ CacheNotFoundError,
14
+ DataType,
15
+ ICacheAdapter,
16
+ ICacheService,
17
+ )
18
+
19
+
20
+ class ForceRefresh(Exception):
21
+ pass
22
+
23
+
24
+ class CacheService(ICacheService):
25
+ def __init__(self, adapter: ICacheAdapter):
26
+ self.adapter = adapter
27
+
28
+ self.deserializers = {
29
+ DataType.TEXT: self.__get_text,
30
+ DataType.JSON: self.__get_json,
31
+ DataType.BINARY: self.__get_binary,
32
+ DataType.PICKLE: self.__get_pickle,
33
+ }
34
+
35
+ def __call__(
36
+ self,
37
+ key_builder: Callable,
38
+ cache_type: DataType,
39
+ ttl: datetime.timedelta | None = None,
40
+ auto_cache: bool = True,
41
+ ):
42
+ """
43
+ Decorator to cache function results.
44
+
45
+ Args:
46
+ key_builder: Function that takes the same parameters as the decorated function
47
+ and returns a cache key (string or dict)
48
+ cache_type: Cache type to use (TEXT, JSON, BINARY, PICKLE).
49
+ Required when auto_cache=True (default: None)
50
+ ttl: Time-to-live for cached data. If specified, cached data will expire
51
+ after this duration (default: None)
52
+ auto_cache: Whether to automatically cache the result (default: True)
53
+
54
+ Example:
55
+ >>> # Create cache service instance
56
+ >>> cache_service = CacheService(adapter)
57
+ >>>
58
+ >>> # Apply cache decorator with explicit cache type
59
+ >>> @cache_service(lambda user_id, include_profile=False: f"user_{user_id}_profile_{include_profile}",
60
+ ... cache_type=DataType.JSON)
61
+ ... def get_user_data(user_id: int, include_profile: bool = False):
62
+ ... # Expensive operation (e.g., database query, API call)
63
+ ... return {"id": user_id, "name": "John Doe", "profile": include_profile}
64
+ >>>
65
+ >>> # Disable auto-caching (only retrieves from cache, doesn't store)
66
+ >>> @cache_service(lambda x: f"key_{x}", cache_type=DataType.TEXT, auto_cache=False)
67
+ ... def get_data_no_auto_cache(x):
68
+ ... return f"data_{x}"
69
+ >>>
70
+ >>> # Cache text data explicitly
71
+ >>> @cache_service(lambda x: f"key_{x}", cache_type=DataType.TEXT)
72
+ ... def get_text_data(x):
73
+ ... return f"data_{x}"
74
+ >>>
75
+ >>> # Cache with TTL (expires after 1 hour)
76
+ >>> @cache_service(lambda x: f"key_{x}", cache_type=DataType.JSON,
77
+ ... ttl=datetime.timedelta(hours=1))
78
+ ... def get_data_with_ttl(x):
79
+ ... return {"data": x, "timestamp": datetime.datetime.now()}
80
+ >>>
81
+ >>> # Force cache refresh with special parameter
82
+ >>> result = get_user_data(123, True, force_cache_refresh=True) # Bypasses cache
83
+ >>>
84
+ >>> # First call - executes function and caches result
85
+ >>> result1 = get_user_data(123, True) # Cache key: "user_123_profile_True"
86
+ >>>
87
+ >>> # Second call - returns cached result
88
+ >>> result2 = get_user_data(123, True) # Returns from cache
89
+ """
90
+
91
+ logger.debug(
92
+ f"Cache decorator called with key_builder: {key_builder}, auto_cache: {auto_cache}, cache_type: {cache_type}, ttl: {ttl}"
93
+ )
94
+
95
+ def decorator(func):
96
+ def wrapper(*args, **kwargs):
97
+ # Step 1: Create a complete mapping of all function arguments
98
+ # This will contain all arguments passed to the decorated function,
99
+ # including positional args, keyword args, and default values
100
+ mapped_args = OrderedDict()
101
+ func_args = inspect.signature(func).parameters
102
+ func_args_list = list(func_args.keys())
103
+
104
+ # Step 2: Map positional arguments to their parameter names
105
+ # Convert positional args like func(a, b, c) to named args like {'x': a, 'y': b, 'z': c}
106
+ for arg_index in range(len(args)):
107
+ arg_name = func_args_list[arg_index]
108
+ mapped_args[arg_name] = args[arg_index]
109
+
110
+ # Step 3: Add keyword arguments to the mapping
111
+ # Only include kwargs that are actually parameters of the decorated function
112
+ for arg_name, arg_value in kwargs.items():
113
+ if arg_name in func_args_list:
114
+ mapped_args[arg_name] = arg_value
115
+
116
+ # Step 4: Fill in default values for parameters that weren't provided
117
+ # This ensures we have a complete picture of all function arguments
118
+ for arg_name, arg_value in func_args.items():
119
+ if (
120
+ arg_value.default is not arg_value.empty
121
+ and arg_name not in mapped_args
122
+ ):
123
+ mapped_args[arg_name] = arg_value.default
124
+
125
+ # Step 5: Filter arguments to only include those needed by the key_builder
126
+ # The key_builder function may only need a subset of the decorated function's arguments
127
+ key_builder_args = inspect.signature(key_builder).parameters
128
+ filtered_args = OrderedDict()
129
+
130
+ # Only pass arguments that the key_builder function expects
131
+ for arg_name, arg_value in mapped_args.items():
132
+ if arg_name in key_builder_args:
133
+ filtered_args[arg_name] = arg_value
134
+
135
+ # Build cache key using the provided key_builder function
136
+ cache_key = key_builder(**filtered_args)
137
+
138
+ # Try to get from cache first
139
+ try:
140
+ if "force_cache_refresh" in kwargs:
141
+ del kwargs["force_cache_refresh"]
142
+ raise ForceRefresh()
143
+
144
+ cached_data = self.__get_cached_data(cache_key, ttl)
145
+
146
+ if cached_data.data_type == cache_type:
147
+ return self.deserializers[cache_type](cached_data)
148
+ else:
149
+ raise CacheNotFoundError(
150
+ f"Cache Data Type change from {cached_data.data_type} to {cache_type}."
151
+ )
152
+
153
+ except (CacheNotFoundError, CacheExpiredError, ForceRefresh):
154
+ # If not in cache or expired, execute function
155
+ result = func(*args, **kwargs)
156
+
157
+ # Cache result if auto_cache is enabled
158
+ if auto_cache:
159
+ # Use specified cache_type
160
+ if cache_type == DataType.TEXT:
161
+ self.set_text(cache_key, result)
162
+ elif cache_type == DataType.JSON:
163
+ self.set_json(cache_key, result)
164
+ elif cache_type == DataType.BINARY:
165
+ self.set_binary(cache_key, result)
166
+ elif cache_type == DataType.PICKLE:
167
+ self.set_pickle(cache_key, result)
168
+ else:
169
+ # Require explicit cache type specification
170
+ raise ValueError(
171
+ f"cache_type must be explicitly specified. "
172
+ f"Result type: {type(result).__name__}. "
173
+ f"Available types: {[dt.name for dt in DataType]}"
174
+ f"Cache key: {cache_key}"
175
+ f"cache_type: {cache_type}"
176
+ )
177
+
178
+ return result
179
+
180
+ return wrapper
181
+
182
+ return decorator
183
+
184
+ def __get_cached_data(
185
+ self, key: str, ttl: datetime.timedelta | None = None
186
+ ) -> CachedData:
187
+ try:
188
+ cached_data = self.adapter.get(key)
189
+ except (CacheNotFoundError, Exception) as _:
190
+ raise CacheNotFoundError(f"Cache not found: {key}")
191
+
192
+ if (
193
+ ttl
194
+ and datetime.datetime.fromisoformat(cached_data.created_at) + ttl
195
+ < datetime.datetime.now()
196
+ ):
197
+ raise CacheExpiredError(
198
+ f"Cache expired: {key}. TTL: {ttl}. Created at: {cached_data.created_at}."
199
+ )
200
+
201
+ return cached_data
202
+
203
+ def get(self, key: str, ttl: datetime.timedelta | None = None) -> Any:
204
+ cached_data = self.__get_cached_data(key, ttl)
205
+ return self.deserializers[cached_data.data_type](cached_data)
206
+
207
+ def __get_text(self, data: CachedData) -> str:
208
+ return data.data
209
+
210
+ def __get_json(self, data: CachedData) -> dict:
211
+ return json.loads(data.data)
212
+
213
+ def __get_binary(self, data: CachedData) -> bytes:
214
+ return base64.b64decode(data.data)
215
+
216
+ def __get_pickle(self, data: CachedData) -> Any:
217
+ return pickle.loads(base64.b64decode(data.data))
218
+
219
+ def set_text(self, key: str, value: str) -> None:
220
+ assert isinstance(value, str), f"Value must be a string. Got {type(value)}"
221
+ cached_data = CachedData(key=key, data=value, data_type=DataType.TEXT)
222
+ self.adapter.set(key, cached_data)
223
+
224
+ def set_json(self, key: str, value: Any) -> None:
225
+ cached_data = CachedData(
226
+ key=key, data=json.dumps(value), data_type=DataType.JSON
227
+ )
228
+ self.adapter.set(key, cached_data)
229
+
230
+ def set_binary(self, key: str, value: bytes) -> None:
231
+ assert isinstance(value, bytes), f"Value must be a bytes. Got {type(value)}"
232
+ cached_data = CachedData(
233
+ key=key, data=base64.b64encode(value).decode(), data_type=DataType.BINARY
234
+ )
235
+ self.adapter.set(key, cached_data)
236
+
237
+ def set_pickle(self, key: str, value: Any) -> None:
238
+ cached_data = CachedData(
239
+ key=key,
240
+ data=base64.b64encode(pickle.dumps(value)).decode(),
241
+ data_type=DataType.PICKLE,
242
+ )
243
+ self.adapter.set(key, cached_data)
244
+
245
+ def exists(self, key: str) -> bool:
246
+ return self.adapter.exists(key)
@@ -0,0 +1,85 @@
1
+ import datetime
2
+ import time
3
+
4
+ from naas_abi_core.services.cache.CachePort import (
5
+ CachedData,
6
+ CacheNotFoundError,
7
+ DataType,
8
+ ICacheAdapter,
9
+ )
10
+ from naas_abi_core.services.cache.CacheService import CacheService
11
+
12
+
13
+ class CacheMemoryAdapter(ICacheAdapter):
14
+ def __init__(self):
15
+ self.cache = {}
16
+
17
+ def get(self, key: str) -> CachedData:
18
+ if key not in self.cache:
19
+ raise CacheNotFoundError(f"Cache not found: {key}")
20
+ return self.cache[key]
21
+
22
+ def set(self, key: str, value: CachedData) -> None:
23
+ self.cache[key] = value
24
+
25
+ def delete(self, key: str) -> None:
26
+ if key not in self.cache:
27
+ raise CacheNotFoundError(f"Cache not found: {key}")
28
+ del self.cache[key]
29
+
30
+ def exists(self, key: str) -> bool:
31
+ return key in self.cache
32
+
33
+
34
+ def test_cache_service():
35
+ cache_service = CacheService(CacheMemoryAdapter())
36
+
37
+ salt = "good"
38
+
39
+ @cache_service(
40
+ lambda x: f"key_{x}",
41
+ cache_type=DataType.TEXT,
42
+ ttl=datetime.timedelta(seconds=1),
43
+ )
44
+ def get_text_data(x: str) -> str:
45
+ """Get text data"""
46
+ return f"data_{x}_{salt}"
47
+
48
+ # First call will not be cached and will populate it
49
+ assert get_text_data("test") == "data_test_good"
50
+
51
+ # We change the salt, so if the method is executed again, the assertion will fail. Here we want to
52
+ # make sure that we are getting the data from the cache.
53
+ salt = "bad"
54
+
55
+ assert get_text_data("test") == "data_test_good"
56
+
57
+ # We sleep for 1 second to make sure the cache will expire.
58
+ time.sleep(1)
59
+
60
+ assert get_text_data("test") == "data_test_bad"
61
+
62
+
63
+ def test_non_matching_arguments():
64
+ cache_service = CacheService(CacheMemoryAdapter())
65
+
66
+ @cache_service(
67
+ lambda x, z: f"key_{x}_{z}",
68
+ cache_type=DataType.TEXT,
69
+ ttl=datetime.timedelta(seconds=1),
70
+ )
71
+ def get_text_data(x: str, y: str, z: str = "toto") -> str:
72
+ """Get text data"""
73
+ return f"{x}_{y}_{z}"
74
+
75
+ assert get_text_data("test", "test", "tati") == "test_test_tati"
76
+ assert get_text_data(x="test", y="tata", z="tutu") == "test_tata_tutu"
77
+ assert get_text_data(x="test", y="toto") == "test_toto_toto"
78
+
79
+ # There is cache
80
+ assert get_text_data(x="test", y="yolo") == "test_toto_toto"
81
+
82
+ # Same but for cache refresh
83
+ assert (
84
+ get_text_data(x="test", y="yolo", force_cache_refresh=True) == "test_yolo_toto"
85
+ )
@@ -0,0 +1,39 @@
1
+ import hashlib
2
+ import json
3
+ import os
4
+
5
+ from naas_abi_core.services.cache.CachePort import (
6
+ CachedData,
7
+ CacheNotFoundError,
8
+ ICacheAdapter,
9
+ )
10
+
11
+
12
+ class CacheFSAdapter(ICacheAdapter):
13
+ def __init__(self, cache_dir: str):
14
+ self.cache_dir = cache_dir
15
+
16
+ if not os.path.exists(self.cache_dir):
17
+ os.makedirs(self.cache_dir)
18
+
19
+ def __key_to_sha256(self, key: str) -> str:
20
+ return hashlib.sha256(key.encode()).hexdigest()
21
+
22
+ def get(self, key: str) -> CachedData:
23
+ if not os.path.exists(os.path.join(self.cache_dir, self.__key_to_sha256(key))):
24
+ raise CacheNotFoundError(f"Cache file not found: {key}")
25
+
26
+ with open(os.path.join(self.cache_dir, self.__key_to_sha256(key)), "r") as f:
27
+ return CachedData(**json.load(f))
28
+
29
+ def set(self, key: str, value: CachedData) -> None:
30
+ with open(os.path.join(self.cache_dir, self.__key_to_sha256(key)), "w") as f:
31
+ json.dump(value.model_dump(), f, indent=4)
32
+
33
+ def delete(self, key: str) -> None:
34
+ if not os.path.exists(os.path.join(self.cache_dir, self.__key_to_sha256(key))):
35
+ raise CacheNotFoundError(f"Cache file not found: {key}")
36
+ os.remove(os.path.join(self.cache_dir, self.__key_to_sha256(key)))
37
+
38
+ def exists(self, key: str) -> bool:
39
+ return os.path.exists(os.path.join(self.cache_dir, self.__key_to_sha256(key)))
@@ -0,0 +1,57 @@
1
+ from naas_abi_core.services.object_storage.adapters.secondary.ObjectStorageSecondaryAdapterFS import (
2
+ ObjectStorageSecondaryAdapterFS,
3
+ )
4
+ from naas_abi_core.services.object_storage.adapters.secondary.ObjectStorageSecondaryAdapterNaas import (
5
+ ObjectStorageSecondaryAdapterNaas,
6
+ )
7
+ from naas_abi_core.services.object_storage.adapters.secondary.ObjectStorageSecondaryAdapterS3 import (
8
+ ObjectStorageSecondaryAdapterS3,
9
+ )
10
+ from naas_abi_core.services.object_storage.ObjectStorageService import (
11
+ ObjectStorageService,
12
+ )
13
+ from naas_abi_core.utils.Storage import find_storage_folder
14
+
15
+
16
+ class ObjectStorageFactory:
17
+ @staticmethod
18
+ def ObjectStorageServiceFS__find_storage(
19
+ needle: str = "storage",
20
+ ) -> ObjectStorageService:
21
+ import os
22
+
23
+ return ObjectStorageService(
24
+ ObjectStorageSecondaryAdapterFS(find_storage_folder(os.getcwd()))
25
+ )
26
+
27
+ @staticmethod
28
+ def ObjectStorageServiceFS(base_path: str) -> ObjectStorageService:
29
+ return ObjectStorageService(ObjectStorageSecondaryAdapterFS(base_path))
30
+
31
+ @staticmethod
32
+ def ObjectStorageServiceS3(
33
+ access_key_id: str,
34
+ secret_access_key: str,
35
+ bucket_name: str,
36
+ base_prefix: str,
37
+ session_token: str | None = None,
38
+ ) -> ObjectStorageService:
39
+ return ObjectStorageService(
40
+ ObjectStorageSecondaryAdapterS3(
41
+ bucket_name,
42
+ access_key_id,
43
+ secret_access_key,
44
+ base_prefix,
45
+ session_token,
46
+ )
47
+ )
48
+
49
+ @staticmethod
50
+ def ObjectStorageServiceNaas(
51
+ naas_api_key: str, workspace_id: str, storage_name: str, base_prefix: str = ""
52
+ ) -> ObjectStorageService:
53
+ return ObjectStorageService(
54
+ ObjectStorageSecondaryAdapterNaas(
55
+ naas_api_key, workspace_id, storage_name, base_prefix
56
+ )
57
+ )
@@ -0,0 +1,47 @@
1
+ from abc import ABC, abstractmethod
2
+ from queue import Queue
3
+ from typing import Optional
4
+
5
+
6
+ class Exceptions:
7
+ class ObjectNotFound(Exception):
8
+ pass
9
+
10
+ class ObjectAlreadyExists(Exception):
11
+ pass
12
+
13
+
14
+ class IObjectStorageAdapter(ABC):
15
+ @abstractmethod
16
+ def get_object(self, prefix: str, key: str) -> bytes:
17
+ pass
18
+
19
+ @abstractmethod
20
+ def put_object(self, prefix: str, key: str, content: bytes) -> None:
21
+ pass
22
+
23
+ @abstractmethod
24
+ def delete_object(self, prefix: str, key: str) -> None:
25
+ pass
26
+
27
+ @abstractmethod
28
+ def list_objects(self, prefix: str, queue: Optional[Queue] = None) -> list[str]:
29
+ pass
30
+
31
+
32
+ class IObjectStorageDomain(ABC):
33
+ @abstractmethod
34
+ def get_object(self, prefix: str, key: str) -> bytes:
35
+ pass
36
+
37
+ @abstractmethod
38
+ def put_object(self, prefix: str, key: str, content: bytes) -> None:
39
+ pass
40
+
41
+ @abstractmethod
42
+ def delete_object(self, prefix: str, key: str) -> None:
43
+ pass
44
+
45
+ @abstractmethod
46
+ def list_objects(self, prefix: str, queue: Optional[Queue] = None) -> list[str]:
47
+ pass
@@ -0,0 +1,41 @@
1
+ from queue import Queue
2
+ from typing import Optional
3
+
4
+ from naas_abi_core.services.object_storage.ObjectStoragePort import (
5
+ IObjectStorageAdapter,
6
+ IObjectStorageDomain,
7
+ )
8
+
9
+
10
+ class ObjectStorageService(IObjectStorageDomain):
11
+ adapter: IObjectStorageAdapter
12
+
13
+ def __init__(self, adapter: IObjectStorageAdapter):
14
+ self.adapter = adapter
15
+
16
+ # Function to avoid creating a new folder 'storage' while using FS adapter
17
+ def __remove_storage_prefix(self, prefix: str) -> str:
18
+ if prefix.startswith("storage/"):
19
+ return prefix.replace("storage/", "")
20
+ return prefix
21
+
22
+ def get_object(self, prefix: str, key: str) -> bytes:
23
+ prefix = self.__remove_storage_prefix(prefix)
24
+ return self.adapter.get_object(prefix, key)
25
+
26
+ def put_object(self, prefix: str, key: str, content: bytes) -> None:
27
+ prefix = self.__remove_storage_prefix(prefix)
28
+ self.adapter.put_object(prefix, key, content)
29
+
30
+ def delete_object(self, prefix: str, key: str) -> None:
31
+ prefix = self.__remove_storage_prefix(prefix)
32
+ self.adapter.delete_object(prefix, key)
33
+
34
+ def list_objects(
35
+ self, prefix: str = "", queue: Optional[Queue] = None
36
+ ) -> list[str]:
37
+ prefix = self.__remove_storage_prefix(prefix)
38
+ if prefix == "/":
39
+ prefix = ""
40
+
41
+ return self.adapter.list_objects(prefix, queue)
@@ -0,0 +1,52 @@
1
+ import os
2
+ from queue import Queue
3
+ from typing import Optional
4
+
5
+ from naas_abi_core.services.object_storage.ObjectStoragePort import (
6
+ Exceptions,
7
+ IObjectStorageAdapter,
8
+ )
9
+
10
+
11
+ class ObjectStorageSecondaryAdapterFS(IObjectStorageAdapter):
12
+ def __init__(self, base_path: str):
13
+ self.base_path = base_path
14
+ self.__create_path(base_path)
15
+
16
+ def __create_path(self, prefix: str) -> None:
17
+ os.makedirs(os.path.join(self.base_path, prefix), exist_ok=True)
18
+
19
+ def __path_exists(self, prefix: str, key: str | None = None) -> bool:
20
+ if key is None:
21
+ exists = os.path.exists(os.path.join(self.base_path, prefix))
22
+ else:
23
+ exists = os.path.exists(os.path.join(self.base_path, prefix, key))
24
+
25
+ if not exists:
26
+ raise Exceptions.ObjectNotFound(f"Object {prefix}/{key} not found")
27
+
28
+ return exists
29
+
30
+ def get_object(self, prefix: str, key: str) -> bytes:
31
+ self.__path_exists(prefix, key)
32
+
33
+ with open(os.path.join(self.base_path, prefix, key), "rb") as f:
34
+ return f.read()
35
+
36
+ def put_object(self, prefix: str, key: str, content: bytes) -> None:
37
+ self.__create_path(prefix)
38
+
39
+ with open(os.path.join(self.base_path, prefix, key), "wb") as f:
40
+ f.write(content)
41
+
42
+ def delete_object(self, prefix: str, key: str) -> None:
43
+ self.__path_exists(prefix, key)
44
+
45
+ os.remove(os.path.join(self.base_path, prefix, key))
46
+
47
+ def list_objects(self, prefix: str, queue: Optional[Queue] = None) -> list[str]:
48
+ self.__path_exists(prefix)
49
+ return [
50
+ os.path.join(prefix, f)
51
+ for f in os.listdir(os.path.join(self.base_path, prefix))
52
+ ]