pyxecm 2.0.3__py3-none-any.whl → 3.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.
Potentially problematic release.
This version of pyxecm might be problematic. Click here for more details.
- pyxecm/coreshare.py +76 -8
- pyxecm/helper/data.py +16 -24
- pyxecm/helper/otel_config.py +26 -0
- pyxecm/helper/web.py +1 -2
- pyxecm/otca.py +1356 -16
- pyxecm/otcs.py +4238 -758
- pyxecm/otds.py +4 -12
- pyxecm/otmm.py +4 -5
- pyxecm/py.typed +0 -0
- pyxecm-3.0.0.dist-info/METADATA +48 -0
- pyxecm-3.0.0.dist-info/RECORD +96 -0
- {pyxecm-2.0.3.dist-info → pyxecm-3.0.0.dist-info}/WHEEL +1 -2
- pyxecm-3.0.0.dist-info/entry_points.txt +4 -0
- {pyxecm/customizer/api → pyxecm_api}/__main__.py +1 -1
- pyxecm_api/agents/__init__.py +7 -0
- pyxecm_api/agents/app.py +13 -0
- pyxecm_api/agents/functions.py +119 -0
- pyxecm_api/agents/models.py +10 -0
- pyxecm_api/agents/otcm_knowledgegraph/functions.py +85 -0
- pyxecm_api/agents/otcm_knowledgegraph/models.py +61 -0
- pyxecm_api/agents/otcm_knowledgegraph/router.py +74 -0
- pyxecm_api/agents/otcm_user_agent/models.py +20 -0
- pyxecm_api/agents/otcm_user_agent/router.py +65 -0
- pyxecm_api/agents/otcm_workspace_agent/models.py +40 -0
- pyxecm_api/agents/otcm_workspace_agent/router.py +200 -0
- pyxecm_api/app.py +221 -0
- {pyxecm/customizer/api → pyxecm_api}/auth/functions.py +10 -2
- {pyxecm/customizer/api → pyxecm_api}/auth/router.py +4 -3
- {pyxecm/customizer/api → pyxecm_api}/common/functions.py +39 -9
- {pyxecm/customizer/api → pyxecm_api}/common/metrics.py +1 -2
- {pyxecm/customizer/api → pyxecm_api}/common/router.py +12 -11
- {pyxecm/customizer/api → pyxecm_api}/settings.py +30 -6
- {pyxecm/customizer/api → pyxecm_api}/terminal/router.py +1 -1
- {pyxecm/customizer/api → pyxecm_api}/v1_csai/router.py +39 -10
- pyxecm_api/v1_csai/statics/bindings/utils.js +189 -0
- pyxecm_api/v1_csai/statics/tom-select/tom-select.complete.min.js +356 -0
- pyxecm_api/v1_csai/statics/tom-select/tom-select.css +334 -0
- pyxecm_api/v1_csai/statics/vis-9.1.2/vis-network.css +1 -0
- pyxecm_api/v1_csai/statics/vis-9.1.2/vis-network.min.js +27 -0
- pyxecm_api/v1_maintenance/__init__.py +1 -0
- {pyxecm/customizer/api → pyxecm_api}/v1_maintenance/functions.py +3 -3
- {pyxecm/customizer/api → pyxecm_api}/v1_maintenance/router.py +8 -8
- pyxecm_api/v1_otcs/__init__.py +1 -0
- {pyxecm/customizer/api → pyxecm_api}/v1_otcs/functions.py +7 -5
- {pyxecm/customizer/api → pyxecm_api}/v1_otcs/router.py +24 -13
- pyxecm_api/v1_payload/__init__.py +1 -0
- {pyxecm/customizer/api → pyxecm_api}/v1_payload/functions.py +10 -7
- {pyxecm/customizer/api → pyxecm_api}/v1_payload/router.py +11 -10
- {pyxecm/customizer → pyxecm_customizer}/__init__.py +8 -0
- {pyxecm/customizer → pyxecm_customizer}/__main__.py +15 -21
- {pyxecm/customizer → pyxecm_customizer}/browser_automation.py +414 -103
- {pyxecm/customizer → pyxecm_customizer}/customizer.py +178 -116
- {pyxecm/customizer → pyxecm_customizer}/guidewire.py +60 -20
- {pyxecm/customizer → pyxecm_customizer}/k8s.py +4 -4
- pyxecm_customizer/knowledge_graph.py +719 -0
- pyxecm_customizer/log.py +35 -0
- {pyxecm/customizer → pyxecm_customizer}/m365.py +41 -33
- {pyxecm/customizer → pyxecm_customizer}/payload.py +2359 -1991
- {pyxecm/customizer/api/common → pyxecm_customizer}/payload_list.py +57 -65
- {pyxecm/customizer → pyxecm_customizer}/salesforce.py +1 -1
- {pyxecm/customizer → pyxecm_customizer}/sap.py +6 -2
- {pyxecm/customizer → pyxecm_customizer}/servicenow.py +2 -4
- {pyxecm/customizer → pyxecm_customizer}/settings.py +7 -6
- {pyxecm/customizer → pyxecm_customizer}/successfactors.py +40 -28
- {pyxecm/customizer → pyxecm_customizer}/translate.py +14 -10
- {pyxecm/maintenance_page → pyxecm_maintenance_page}/__main__.py +1 -1
- {pyxecm/maintenance_page → pyxecm_maintenance_page}/app.py +16 -6
- pyxecm/customizer/api/app.py +0 -163
- pyxecm/customizer/log.py +0 -107
- pyxecm/customizer/nhc.py +0 -1169
- pyxecm/customizer/openapi.py +0 -258
- pyxecm/customizer/pht.py +0 -1357
- pyxecm-2.0.3.dist-info/METADATA +0 -119
- pyxecm-2.0.3.dist-info/RECORD +0 -78
- pyxecm-2.0.3.dist-info/licenses/LICENSE +0 -202
- pyxecm-2.0.3.dist-info/top_level.txt +0 -1
- {pyxecm/customizer/api → pyxecm_api}/__init__.py +0 -0
- {pyxecm/customizer/api/auth → pyxecm_api/agents/otcm_knowledgegraph}/__init__.py +0 -0
- {pyxecm/customizer/api/common → pyxecm_api/agents/otcm_user_agent}/__init__.py +0 -0
- {pyxecm/customizer/api/v1_csai → pyxecm_api/agents/otcm_workspace_agent}/__init__.py +0 -0
- {pyxecm/customizer/api/v1_maintenance → pyxecm_api/auth}/__init__.py +0 -0
- {pyxecm/customizer/api → pyxecm_api}/auth/models.py +0 -0
- {pyxecm/customizer/api/v1_otcs → pyxecm_api/common}/__init__.py +0 -0
- {pyxecm/customizer/api → pyxecm_api}/common/models.py +0 -0
- {pyxecm/customizer/api → pyxecm_api}/terminal/__init__.py +0 -0
- {pyxecm/customizer/api/v1_payload → pyxecm_api/v1_csai}/__init__.py +0 -0
- {pyxecm/customizer/api → pyxecm_api}/v1_csai/models.py +0 -0
- {pyxecm/customizer/api → pyxecm_api}/v1_maintenance/models.py +0 -0
- {pyxecm/customizer/api → pyxecm_api}/v1_payload/models.py +0 -0
- {pyxecm/customizer → pyxecm_customizer}/exceptions.py +0 -0
- {pyxecm/maintenance_page → pyxecm_maintenance_page}/__init__.py +0 -0
- {pyxecm/maintenance_page → pyxecm_maintenance_page}/settings.py +0 -0
- {pyxecm/maintenance_page → pyxecm_maintenance_page}/static/favicon.avif +0 -0
- {pyxecm/maintenance_page → pyxecm_maintenance_page}/templates/maintenance.html +0 -0
pyxecm/otca.py
CHANGED
|
@@ -45,6 +45,16 @@ REQUEST_MAX_RETRIES = 2
|
|
|
45
45
|
|
|
46
46
|
default_logger = logging.getLogger(MODULE_NAME)
|
|
47
47
|
|
|
48
|
+
try:
|
|
49
|
+
from pyvis.network import Network
|
|
50
|
+
|
|
51
|
+
pyvis_installed = True
|
|
52
|
+
except ModuleNotFoundError:
|
|
53
|
+
default_logger.warning(
|
|
54
|
+
"Module pyvis is not installed. Customizer will not support graph visualization.",
|
|
55
|
+
)
|
|
56
|
+
pyvis_installed = False
|
|
57
|
+
|
|
48
58
|
|
|
49
59
|
class OTCA:
|
|
50
60
|
"""Interact with Content Aviator REST API."""
|
|
@@ -55,15 +65,19 @@ class OTCA:
|
|
|
55
65
|
_context = ""
|
|
56
66
|
_embed_token: str | None = None
|
|
57
67
|
_chat_token: str | None = None
|
|
68
|
+
_chat_token_hashed: str | None = None
|
|
69
|
+
_node_dictionary: dict = {}
|
|
58
70
|
|
|
59
71
|
def __init__(
|
|
60
72
|
self,
|
|
61
73
|
chat_url: str,
|
|
62
74
|
embed_url: str,
|
|
75
|
+
studio_url: str,
|
|
63
76
|
otds_url: str,
|
|
64
77
|
client_id: str,
|
|
65
78
|
client_secret: str,
|
|
66
|
-
|
|
79
|
+
content_system: dict | None = None,
|
|
80
|
+
otcs_object: OTCS | None = None,
|
|
67
81
|
synonyms: list | None = None,
|
|
68
82
|
inline_citation: bool = True,
|
|
69
83
|
logger: logging.Logger = default_logger,
|
|
@@ -75,14 +89,18 @@ class OTCA:
|
|
|
75
89
|
The Content Aviator base URL for chat.
|
|
76
90
|
embed_url (str):
|
|
77
91
|
The Content Aviator base URL for embedding.
|
|
92
|
+
studio_url (str):
|
|
93
|
+
The base URL of Content Aviator Studio.
|
|
78
94
|
otds_url (str):
|
|
79
95
|
The OTDS URL.
|
|
80
96
|
client_id (str):
|
|
81
97
|
The Core Share Client ID.
|
|
82
98
|
client_secret (str):
|
|
83
99
|
The Core Share client secret.
|
|
100
|
+
content_system (dict | None):
|
|
101
|
+
The Content System configuration for the services which control the authentication.
|
|
84
102
|
otcs_object (OTCS):
|
|
85
|
-
The OTCS object
|
|
103
|
+
The OTCS object..
|
|
86
104
|
synonyms (list):
|
|
87
105
|
List of synonyms that are used to generate a better response to the user.
|
|
88
106
|
inline_citation (bool):
|
|
@@ -102,6 +120,13 @@ class OTCA:
|
|
|
102
120
|
otca_config["chatUrl"] = chat_url + "/v1/chat"
|
|
103
121
|
otca_config["searchUrl"] = chat_url + "/v1/context"
|
|
104
122
|
otca_config["embedUrl"] = embed_url + "/v1/embeddings"
|
|
123
|
+
otca_config["studioGraphsUrl"] = studio_url + "/studio/v1/graphs"
|
|
124
|
+
otca_config["studioAgentsUrl"] = studio_url + "/studio/v1/agents"
|
|
125
|
+
otca_config["studioToolsUrl"] = studio_url + "/studio/v1/tools"
|
|
126
|
+
otca_config["studioRulesUrl"] = studio_url + "/studio/v1/rules"
|
|
127
|
+
otca_config["studioModelsUrl"] = studio_url + "/studio/v1/api/models"
|
|
128
|
+
|
|
129
|
+
otca_config["content_system"] = content_system if content_system else {"chat": "xecm", "embed": "xecm"}
|
|
105
130
|
otca_config["clientId"] = client_id
|
|
106
131
|
otca_config["clientSecret"] = client_secret
|
|
107
132
|
otca_config["otdsUrl"] = otds_url
|
|
@@ -196,10 +221,24 @@ class OTCA:
|
|
|
196
221
|
if content_type:
|
|
197
222
|
request_header["Content-Type"] = content_type
|
|
198
223
|
|
|
199
|
-
|
|
200
|
-
|
|
224
|
+
# Configure default Content System
|
|
225
|
+
content_system = self.config()["content_system"].get(service_type, "none")
|
|
226
|
+
|
|
227
|
+
if content_system == "none":
|
|
228
|
+
return request_header
|
|
201
229
|
|
|
202
|
-
|
|
230
|
+
if service_type == "chat":
|
|
231
|
+
if self._chat_token is None:
|
|
232
|
+
self.authenticate_chat()
|
|
233
|
+
|
|
234
|
+
if content_system == "xecm":
|
|
235
|
+
request_header["Authorization"] = "Bearer {}".format(self._chat_token_hashed)
|
|
236
|
+
elif content_system == "xecm-direct":
|
|
237
|
+
request_header["otcsticket"] = self._chat_token
|
|
238
|
+
|
|
239
|
+
elif service_type == "embed":
|
|
240
|
+
if self._embed_token is None:
|
|
241
|
+
self.authenticate_embed()
|
|
203
242
|
request_header["Authorization"] = "Bearer {}".format(self._embed_token)
|
|
204
243
|
|
|
205
244
|
return request_header
|
|
@@ -220,6 +259,7 @@ class OTCA:
|
|
|
220
259
|
success_message: str = "",
|
|
221
260
|
max_retries: int = REQUEST_MAX_RETRIES,
|
|
222
261
|
retry_forever: bool = False,
|
|
262
|
+
parse_request_response: bool = True,
|
|
223
263
|
) -> dict | None:
|
|
224
264
|
"""Call an Content Aviator REST API in a safe way.
|
|
225
265
|
|
|
@@ -251,6 +291,9 @@ class OTCA:
|
|
|
251
291
|
Number of retries on connection errors. Defaults to REQUEST_MAX_RETRIES.
|
|
252
292
|
retry_forever (bool, optional):
|
|
253
293
|
Whether to wait forever without timeout. Defaults to False.
|
|
294
|
+
parse_request_response (bool, optional):
|
|
295
|
+
Whether the response text should be interpreted as JSON and loaded
|
|
296
|
+
into a dictionary. Defaults to True.
|
|
254
297
|
|
|
255
298
|
Returns:
|
|
256
299
|
dict | None:
|
|
@@ -261,6 +304,17 @@ class OTCA:
|
|
|
261
304
|
retries = 0
|
|
262
305
|
while True:
|
|
263
306
|
try:
|
|
307
|
+
self.logger.debug(
|
|
308
|
+
"Sending %s request ->\nurl: %s\nheaders: %s\ndata: %s\njson: %s\nfiles: %s\ntimeout: %s",
|
|
309
|
+
method,
|
|
310
|
+
url,
|
|
311
|
+
json.dumps(headers, indent=2),
|
|
312
|
+
json.dumps(data, indent=2),
|
|
313
|
+
json.dumps(json_data, indent=2),
|
|
314
|
+
files,
|
|
315
|
+
timeout,
|
|
316
|
+
)
|
|
317
|
+
|
|
264
318
|
response = requests.request(
|
|
265
319
|
method=method,
|
|
266
320
|
url=url,
|
|
@@ -274,7 +328,10 @@ class OTCA:
|
|
|
274
328
|
if response.ok:
|
|
275
329
|
if success_message:
|
|
276
330
|
self.logger.debug(success_message)
|
|
277
|
-
|
|
331
|
+
if parse_request_response:
|
|
332
|
+
return self.parse_request_response(response, show_error=show_error)
|
|
333
|
+
else:
|
|
334
|
+
return response
|
|
278
335
|
# Check if Session has expired - then re-authenticate and try once more
|
|
279
336
|
elif response.status_code == 401 and retries == 0:
|
|
280
337
|
self.logger.debug("Session has expired - try to re-authenticate...")
|
|
@@ -404,7 +461,7 @@ class OTCA:
|
|
|
404
461
|
|
|
405
462
|
# end method definition
|
|
406
463
|
|
|
407
|
-
def authenticate_chat(self) -> str
|
|
464
|
+
def authenticate_chat(self) -> str:
|
|
408
465
|
"""Authenticate for Chat service at Content Aviator / CSAI.
|
|
409
466
|
|
|
410
467
|
Returns:
|
|
@@ -413,11 +470,20 @@ class OTCA:
|
|
|
413
470
|
|
|
414
471
|
"""
|
|
415
472
|
|
|
473
|
+
if self.otcs_object is None:
|
|
474
|
+
msg = "OTCS Object is not defined, authentication failed."
|
|
475
|
+
raise AttributeError(msg)
|
|
476
|
+
|
|
416
477
|
token = self.otcs_object.otcs_ticket() or self.otcs_object.authenticate()
|
|
417
478
|
|
|
418
|
-
if token and "otcsticket" in token:
|
|
479
|
+
if isinstance(token, dict) and "otcsticket" in token:
|
|
480
|
+
token = token["otcsticket"]
|
|
481
|
+
|
|
482
|
+
if token:
|
|
483
|
+
self._chat_token = token
|
|
484
|
+
|
|
419
485
|
# Encode the input string before hashing
|
|
420
|
-
encoded_string = token
|
|
486
|
+
encoded_string = token.encode("utf-8")
|
|
421
487
|
|
|
422
488
|
# Create a new SHA-512 hash object
|
|
423
489
|
sha512 = hashlib.sha512()
|
|
@@ -428,11 +494,14 @@ class OTCA:
|
|
|
428
494
|
# Get the hexadecimal representation of the hash
|
|
429
495
|
hashed_output = sha512.hexdigest()
|
|
430
496
|
|
|
431
|
-
self.
|
|
497
|
+
self._chat_token_hashed = hashed_output
|
|
432
498
|
|
|
433
499
|
return self._chat_token
|
|
434
500
|
|
|
435
|
-
|
|
501
|
+
else:
|
|
502
|
+
self.logger.error("Authentication failed. Token not found.")
|
|
503
|
+
|
|
504
|
+
return None
|
|
436
505
|
|
|
437
506
|
# end method definition
|
|
438
507
|
|
|
@@ -591,7 +660,7 @@ class OTCA:
|
|
|
591
660
|
) -> dict:
|
|
592
661
|
"""Semantic search for text chunks.
|
|
593
662
|
|
|
594
|
-
Search requests are meant to be called as end-users.
|
|
663
|
+
Search requests are meant to be called as end-users. This should involve
|
|
595
664
|
passing the end-user's access token via the Authorization HTTP header.
|
|
596
665
|
The chat service use OTDS's token endpoint to ensure that the token is valid.
|
|
597
666
|
|
|
@@ -683,18 +752,19 @@ class OTCA:
|
|
|
683
752
|
|
|
684
753
|
Args:
|
|
685
754
|
content (str | None):
|
|
686
|
-
Content to be embedded. Can be empty for "delete" operations.
|
|
755
|
+
Content to be embedded. This is a document chunk. Can be empty for "delete" operations.
|
|
687
756
|
operation (str):
|
|
688
757
|
This can be either "add", "update" or "delete".
|
|
689
758
|
document_id (int):
|
|
690
|
-
The ID of the document the content originates from.
|
|
759
|
+
The ID of the document the content originates from. This becmes metadata in the vector store.
|
|
691
760
|
workspace_id (int):
|
|
692
|
-
The ID of the workspace the content originates from.
|
|
761
|
+
The ID of the workspace the content originates from. This becomes metadata in the vector store.
|
|
693
762
|
additional_metadata (dict | None):
|
|
694
763
|
Dictionary with additional metadata.
|
|
695
764
|
|
|
696
765
|
Returns:
|
|
697
|
-
dict:
|
|
766
|
+
dict:
|
|
767
|
+
REST API response or None in case of an error.
|
|
698
768
|
|
|
699
769
|
"""
|
|
700
770
|
|
|
@@ -733,3 +803,1273 @@ class OTCA:
|
|
|
733
803
|
)
|
|
734
804
|
|
|
735
805
|
# end method definition
|
|
806
|
+
|
|
807
|
+
def get_graphs(self) -> list | None:
|
|
808
|
+
"""Get all graphs.
|
|
809
|
+
|
|
810
|
+
Returns:
|
|
811
|
+
list:
|
|
812
|
+
A list of all graphs.
|
|
813
|
+
|
|
814
|
+
Example:
|
|
815
|
+
[
|
|
816
|
+
{
|
|
817
|
+
'id': '60fa6f74-a0a6-4f95-abf4-80a3ae19913c',
|
|
818
|
+
'name': 'supervisor',
|
|
819
|
+
'description': None,
|
|
820
|
+
'attributes': None,
|
|
821
|
+
'createdAt': '2025-07-01T09:06:28.123Z',
|
|
822
|
+
'updatedAt': '2025-07-01T09:06:28.123Z',
|
|
823
|
+
'tenantId': '010bae82-7b31-4e52-9db4-00bde19aa398'
|
|
824
|
+
},
|
|
825
|
+
{
|
|
826
|
+
'id': 'f287ef5e-0acf-47cf-91cb-64b3195ceeb8',
|
|
827
|
+
'name': 'breakdown',
|
|
828
|
+
'description': None,
|
|
829
|
+
'attributes': None,
|
|
830
|
+
'createdAt': '2025-07-01T09:06:28.123Z',
|
|
831
|
+
'updatedAt': '2025-07-01T09:06:28.123Z',
|
|
832
|
+
'tenantId': '010bae82-7b31-4e52-9db4-00bde19aa398'
|
|
833
|
+
},
|
|
834
|
+
{
|
|
835
|
+
'id': '378a6369-6f78-4ccc-a1f1-8b070973be24',
|
|
836
|
+
'name': 'root',
|
|
837
|
+
'description': None,
|
|
838
|
+
'attributes': None,
|
|
839
|
+
'createdAt': '2025-07-01T09:06:28.123Z',
|
|
840
|
+
'updatedAt': '2025-07-01T09:06:28.123Z',
|
|
841
|
+
'tenantId': '010bae82-7b31-4e52-9db4-00bde19aa398'
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
'id': '6925e805-eaea-4054-a07f-e3e48c7bab15',
|
|
845
|
+
'name': 'answer',
|
|
846
|
+
'description': None,
|
|
847
|
+
'attributes': None,
|
|
848
|
+
'createdAt': '2025-07-01T09:06:28.123Z',
|
|
849
|
+
'updatedAt': '2025-07-01T09:06:28.123Z',
|
|
850
|
+
'tenantId': '010bae82-7b31-4e52-9db4-00bde19aa398'
|
|
851
|
+
}
|
|
852
|
+
]
|
|
853
|
+
|
|
854
|
+
"""
|
|
855
|
+
|
|
856
|
+
request_url = self.config()["studioGraphsUrl"]
|
|
857
|
+
request_header = self.request_header(service_type="studio")
|
|
858
|
+
|
|
859
|
+
response = self.do_request(
|
|
860
|
+
url=request_url,
|
|
861
|
+
method="GET",
|
|
862
|
+
headers=request_header,
|
|
863
|
+
timeout=None,
|
|
864
|
+
show_error=True,
|
|
865
|
+
failure_message="Failed get graphs",
|
|
866
|
+
)
|
|
867
|
+
|
|
868
|
+
if response is None:
|
|
869
|
+
return None
|
|
870
|
+
|
|
871
|
+
return response.get("results", [])
|
|
872
|
+
|
|
873
|
+
# end method definition
|
|
874
|
+
|
|
875
|
+
def get_graphs_iterator(self) -> iter:
|
|
876
|
+
"""Get an iterator object that can be used to traverse graphs.
|
|
877
|
+
|
|
878
|
+
Returns:
|
|
879
|
+
iter:
|
|
880
|
+
A generator yielding one graph per iteration.
|
|
881
|
+
If the REST API fails, returns no value.
|
|
882
|
+
|
|
883
|
+
Yields:
|
|
884
|
+
Iterator[iter]:
|
|
885
|
+
One graph at a time.
|
|
886
|
+
|
|
887
|
+
"""
|
|
888
|
+
|
|
889
|
+
graphs: list = self.get_graphs()
|
|
890
|
+
|
|
891
|
+
yield from graphs
|
|
892
|
+
|
|
893
|
+
# end method definition
|
|
894
|
+
|
|
895
|
+
def get_graph(self, graph_id: str) -> dict | None:
|
|
896
|
+
"""Get a graph by its ID.
|
|
897
|
+
|
|
898
|
+
Args:
|
|
899
|
+
graph_id (str):
|
|
900
|
+
The ID of the graph.
|
|
901
|
+
|
|
902
|
+
Returns:
|
|
903
|
+
dict | None:
|
|
904
|
+
Graph data or none in case of an error.
|
|
905
|
+
|
|
906
|
+
Example:
|
|
907
|
+
{
|
|
908
|
+
'id': 'a245ddcb-2df0-465a-abab-b21222245ba9',
|
|
909
|
+
'name': 'supervisor',
|
|
910
|
+
'description': None,
|
|
911
|
+
'attributes': None,
|
|
912
|
+
'createdAt': '2025-07-01T16:51:56.703Z',
|
|
913
|
+
'updatedAt': '2025-07-01T16:51:56.703Z',
|
|
914
|
+
'tenantId': '05f43f12-5865-46cd-8954-1af3dc575e88'
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
"""
|
|
918
|
+
|
|
919
|
+
request_url = self.config()["studioGraphsUrl"] + "/" + graph_id
|
|
920
|
+
request_header = self.request_header(service_type="studio")
|
|
921
|
+
|
|
922
|
+
return self.do_request(
|
|
923
|
+
url=request_url,
|
|
924
|
+
method="GET",
|
|
925
|
+
headers=request_header,
|
|
926
|
+
timeout=None,
|
|
927
|
+
show_error=True,
|
|
928
|
+
failure_message="Failed get graphs",
|
|
929
|
+
)
|
|
930
|
+
|
|
931
|
+
# end method definition
|
|
932
|
+
|
|
933
|
+
def get_graph_nodes(self, graph_id: str) -> list | None:
|
|
934
|
+
"""Get all nodes of a graph.
|
|
935
|
+
|
|
936
|
+
Args:
|
|
937
|
+
graph_id (str):
|
|
938
|
+
The ID of the Graph to retrieve the nodes for.
|
|
939
|
+
|
|
940
|
+
Returns:
|
|
941
|
+
list | None:
|
|
942
|
+
A list of all nodes of the graph.
|
|
943
|
+
|
|
944
|
+
Example:
|
|
945
|
+
[
|
|
946
|
+
{
|
|
947
|
+
'id': '1b99d09f-9e36-4da9-8fe0-8ebe0652fef3',
|
|
948
|
+
'name': 'decision',
|
|
949
|
+
'description': None,
|
|
950
|
+
'discriminator': 1,
|
|
951
|
+
'createdAt': '2025-07-01T09:06:28.140Z',
|
|
952
|
+
'updatedAt': '2025-07-01T09:06:28.140Z',
|
|
953
|
+
'version': 0,
|
|
954
|
+
'status': 0,
|
|
955
|
+
'graphId': '60fa6f74-a0a6-4f95-abf4-80a3ae19913c',
|
|
956
|
+
'attributes': {
|
|
957
|
+
'studio': 'routerAgent'
|
|
958
|
+
},
|
|
959
|
+
'klassId': '20470453-2179-4392-aa0e-bc46ae3f3e80'
|
|
960
|
+
}
|
|
961
|
+
]
|
|
962
|
+
|
|
963
|
+
"""
|
|
964
|
+
|
|
965
|
+
request_url = self.config()["studioGraphsUrl"] + "/" + graph_id + "/nodes"
|
|
966
|
+
request_header = self.request_header(service_type="studio")
|
|
967
|
+
|
|
968
|
+
response = self.do_request(
|
|
969
|
+
url=request_url,
|
|
970
|
+
method="GET",
|
|
971
|
+
headers=request_header,
|
|
972
|
+
timeout=None,
|
|
973
|
+
show_error=True,
|
|
974
|
+
failure_message="Failed get list of graph nodes!",
|
|
975
|
+
)
|
|
976
|
+
|
|
977
|
+
if response is None:
|
|
978
|
+
return None
|
|
979
|
+
|
|
980
|
+
return response.get("results", [])
|
|
981
|
+
|
|
982
|
+
# end method definition
|
|
983
|
+
|
|
984
|
+
def get_graph_nodes_iterator(self, graph_id: str) -> iter:
|
|
985
|
+
"""Get an iterator object that can be used to traverse graph nodes.
|
|
986
|
+
|
|
987
|
+
Returns:
|
|
988
|
+
iter:
|
|
989
|
+
A generator yielding one node per iteration.
|
|
990
|
+
If the REST API fails, returns no value.
|
|
991
|
+
|
|
992
|
+
Yields:
|
|
993
|
+
Iterator[iter]:
|
|
994
|
+
One node at a time.
|
|
995
|
+
|
|
996
|
+
"""
|
|
997
|
+
|
|
998
|
+
nodes: list = self.get_graph_nodes(graph_id=graph_id)
|
|
999
|
+
|
|
1000
|
+
yield from nodes
|
|
1001
|
+
|
|
1002
|
+
# end method definition
|
|
1003
|
+
|
|
1004
|
+
def get_graph_nodes_by_name(self, name: str) -> list | None:
|
|
1005
|
+
"""Get all nodes of a graph by name.
|
|
1006
|
+
|
|
1007
|
+
Args:
|
|
1008
|
+
name (str):
|
|
1009
|
+
The Name of the Graph to retrieve the nodes for.
|
|
1010
|
+
|
|
1011
|
+
Returns:
|
|
1012
|
+
list | None:
|
|
1013
|
+
A list of all nodes of the graph.
|
|
1014
|
+
|
|
1015
|
+
"""
|
|
1016
|
+
|
|
1017
|
+
graphs = self.get_graphs()
|
|
1018
|
+
|
|
1019
|
+
if graphs is None:
|
|
1020
|
+
return None
|
|
1021
|
+
|
|
1022
|
+
graph = next((g for g in graphs if g["name"] == name), None)
|
|
1023
|
+
|
|
1024
|
+
if graph is None:
|
|
1025
|
+
self.logger.error("Graph -> '%s' not found!", name)
|
|
1026
|
+
return None
|
|
1027
|
+
|
|
1028
|
+
request_url = self.config()["studioGraphsUrl"] + "/" + graph["id"] + "/nodes"
|
|
1029
|
+
request_header = self.request_header(service_type="studio")
|
|
1030
|
+
|
|
1031
|
+
response = self.do_request(
|
|
1032
|
+
url=request_url,
|
|
1033
|
+
method="GET",
|
|
1034
|
+
headers=request_header,
|
|
1035
|
+
timeout=None,
|
|
1036
|
+
show_error=True,
|
|
1037
|
+
failure_message="Failed get list of graphs!",
|
|
1038
|
+
)
|
|
1039
|
+
|
|
1040
|
+
if response is None:
|
|
1041
|
+
return None
|
|
1042
|
+
|
|
1043
|
+
return response.get("results", [])
|
|
1044
|
+
|
|
1045
|
+
# end method definition
|
|
1046
|
+
|
|
1047
|
+
def get_graph_edges(self, graph_id: str) -> list | None:
|
|
1048
|
+
"""Get all edges of a graph.
|
|
1049
|
+
|
|
1050
|
+
Args:
|
|
1051
|
+
graph_id (str):
|
|
1052
|
+
The ID of the Graph to retrieve the edges for.
|
|
1053
|
+
|
|
1054
|
+
Returns:
|
|
1055
|
+
list | None:
|
|
1056
|
+
A list of all edges of the graph.
|
|
1057
|
+
|
|
1058
|
+
Example:
|
|
1059
|
+
[
|
|
1060
|
+
{
|
|
1061
|
+
'id': '420610ae-68d0-47d2-9807-6e5a5f75a02d',
|
|
1062
|
+
'sourceId': '8cec788a-23eb-4480-a004-1f3c7edd1054',
|
|
1063
|
+
'targetId': '233db9e1-1f5a-4fb6-8f70-fca46199c224',
|
|
1064
|
+
'type': 0,
|
|
1065
|
+
'graphId': '60fa6f74-a0a6-4f95-abf4-80a3ae19913c',
|
|
1066
|
+
'createdAt': '2025-07-01T09:06:28.192Z',
|
|
1067
|
+
'updatedAt': '2025-07-01T09:06:28.192Z',
|
|
1068
|
+
'version': 0,
|
|
1069
|
+
'status': 0,
|
|
1070
|
+
'attributes': None
|
|
1071
|
+
}
|
|
1072
|
+
]
|
|
1073
|
+
|
|
1074
|
+
"""
|
|
1075
|
+
|
|
1076
|
+
request_url = self.config()["studioGraphsUrl"] + "/" + graph_id + "/edges"
|
|
1077
|
+
request_header = self.request_header(service_type="studio")
|
|
1078
|
+
|
|
1079
|
+
response = self.do_request(
|
|
1080
|
+
url=request_url,
|
|
1081
|
+
method="GET",
|
|
1082
|
+
headers=request_header,
|
|
1083
|
+
timeout=None,
|
|
1084
|
+
show_error=True,
|
|
1085
|
+
failure_message="Failed get list of graph edges!",
|
|
1086
|
+
)
|
|
1087
|
+
|
|
1088
|
+
if response is None:
|
|
1089
|
+
return None
|
|
1090
|
+
|
|
1091
|
+
return response.get("results", [])
|
|
1092
|
+
|
|
1093
|
+
# end method definition
|
|
1094
|
+
|
|
1095
|
+
def get_graph_edges_iterator(self, graph_id: str) -> iter:
|
|
1096
|
+
"""Get an iterator object that can be used to traverse graph edges.
|
|
1097
|
+
|
|
1098
|
+
Returns:
|
|
1099
|
+
iter:
|
|
1100
|
+
A generator yielding one edge per iteration.
|
|
1101
|
+
If the REST API fails, returns no value.
|
|
1102
|
+
|
|
1103
|
+
Yields:
|
|
1104
|
+
Iterator[iter]:
|
|
1105
|
+
One edge at a time.
|
|
1106
|
+
|
|
1107
|
+
"""
|
|
1108
|
+
|
|
1109
|
+
edges: list = self.get_graph_edges(graph_id=graph_id)
|
|
1110
|
+
|
|
1111
|
+
yield from edges
|
|
1112
|
+
|
|
1113
|
+
# end method definition
|
|
1114
|
+
|
|
1115
|
+
def visualize_graph(self, graph_id: str) -> str:
|
|
1116
|
+
"""Visualize a graph.
|
|
1117
|
+
|
|
1118
|
+
Args:
|
|
1119
|
+
graph_id (str):
|
|
1120
|
+
The ID of the graph.
|
|
1121
|
+
|
|
1122
|
+
Returns:
|
|
1123
|
+
str: Filename of the generated html file
|
|
1124
|
+
|
|
1125
|
+
"""
|
|
1126
|
+
|
|
1127
|
+
if not pyvis_installed:
|
|
1128
|
+
self.logger.warning("Cannot visualize graph. Python module pyvis not installed!")
|
|
1129
|
+
return None
|
|
1130
|
+
|
|
1131
|
+
graph = self.get_graph(graph_id=graph_id)
|
|
1132
|
+
graph_id = graph["id"]
|
|
1133
|
+
graph_name = graph["name"]
|
|
1134
|
+
net = Network(notebook=False, directed=True, height="1000px", width="100%", filter_menu=True, select_menu=True)
|
|
1135
|
+
net.heading = f"Aviator Studio graph: {graph_name}"
|
|
1136
|
+
nodes = self.get_graph_nodes_iterator(graph_id=graph_id)
|
|
1137
|
+
for node in nodes:
|
|
1138
|
+
node_id = node["id"]
|
|
1139
|
+
node_name = node["name"]
|
|
1140
|
+
node_attributes = node["attributes"]
|
|
1141
|
+
self._node_dictionary[(graph_id, node_id)] = node_name
|
|
1142
|
+
if node_attributes and "APISchema" in node_attributes:
|
|
1143
|
+
net.add_node(n_id=node_id, label=node_name, title=json.dumps(node, indent=2), color="green")
|
|
1144
|
+
else:
|
|
1145
|
+
net.add_node(n_id=node_id, label=node_name, title=json.dumps(node, indent=2))
|
|
1146
|
+
relationships = self.get_graph_node_relationships_iterator(
|
|
1147
|
+
graph_id=graph_id, node_id=node_id, relation_type="rules"
|
|
1148
|
+
)
|
|
1149
|
+
for relationship in relationships:
|
|
1150
|
+
for rule in relationship["rules"] or []:
|
|
1151
|
+
net.add_node(
|
|
1152
|
+
n_id=rule["id"], label=rule["name"], title=json.dumps(rule, indent=2), shape="box", color="red"
|
|
1153
|
+
)
|
|
1154
|
+
net.add_edge(source=node_id, to=rule["id"])
|
|
1155
|
+
relationships = self.get_graph_node_relationships_iterator(
|
|
1156
|
+
graph_id=graph_id, node_id=node_id, relation_type="prompts"
|
|
1157
|
+
)
|
|
1158
|
+
for relationship in relationships:
|
|
1159
|
+
for prompt in relationship["prompts"] or []:
|
|
1160
|
+
net.add_node(
|
|
1161
|
+
n_id=prompt["id"],
|
|
1162
|
+
label=prompt["name"],
|
|
1163
|
+
title=json.dumps(prompt, indent=2),
|
|
1164
|
+
shape="oval",
|
|
1165
|
+
color="green",
|
|
1166
|
+
)
|
|
1167
|
+
net.add_edge(source=node_id, to=prompt["id"])
|
|
1168
|
+
edges = self.get_graph_edges_iterator(graph_id=graph_id)
|
|
1169
|
+
for edge in edges:
|
|
1170
|
+
edge_source_id = edge["sourceId"]
|
|
1171
|
+
edge_target_id = edge["targetId"]
|
|
1172
|
+
net.add_edge(source=edge_source_id, to=edge_target_id)
|
|
1173
|
+
|
|
1174
|
+
html_file = "{}.html".format(graph_name)
|
|
1175
|
+
net.save_graph(html_file)
|
|
1176
|
+
self.logger.info("Graph visualization saved to -> %s", html_file)
|
|
1177
|
+
|
|
1178
|
+
return html_file
|
|
1179
|
+
|
|
1180
|
+
# end method definition
|
|
1181
|
+
|
|
1182
|
+
def get_model_types(self) -> list:
|
|
1183
|
+
"""Get a list of all model types. Hardcoded.
|
|
1184
|
+
|
|
1185
|
+
Returns:
|
|
1186
|
+
list:
|
|
1187
|
+
Model types.
|
|
1188
|
+
|
|
1189
|
+
"""
|
|
1190
|
+
|
|
1191
|
+
return ["tenants", "graphs", "nodes", "edges", "actions", "tools", "prompts", "rules", "klasses"]
|
|
1192
|
+
|
|
1193
|
+
# end method definition
|
|
1194
|
+
|
|
1195
|
+
def get_models(self, model_type: str) -> list | None:
|
|
1196
|
+
"""Get all model details by type.
|
|
1197
|
+
|
|
1198
|
+
Args:
|
|
1199
|
+
model_type (str):
|
|
1200
|
+
The type of the model. Possible model types:
|
|
1201
|
+
* tenants
|
|
1202
|
+
* graphs
|
|
1203
|
+
* nodes
|
|
1204
|
+
* edges
|
|
1205
|
+
* actions
|
|
1206
|
+
* tools
|
|
1207
|
+
* prompts
|
|
1208
|
+
* rules
|
|
1209
|
+
* klasses
|
|
1210
|
+
|
|
1211
|
+
Returns:
|
|
1212
|
+
list | None:
|
|
1213
|
+
A list of all models of a given type.
|
|
1214
|
+
|
|
1215
|
+
"""
|
|
1216
|
+
|
|
1217
|
+
request_url = self.config()["studioModelsUrl"] + "/" + model_type
|
|
1218
|
+
request_header = self.request_header(service_type="studio")
|
|
1219
|
+
|
|
1220
|
+
response = self.do_request(
|
|
1221
|
+
url=request_url,
|
|
1222
|
+
method="GET",
|
|
1223
|
+
headers=request_header,
|
|
1224
|
+
timeout=None,
|
|
1225
|
+
show_error=True,
|
|
1226
|
+
failure_message="Failed to get list of models!",
|
|
1227
|
+
)
|
|
1228
|
+
|
|
1229
|
+
if response is None:
|
|
1230
|
+
return None
|
|
1231
|
+
|
|
1232
|
+
return response.get("results", [])
|
|
1233
|
+
|
|
1234
|
+
# end method definition
|
|
1235
|
+
|
|
1236
|
+
def get_models_iterator(self, model_type: str) -> iter:
|
|
1237
|
+
"""Get an iterator object that can be used to traverse models.
|
|
1238
|
+
|
|
1239
|
+
Returns:
|
|
1240
|
+
iter:
|
|
1241
|
+
A generator yielding one model per iteration.
|
|
1242
|
+
If the REST API fails, returns no value.
|
|
1243
|
+
|
|
1244
|
+
Yields:
|
|
1245
|
+
Iterator[iter]:
|
|
1246
|
+
One edge at a time.
|
|
1247
|
+
|
|
1248
|
+
"""
|
|
1249
|
+
|
|
1250
|
+
models: list = self.get_models(model_type=model_type)
|
|
1251
|
+
|
|
1252
|
+
yield from models
|
|
1253
|
+
|
|
1254
|
+
# end method definition
|
|
1255
|
+
|
|
1256
|
+
def get_model(self, model_type: str, model_id: str) -> dict | None:
|
|
1257
|
+
"""Get a specific model based on its type and ID.
|
|
1258
|
+
|
|
1259
|
+
Args:
|
|
1260
|
+
model_type (str):
|
|
1261
|
+
The type of the model. Possible model types:
|
|
1262
|
+
* tenants
|
|
1263
|
+
* graphs
|
|
1264
|
+
* nodes
|
|
1265
|
+
* edges
|
|
1266
|
+
* actions
|
|
1267
|
+
* tools
|
|
1268
|
+
* prompts
|
|
1269
|
+
* rules
|
|
1270
|
+
* klasses
|
|
1271
|
+
model_id (str):
|
|
1272
|
+
The ID of the model.
|
|
1273
|
+
|
|
1274
|
+
Returns:
|
|
1275
|
+
dict | None:
|
|
1276
|
+
The model data.
|
|
1277
|
+
|
|
1278
|
+
"""
|
|
1279
|
+
|
|
1280
|
+
request_url = self.config()["studioModelsUrl"] + "/" + model_type + "/" + model_id
|
|
1281
|
+
request_header = self.request_header(service_type="studio")
|
|
1282
|
+
|
|
1283
|
+
return self.do_request(
|
|
1284
|
+
url=request_url,
|
|
1285
|
+
method="GET",
|
|
1286
|
+
headers=request_header,
|
|
1287
|
+
timeout=None,
|
|
1288
|
+
show_error=True,
|
|
1289
|
+
failure_message="Failed to get models with type -> '{}' and ID -> {}!".format(model_type, model_id),
|
|
1290
|
+
)
|
|
1291
|
+
|
|
1292
|
+
# end method definition
|
|
1293
|
+
|
|
1294
|
+
def get_model_by_type_and_name(self, model_type: str, name: str) -> dict | None:
|
|
1295
|
+
"""Get model details by model type and name.
|
|
1296
|
+
|
|
1297
|
+
Args:
|
|
1298
|
+
model_type (str):
|
|
1299
|
+
The type of the model.
|
|
1300
|
+
name (str):
|
|
1301
|
+
The name of the model.
|
|
1302
|
+
|
|
1303
|
+
Returns:
|
|
1304
|
+
dict:
|
|
1305
|
+
Model details or None in case of an error.
|
|
1306
|
+
|
|
1307
|
+
"""
|
|
1308
|
+
|
|
1309
|
+
models = self.get_models(model_type=model_type)
|
|
1310
|
+
if models:
|
|
1311
|
+
return next((model for model in models if model["name"] == name), None)
|
|
1312
|
+
|
|
1313
|
+
return None
|
|
1314
|
+
|
|
1315
|
+
# end method definition
|
|
1316
|
+
|
|
1317
|
+
# end method definition
|
|
1318
|
+
|
|
1319
|
+
def delete_model(self, model_type: str, model_id: str) -> dict | None:
|
|
1320
|
+
"""Delete a model by type and id.
|
|
1321
|
+
|
|
1322
|
+
Args:
|
|
1323
|
+
model_type (str):
|
|
1324
|
+
The type of the model.
|
|
1325
|
+
model_id (str):
|
|
1326
|
+
The model name.
|
|
1327
|
+
|
|
1328
|
+
Returns:
|
|
1329
|
+
dict | None:
|
|
1330
|
+
Dict with the model details
|
|
1331
|
+
|
|
1332
|
+
"""
|
|
1333
|
+
|
|
1334
|
+
self.logger.info("Deleting existing model -> '%s' (%s)", model_type, model_id)
|
|
1335
|
+
|
|
1336
|
+
request_header = self.request_header(service_type="studio")
|
|
1337
|
+
request_url = self.config()["studioModelsUrl"] + "/" + model_type + "/" + model_id
|
|
1338
|
+
return self.do_request(
|
|
1339
|
+
url=request_url,
|
|
1340
|
+
method="DELETE",
|
|
1341
|
+
headers=request_header,
|
|
1342
|
+
timeout=None,
|
|
1343
|
+
show_error=True,
|
|
1344
|
+
failure_message="Failed to delete model -> '{}' ({})!".format(model_type, model_id),
|
|
1345
|
+
)
|
|
1346
|
+
|
|
1347
|
+
# end method definition
|
|
1348
|
+
|
|
1349
|
+
def update_model(self, model_type: str, model_id: str, request_body: dict) -> dict | None:
|
|
1350
|
+
"""Update a model with a given type and ID.
|
|
1351
|
+
|
|
1352
|
+
Args:
|
|
1353
|
+
model_type (str):
|
|
1354
|
+
The type of the model.
|
|
1355
|
+
model_id (str):
|
|
1356
|
+
The ID of the model.
|
|
1357
|
+
request_body (dict):
|
|
1358
|
+
Data to update the model.
|
|
1359
|
+
|
|
1360
|
+
Returns:
|
|
1361
|
+
dict | None:
|
|
1362
|
+
Dict with the model details or None in case of an error.
|
|
1363
|
+
|
|
1364
|
+
"""
|
|
1365
|
+
|
|
1366
|
+
self.logger.info("Updating existing model -> '%s' (%s)", model_type, model_id)
|
|
1367
|
+
|
|
1368
|
+
request_header = self.request_header(service_type="studio")
|
|
1369
|
+
request_url = self.config()["studioModelsUrl"] + "/" + model_type + "/" + model_id
|
|
1370
|
+
return self.do_request(
|
|
1371
|
+
url=request_url,
|
|
1372
|
+
method="PUT",
|
|
1373
|
+
headers=request_header,
|
|
1374
|
+
json_data=request_body,
|
|
1375
|
+
timeout=None,
|
|
1376
|
+
show_error=True,
|
|
1377
|
+
failure_message="Failed to update model -> '{}' ({}).".format(model_type, model_id),
|
|
1378
|
+
)
|
|
1379
|
+
|
|
1380
|
+
# end method definition
|
|
1381
|
+
|
|
1382
|
+
def get_tools(self) -> list | None:
|
|
1383
|
+
"""Get all tools.
|
|
1384
|
+
|
|
1385
|
+
Returns:
|
|
1386
|
+
list:
|
|
1387
|
+
A list of all tools.
|
|
1388
|
+
|
|
1389
|
+
Example:
|
|
1390
|
+
[
|
|
1391
|
+
{
|
|
1392
|
+
'id': '305353d8-7391-497e-9f3f-8a1fe11ceac0',
|
|
1393
|
+
'attributes': {
|
|
1394
|
+
'conditions': [{'messageLength': 2}],
|
|
1395
|
+
'maxHistory': -1,
|
|
1396
|
+
'showInContext': True
|
|
1397
|
+
},
|
|
1398
|
+
'klassId': 'b033b1d5-8182-4883-ba8a-16f0048b01b0',
|
|
1399
|
+
'name': 'rephrase_search',
|
|
1400
|
+
'description': 'Used for creating a standalone search query for retrieving documents. Input should be a dependent user message',
|
|
1401
|
+
'discriminator': 0,
|
|
1402
|
+
'createdAt': '2025-07-01T22:57:13.135Z',
|
|
1403
|
+
'updatedAt': '2025-07-01T22:57:13.135Z',
|
|
1404
|
+
'version': 0,
|
|
1405
|
+
'status': 0,
|
|
1406
|
+
'graphId': '93897862-d999-4fe0-82fc-3f9d03474545'
|
|
1407
|
+
}
|
|
1408
|
+
]
|
|
1409
|
+
|
|
1410
|
+
"""
|
|
1411
|
+
|
|
1412
|
+
return self.get_models(model_type="tools")
|
|
1413
|
+
|
|
1414
|
+
# end method definition
|
|
1415
|
+
|
|
1416
|
+
def get_tools_iterator(self) -> iter:
|
|
1417
|
+
"""Get an iterator object that can be used to traverse tools.
|
|
1418
|
+
|
|
1419
|
+
Returns:
|
|
1420
|
+
iter:
|
|
1421
|
+
A generator yielding one tool per iteration.
|
|
1422
|
+
If the REST API fails, returns no value.
|
|
1423
|
+
|
|
1424
|
+
Yields:
|
|
1425
|
+
Iterator[iter]:
|
|
1426
|
+
One tool at a time.
|
|
1427
|
+
|
|
1428
|
+
"""
|
|
1429
|
+
|
|
1430
|
+
tools: list = self.get_models(model_type="tools")
|
|
1431
|
+
|
|
1432
|
+
yield from tools
|
|
1433
|
+
|
|
1434
|
+
# end method definition
|
|
1435
|
+
|
|
1436
|
+
def get_tool(self, tool_id: str) -> dict | None:
|
|
1437
|
+
r"""Get a tool by its ID.
|
|
1438
|
+
|
|
1439
|
+
Args:
|
|
1440
|
+
tool_id (str):
|
|
1441
|
+
The ID of the tool.
|
|
1442
|
+
|
|
1443
|
+
Returns:
|
|
1444
|
+
dict | None:
|
|
1445
|
+
Tool data or none in case of an error.
|
|
1446
|
+
|
|
1447
|
+
Example:
|
|
1448
|
+
{
|
|
1449
|
+
'id': '49e230ef-024a-4dd8-beeb-70210cec0564',
|
|
1450
|
+
'attributes': {'APISchema': {...}},
|
|
1451
|
+
'klassId': '275843d9-c39f-43e2-ad00-b02ac42b5dd6',
|
|
1452
|
+
'name': 'otcm_workspace_agent_lookup_workspace',
|
|
1453
|
+
'description': 'Lookup a workspace based on its type and a value of one of the workspace attributes.\n\nUse this tool if the workspace name is _not_ specified but the user asks for a specific\nworkspace attribute value like cities, products, or other attributes.\n\nReturn the workspace data if it is found. If it is not found confirm with the user if the workspace should be created or not.\nIf it should be created call the tool: otcm_workspace_agent_create_workspace',
|
|
1454
|
+
'discriminator': 0,
|
|
1455
|
+
'createdAt': '2025-07-01T22:57:13.535Z',
|
|
1456
|
+
'updatedAt': '2025-07-01T22:57:13.535Z',
|
|
1457
|
+
'version': 0,
|
|
1458
|
+
'status': 0,
|
|
1459
|
+
'graphId': '93897862-d999-4fe0-82fc-3f9d03474545'
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
"""
|
|
1463
|
+
|
|
1464
|
+
tool = self.get_model(model_type="tools", model_id=tool_id)
|
|
1465
|
+
|
|
1466
|
+
return tool
|
|
1467
|
+
|
|
1468
|
+
# end method definition
|
|
1469
|
+
|
|
1470
|
+
def register_tool(
|
|
1471
|
+
self,
|
|
1472
|
+
request_body: dict,
|
|
1473
|
+
) -> dict:
|
|
1474
|
+
r"""Register a Tool in Content Aviator.
|
|
1475
|
+
|
|
1476
|
+
Requests are meant to be called as a service user. This would involve passing a service user's access token
|
|
1477
|
+
(token from a particular OAuth confidential client, using client credentials grant).
|
|
1478
|
+
|
|
1479
|
+
Args:
|
|
1480
|
+
request_body (dict):
|
|
1481
|
+
Body for the request. Needs to look like:
|
|
1482
|
+
example:
|
|
1483
|
+
{
|
|
1484
|
+
"name": "tool name",
|
|
1485
|
+
"description": "description of the tool",
|
|
1486
|
+
"APISchema": {} # dict of the APISchema, compliant with openapi 3.0.0
|
|
1487
|
+
"requestTemplate": {
|
|
1488
|
+
"data": {
|
|
1489
|
+
"context": {
|
|
1490
|
+
"where": "memory.input.where",
|
|
1491
|
+
"query": "memory.input.query"
|
|
1492
|
+
}
|
|
1493
|
+
},
|
|
1494
|
+
},
|
|
1495
|
+
"responseTemplate": {},
|
|
1496
|
+
"agents": ["retrieverAgent"],
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
Returns:
|
|
1500
|
+
dict: Tool details or None in case of an error.
|
|
1501
|
+
|
|
1502
|
+
Example:
|
|
1503
|
+
{
|
|
1504
|
+
'id': '27ce608f-41ea-4128-aff9-91facc66bcfa',
|
|
1505
|
+
'attributes': {
|
|
1506
|
+
'APISchema': {
|
|
1507
|
+
'openapi': '3.0.0',
|
|
1508
|
+
'info': {
|
|
1509
|
+
'title': 'otcm_workspace_agent_find_workspace',
|
|
1510
|
+
'version': '0.0.0'
|
|
1511
|
+
},
|
|
1512
|
+
'servers': [{'url': 'http://customizer:8000'}],
|
|
1513
|
+
'paths': {
|
|
1514
|
+
'/agents/otcm_workspace_agent/find_workspace': {
|
|
1515
|
+
'post': {
|
|
1516
|
+
'tags': [...],
|
|
1517
|
+
'summary': 'Find the markdown link to a workspace by workspace name and workspace type and display the link.',
|
|
1518
|
+
'description': 'Find a workspace by workspace name and workspace type.\n\nThe returned workspace is an OTCS workspace object. Show the markdown link in the chat response, sothat the user can click on it.',
|
|
1519
|
+
'operationId': 'otcm_workspace_agent_find_workspace_agents_otcm_workspace_agent_find_workspace_post',
|
|
1520
|
+
'requestBody': {...},
|
|
1521
|
+
'responses': {
|
|
1522
|
+
'200': {
|
|
1523
|
+
'description': 'Workspace found',
|
|
1524
|
+
'content': {
|
|
1525
|
+
'application/json': {
|
|
1526
|
+
'schema': {'$ref': '#/components/schemas/WorkspaceModel'}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
},
|
|
1530
|
+
'403': {
|
|
1531
|
+
'description': 'Invalid credentials'
|
|
1532
|
+
},
|
|
1533
|
+
'404': {
|
|
1534
|
+
'description': 'Workspace not found'
|
|
1535
|
+
},
|
|
1536
|
+
'422': {
|
|
1537
|
+
'description': 'Validation Error',
|
|
1538
|
+
'content': {...}
|
|
1539
|
+
}
|
|
1540
|
+
},
|
|
1541
|
+
'security': [...]
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
},
|
|
1545
|
+
'components': {
|
|
1546
|
+
'schemas': {
|
|
1547
|
+
'Body_otcm_workspace_agent_find_workspace_agents_otcm_workspace_agent_find_workspace_post': {
|
|
1548
|
+
'properties': {...},
|
|
1549
|
+
'type': 'object',
|
|
1550
|
+
'required': [...],
|
|
1551
|
+
'title': 'Body_otcm_workspace_agent_find_workspace_agents_otcm_workspace_agent_find_workspace_post'
|
|
1552
|
+
},
|
|
1553
|
+
'Context': {
|
|
1554
|
+
'properties': {...},
|
|
1555
|
+
'type': 'object',
|
|
1556
|
+
'required': [...],
|
|
1557
|
+
'title': 'Context',
|
|
1558
|
+
'description': 'Define Model that is used to provide static context information for tools.'
|
|
1559
|
+
},
|
|
1560
|
+
'HTTPValidationError': {
|
|
1561
|
+
'properties': {...},
|
|
1562
|
+
'type': 'object',
|
|
1563
|
+
'title': 'HTTPValidationError'
|
|
1564
|
+
},
|
|
1565
|
+
'ValidationError': {
|
|
1566
|
+
'properties': {...},
|
|
1567
|
+
'type': 'object',
|
|
1568
|
+
'required': [...],
|
|
1569
|
+
'title': 'ValidationError'
|
|
1570
|
+
},
|
|
1571
|
+
'WorkspaceModel': {
|
|
1572
|
+
'properties': {...},
|
|
1573
|
+
'type': 'object',
|
|
1574
|
+
'title': 'WorkspaceModel',
|
|
1575
|
+
'description': 'Defines Model for describing workspaces in OTCM (Opentext Content Management).\n\nTo display an instance of this model, please display the link.'
|
|
1576
|
+
}
|
|
1577
|
+
},
|
|
1578
|
+
'securitySchemes': {...}
|
|
1579
|
+
}
|
|
1580
|
+
},
|
|
1581
|
+
'showInContext': True,
|
|
1582
|
+
'responseFormat': 'content_and_artifact',
|
|
1583
|
+
'requestTemplate': {
|
|
1584
|
+
'data': {
|
|
1585
|
+
'context': {
|
|
1586
|
+
'query': 'memory.input.query',
|
|
1587
|
+
'where': 'memory.input.where'
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
},
|
|
1591
|
+
'responseTemplate': {}
|
|
1592
|
+
},
|
|
1593
|
+
'klassId': '1d8dbd52-5dee-4645-841d-2889fac74b13',
|
|
1594
|
+
'name': 'otcm_workspace_agent_find_workspace',
|
|
1595
|
+
'description': 'Find a workspace by workspace name and workspace type.\n\nThe returned workspace is an OTCS workspace object. Show the markdown link in the chat response, sothat the user can click on it.',
|
|
1596
|
+
'discriminator': 0,
|
|
1597
|
+
'createdAt': '2025-07-09T12:35:26.464Z',
|
|
1598
|
+
'updatedAt': '2025-07-09T12:35:26.464Z',
|
|
1599
|
+
'version': 0,
|
|
1600
|
+
'status': 0,
|
|
1601
|
+
'graphId': '440aae89-8942-4bb0-8107-291227f8ad92'
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
"""
|
|
1605
|
+
|
|
1606
|
+
# Validations:
|
|
1607
|
+
for key in ["name", "description", "APISchema", "agents"]:
|
|
1608
|
+
if key not in request_body:
|
|
1609
|
+
self.logger.error("%s is missing in provided request body for tool registration!", key)
|
|
1610
|
+
return None
|
|
1611
|
+
|
|
1612
|
+
# Check if the tool already exists and need to be updated only:
|
|
1613
|
+
self.logger.debug("Check if tool -> '%s' already exists...", request_body["name"])
|
|
1614
|
+
model = self.get_model_by_type_and_name(model_type="tools", name=request_body["name"])
|
|
1615
|
+
if model:
|
|
1616
|
+
self.logger.info("Updating existing tool -> '%s'...", request_body["name"])
|
|
1617
|
+
|
|
1618
|
+
update_body = {
|
|
1619
|
+
"description": request_body["description"],
|
|
1620
|
+
"attributes": {**model.get("attributes", {}), "APISchema": request_body["APISchema"]},
|
|
1621
|
+
}
|
|
1622
|
+
response = self.update_model(model_type="tools", model_id=model["id"], request_body=update_body)
|
|
1623
|
+
if not response:
|
|
1624
|
+
self.logger.error("Failed to update model -> '%s' (%s)", request_body["name"], model["id"])
|
|
1625
|
+
else:
|
|
1626
|
+
self.logger.info("Registering new tool -> '%s'...", request_body["name"])
|
|
1627
|
+
request_header = self.request_header(service_type="studio")
|
|
1628
|
+
request_url = self.config()["studioToolsUrl"]
|
|
1629
|
+
response = self.do_request(
|
|
1630
|
+
url=request_url,
|
|
1631
|
+
method="POST",
|
|
1632
|
+
headers=request_header,
|
|
1633
|
+
json_data=request_body,
|
|
1634
|
+
timeout=None,
|
|
1635
|
+
show_error=True,
|
|
1636
|
+
failure_message="Failed to register tool -> '{}'!".format(request_body["name"]),
|
|
1637
|
+
)
|
|
1638
|
+
|
|
1639
|
+
return response
|
|
1640
|
+
|
|
1641
|
+
# end method definition
|
|
1642
|
+
|
|
1643
|
+
def get_rules(self) -> list | None:
|
|
1644
|
+
r"""Get all rules.
|
|
1645
|
+
|
|
1646
|
+
Returns:
|
|
1647
|
+
list:
|
|
1648
|
+
A list of all rules.
|
|
1649
|
+
|
|
1650
|
+
Example:
|
|
1651
|
+
[
|
|
1652
|
+
{
|
|
1653
|
+
'id': '4d089d1e-205d-4ff4-8128-7c2a83bd2462',
|
|
1654
|
+
'name': 'evaluateLastToolResult',
|
|
1655
|
+
'description': 'Equivalent of previous "toolResult" check. Evaluates the result of last tool executed and compares it with a given string. Returns `true` or `false`. This is the equivalent of \n ```const lastTool = memory.response.called[memory.response.called.length - 1];\nreturn lastTool?.result === (andConditions[condition]);```',
|
|
1656
|
+
'createdAt': '2025-07-01T16:51:56.703Z',
|
|
1657
|
+
'updatedAt': '2025-07-01T16:51:56.703Z',
|
|
1658
|
+
'rule': {
|
|
1659
|
+
'===': [
|
|
1660
|
+
'<<nodeResult>>',
|
|
1661
|
+
{
|
|
1662
|
+
'get': [
|
|
1663
|
+
{...}, 'result'
|
|
1664
|
+
]
|
|
1665
|
+
}
|
|
1666
|
+
]
|
|
1667
|
+
},
|
|
1668
|
+
'status': 0,
|
|
1669
|
+
'tenantId': '05f43f12-5865-46cd-8954-1af3dc575e88'
|
|
1670
|
+
}
|
|
1671
|
+
]
|
|
1672
|
+
|
|
1673
|
+
"""
|
|
1674
|
+
|
|
1675
|
+
request_url = self.config()["studioRulesUrl"]
|
|
1676
|
+
request_header = self.request_header(service_type="studio")
|
|
1677
|
+
|
|
1678
|
+
response = self.do_request(
|
|
1679
|
+
url=request_url,
|
|
1680
|
+
method="GET",
|
|
1681
|
+
headers=request_header,
|
|
1682
|
+
timeout=None,
|
|
1683
|
+
show_error=True,
|
|
1684
|
+
failure_message="Failed get rules!",
|
|
1685
|
+
)
|
|
1686
|
+
|
|
1687
|
+
if response is None:
|
|
1688
|
+
return None
|
|
1689
|
+
|
|
1690
|
+
return response.get("results", [])
|
|
1691
|
+
|
|
1692
|
+
# end method definition
|
|
1693
|
+
|
|
1694
|
+
def get_rules_iterator(self) -> iter:
|
|
1695
|
+
"""Get an iterator object that can be used to traverse rules.
|
|
1696
|
+
|
|
1697
|
+
Returns:
|
|
1698
|
+
iter:
|
|
1699
|
+
A generator yielding one rule per iteration.
|
|
1700
|
+
If the REST API fails, returns no value.
|
|
1701
|
+
|
|
1702
|
+
Yields:
|
|
1703
|
+
Iterator[iter]:
|
|
1704
|
+
One rule at a time.
|
|
1705
|
+
|
|
1706
|
+
"""
|
|
1707
|
+
|
|
1708
|
+
rules: list = self.get_rules()
|
|
1709
|
+
|
|
1710
|
+
yield from rules
|
|
1711
|
+
|
|
1712
|
+
# end method definition
|
|
1713
|
+
|
|
1714
|
+
def get_rule(self, rule_id: str) -> dict | None:
|
|
1715
|
+
r"""Get a rule by its ID.
|
|
1716
|
+
|
|
1717
|
+
Args:
|
|
1718
|
+
rule_id (str):
|
|
1719
|
+
The ID of the rule.
|
|
1720
|
+
|
|
1721
|
+
Returns:
|
|
1722
|
+
dict | None:
|
|
1723
|
+
Rule data or none in case of an error.
|
|
1724
|
+
|
|
1725
|
+
Example:
|
|
1726
|
+
{
|
|
1727
|
+
'id': '4d089d1e-205d-4ff4-8128-7c2a83bd2462',
|
|
1728
|
+
'name': 'evaluateLastToolResult',
|
|
1729
|
+
'description': 'Equivalent of previous "toolResult" check. Evaluates the result of last tool executed and compares it with a given string. Returns `true` or `false`. This is the equivalent of \n ```const lastTool = memory.response.called[memory.response.called.length - 1];\nreturn lastTool?.result === (andConditions[condition]);```',
|
|
1730
|
+
'createdAt': '2025-07-01T16:51:56.703Z',
|
|
1731
|
+
'updatedAt': '2025-07-01T16:51:56.703Z',
|
|
1732
|
+
'rule': {
|
|
1733
|
+
'===': [
|
|
1734
|
+
'<<nodeResult>>',
|
|
1735
|
+
{
|
|
1736
|
+
'get': [
|
|
1737
|
+
{...}, 'result'
|
|
1738
|
+
]
|
|
1739
|
+
}
|
|
1740
|
+
]
|
|
1741
|
+
},
|
|
1742
|
+
'status': 0,
|
|
1743
|
+
'tenantId': '05f43f12-5865-46cd-8954-1af3dc575e88'
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
"""
|
|
1747
|
+
|
|
1748
|
+
rule = self.get_model(model_type="rules", model_id=rule_id)
|
|
1749
|
+
|
|
1750
|
+
return rule
|
|
1751
|
+
|
|
1752
|
+
# end method definition
|
|
1753
|
+
|
|
1754
|
+
def get_prompts(self) -> list | None:
|
|
1755
|
+
r"""Get all prompts.
|
|
1756
|
+
|
|
1757
|
+
Returns:
|
|
1758
|
+
list:
|
|
1759
|
+
A list of all prompts.
|
|
1760
|
+
|
|
1761
|
+
Example:
|
|
1762
|
+
[
|
|
1763
|
+
{
|
|
1764
|
+
'id': '1aeb9fa1-cb26-4b07-a736-20d25a4ab939',
|
|
1765
|
+
'name': 'general_system',
|
|
1766
|
+
'template': "Your name is Aviator and you are a friendly chatbot assisting users with their queries about documents. The DOCUMENTS contains text of tool calls, arguments and their responses in the following format:\n Tool '[test]' called with arguments '[args]' and returned: [tool response]. \n When responding: \n 1. If one or more tool responses are present, answer directly using the information in the tool response. Do not refer to the tool call or tool response explicitly. \n 2. If no tool response is present, reply that you do not know. \n 3. If the information is out of the scope of the document or you are unsure of the answer, reply that you do not know. If the user explicitly requests to provide, show, display, generate a specific output format like a table, a list or a code block, please prioritize that format, when providing an answer. \nDOCUMENTS: {context}",
|
|
1767
|
+
'type': 0,
|
|
1768
|
+
'createdAt': '2025-07-01T16:51:56.703Z',
|
|
1769
|
+
'updatedAt': '2025-07-01T16:51:56.703Z',
|
|
1770
|
+
'version': None,
|
|
1771
|
+
'status': 0,
|
|
1772
|
+
'tenantId': '05f43f12-5865-46cd-8954-1af3dc575e88',
|
|
1773
|
+
'attributes': None,
|
|
1774
|
+
'description': None
|
|
1775
|
+
},
|
|
1776
|
+
...
|
|
1777
|
+
]
|
|
1778
|
+
|
|
1779
|
+
"""
|
|
1780
|
+
|
|
1781
|
+
return self.get_models(model_type="prompts")
|
|
1782
|
+
|
|
1783
|
+
# end method definition
|
|
1784
|
+
|
|
1785
|
+
def get_prompts_iterator(self) -> iter:
|
|
1786
|
+
"""Get an iterator object that can be used to traverse prompts.
|
|
1787
|
+
|
|
1788
|
+
Returns:
|
|
1789
|
+
iter:
|
|
1790
|
+
A generator yielding one prompt per iteration.
|
|
1791
|
+
If the REST API fails, returns no value.
|
|
1792
|
+
|
|
1793
|
+
Yields:
|
|
1794
|
+
Iterator[iter]:
|
|
1795
|
+
One tool at a time.
|
|
1796
|
+
|
|
1797
|
+
"""
|
|
1798
|
+
|
|
1799
|
+
prompts: list = self.get_models(model_type="prompts")
|
|
1800
|
+
|
|
1801
|
+
yield from prompts
|
|
1802
|
+
|
|
1803
|
+
# end method definition
|
|
1804
|
+
|
|
1805
|
+
def get_prompt(self, prompt_id: str) -> dict | None:
|
|
1806
|
+
r"""Get a rule by its ID.
|
|
1807
|
+
|
|
1808
|
+
Args:
|
|
1809
|
+
prompt_id (str):
|
|
1810
|
+
The ID of the prompt.
|
|
1811
|
+
|
|
1812
|
+
Returns:
|
|
1813
|
+
dict | None:
|
|
1814
|
+
Prompt data or none in case of an error.
|
|
1815
|
+
|
|
1816
|
+
Example:
|
|
1817
|
+
{
|
|
1818
|
+
'id': '1aeb9fa1-cb26-4b07-a736-20d25a4ab939',
|
|
1819
|
+
'name': 'general_system',
|
|
1820
|
+
'template': "Your name is Aviator and you are a friendly chatbot assisting users with their queries about documents. The DOCUMENTS contains text of tool calls, arguments and their responses in the following format:\n Tool '[test]' called with arguments '[args]' and returned: [tool response]. \n When responding: \n 1. If one or more tool responses are present, answer directly using the information in the tool response. Do not refer to the tool call or tool response explicitly. \n 2. If no tool response is present, reply that you do not know. \n 3. If the information is out of the scope of the document or you are unsure of the answer, reply that you do not know. If the user explicitly requests to provide, show, display, generate a specific output format like a table, a list or a code block, please prioritize that format, when providing an answer. \nDOCUMENTS: {context}",
|
|
1821
|
+
'type': 0,
|
|
1822
|
+
'createdAt': '2025-07-01T16:51:56.703Z',
|
|
1823
|
+
'updatedAt': '2025-07-01T16:51:56.703Z',
|
|
1824
|
+
'version': None,
|
|
1825
|
+
'status': 0,
|
|
1826
|
+
'tenantId': '05f43f12-5865-46cd-8954-1af3dc575e88',
|
|
1827
|
+
'attributes': None,
|
|
1828
|
+
'description': None
|
|
1829
|
+
},
|
|
1830
|
+
|
|
1831
|
+
"""
|
|
1832
|
+
|
|
1833
|
+
prompt = self.get_model(model_type="prompts", model_id=prompt_id)
|
|
1834
|
+
|
|
1835
|
+
return prompt
|
|
1836
|
+
|
|
1837
|
+
# end method definition
|
|
1838
|
+
|
|
1839
|
+
def get_actions(self) -> list | None:
|
|
1840
|
+
r"""Get all actions.
|
|
1841
|
+
|
|
1842
|
+
Returns:
|
|
1843
|
+
list:
|
|
1844
|
+
A list of all actions.
|
|
1845
|
+
|
|
1846
|
+
Example:
|
|
1847
|
+
[
|
|
1848
|
+
{
|
|
1849
|
+
'id': '98dec337-8284-4d30-8a6d-0da099aa025a',
|
|
1850
|
+
'attributes': {'studio': 'routerAgent'},
|
|
1851
|
+
'klassId': '3d2d2500-483a-4af6-9103-79da80994852',
|
|
1852
|
+
'name': 'decision',
|
|
1853
|
+
'description': None,
|
|
1854
|
+
'discriminator': 1,
|
|
1855
|
+
'createdAt': '2025-07-02T06:45:04.117Z',
|
|
1856
|
+
'updatedAt': '2025-07-02T06:45:04.117Z',
|
|
1857
|
+
'version': 0,
|
|
1858
|
+
'status': 0,
|
|
1859
|
+
'graphId': '02a6ae86-dbf5-4007-ad66-090a145bc81a'
|
|
1860
|
+
},
|
|
1861
|
+
...
|
|
1862
|
+
]
|
|
1863
|
+
|
|
1864
|
+
"""
|
|
1865
|
+
|
|
1866
|
+
return self.get_models(model_type="actions")
|
|
1867
|
+
|
|
1868
|
+
# end method definition
|
|
1869
|
+
|
|
1870
|
+
def get_actions_iterator(self) -> iter:
|
|
1871
|
+
"""Get an iterator object that can be used to traverse actions.
|
|
1872
|
+
|
|
1873
|
+
Returns:
|
|
1874
|
+
iter:
|
|
1875
|
+
A generator yielding one action per iteration.
|
|
1876
|
+
If the REST API fails, returns no value.
|
|
1877
|
+
|
|
1878
|
+
Yields:
|
|
1879
|
+
Iterator[iter]:
|
|
1880
|
+
One action at a time.
|
|
1881
|
+
|
|
1882
|
+
"""
|
|
1883
|
+
|
|
1884
|
+
actions: list = self.get_models(model_type="actions")
|
|
1885
|
+
|
|
1886
|
+
yield from actions
|
|
1887
|
+
|
|
1888
|
+
# end method definition
|
|
1889
|
+
|
|
1890
|
+
def get_action(self, action_id: str) -> dict | None:
|
|
1891
|
+
r"""Get a action by its ID.
|
|
1892
|
+
|
|
1893
|
+
Args:
|
|
1894
|
+
action_id (str):
|
|
1895
|
+
The ID of the action.
|
|
1896
|
+
|
|
1897
|
+
Returns:
|
|
1898
|
+
dict | None:
|
|
1899
|
+
Action data or none in case of an error.
|
|
1900
|
+
|
|
1901
|
+
Example:
|
|
1902
|
+
{
|
|
1903
|
+
'id': '98dec337-8284-4d30-8a6d-0da099aa025a',
|
|
1904
|
+
'attributes': {'studio': 'routerAgent'},
|
|
1905
|
+
'klassId': '3d2d2500-483a-4af6-9103-79da80994852',
|
|
1906
|
+
'name': 'decision',
|
|
1907
|
+
'description': None,
|
|
1908
|
+
'discriminator': 1,
|
|
1909
|
+
'createdAt': '2025-07-02T06:45:04.117Z',
|
|
1910
|
+
'updatedAt': '2025-07-02T06:45:04.117Z',
|
|
1911
|
+
'version': 0,
|
|
1912
|
+
'status': 0,
|
|
1913
|
+
'graphId': '02a6ae86-dbf5-4007-ad66-090a145bc81a'
|
|
1914
|
+
},
|
|
1915
|
+
|
|
1916
|
+
"""
|
|
1917
|
+
|
|
1918
|
+
action = self.get_model(model_type="actions", model_id=action_id)
|
|
1919
|
+
|
|
1920
|
+
return action
|
|
1921
|
+
|
|
1922
|
+
# end method definition
|
|
1923
|
+
|
|
1924
|
+
def get_klasses(self) -> list | None:
|
|
1925
|
+
r"""Get all klasses.
|
|
1926
|
+
|
|
1927
|
+
Returns:
|
|
1928
|
+
list:
|
|
1929
|
+
A list of all klasses.
|
|
1930
|
+
|
|
1931
|
+
Example:
|
|
1932
|
+
[
|
|
1933
|
+
{
|
|
1934
|
+
'id': '20cfe232-cf03-4b77-a4e6-bc9339371a37',
|
|
1935
|
+
'name': 'RephraseSearch',
|
|
1936
|
+
'tenantId': 'eb6fee1e-da08-4046-9867-e96ac0ec5bdf',
|
|
1937
|
+
'path': '../langchain_tools/tools/rephraseSearch',
|
|
1938
|
+
'type': 8,
|
|
1939
|
+
'createdAt': '2025-07-02T06:45:04.099Z',
|
|
1940
|
+
'updatedAt': '2025-07-02T06:45:04.099Z',
|
|
1941
|
+
'description': None
|
|
1942
|
+
},
|
|
1943
|
+
...
|
|
1944
|
+
]
|
|
1945
|
+
|
|
1946
|
+
"""
|
|
1947
|
+
|
|
1948
|
+
return self.get_models(model_type="klasses")
|
|
1949
|
+
|
|
1950
|
+
# end method definition
|
|
1951
|
+
|
|
1952
|
+
def get_klasses_iterator(self) -> iter:
|
|
1953
|
+
"""Get an iterator object that can be used to traverse klasses.
|
|
1954
|
+
|
|
1955
|
+
Returns:
|
|
1956
|
+
iter:
|
|
1957
|
+
A generator yielding one klass per iteration.
|
|
1958
|
+
If the REST API fails, returns no value.
|
|
1959
|
+
|
|
1960
|
+
Yields:
|
|
1961
|
+
Iterator[iter]:
|
|
1962
|
+
One klass at a time.
|
|
1963
|
+
|
|
1964
|
+
"""
|
|
1965
|
+
|
|
1966
|
+
klasses: list = self.get_models(model_type="klasses")
|
|
1967
|
+
|
|
1968
|
+
yield from klasses
|
|
1969
|
+
|
|
1970
|
+
# end method definition
|
|
1971
|
+
|
|
1972
|
+
def get_klass(self, klass_id: str) -> dict | None:
|
|
1973
|
+
r"""Get a klass by its ID.
|
|
1974
|
+
|
|
1975
|
+
Args:
|
|
1976
|
+
klass_id (str):
|
|
1977
|
+
The ID of the klass.
|
|
1978
|
+
|
|
1979
|
+
Returns:
|
|
1980
|
+
dict | None:
|
|
1981
|
+
Klass data or none in case of an error.
|
|
1982
|
+
|
|
1983
|
+
Example:
|
|
1984
|
+
{
|
|
1985
|
+
'id': '20cfe232-cf03-4b77-a4e6-bc9339371a37',
|
|
1986
|
+
'name': 'RephraseSearch',
|
|
1987
|
+
'tenantId': 'eb6fee1e-da08-4046-9867-e96ac0ec5bdf',
|
|
1988
|
+
'path': '../langchain_tools/tools/rephraseSearch',
|
|
1989
|
+
'type': 8,
|
|
1990
|
+
'createdAt': '2025-07-02T06:45:04.099Z',
|
|
1991
|
+
'updatedAt': '2025-07-02T06:45:04.099Z',
|
|
1992
|
+
'description': None
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
"""
|
|
1996
|
+
|
|
1997
|
+
klass = self.get_model(model_type="klasses", model_id=klass_id)
|
|
1998
|
+
|
|
1999
|
+
return klass
|
|
2000
|
+
|
|
2001
|
+
# end method definition
|
|
2002
|
+
|
|
2003
|
+
def get_graph_node_relationships(self, graph_id: str, node_id: str, relation_type: str) -> list | None:
|
|
2004
|
+
"""Get all relations to prompts or rules for a graph node.
|
|
2005
|
+
|
|
2006
|
+
Args:
|
|
2007
|
+
graph_id (str):
|
|
2008
|
+
The ID of the Graph to retrieve the relationships for.
|
|
2009
|
+
node_id (str):
|
|
2010
|
+
The ID of the Graph node to retrieve the relationships for.
|
|
2011
|
+
relation_type (str):
|
|
2012
|
+
This can either be "prompts" or "rules".
|
|
2013
|
+
|
|
2014
|
+
Returns:
|
|
2015
|
+
list | None:
|
|
2016
|
+
A list of relationships for the node.
|
|
2017
|
+
|
|
2018
|
+
Example:
|
|
2019
|
+
[
|
|
2020
|
+
]
|
|
2021
|
+
|
|
2022
|
+
"""
|
|
2023
|
+
|
|
2024
|
+
request_url = self.config()["studioGraphsUrl"] + "/" + graph_id + "/nodes/" + node_id + "/" + relation_type
|
|
2025
|
+
request_header = self.request_header(service_type="studio")
|
|
2026
|
+
|
|
2027
|
+
response = self.do_request(
|
|
2028
|
+
url=request_url,
|
|
2029
|
+
method="GET",
|
|
2030
|
+
headers=request_header,
|
|
2031
|
+
timeout=None,
|
|
2032
|
+
show_error=True,
|
|
2033
|
+
failure_message="Failed get list of graph node relationships!",
|
|
2034
|
+
)
|
|
2035
|
+
|
|
2036
|
+
if response is None:
|
|
2037
|
+
return None
|
|
2038
|
+
|
|
2039
|
+
return response.get("results", [])
|
|
2040
|
+
|
|
2041
|
+
# end method definition
|
|
2042
|
+
|
|
2043
|
+
def get_graph_node_relationships_iterator(
|
|
2044
|
+
self, graph_id: str, node_id: str, relation_type: str | list = "prompts"
|
|
2045
|
+
) -> iter:
|
|
2046
|
+
"""Get an iterator object that can be used to traverse prompts.
|
|
2047
|
+
|
|
2048
|
+
Args:
|
|
2049
|
+
graph_id (str):
|
|
2050
|
+
The ID of the Graph to retrieve the relationships for.
|
|
2051
|
+
node_id (str):
|
|
2052
|
+
The ID of the Graph node to retrieve the relationships for.
|
|
2053
|
+
relation_type (str):
|
|
2054
|
+
This can either be "prompts" or "rules".
|
|
2055
|
+
|
|
2056
|
+
Returns:
|
|
2057
|
+
iter:
|
|
2058
|
+
A generator yielding one relationship per iteration.
|
|
2059
|
+
If the REST API fails, returns no value.
|
|
2060
|
+
|
|
2061
|
+
Yields:
|
|
2062
|
+
Iterator[iter]:
|
|
2063
|
+
One relationship at a time.
|
|
2064
|
+
|
|
2065
|
+
"""
|
|
2066
|
+
|
|
2067
|
+
relationships: list = self.get_graph_node_relationships(
|
|
2068
|
+
graph_id=graph_id, node_id=node_id, relation_type=relation_type
|
|
2069
|
+
)
|
|
2070
|
+
if not relationships:
|
|
2071
|
+
return
|
|
2072
|
+
|
|
2073
|
+
yield from relationships
|
|
2074
|
+
|
|
2075
|
+
# end method definition
|