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,131 @@
1
+ import os
2
+ from dataclasses import dataclass
3
+ from datetime import datetime, timedelta
4
+ from queue import Queue
5
+ from typing import Optional
6
+
7
+ import pydash
8
+ import requests
9
+
10
+ # Load S3 secondary adapter as we will use it.
11
+ from naas_abi_core.services.object_storage.adapters.secondary.ObjectStorageSecondaryAdapterS3 import (
12
+ ObjectStorageSecondaryAdapterS3,
13
+ )
14
+ from naas_abi_core.services.object_storage.ObjectStoragePort import (
15
+ IObjectStorageAdapter,
16
+ )
17
+
18
+ NAAS_API_URL = "https://api.naas.ai/"
19
+ CREDENTIALS_EXPIRATION_TIME = timedelta(minutes=20)
20
+
21
+
22
+ @dataclass
23
+ class Credentials:
24
+ bucket_name: str
25
+ bucket_prefix: str
26
+ access_key_id: str
27
+ secret_key: str
28
+ session_token: str
29
+ region_name: str
30
+ created_at: datetime
31
+
32
+
33
+ class ObjectStorageSecondaryAdapterNaas(IObjectStorageAdapter):
34
+ __naas_api_key: str
35
+ __workspace_id: str
36
+ __storage_name: str
37
+ __credentials: Credentials | None
38
+ __s3_adapter: ObjectStorageSecondaryAdapterS3 | None
39
+
40
+ def __init__(
41
+ self,
42
+ naas_api_key: str,
43
+ workspace_id: str,
44
+ storage_name: str,
45
+ base_prefix: str = "",
46
+ ):
47
+ self.__naas_api_key = naas_api_key
48
+ self.__workspace_id = workspace_id
49
+ self.__storage_name = storage_name
50
+ self.__base_prefix = base_prefix
51
+ self.__credentials: Credentials | None = None
52
+ self.__s3_adapter: ObjectStorageSecondaryAdapterS3 | None = None
53
+
54
+ def ensure_credentials(self) -> Credentials:
55
+ if self.__credentials is None:
56
+ # TODO: Handle raise_for_status.
57
+ self.__refresh_credentials()
58
+
59
+ assert self.__credentials is not None
60
+
61
+ # If credentials are older than 10 minutes, refresh them
62
+ if self.__credentials.created_at < datetime.now() - CREDENTIALS_EXPIRATION_TIME:
63
+ self.__refresh_credentials()
64
+
65
+ return self.__credentials
66
+
67
+ def __refresh_credentials(self) -> None:
68
+ response = requests.post(
69
+ f"{NAAS_API_URL}/workspace/{self.__workspace_id}/storage/credentials/",
70
+ headers={"Authorization": f"Bearer {self.__naas_api_key}"},
71
+ json={
72
+ "name": self.__storage_name,
73
+ },
74
+ )
75
+
76
+ response.raise_for_status()
77
+
78
+ credentials = response.json()
79
+
80
+ self.__credentials = Credentials(
81
+ bucket_name=pydash.get(credentials, "credentials.s3.endpoint_url").split(
82
+ "/"
83
+ )[2],
84
+ bucket_prefix="/".join(
85
+ pydash.get(credentials, "credentials.s3.endpoint_url").split("/")[3:]
86
+ ),
87
+ access_key_id=pydash.get(credentials, "credentials.s3.access_key_id"),
88
+ secret_key=pydash.get(credentials, "credentials.s3.secret_key"),
89
+ session_token=pydash.get(credentials, "credentials.s3.session_token"),
90
+ region_name=pydash.get(credentials, "credentials.s3.region_name"),
91
+ created_at=datetime.now(),
92
+ )
93
+
94
+ # Re instantiate the S3 adapter with the new credentials
95
+ self.__s3_adapter = ObjectStorageSecondaryAdapterS3(
96
+ bucket_name=self.__credentials.bucket_name,
97
+ access_key_id=self.__credentials.access_key_id,
98
+ secret_access_key=self.__credentials.secret_key,
99
+ base_prefix=os.path.join(
100
+ self.__credentials.bucket_prefix, self.__base_prefix
101
+ ),
102
+ session_token=self.__credentials.session_token,
103
+ )
104
+
105
+ def get_object(self, prefix: str, key: str) -> bytes:
106
+ self.ensure_credentials()
107
+
108
+ assert self.__s3_adapter is not None
109
+
110
+ return self.__s3_adapter.get_object(prefix, key)
111
+
112
+ def put_object(self, prefix: str, key: str, content: bytes):
113
+ self.ensure_credentials()
114
+
115
+ assert self.__s3_adapter is not None
116
+
117
+ return self.__s3_adapter.put_object(prefix, key, content)
118
+
119
+ def delete_object(self, prefix: str, key: str):
120
+ self.ensure_credentials()
121
+
122
+ assert self.__s3_adapter is not None
123
+
124
+ return self.__s3_adapter.delete_object(prefix, key)
125
+
126
+ def list_objects(self, prefix: str, queue: Optional[Queue] = None) -> list[str]:
127
+ self.ensure_credentials()
128
+
129
+ assert self.__s3_adapter is not None
130
+
131
+ return self.__s3_adapter.list_objects(prefix, queue)
@@ -0,0 +1,171 @@
1
+ from queue import Queue
2
+ from typing import Optional
3
+
4
+ import boto3
5
+ from botocore.exceptions import ClientError
6
+ from naas_abi_core.services.object_storage.ObjectStoragePort import (
7
+ Exceptions,
8
+ IObjectStorageAdapter,
9
+ )
10
+
11
+
12
+ class ObjectStorageSecondaryAdapterS3(IObjectStorageAdapter):
13
+ """S3 implementation of the Object Storage adapter using boto3."""
14
+
15
+ def __init__(
16
+ self,
17
+ bucket_name: str,
18
+ access_key_id: str,
19
+ secret_access_key: str,
20
+ base_prefix: str = "",
21
+ session_token: str | None = None,
22
+ ):
23
+ """Initialize S3 adapter with bucket name and credentials.
24
+
25
+ Args:
26
+ bucket_name (str): Name of the S3 bucket to use
27
+ access_key_id (str): AWS access key ID
28
+ secret_access_key (str): AWS secret access key
29
+ base_prefix (str, optional): Base prefix to prepend to all operations. Defaults to ""
30
+ session_token (str, optional): AWS session token. Defaults to None
31
+ """
32
+ self.bucket_name = bucket_name
33
+ self.base_prefix = base_prefix.rstrip("/") # Remove trailing slash if present
34
+ self.s3_client = boto3.client(
35
+ "s3",
36
+ aws_access_key_id=access_key_id,
37
+ aws_secret_access_key=secret_access_key,
38
+ aws_session_token=session_token,
39
+ )
40
+
41
+ def __get_full_key(self, prefix: str, key: str | None = None) -> str:
42
+ """Construct full key path including base prefix.
43
+
44
+ Args:
45
+ prefix (str): Prefix/folder path
46
+ key (str, optional): Object key name
47
+
48
+ Returns:
49
+ str: Full key path
50
+ """
51
+ # if key is None:
52
+ # return f"{self.base_prefix}/{prefix}".lstrip('/')
53
+ return f"{self.base_prefix + '/' if self.base_prefix else ''}{prefix + '/' if prefix else ''}{key if key else ''}".lstrip(
54
+ "/"
55
+ )
56
+
57
+ def __object_exists(self, prefix: str, key: str | None = None) -> bool:
58
+ """Check if an object exists in S3.
59
+
60
+ Args:
61
+ prefix (str): Prefix/folder path
62
+ key (str, optional): Object key name
63
+
64
+ Returns:
65
+ bool: True if exists, raises exception if not
66
+ """
67
+ try:
68
+ if key is None:
69
+ # Check if prefix exists by listing objects
70
+ response = self.s3_client.list_objects_v2(
71
+ Bucket=self.bucket_name,
72
+ Prefix=self.__get_full_key(prefix),
73
+ MaxKeys=1,
74
+ )
75
+ if "Contents" not in response:
76
+ raise Exceptions.ObjectNotFound(f"Prefix {prefix} not found")
77
+ else:
78
+ # Check specific object
79
+ self.s3_client.head_object(
80
+ Bucket=self.bucket_name, Key=self.__get_full_key(prefix, key)
81
+ )
82
+ return True
83
+ except ClientError as e:
84
+ if e.response["Error"]["Code"] in ["404", "NoSuchKey"]:
85
+ raise Exceptions.ObjectNotFound(f"Object {prefix}/{key} not found")
86
+ raise e
87
+
88
+ def get_object(self, prefix: str, key: str) -> bytes:
89
+ """Get object from S3 bucket.
90
+
91
+ Args:
92
+ prefix (str): Prefix/folder path
93
+ key (str): Object key name
94
+
95
+ Returns:
96
+ bytes: Object content
97
+ """
98
+ self.__object_exists(prefix, key)
99
+
100
+ response = self.s3_client.get_object(
101
+ Bucket=self.bucket_name, Key=self.__get_full_key(prefix, key)
102
+ )
103
+ return response["Body"].read()
104
+
105
+ def put_object(self, prefix: str, key: str, content: bytes) -> None:
106
+ """Put object into S3 bucket.
107
+
108
+ Args:
109
+ prefix (str): Prefix/folder path
110
+ key (str): Object key name
111
+ content (bytes): Content to upload
112
+ """
113
+ self.s3_client.put_object(
114
+ Bucket=self.bucket_name, Key=self.__get_full_key(prefix, key), Body=content
115
+ )
116
+
117
+ def delete_object(self, prefix: str, key: str) -> None:
118
+ """Delete object from S3 bucket.
119
+
120
+ Args:
121
+ prefix (str): Prefix/folder path
122
+ key (str): Object key name
123
+ """
124
+ self.__object_exists(prefix, key)
125
+
126
+ self.s3_client.delete_object(
127
+ Bucket=self.bucket_name, Key=self.__get_full_key(prefix, key)
128
+ )
129
+
130
+ def list_objects(self, prefix: str, queue: Optional[Queue] = None) -> list[str]:
131
+ """List objects in S3 bucket with given prefix.
132
+
133
+ Args:
134
+ prefix (str): Prefix/folder path to list
135
+
136
+ Returns:
137
+ list[str]: List of object keys at depth 1 only (direct children)
138
+ """
139
+ self.__object_exists(prefix)
140
+
141
+ objects = []
142
+ paginator = self.s3_client.get_paginator("list_objects_v2")
143
+
144
+ for page in paginator.paginate(
145
+ Bucket=self.bucket_name,
146
+ Prefix=self.__get_full_key(prefix),
147
+ Delimiter="/", # This makes it list only one level deep
148
+ ):
149
+ # Get regular objects at this level
150
+ if "Contents" in page:
151
+ for obj in page["Contents"]:
152
+ # Remove base prefix from returned keys
153
+ key = obj["Key"]
154
+ if self.base_prefix:
155
+ key = key.replace(f"{self.base_prefix}/", "", 1)
156
+ if key != "":
157
+ objects.append(key)
158
+ if queue:
159
+ queue.put(key)
160
+
161
+ # Get subfolders at this level
162
+ if "CommonPrefixes" in page:
163
+ for prefix_obj in page["CommonPrefixes"]:
164
+ prefix_key = prefix_obj["Prefix"]
165
+ if self.base_prefix:
166
+ prefix_key = prefix_key.replace(f"{self.base_prefix}/", "", 1)
167
+ if prefix_key != "":
168
+ objects.append(prefix_key)
169
+ if queue:
170
+ queue.put(prefix_key)
171
+ return objects
@@ -0,0 +1,36 @@
1
+ from abc import ABC, abstractmethod
2
+ from rdflib import Graph
3
+ from langchain_core.language_models import BaseChatModel
4
+
5
+
6
+ class IOntologyNERPort(ABC):
7
+ __chat_model: BaseChatModel
8
+
9
+ @abstractmethod
10
+ def named_entity_recognition(self, input: str, ontology_str: str) -> Graph:
11
+ """Apply Named Entity Recognition (NER) on input text to map entities to ontology concepts.
12
+
13
+ Args:
14
+ input (str): The unstructured text to process with NER
15
+ ontology_str (str): The ontology in Turtle format to map entities against
16
+
17
+ Returns:
18
+ Graph: An RDFLib Graph containing the mapped entities and their relationships from the input text
19
+ """
20
+ pass
21
+
22
+
23
+ class IOntologyService(ABC):
24
+ __ner_adaptor: IOntologyNERPort
25
+
26
+ @abstractmethod
27
+ def named_entity_recognition(self, input: str) -> Graph:
28
+ """Apply Named Entity Recognition (NER) on input text to map entities to ontology concepts.
29
+
30
+ Args:
31
+ input (str): The input text to process with NER
32
+
33
+ Returns:
34
+ Graph: An RDFLib Graph containing the mapped entities and their relationships
35
+ """
36
+ pass
@@ -0,0 +1,17 @@
1
+ from naas_abi_core.services.ontology.OntologyPorts import (
2
+ IOntologyNERPort,
3
+ IOntologyService,
4
+ )
5
+ from rdflib import Graph
6
+
7
+
8
+ class OntologyService(IOntologyService):
9
+ __ontology_str: str
10
+ __ner_adaptor: IOntologyNERPort
11
+
12
+ def __init__(self, ner_adaptor: IOntologyNERPort, ontology_str: str):
13
+ self.__ner_adaptor = ner_adaptor
14
+ self.__ontology_str = ontology_str
15
+
16
+ def named_entity_recognition(self, input: str) -> Graph:
17
+ return self.__ner_adaptor.named_entity_recognition(input, self.__ontology_str)
@@ -0,0 +1,37 @@
1
+ from langchain_core.language_models import BaseChatModel
2
+ from langchain_core.messages import HumanMessage, SystemMessage
3
+ from naas_abi_core.services.ontology.OntologyPorts import IOntologyNERPort
4
+ from rdflib import Graph
5
+
6
+
7
+ class OntologyService_SecondaryAdaptor_NERPort(IOntologyNERPort):
8
+ __chat_model: BaseChatModel
9
+
10
+ def __init__(self, chat_model: BaseChatModel):
11
+ self.__chat_model = chat_model
12
+
13
+ def named_entity_recognition(self, input: str, ontology_str: str) -> Graph:
14
+ messages = [
15
+ SystemMessage(
16
+ content=f"""
17
+ You are an expert in Named Entity Recognition (NER).
18
+ You are given a string of unstructured text and an ontology in Turtle format.
19
+ Your task is to map the entities in the text to the ontology concepts.
20
+
21
+ Here is the ontology in Turtle format:
22
+ {ontology_str}
23
+
24
+
25
+ Only output the Turtle formated file ready to be stored in a file. You must add all prefixes.
26
+ """
27
+ ),
28
+ HumanMessage(content=input),
29
+ ]
30
+ response = self.__chat_model.invoke(messages)
31
+ assert isinstance(response.content, str)
32
+ sanitized_response = response.content.replace("```turtle", "").replace(
33
+ "```", ""
34
+ )
35
+ g = Graph()
36
+ g.parse(data=sanitized_response, format="turtle")
37
+ return g
@@ -0,0 +1,138 @@
1
+ """Secret Service Module
2
+
3
+ This module provides a unified interface for managing secrets across different storage systems.
4
+ The Secret service acts as a facade that coordinates multiple secret adapters, allowing applications
5
+ to retrieve, store, and manage secrets from various sources like environment variables, files,
6
+ cloud services, or other secret management systems.
7
+
8
+ The module implements the adapter pattern to support pluggable secret storage backends while
9
+ providing a consistent API for secret operations.
10
+
11
+ Classes:
12
+ Secret: Main service class implementing ISecretService interface
13
+
14
+ Example:
15
+ >>> from naas_abi_core.services.secret.adapters import EnvVarSecretAdapter, FileSecretAdapter
16
+ >>> adapters = [EnvVarSecretAdapter(), FileSecretAdapter("/path/to/secrets")]
17
+ >>> secret_service = Secret(adapters)
18
+ >>> api_key = secret_service.get("API_KEY", "default_key")
19
+ """
20
+
21
+ from typing import Any, Dict, List
22
+
23
+ from naas_abi_core.services.secret.SecretPorts import ISecretAdapter, ISecretService
24
+
25
+
26
+ class Secret(ISecretService):
27
+ """Secret service for managing and retrieving secrets.
28
+
29
+ This service provides a unified interface for accessing secrets regardless of their storage location
30
+ (environment variables, files, cloud services, etc.) through the use of adapters.
31
+
32
+ Attributes:
33
+ __adapter (ISecretAdapter): The adapter implementation used for retrieving secrets
34
+ from the underlying storage system.
35
+
36
+ Example:
37
+ >>> secret_service = Secret(EnvVarSecretAdapter())
38
+ >>> api_key = secret_service.get("API_KEY")
39
+ """
40
+
41
+ __adapters: List[ISecretAdapter]
42
+
43
+ def __init__(self, adapters: List[ISecretAdapter]):
44
+ self.__adapters = adapters
45
+
46
+ def get(self, key: str, default: Any = None) -> str:
47
+ """Retrieve a secret value by its key.
48
+
49
+ Searches for the secret across all configured adapters in order. Returns the value
50
+ from the first adapter that contains the key, or the default value if not found.
51
+
52
+ Args:
53
+ key (str): The secret key to retrieve.
54
+ default (Any, optional): The value to return if the key is not found.
55
+ Defaults to None.
56
+
57
+ Returns:
58
+ str: The secret value if found, otherwise the default value.
59
+
60
+ Example:
61
+ >>> secret_service = Secret([EnvVarSecretAdapter()])
62
+ >>> database_url = secret_service.get("DATABASE_URL", "sqlite:///:memory:")
63
+ """
64
+ for adapter in self.__adapters:
65
+ value = adapter.get(key, None)
66
+
67
+ if value is not None:
68
+ return value
69
+
70
+ return default
71
+
72
+ def set(self, key: str, value: str):
73
+ """Set a secret value across all configured adapters.
74
+
75
+ Stores the secret key-value pair in all adapters. This ensures consistency
76
+ across different secret storage systems.
77
+
78
+ Args:
79
+ key (str): The secret key to set.
80
+ value (str): The secret value to store.
81
+
82
+ Note:
83
+ If any adapter fails to set the value, the operation continues with
84
+ remaining adapters. Consider implementing error handling based on
85
+ your specific requirements.
86
+
87
+ Example:
88
+ >>> secret_service = Secret([EnvVarSecretAdapter(), FileSecretAdapter()])
89
+ >>> secret_service.set("API_KEY", "your-secret-api-key")
90
+ """
91
+ for adapter in self.__adapters:
92
+ adapter.set(key, value)
93
+
94
+ def remove(self, key: str):
95
+ """Remove a secret key from all configured adapters.
96
+
97
+ Deletes the specified key from all adapters. This ensures the secret
98
+ is completely removed from all storage systems.
99
+
100
+ Args:
101
+ key (str): The secret key to remove.
102
+
103
+ Note:
104
+ If any adapter fails to remove the key, the operation continues with
105
+ remaining adapters. The method does not raise exceptions for missing keys.
106
+
107
+ Example:
108
+ >>> secret_service = Secret([EnvVarSecretAdapter(), FileSecretAdapter()])
109
+ >>> secret_service.remove("OLD_API_KEY")
110
+ """
111
+ for adapter in self.__adapters:
112
+ adapter.remove(key)
113
+
114
+ def list(self) -> Dict[str, str | None]:
115
+ """Retrieve all secrets from all configured adapters.
116
+
117
+ Combines secrets from all adapters into a single dictionary. When the same
118
+ key exists in multiple adapters, the value from the adapter with higher
119
+ priority (earlier in the list) takes precedence.
120
+
121
+ Returns:
122
+ Dict[str, str]: A dictionary containing all secret key-value pairs.
123
+
124
+ Note:
125
+ The order of adapters matters for conflict resolution. Adapters are
126
+ processed in reverse order, so earlier adapters override later ones.
127
+
128
+ Example:
129
+ >>> secret_service = Secret([EnvVarSecretAdapter(), FileSecretAdapter()])
130
+ >>> all_secrets = secret_service.list()
131
+ >>> print(f"Found {len(all_secrets)} secrets")
132
+ """
133
+ secrets = {}
134
+
135
+ for adapter in [adapter for adapter in self.__adapters][::-1]:
136
+ secrets.update(adapter.list())
137
+
138
+ return secrets
@@ -0,0 +1,45 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any, Dict, List
3
+
4
+
5
+ class SecretAuthenticationError(Exception):
6
+ pass
7
+
8
+
9
+ class ISecretAdapter(ABC):
10
+ @abstractmethod
11
+ def get(self, key: str, default: Any = None) -> str | Any | None:
12
+ raise NotImplementedError()
13
+
14
+ @abstractmethod
15
+ def set(self, key: str, value: str):
16
+ raise NotImplementedError()
17
+
18
+ @abstractmethod
19
+ def remove(self, key: str):
20
+ raise NotImplementedError()
21
+
22
+ @abstractmethod
23
+ def list(self) -> Dict[str, str | None]:
24
+ raise NotImplementedError()
25
+
26
+
27
+ class ISecretService(ABC):
28
+ __adapter: List[ISecretAdapter]
29
+
30
+ @abstractmethod
31
+ def get(self, key: str, default: Any = None) -> str | Any | None:
32
+ raise NotImplementedError()
33
+
34
+ @abstractmethod
35
+ def set(self, key: str, value: str):
36
+ raise NotImplementedError()
37
+
38
+ @abstractmethod
39
+ def remove(self, key: str):
40
+ raise NotImplementedError()
41
+
42
+ @abstractmethod
43
+ def list(self) -> Dict[str, str | None]:
44
+ raise NotImplementedError()
45
+ raise NotImplementedError()
@@ -0,0 +1,65 @@
1
+ from typing import Any, Dict
2
+
3
+ import pytest
4
+ from naas_abi_core.services.secret.Secret import Secret
5
+ from naas_abi_core.services.secret.SecretPorts import ISecretAdapter
6
+
7
+
8
+ @pytest.fixture
9
+ def TestSecretAdapter():
10
+ class TestSecretAdapter(ISecretAdapter):
11
+ def __init__(self, secrets: Dict[str, str | None]):
12
+ self.secrets = secrets or {}
13
+
14
+ def get(self, key: str, default: Any = None) -> str | Any | None:
15
+ return self.secrets.get(key, default)
16
+
17
+ def set(self, key: str, value: str):
18
+ self.secrets[key] = value
19
+
20
+ def remove(self, key: str):
21
+ self.secrets.pop(key, None)
22
+
23
+ def list(self) -> Dict[str, str | None]:
24
+ return self.secrets
25
+
26
+ return TestSecretAdapter
27
+
28
+
29
+ def test_secret(TestSecretAdapter):
30
+ secret = Secret(
31
+ [
32
+ TestSecretAdapter(
33
+ {
34
+ "hello": "world",
35
+ }
36
+ ),
37
+ TestSecretAdapter(
38
+ {
39
+ "hello": "abi",
40
+ "second": "second",
41
+ }
42
+ ),
43
+ ]
44
+ )
45
+
46
+ assert secret.get("hello") == "world"
47
+ assert secret.list() == {
48
+ "hello": "world",
49
+ "second": "second",
50
+ }
51
+
52
+ secret.remove("hello")
53
+ assert secret.get("hello") is None
54
+ assert secret.list() == {
55
+ "second": "second",
56
+ }
57
+
58
+ secret.remove("second")
59
+ assert secret.list() == {}
60
+
61
+ secret.set("hello", "world")
62
+ assert secret.get("hello") == "world"
63
+ assert secret.list() == {
64
+ "hello": "world",
65
+ }