ibm-watsonx-orchestrate 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. ibm_watsonx_orchestrate/__init__.py +28 -0
  2. ibm_watsonx_orchestrate/agent_builder/__init__.py +0 -0
  3. ibm_watsonx_orchestrate/agent_builder/agents/__init__.py +5 -0
  4. ibm_watsonx_orchestrate/agent_builder/agents/agent.py +27 -0
  5. ibm_watsonx_orchestrate/agent_builder/agents/assistant_agent.py +28 -0
  6. ibm_watsonx_orchestrate/agent_builder/agents/external_agent.py +28 -0
  7. ibm_watsonx_orchestrate/agent_builder/agents/types.py +204 -0
  8. ibm_watsonx_orchestrate/agent_builder/connections/__init__.py +27 -0
  9. ibm_watsonx_orchestrate/agent_builder/connections/connections.py +123 -0
  10. ibm_watsonx_orchestrate/agent_builder/connections/types.py +260 -0
  11. ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base.py +27 -0
  12. ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base_requests.py +59 -0
  13. ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +243 -0
  14. ibm_watsonx_orchestrate/agent_builder/tools/__init__.py +4 -0
  15. ibm_watsonx_orchestrate/agent_builder/tools/base_tool.py +36 -0
  16. ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py +332 -0
  17. ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py +195 -0
  18. ibm_watsonx_orchestrate/agent_builder/tools/types.py +162 -0
  19. ibm_watsonx_orchestrate/agent_builder/utils/__init__.py +0 -0
  20. ibm_watsonx_orchestrate/agent_builder/utils/pydantic_utils.py +149 -0
  21. ibm_watsonx_orchestrate/cli/__init__.py +0 -0
  22. ibm_watsonx_orchestrate/cli/commands/__init__.py +0 -0
  23. ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +192 -0
  24. ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +660 -0
  25. ibm_watsonx_orchestrate/cli/commands/channels/channels_command.py +15 -0
  26. ibm_watsonx_orchestrate/cli/commands/channels/channels_controller.py +16 -0
  27. ibm_watsonx_orchestrate/cli/commands/channels/types.py +15 -0
  28. ibm_watsonx_orchestrate/cli/commands/channels/webchat/channels_webchat_command.py +32 -0
  29. ibm_watsonx_orchestrate/cli/commands/channels/webchat/channels_webchat_controller.py +141 -0
  30. ibm_watsonx_orchestrate/cli/commands/chat/chat_command.py +43 -0
  31. ibm_watsonx_orchestrate/cli/commands/connections/connections_command.py +307 -0
  32. ibm_watsonx_orchestrate/cli/commands/connections/connections_controller.py +517 -0
  33. ibm_watsonx_orchestrate/cli/commands/environment/environment_command.py +78 -0
  34. ibm_watsonx_orchestrate/cli/commands/environment/environment_controller.py +189 -0
  35. ibm_watsonx_orchestrate/cli/commands/environment/types.py +9 -0
  36. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_command.py +79 -0
  37. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +201 -0
  38. ibm_watsonx_orchestrate/cli/commands/login/login_command.py +17 -0
  39. ibm_watsonx_orchestrate/cli/commands/models/models_command.py +128 -0
  40. ibm_watsonx_orchestrate/cli/commands/server/server_command.py +623 -0
  41. ibm_watsonx_orchestrate/cli/commands/settings/__init__.py +0 -0
  42. ibm_watsonx_orchestrate/cli/commands/settings/observability/__init__.py +0 -0
  43. ibm_watsonx_orchestrate/cli/commands/settings/observability/langfuse/__init__.py +0 -0
  44. ibm_watsonx_orchestrate/cli/commands/settings/observability/langfuse/langfuse_command.py +175 -0
  45. ibm_watsonx_orchestrate/cli/commands/settings/observability/observability_command.py +11 -0
  46. ibm_watsonx_orchestrate/cli/commands/settings/settings_command.py +10 -0
  47. ibm_watsonx_orchestrate/cli/commands/tools/tools_command.py +85 -0
  48. ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +564 -0
  49. ibm_watsonx_orchestrate/cli/commands/tools/types.py +10 -0
  50. ibm_watsonx_orchestrate/cli/config.py +226 -0
  51. ibm_watsonx_orchestrate/cli/main.py +32 -0
  52. ibm_watsonx_orchestrate/client/__init__.py +0 -0
  53. ibm_watsonx_orchestrate/client/agents/agent_client.py +46 -0
  54. ibm_watsonx_orchestrate/client/agents/assistant_agent_client.py +38 -0
  55. ibm_watsonx_orchestrate/client/agents/external_agent_client.py +38 -0
  56. ibm_watsonx_orchestrate/client/analytics/__init__.py +0 -0
  57. ibm_watsonx_orchestrate/client/analytics/llm/__init__.py +0 -0
  58. ibm_watsonx_orchestrate/client/analytics/llm/analytics_llm_client.py +50 -0
  59. ibm_watsonx_orchestrate/client/base_api_client.py +113 -0
  60. ibm_watsonx_orchestrate/client/base_service_instance.py +10 -0
  61. ibm_watsonx_orchestrate/client/client.py +71 -0
  62. ibm_watsonx_orchestrate/client/client_errors.py +359 -0
  63. ibm_watsonx_orchestrate/client/connections/__init__.py +10 -0
  64. ibm_watsonx_orchestrate/client/connections/connections_client.py +162 -0
  65. ibm_watsonx_orchestrate/client/connections/utils.py +27 -0
  66. ibm_watsonx_orchestrate/client/credentials.py +123 -0
  67. ibm_watsonx_orchestrate/client/knowledge_bases/knowledge_base_client.py +46 -0
  68. ibm_watsonx_orchestrate/client/local_service_instance.py +91 -0
  69. ibm_watsonx_orchestrate/client/service_instance.py +73 -0
  70. ibm_watsonx_orchestrate/client/tools/tool_client.py +41 -0
  71. ibm_watsonx_orchestrate/client/utils.py +95 -0
  72. ibm_watsonx_orchestrate/docker/compose-lite.yml +595 -0
  73. ibm_watsonx_orchestrate/docker/default.env +125 -0
  74. ibm_watsonx_orchestrate/docker/sdk/ibm_watsonx_orchestrate-0.6.0-py3-none-any.whl +0 -0
  75. ibm_watsonx_orchestrate/docker/sdk/ibm_watsonx_orchestrate-0.6.0.tar.gz +0 -0
  76. ibm_watsonx_orchestrate/docker/start-up.sh +61 -0
  77. ibm_watsonx_orchestrate/docker/tempus/common-config.yaml +1 -0
  78. ibm_watsonx_orchestrate/run/__init__.py +0 -0
  79. ibm_watsonx_orchestrate/run/connections.py +40 -0
  80. ibm_watsonx_orchestrate/utils/__init__.py +0 -0
  81. ibm_watsonx_orchestrate/utils/logging/__init__.py +0 -0
  82. ibm_watsonx_orchestrate/utils/logging/logger.py +26 -0
  83. ibm_watsonx_orchestrate/utils/logging/logging.yaml +18 -0
  84. ibm_watsonx_orchestrate/utils/utils.py +15 -0
  85. ibm_watsonx_orchestrate-1.0.0.dist-info/METADATA +34 -0
  86. ibm_watsonx_orchestrate-1.0.0.dist-info/RECORD +89 -0
  87. ibm_watsonx_orchestrate-1.0.0.dist-info/WHEEL +4 -0
  88. ibm_watsonx_orchestrate-1.0.0.dist-info/entry_points.txt +2 -0
  89. ibm_watsonx_orchestrate-1.0.0.dist-info/licenses/LICENSE +22 -0
@@ -0,0 +1,359 @@
1
+ # -----------------------------------------------------------------------------------------
2
+ # (C) Copyright IBM Corp. 2023-2024.
3
+ # https://opensource.org/licenses/BSD-3-Clause
4
+ # -----------------------------------------------------------------------------------------
5
+ from __future__ import annotations
6
+ from typing import TYPE_CHECKING
7
+
8
+ import logging
9
+ import sys
10
+ import re
11
+
12
+ if TYPE_CHECKING:
13
+ from requests import Response
14
+
15
+ __all__ = [
16
+ "ClientError",
17
+ "MissingValue",
18
+ "MissingMetaProp",
19
+ "NotUrlNorID",
20
+ "NoCredentialsProvided",
21
+ "ApiRequestFailure",
22
+ "UnexpectedType",
23
+ "ForbiddenActionForPlan",
24
+ "NoVirtualDeploymentSupportedForICP",
25
+ "MissingArgument",
26
+ "WrongEnvironmentVersion",
27
+ "CannotAutogenerateBedrockUrl",
28
+ "WrongMetaProps",
29
+ "CannotSetProjectOrSpace",
30
+ "ForbiddenActionForGitBasedProject",
31
+ "CannotInstallLibrary",
32
+ "DataStreamError",
33
+ "WrongLocationProperty",
34
+ "WrongFileLocation",
35
+ "EmptyDataSource",
36
+ "SpaceIDandProjectIDCannotBeNone",
37
+ "ParamOutOfRange",
38
+ "InvalidMultipleArguments",
39
+ "ValidationError",
40
+ "InvalidValue",
41
+ "PromptVariablesError",
42
+ "UnsupportedOperation",
43
+ "MissingExtension",
44
+ "InvalidCredentialsError",
45
+ ]
46
+
47
+
48
+ class ClientError(Exception):
49
+ def __init__(
50
+ self,
51
+ error_msg: str,
52
+ reason: str | list | None = None,
53
+ logg_messages: bool = True,
54
+ ):
55
+ # Check if URL contains `internal` or `private` in host part of URL and hide it
56
+ pattern = (
57
+ r"\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)"
58
+ r"(?:[\w.-]*(internal|private)[^\s]*))"
59
+ )
60
+ self.error_msg = re.sub(
61
+ pattern,
62
+ lambda m: f"[{m.group(2).capitalize()} URL]",
63
+ str(error_msg),
64
+ re.IGNORECASE,
65
+ )
66
+ self.reason = reason
67
+ if logg_messages:
68
+ logging.getLogger(__name__).warning(self.__str__())
69
+ logging.getLogger(__name__).debug(
70
+ str(self.error_msg)
71
+ + (
72
+ "\nReason: " + str(self.reason)
73
+ if sys.exc_info()[0] is not None
74
+ else ""
75
+ )
76
+ )
77
+
78
+ def __str__(self) -> str:
79
+ return str(self.error_msg) + (
80
+ "\nReason: " + str(self.reason) if self.reason is not None else ""
81
+ )
82
+
83
+
84
+ class MissingValue(ClientError, ValueError):
85
+ def __init__(self, value_name: str, reason: str | None = None):
86
+ ClientError.__init__(self, 'No "' + value_name + '" provided.', reason)
87
+
88
+
89
+ class MissingMetaProp(MissingValue):
90
+ def __init__(self, name: str, reason: str | None = None):
91
+ ClientError.__init__(
92
+ self, "Missing meta_prop with name: '{}'.".format(name), reason
93
+ )
94
+
95
+
96
+ class NotUrlNorID(ClientError, ValueError):
97
+ def __init__(self, value_name: str, value: str, reason: str | None = None):
98
+ ClientError.__init__(
99
+ self,
100
+ "Invalid value of '{}' - it is not url nor id: '{}'".format(
101
+ value_name, value
102
+ ),
103
+ reason,
104
+ )
105
+
106
+
107
+ class NoCredentialsProvided(MissingValue):
108
+ def __init__(self, reason: str | None = None):
109
+ MissingValue.__init__(self, "WML credentials", reason)
110
+
111
+
112
+ class ApiRequestFailure(ClientError):
113
+ def __init__(self, error_msg: str, response: Response, reason: str | None = None):
114
+ self.response = response
115
+ if str(response.status_code) == "404" and "DOCTYPE" in str(response.content):
116
+ raise MissingWMLComponent()
117
+
118
+ elif str(
119
+ response.status_code
120
+ ) == "400" and "Invalid content. You cannot include any tags in the HTTP request." in str(
121
+ response.content
122
+ ):
123
+ ClientError.__init__(
124
+ self,
125
+ f"Please check if any parameter that you provided include HTTP tag. "
126
+ f"If yes, please remove it and try again.",
127
+ reason=str(response.content),
128
+ )
129
+
130
+ else:
131
+ ClientError.__init__(
132
+ self,
133
+ "{} ({} {})\nStatus code: {}, body: {}".format(
134
+ error_msg,
135
+ response.request.method,
136
+ response.request.url,
137
+ response.status_code,
138
+ (
139
+ response.text
140
+ if response.apparent_encoding is not None
141
+ else "[binary content, "
142
+ + str(len(response.content))
143
+ + " bytes]"
144
+ ),
145
+ ),
146
+ reason,
147
+ )
148
+
149
+
150
+ class UnexpectedType(ClientError, ValueError):
151
+ def __init__(self, el_name: str, expected_type: type, actual_type: type):
152
+ ClientError.__init__(
153
+ self,
154
+ "Unexpected type of '{}', expected: {}, actual: '{}'.".format(
155
+ el_name,
156
+ (
157
+ "'{}'".format(expected_type)
158
+ if type(expected_type) == type
159
+ else expected_type
160
+ ),
161
+ actual_type,
162
+ ),
163
+ )
164
+
165
+
166
+ class ForbiddenActionForPlan(ClientError):
167
+ def __init__(self, operation_name: str, expected_plans: list, actual_plan: str):
168
+ ClientError.__init__(
169
+ self,
170
+ "Operation '{}' is available only for {} plan, while this instance has '{}' plan.".format(
171
+ operation_name,
172
+ (
173
+ (
174
+ "one of {} as".format(expected_plans)
175
+ if len(expected_plans) > 1
176
+ else "'{}'".format(expected_plans[0])
177
+ )
178
+ if type(expected_plans) is list
179
+ else "'{}'".format(expected_plans)
180
+ ),
181
+ actual_plan,
182
+ ),
183
+ )
184
+
185
+
186
+ class NoVirtualDeploymentSupportedForICP(MissingValue):
187
+ def __init__(self, reason: str | None = None):
188
+ MissingValue.__init__(self, "No Virtual deployment supported for ICP", reason)
189
+
190
+
191
+ class MissingArgument(ClientError, ValueError):
192
+ def __init__(self, value_name: str, reason: str | None = None):
193
+ ClientError.__init__(self, f"Argument: {value_name} missing.", reason)
194
+
195
+
196
+ class WrongEnvironmentVersion(ClientError, ValueError):
197
+ def __init__(
198
+ self, used_version: str, environment_name: str, supported_versions: tuple
199
+ ):
200
+ ClientError.__init__(
201
+ self,
202
+ "Version used in credentials not supported in this environment",
203
+ reason=f"Version {used_version} isn't supported in "
204
+ f"{environment_name} environment, "
205
+ f"select from {supported_versions}",
206
+ )
207
+
208
+
209
+ class CannotAutogenerateBedrockUrl(ClientError, ValueError):
210
+ def __init__(self, e1: Exception, e2: Exception):
211
+ ClientError.__init__(
212
+ self,
213
+ "Attempt of generating `bedrock_url` automatically failed. "
214
+ "If iamintegration=True, please provide `bedrock_url` in credentials. "
215
+ "If iamintegration=False, please validate your credentials.",
216
+ reason=[e1, e2],
217
+ )
218
+
219
+
220
+ class WrongMetaProps(MissingValue):
221
+ def __init__(self, reason: str | None = None):
222
+ ClientError.__init__(self, "Wrong metaprops.", reason)
223
+
224
+
225
+ class MissingWMLComponent(ClientError):
226
+ def __init__(self) -> None:
227
+ ClientError.__init__(
228
+ self,
229
+ f"Missing WML Component",
230
+ reason=f"It appears that WML component is not installed on your environment. "
231
+ f"Contact your cluster administrator.",
232
+ )
233
+
234
+
235
+ class CannotSetProjectOrSpace(ClientError):
236
+ def __init__(self, reason: str):
237
+ ClientError.__init__(self, f"Cannot set Project or Space", reason=reason)
238
+
239
+
240
+ class ForbiddenActionForGitBasedProject(ClientError):
241
+ def __init__(self, reason: str):
242
+ ClientError.__init__(
243
+ self, f"This action is not supported for git based project.", reason=reason
244
+ )
245
+
246
+
247
+ class CannotInstallLibrary(ClientError, ValueError):
248
+ def __init__(self, lib_name: str, reason: str):
249
+ ClientError.__init__(
250
+ self,
251
+ f"Library '{lib_name}' cannot be installed! Please install it manually.",
252
+ reason,
253
+ )
254
+
255
+
256
+ class DataStreamError(ClientError, ConnectionError):
257
+ def __init__(self, reason: str):
258
+ ClientError.__init__(
259
+ self, "Cannot fetch data via Flight Service. Try again.", reason
260
+ )
261
+
262
+
263
+ class WrongLocationProperty(ClientError, ConnectionError):
264
+ def __init__(self, reason: str):
265
+ ClientError.__init__(
266
+ self, "Cannot fetch data via Flight Service. Try again.", reason
267
+ )
268
+
269
+
270
+ class WrongFileLocation(ClientError, ValueError):
271
+ def __init__(self, reason: str):
272
+ ClientError.__init__(
273
+ self, "Cannot fetch data via Flight Service. Try again.", reason
274
+ )
275
+
276
+
277
+ class EmptyDataSource(ClientError, ValueError):
278
+ def __init__(self) -> None:
279
+ ClientError.__init__(
280
+ self,
281
+ "Cannot fetch data via Flight Service. "
282
+ "Verify if data were saved under data connection and try again.",
283
+ )
284
+
285
+
286
+ class SpaceIDandProjectIDCannotBeNone(ClientError, ValueError):
287
+ def __init__(self, reason: str):
288
+ ClientError.__init__(self, f"Missing 'space_id' or 'project_id'.", reason)
289
+
290
+
291
+ class ParamOutOfRange(ClientError, ValueError):
292
+ def __init__(
293
+ self, param_name: str, value: int | float, min: int | float, max: int | float
294
+ ):
295
+ ClientError.__init__(
296
+ self,
297
+ f"Value of parameter `{param_name}`, {value}, is out of expected range - between {min} and {max}.",
298
+ )
299
+
300
+
301
+ class InvalidMultipleArguments(ClientError, ValueError):
302
+ def __init__(self, params_names_list: list, reason: str | None = None):
303
+ ClientError.__init__(
304
+ self, f"One of {params_names_list} parameters should be set.", reason
305
+ )
306
+
307
+
308
+ class ValidationError(ClientError, KeyError):
309
+ def __init__(self, key: str, additional_msg: str | None = None):
310
+ msg = (
311
+ f"Invalid prompt template; check for"
312
+ f" mismatched or missing input variables."
313
+ f" Missing input variable: {key}."
314
+ )
315
+ if additional_msg is not None:
316
+ msg += "\n" + additional_msg
317
+ ClientError.__init__(self, msg)
318
+
319
+
320
+ class PromptVariablesError(ClientError, KeyError):
321
+ def __init__(self, key: str):
322
+ ClientError.__init__(
323
+ self,
324
+ (
325
+ f"Prompt template contains input variables."
326
+ f" Missing {key} in `prompt_variables`"
327
+ ),
328
+ )
329
+
330
+
331
+ class InvalidValue(ClientError, ValueError):
332
+ def __init__(self, value_name: str, reason: str | None = None):
333
+ ClientError.__init__(
334
+ self, 'Inappropriate value of "' + value_name + '"', reason
335
+ )
336
+
337
+
338
+ class UnsupportedOperation(ClientError):
339
+ def __init__(self, reason: str):
340
+ ClientError.__init__(
341
+ self, f"Operation is unsupported for this release.", reason
342
+ )
343
+
344
+
345
+ class MissingExtension(ClientError, ImportError):
346
+ def __init__(self, extension_name: str):
347
+ ClientError.__init__(
348
+ self,
349
+ f"Could not import {extension_name}: Please install `{extension_name}` extension.",
350
+ )
351
+
352
+
353
+ class InvalidCredentialsError(ClientError):
354
+ def __init__(self, reason: str | Response, logg_messages: bool = True):
355
+ ClientError.__init__(
356
+ self,
357
+ f"Attempt of authenticating connection to service failed, please validate your credentials. Error: {reason}",
358
+ logg_messages=logg_messages,
359
+ )
@@ -0,0 +1,10 @@
1
+ from .connections_client import (
2
+ ConnectionAuthType,
3
+ ConnectionSecurityScheme,
4
+ ConnectionType
5
+ )
6
+
7
+ from .utils import (
8
+ get_connections_client,
9
+ get_connection_type
10
+ )
@@ -0,0 +1,162 @@
1
+ from typing import List
2
+
3
+ from pydantic import BaseModel, ValidationError
4
+ from typing import Optional
5
+
6
+ from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient, ClientAPIException
7
+ from ibm_watsonx_orchestrate.agent_builder.connections.types import ConnectionEnvironment, ConnectionPreference, ConnectionAuthType, ConnectionSecurityScheme, IdpConfigData, AppConfigData, ConnectionType
8
+
9
+ import logging
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class ListConfigsResponse(BaseModel):
14
+ connection_id: str = None,
15
+ app_id: str = None
16
+ name: str = None
17
+ security_scheme: ConnectionSecurityScheme | None = None,
18
+ auth_type: ConnectionAuthType | None = None,
19
+ environment: ConnectionEnvironment | None = None,
20
+ preference: ConnectionPreference | None = None,
21
+ credentials_entered: bool | None = False
22
+
23
+ class GetConfigResponse(BaseModel):
24
+ config_id: str = None
25
+ tenant_id: str = None
26
+ app_id: str = None
27
+ environment: ConnectionEnvironment = None
28
+ preference: ConnectionPreference = None
29
+ auth_type: ConnectionAuthType | None = None
30
+ sso: bool = None
31
+ security_scheme: ConnectionSecurityScheme = None
32
+ server_url: str | None = None
33
+ idp_config_data: Optional[IdpConfigData] = None
34
+ app_config_data: Optional[AppConfigData] = None
35
+
36
+ class GetConnectionResponse(BaseModel):
37
+ connection_id: str = None
38
+ app_id: str = None
39
+ tenant_id: str = None
40
+
41
+
42
+
43
+ class ConnectionsClient(BaseAPIClient):
44
+ """
45
+ Client to handle CRUD operations for Connections endpoint
46
+ """
47
+ # POST api/v1/connections/applications
48
+ def create(self, payload: dict) -> None:
49
+ self._post("/connections/applications", data=payload)
50
+
51
+ # DELETE api/v1/connections/applications/{app_id}
52
+ def delete(self, app_id: str) -> dict:
53
+ return self._delete(f"/connections/applications/{app_id}")
54
+
55
+ # GET /api/v1/connections/applications/{app_id}
56
+ def get(self, app_id: str) -> GetConnectionResponse:
57
+ try:
58
+ return GetConnectionResponse.model_validate(self._get(f"/connections/applications/{app_id}"))
59
+ except ClientAPIException as e:
60
+ if e.response.status_code == 404:
61
+ return None
62
+ raise e
63
+
64
+
65
+ # GET api/v1/connections/applications
66
+ def list(self) -> List[ListConfigsResponse]:
67
+ try:
68
+ res = self._get(f"/connections/applications")
69
+ return [ListConfigsResponse.model_validate(conn) for conn in res.get("applications", [])]
70
+ except ValidationError as e:
71
+ logger.error("Recieved unexpected response from server")
72
+ raise e
73
+ except ClientAPIException as e:
74
+ if e.response.status_code == 404:
75
+ return []
76
+ raise e
77
+
78
+
79
+ # POST /api/v1/connections/applications/{app_id}/configurations
80
+ def create_config(self, app_id: str, payload: dict) -> None:
81
+ self._post(f"/connections/applications/{app_id}/configurations", data=payload)
82
+
83
+ # PATCH /api/v1/connections/applications/{app_id}/configurations/{env}
84
+ def update_config(self, app_id: str, env: ConnectionEnvironment, payload: dict) -> None:
85
+ self._patch(f"/connections/applications/{app_id}/configurations/{env}", data=payload)
86
+
87
+ # `GET /api/v1/connections/applications/{app_id}/configurations/{env}'
88
+ def get_config(self, app_id: str, env: ConnectionEnvironment) -> GetConfigResponse:
89
+ try:
90
+ res = self._get(f"/connections/applications/{app_id}/configurations/{env}")
91
+ return GetConfigResponse.model_validate(res)
92
+ except ClientAPIException as e:
93
+ if e.response.status_code == 404:
94
+ return None
95
+ raise e
96
+
97
+ # POST /api/v1/connections/applications/{app_id}/configs/{env}/credentials
98
+ # POST /api/v1/connections/applications/{app_id}/configs/{env}/runtime_credentials
99
+ def create_credentials(self, app_id: str, env: ConnectionEnvironment, payload: dict, use_sso: bool) -> None:
100
+ if use_sso:
101
+ self._post(f"/connections/applications/{app_id}/configs/{env}/credentials", data=payload)
102
+ else:
103
+ self._post(f"/connections/applications/{app_id}/configs/{env}/runtime_credentials", data=payload)
104
+
105
+ # PATCH /api/v1/connections/applications/{app_id}/configs/{env}/credentials
106
+ # PATCH /api/v1/connections/applications/{app_id}/configs/{env}/runtime_credentials
107
+ def update_credentials(self, app_id: str, env: ConnectionEnvironment, payload: dict, use_sso: bool) -> None:
108
+ if use_sso:
109
+ self._patch(f"/connections/applications/{app_id}/configs/{env}/credentials", data=payload)
110
+ else:
111
+ self._patch(f"/connections/applications/{app_id}/configs/{env}/runtime_credentials", data=payload)
112
+
113
+ # GET /api/v1/connections/applications/{app_id}/configs/credentials?env={env}
114
+ # GET /api/v1/connections/applications/{app_id}/configs/runtime_credentials?env={env}
115
+ def get_credentials(self, app_id: str, env: ConnectionEnvironment, use_sso: bool) -> dict:
116
+ try:
117
+ if use_sso:
118
+ return self._get(f"/connections/applications/{app_id}/credentials?env={env}")
119
+ else:
120
+ return self._get(f"/connections/applications/{app_id}/configs/runtime_credentials?env={env}")
121
+ except ClientAPIException as e:
122
+ if e.response.status_code == 404:
123
+ return None
124
+ raise e
125
+
126
+ # DELETE /api/v1/connections/applications/{app_id}/configs/{env}/credentials
127
+ # DELETE /api/v1/connections/applications/{app_id}/configs/{env}/runtime_credentials
128
+ def delete_credentials(self, app_id: str, env: ConnectionEnvironment, use_sso: bool) -> None:
129
+ if use_sso:
130
+ self._delete(f"/connections/applications/{app_id}/configs/{env}/credentials")
131
+ else:
132
+ self._delete(f"/connections/applications/{app_id}/configs/{env}/runtime_credentials")
133
+
134
+ def get_draft_by_app_id(self, app_id: str) -> GetConnectionResponse:
135
+ return self.get(app_id=app_id)
136
+
137
+ def get_draft_by_app_ids(self, app_ids: List[str]) -> List[GetConnectionResponse]:
138
+ connections = []
139
+ for app_id in app_ids:
140
+ connection = self.get_draft_by_app_id(app_id)
141
+ if connection:
142
+ connections += connection
143
+ return connections
144
+
145
+ def get_draft_by_id(self, conn_id) -> str:
146
+ """Retrieve the app ID for a given connection ID."""
147
+ if conn_id is None:
148
+ return ""
149
+ try:
150
+ connections = self.list()
151
+ except ClientAPIException as e:
152
+ if e.response.status_code == 404:
153
+ logger.warning(f"Connections not found. Returning connection ID: {conn_id}")
154
+ return conn_id
155
+ raise
156
+
157
+ app_id = next((conn.app_id for conn in connections if conn.connection_id == conn_id), None)
158
+
159
+ if app_id is None:
160
+ logger.warning(f"Connection with ID {conn_id} not found. Returning connection ID.")
161
+ return conn_id
162
+ return app_id
@@ -0,0 +1,27 @@
1
+ from ibm_watsonx_orchestrate.client.utils import instantiate_client, is_local_dev
2
+ from ibm_watsonx_orchestrate.client.connections.connections_client import ConnectionsClient
3
+ from ibm_watsonx_orchestrate.cli.config import Config, ENVIRONMENTS_SECTION_HEADER, CONTEXT_SECTION_HEADER, CONTEXT_ACTIVE_ENV_OPT, ENV_WXO_URL_OPT
4
+ from ibm_watsonx_orchestrate.agent_builder.connections.types import ConnectionType, ConnectionAuthType, ConnectionSecurityScheme
5
+
6
+ LOCAL_CONNECTION_MANAGER_PORT = 3001
7
+
8
+ def _get_connections_manager_url() -> str:
9
+ cfg = Config()
10
+ active_env = cfg.get(CONTEXT_SECTION_HEADER, CONTEXT_ACTIVE_ENV_OPT)
11
+ url = cfg.get(ENVIRONMENTS_SECTION_HEADER, active_env, ENV_WXO_URL_OPT)
12
+
13
+ if is_local_dev(url):
14
+ url_parts = url.split(":")
15
+ url_parts[-1] = str(LOCAL_CONNECTION_MANAGER_PORT)
16
+ url = ":".join(url_parts)
17
+ url = url + "/api/v1"
18
+ return url
19
+ return None
20
+
21
+ def get_connections_client() -> ConnectionsClient:
22
+ return instantiate_client(client=ConnectionsClient, url=_get_connections_manager_url())
23
+
24
+ def get_connection_type(security_scheme: ConnectionSecurityScheme, auth_type: ConnectionAuthType) -> ConnectionType:
25
+ if security_scheme != ConnectionSecurityScheme.OAUTH2:
26
+ return ConnectionType(security_scheme)
27
+ return ConnectionType(auth_type)
@@ -0,0 +1,123 @@
1
+ # -----------------------------------------------------------------------------------------
2
+ # (C) Copyright IBM Corp. 2024.
3
+ # https://opensource.org/licenses/BSD-3-Clause
4
+ # -----------------------------------------------------------------------------------------
5
+
6
+ from __future__ import annotations
7
+
8
+ import os
9
+ from typing import Any
10
+
11
+
12
+ class Credentials:
13
+ """This class encapsulate passed credentials and additional params.
14
+
15
+ :param url: URL of the service
16
+ :type url: str
17
+
18
+ :param api_key: service API key used in API key authentication
19
+ :type api_key: str, optional
20
+
21
+ :param token: service token, used in token authentication
22
+ :type token: str, optional
23
+
24
+ :param verify: certificate verification flag
25
+ :type verify: bool, optional
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ *,
31
+ url: str | None = None,
32
+ iam_url: str | None = None,
33
+ api_key: str | None = None,
34
+ token: str | None = None,
35
+ verify: str | bool | None = None,
36
+ auth_type: str | None = None,
37
+ ) -> None:
38
+ env_credentials = Credentials._get_values_from_env_vars()
39
+ self.url = url
40
+ self.iam_url = iam_url if iam_url is not None else "https://iam.platform.saas.ibm.com"
41
+ self.api_key = api_key
42
+ self.token = token
43
+ self.local_global_token = None
44
+ self.verify = verify
45
+ self.auth_type = auth_type
46
+ self._is_env_token = token is None and "token" in env_credentials
47
+
48
+ for k, v in env_credentials.items():
49
+ if self.__dict__.get(k) is None:
50
+ self.__dict__[k] = v
51
+
52
+ @staticmethod
53
+ def from_dict(dict: dict) -> Credentials:
54
+ creds = Credentials()
55
+ for k, v in dict.items():
56
+ setattr(creds, k, v)
57
+
58
+ return creds
59
+
60
+ @staticmethod
61
+ def _get_values_from_env_vars() -> dict[str, Any]:
62
+ def get_value_from_file(filename: str) -> str:
63
+ with open(filename, "r") as f:
64
+ return f.read()
65
+
66
+ def get_verify_value(x: str) -> bool | str:
67
+ if x in ["True", "False"]:
68
+ return x == "True"
69
+ else:
70
+ return x
71
+
72
+ env_vars_mapping = {
73
+ "WXO_CLIENT_VERIFY_REQUESTS": lambda x: ("verify", get_verify_value(x)),
74
+ "USER_ACCESS_TOKEN": lambda x: ("token", x.replace("Bearer ", "")),
75
+ "RUNTIME_ENV_ACCESS_TOKEN_FILE": lambda x: (
76
+ "token",
77
+ get_value_from_file(x).replace("Bearer ", ""),
78
+ ),
79
+ "WXO_URL": lambda x: ("url", x),
80
+ }
81
+
82
+ return dict(
83
+ [
84
+ f(os.environ[k])
85
+ for k, f in env_vars_mapping.items()
86
+ if os.environ.get(k) is not None and os.environ.get(k) != ""
87
+ ]
88
+ )
89
+
90
+ def to_dict(self) -> dict[str, Any]:
91
+ """Get dictionary from the Credentials object.
92
+
93
+ :return: dictionary with credentials
94
+ :rtype: dict
95
+
96
+ **Example**
97
+
98
+ .. code-block:: python
99
+
100
+ from ibm_watsonx_orchestrate import Credentials
101
+
102
+ credentials = Credentials.from_dict({
103
+ 'url': "<url>",
104
+ 'apikey': "<api_key>"
105
+ })
106
+
107
+ credentials_dict = credentials.to_dict()
108
+
109
+ """
110
+ data = dict(
111
+ [
112
+ (k, v)
113
+ for k, v in self.__dict__.items()
114
+ if v is not None and not k.startswith("_")
115
+ ]
116
+ )
117
+ return data
118
+
119
+ def __getitem__(self, key: str) -> Any:
120
+ return self.to_dict()[key]
121
+
122
+ def get(self, key: str, default: Any | None = None) -> Any:
123
+ return self.to_dict().get(key, default)