ibm-watsonx-orchestrate 1.6.0b0__py3-none-any.whl → 1.6.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 (42) hide show
  1. ibm_watsonx_orchestrate/__init__.py +1 -1
  2. ibm_watsonx_orchestrate/agent_builder/agents/agent.py +1 -0
  3. ibm_watsonx_orchestrate/agent_builder/agents/types.py +5 -1
  4. ibm_watsonx_orchestrate/agent_builder/agents/webchat_customizations/__init__.py +2 -0
  5. ibm_watsonx_orchestrate/agent_builder/agents/webchat_customizations/prompts.py +34 -0
  6. ibm_watsonx_orchestrate/agent_builder/agents/webchat_customizations/welcome_content.py +20 -0
  7. ibm_watsonx_orchestrate/agent_builder/connections/__init__.py +2 -2
  8. ibm_watsonx_orchestrate/agent_builder/connections/connections.py +21 -7
  9. ibm_watsonx_orchestrate/agent_builder/connections/types.py +39 -36
  10. ibm_watsonx_orchestrate/agent_builder/tools/flow_tool.py +83 -0
  11. ibm_watsonx_orchestrate/agent_builder/tools/types.py +7 -1
  12. ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +56 -18
  13. ibm_watsonx_orchestrate/cli/commands/channels/webchat/channels_webchat_controller.py +104 -21
  14. ibm_watsonx_orchestrate/cli/commands/chat/chat_command.py +2 -0
  15. ibm_watsonx_orchestrate/cli/commands/connections/connections_command.py +26 -18
  16. ibm_watsonx_orchestrate/cli/commands/connections/connections_controller.py +61 -61
  17. ibm_watsonx_orchestrate/cli/commands/environment/environment_controller.py +1 -1
  18. ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_command.py +118 -30
  19. ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_controller.py +22 -9
  20. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +2 -2
  21. ibm_watsonx_orchestrate/cli/commands/server/server_command.py +123 -5
  22. ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_controller.py +9 -3
  23. ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +107 -22
  24. ibm_watsonx_orchestrate/client/agents/agent_client.py +74 -6
  25. ibm_watsonx_orchestrate/client/base_api_client.py +2 -1
  26. ibm_watsonx_orchestrate/client/connections/connections_client.py +18 -9
  27. ibm_watsonx_orchestrate/client/connections/utils.py +4 -2
  28. ibm_watsonx_orchestrate/client/local_service_instance.py +1 -1
  29. ibm_watsonx_orchestrate/client/service_instance.py +3 -3
  30. ibm_watsonx_orchestrate/client/tools/tempus_client.py +8 -3
  31. ibm_watsonx_orchestrate/client/utils.py +10 -0
  32. ibm_watsonx_orchestrate/docker/compose-lite.yml +228 -67
  33. ibm_watsonx_orchestrate/docker/default.env +32 -13
  34. ibm_watsonx_orchestrate/docker/proxy-config-single.yaml +12 -0
  35. ibm_watsonx_orchestrate/flow_builder/flows/flow.py +15 -5
  36. ibm_watsonx_orchestrate/flow_builder/utils.py +78 -48
  37. ibm_watsonx_orchestrate/run/connections.py +4 -4
  38. {ibm_watsonx_orchestrate-1.6.0b0.dist-info → ibm_watsonx_orchestrate-1.6.1.dist-info}/METADATA +2 -2
  39. {ibm_watsonx_orchestrate-1.6.0b0.dist-info → ibm_watsonx_orchestrate-1.6.1.dist-info}/RECORD +42 -37
  40. {ibm_watsonx_orchestrate-1.6.0b0.dist-info → ibm_watsonx_orchestrate-1.6.1.dist-info}/WHEEL +0 -0
  41. {ibm_watsonx_orchestrate-1.6.0b0.dist-info → ibm_watsonx_orchestrate-1.6.1.dist-info}/entry_points.txt +0 -0
  42. {ibm_watsonx_orchestrate-1.6.0b0.dist-info → ibm_watsonx_orchestrate-1.6.1.dist-info}/licenses/LICENSE +0 -0
@@ -24,6 +24,7 @@ from rich.console import Console
24
24
  from rich.panel import Panel
25
25
 
26
26
  from ibm_watsonx_orchestrate.agent_builder.tools import BaseTool, ToolSpec
27
+ from ibm_watsonx_orchestrate.agent_builder.tools.flow_tool import create_flow_json_tool
27
28
  from ibm_watsonx_orchestrate.agent_builder.tools.openapi_tool import create_openapi_json_tools_from_uri,create_openapi_json_tools_from_content
28
29
  from ibm_watsonx_orchestrate.cli.commands.models.models_controller import ModelHighlighter
29
30
  from ibm_watsonx_orchestrate.cli.commands.tools.types import RegistryType
@@ -38,10 +39,9 @@ from ibm_watsonx_orchestrate.client.tools.tool_client import ToolClient
38
39
  from ibm_watsonx_orchestrate.client.toolkit.toolkit_client import ToolKitClient
39
40
  from ibm_watsonx_orchestrate.client.connections import get_connections_client, get_connection_type
40
41
  from ibm_watsonx_orchestrate.client.utils import instantiate_client, is_local_dev
42
+ from ibm_watsonx_orchestrate.flow_builder.utils import import_flow_support_tools
41
43
  from ibm_watsonx_orchestrate.utils.utils import sanatize_app_id
42
44
  from ibm_watsonx_orchestrate.client.utils import is_local_dev
43
- from ibm_watsonx_orchestrate.client.tools.tempus_client import TempusClient
44
- from ibm_watsonx_orchestrate.flow_builder.utils import import_flow_model
45
45
 
46
46
  from ibm_watsonx_orchestrate import __version__
47
47
 
@@ -57,6 +57,12 @@ class ToolKind(str, Enum):
57
57
  flow = "flow"
58
58
  # skill = "skill"
59
59
 
60
+ def _get_connection_environments() -> List[ConnectionEnvironment]:
61
+ if is_local_dev():
62
+ return [ConnectionEnvironment.DRAFT]
63
+ else:
64
+ return [env.value for env in ConnectionEnvironment]
65
+
60
66
  def validate_app_ids(kind: ToolKind, **args) -> None:
61
67
  app_ids = args.get("app_id")
62
68
  if not app_ids:
@@ -71,7 +77,14 @@ def validate_app_ids(kind: ToolKind, **args) -> None:
71
77
  connections_client = get_connections_client()
72
78
 
73
79
  imported_connections_list = connections_client.list()
74
- imported_connections = {conn.app_id:conn for conn in imported_connections_list}
80
+ imported_connections = {}
81
+ for conn in imported_connections_list:
82
+ app_id = conn.app_id
83
+ conn_env = conn.environment
84
+ if app_id in imported_connections:
85
+ imported_connections[app_id][conn_env] = conn
86
+ else:
87
+ imported_connections[app_id] = {conn_env: conn}
75
88
 
76
89
  for app_id in app_ids:
77
90
  if kind == ToolKind.python:
@@ -89,9 +102,29 @@ def validate_app_ids(kind: ToolKind, **args) -> None:
89
102
  if app_id not in imported_connections:
90
103
  logger.warning(f"No connection found for provided app-id '{app_id}'. Please create the connection using `orchestrate connections add`")
91
104
  else:
92
- if kind == ToolKind.openapi and imported_connections.get(app_id).security_scheme == ConnectionSecurityScheme.KEY_VALUE:
93
- logger.error(f"Key value application connections can not be bound to an openapi tool")
94
- exit(1)
105
+ # Validate that the connection is not key_value when the tool in openapi
106
+ if kind != ToolKind.openapi:
107
+ continue
108
+
109
+ environments = _get_connection_environments()
110
+
111
+ imported_connection = imported_connections.get(app_id)
112
+
113
+ for conn_environment in environments:
114
+ conn = imported_connection.get(conn_environment)
115
+
116
+ if conn is None or conn.security_scheme is None:
117
+ message = f"Connection '{app_id}' is not configured in the '{conn_environment}' environment."
118
+ if conn_environment == ConnectionEnvironment.DRAFT:
119
+ logger.error(message)
120
+ sys.exit(1)
121
+ else:
122
+ logger.warning(message + " If you deploy this tool without setting the live configuration the tool will error during execution.")
123
+ continue
124
+
125
+ if conn.security_scheme == ConnectionSecurityScheme.KEY_VALUE:
126
+ logger.error(f"Key value application connections can not be bound to an openapi tool")
127
+ exit(1)
95
128
 
96
129
  def validate_params(kind: ToolKind, **args) -> None:
97
130
  if kind in {"openapi", "python"} and args["file"] is None:
@@ -157,7 +190,14 @@ def validate_python_connections(tool: BaseTool):
157
190
 
158
191
  provided_connections = list(connections.keys()) if connections else []
159
192
  imported_connections_list = connections_client.list()
160
- imported_connections = {conn.connection_id:conn for conn in imported_connections_list}
193
+ imported_connections = {}
194
+ for conn in imported_connections_list:
195
+ conn_id = conn.connection_id
196
+ conn_env = conn.environment
197
+ if conn_id in imported_connections:
198
+ imported_connections[conn_id][conn_env] = conn
199
+ else:
200
+ imported_connections[conn_id] = {conn_env: conn}
161
201
 
162
202
  validation_failed = False
163
203
 
@@ -186,15 +226,30 @@ def validate_python_connections(tool: BaseTool):
186
226
 
187
227
  connection_id = connections.get(sanatized_expected_tool_app_id)
188
228
  imported_connection = imported_connections.get(connection_id)
189
- imported_connection_auth_type = get_connection_type(security_scheme=imported_connection.security_scheme, auth_type=imported_connection.auth_type)
190
229
 
191
230
  if connection_id and not imported_connection:
192
231
  logger.error(f"The expected connection id '{connection_id}' does not match any known connection. This is likely caused by the connection being deleted. Please rec-reate the connection and re-import the tool")
193
232
  validation_failed = True
233
+
234
+ environments = _get_connection_environments()
235
+
236
+ for conn_environment in environments:
237
+ conn = imported_connection.get(conn_environment)
238
+ conn_identifier = conn.app_id if conn is not None else connection_id
239
+ if conn is None or conn.security_scheme is None:
240
+ message = f"Connection '{conn_identifier}' is not configured in the '{conn_environment}' environment."
241
+ if conn_environment == ConnectionEnvironment.DRAFT:
242
+ logger.error(message)
243
+ sys.exit(1)
244
+ else:
245
+ logger.warning(message + " If you deploy this tool without setting the live configuration the tool will error during execution.")
246
+ continue
194
247
 
195
- if imported_connection and len(expected_tool_conn_types) and imported_connection_auth_type not in expected_tool_conn_types:
196
- logger.error(f"The app-id '{imported_connection.app_id}' is of type '{imported_connection_auth_type.value}'. The tool '{tool.__tool_spec__.name}' accepts connections of the following types '{', '.join(expected_tool_conn_types)}'. Use `orchestrate connections list` to view current connections and use `orchestrate connections add` to create the relevent connection")
197
- validation_failed = True
248
+ imported_connection_auth_type = get_connection_type(security_scheme=conn.security_scheme, auth_type=conn.auth_type)
249
+
250
+ if conn and len(expected_tool_conn_types) and imported_connection_auth_type not in expected_tool_conn_types:
251
+ logger.error(f"The app-id '{conn.app_id}' is of type '{imported_connection_auth_type.value}' in the '{conn_environment}' environment. The tool '{tool.__tool_spec__.name}' accepts connections of the following types '{', '.join(expected_tool_conn_types)}'. Use `orchestrate connections list` to view current connections and use `orchestrate connections add` to create the relevent connection")
252
+ validation_failed = True
198
253
 
199
254
  if validation_failed:
200
255
  exit(1)
@@ -345,17 +400,12 @@ The [bold]flow tool[/bold] is being imported from [green]`{file}`[/green].
345
400
 
346
401
  [bold cyan]Additional information:[/bold cyan]
347
402
 
348
- - The [bold green]get_flow_status[/bold green] tool is being imported to support flow tools. To get a flow's current status, ensure [bold]both this tools and the one you are importing are added to your agent[/bold] to retrieve the flow output.
349
- - Include additional instructions in your agent to call the [bold green]get_flow_status[/bold green] tool to retrieve the flow output. For example: [green]"If you get an instance_id, use the tool get_flow_status to retrieve the current status of a flow."[/green]
403
+ - The [bold green]Get flow status[/bold green] tool is being imported to support flow tools. This tool can query the status of a flow tool instance. You can add it to your agent using the UI or including the following tool name in your agent definition: [green]i__get_flow_status_intrinsic_tool__[/green].
350
404
 
351
405
  """
352
406
 
353
407
  console.print(Panel(message, title="[bold blue]Flow tool support information[/bold blue]", border_style="bright_blue"))
354
408
 
355
-
356
- if not is_local_dev():
357
- raise typer.BadParameter(f"Flow tools are only supported in local environment.")
358
-
359
409
  model = None
360
410
 
361
411
  # Load the Flow JSON model from the file
@@ -422,7 +472,16 @@ The [bold]flow tool[/bold] is being imported from [green]`{file}`[/green].
422
472
  except Exception as e:
423
473
  raise typer.BadParameter(f"Failed to load model from file {file}: {e}")
424
474
 
425
- return await import_flow_model(model)
475
+ tool = create_flow_json_tool(name=model["spec"]["name"],
476
+ description=model["spec"]["description"],
477
+ permission="read_only",
478
+ flow_model=model)
479
+
480
+ tools = import_flow_support_tools()
481
+
482
+ tools.append(tool)
483
+
484
+ return tools
426
485
 
427
486
 
428
487
  async def import_openapi_tool(file: str, connection_id: str) -> List[BaseTool]:
@@ -503,7 +562,20 @@ class ToolsController:
503
562
 
504
563
  def list_tools(self, verbose=False):
505
564
  response = self.get_client().get()
506
- tool_specs = [ToolSpec.model_validate(tool) for tool in response]
565
+ tool_specs = []
566
+ parse_errors = []
567
+
568
+ for tool in response:
569
+ try:
570
+ tool_specs.append(ToolSpec.model_validate(tool))
571
+ except Exception as e:
572
+ name = tool.get('name', None)
573
+ parse_errors.append([
574
+ f"Tool '{name}' could not be parsed",
575
+ json.dumps(tool),
576
+ e
577
+ ])
578
+
507
579
  tools = [BaseTool(spec=spec) for spec in tool_specs]
508
580
 
509
581
  if verbose:
@@ -514,9 +586,16 @@ class ToolsController:
514
586
  rich.print(JSON(json.dumps(tools_list, indent=4)))
515
587
  else:
516
588
  table = rich.table.Table(show_header=True, header_style="bold white", show_lines=True)
517
- columns = ["Name", "Description", "Permission", "Type", "Toolkit", "App ID"]
518
- for column in columns:
519
- table.add_column(column)
589
+ column_args = {
590
+ "Name": {"overflow": "fold"},
591
+ "Description": {},
592
+ "Permission": {},
593
+ "Type": {},
594
+ "Toolkit": {},
595
+ "App ID": {"overflow": "fold"}
596
+ }
597
+ for column in column_args:
598
+ table.add_column(column,**column_args[column])
520
599
 
521
600
  connections_client = get_connections_client()
522
601
  connections = connections_client.list()
@@ -558,6 +637,8 @@ class ToolsController:
558
637
  tool_type=ToolKind.openapi
559
638
  elif tool_binding.mcp is not None:
560
639
  tool_type=ToolKind.mcp
640
+ elif tool_binding.flow is not None:
641
+ tool_type=ToolKind.flow
561
642
  else:
562
643
  tool_type="Unknown"
563
644
 
@@ -583,6 +664,10 @@ class ToolsController:
583
664
 
584
665
  rich.print(table)
585
666
 
667
+ for error in parse_errors:
668
+ for l in error:
669
+ logger.error(l)
670
+
586
671
  def get_all_tools(self) -> dict:
587
672
  return {entry["name"]: entry["id"] for entry in self.get_client().get()}
588
673
 
@@ -3,6 +3,74 @@ from typing_extensions import List, Optional
3
3
  from ibm_watsonx_orchestrate.client.utils import is_local_dev
4
4
  from pydantic import BaseModel
5
5
 
6
+ def transform_agents_from_flat_agent_spec(agents: dict | list[dict] ) -> dict | list[dict]:
7
+ if isinstance(agents,list):
8
+ new_agents = []
9
+ for agent in agents:
10
+ new_agents.append(_transform_agent_from_flat_agent_spec(agent))
11
+ agents = new_agents
12
+ else:
13
+ agents = _transform_agent_from_flat_agent_spec(agents)
14
+
15
+ return agents
16
+
17
+
18
+ def _transform_agent_from_flat_agent_spec(agent_spec: dict ) -> dict:
19
+ transformed = {"additional_properties": {}}
20
+ for key,value in agent_spec.items():
21
+ if key == "starter_prompts":
22
+ if value:
23
+ value.pop("is_default_prompts",None)
24
+ value["customize"] = value.pop("prompts", [])
25
+
26
+ transformed["additional_properties"] |= { key: value }
27
+
28
+ elif key == "welcome_content":
29
+ if value:
30
+ value.pop("is_default_message", None)
31
+
32
+ transformed["additional_properties"] |= { key: value }
33
+
34
+ else:
35
+ transformed |= { key: value }
36
+
37
+ return transformed
38
+
39
+ def transform_agents_to_flat_agent_spec(agents: dict | list[dict] ) -> dict | list[dict]:
40
+ if isinstance(agents,list):
41
+ new_agents = []
42
+ for agent in agents:
43
+ new_agents.append(_transform_agent_to_flat_agent_spec(agent))
44
+ agents = new_agents
45
+ else:
46
+ agents = _transform_agent_to_flat_agent_spec(agents)
47
+
48
+ return agents
49
+
50
+ def _transform_agent_to_flat_agent_spec(agent_spec: dict ) -> dict:
51
+ additional_properties = agent_spec.get("additional_properties", None)
52
+ if not additional_properties:
53
+ return agent_spec
54
+
55
+ transformed = agent_spec
56
+ for key,value in additional_properties.items():
57
+ if key == "starter_prompts":
58
+ if value:
59
+ value["is_default_prompts"] = False
60
+ value["prompts"] = value.pop("customize", [])
61
+
62
+ transformed[key] = value
63
+
64
+ elif key == "welcome_content":
65
+ if value:
66
+ value["is_default_message"] = False
67
+
68
+ transformed[key] = value
69
+
70
+ transformed.pop("additional_properties",None)
71
+
72
+ return transformed
73
+
6
74
  class AgentUpsertResponse(BaseModel):
7
75
  id: Optional[str] = None
8
76
  warning: Optional[str] = None
@@ -17,14 +85,14 @@ class AgentClient(BaseAPIClient):
17
85
 
18
86
 
19
87
  def create(self, payload: dict) -> AgentUpsertResponse:
20
- response = self._post(self.base_endpoint, data=payload)
88
+ response = self._post(self.base_endpoint, data=transform_agents_from_flat_agent_spec(payload))
21
89
  return AgentUpsertResponse.model_validate(response)
22
90
 
23
91
  def get(self) -> dict:
24
- return self._get(f"{self.base_endpoint}?include_hidden=true")
92
+ return transform_agents_to_flat_agent_spec(self._get(f"{self.base_endpoint}?include_hidden=true"))
25
93
 
26
94
  def update(self, agent_id: str, data: dict) -> AgentUpsertResponse:
27
- response = self._patch(f"{self.base_endpoint}/{agent_id}", data=data)
95
+ response = self._patch(f"{self.base_endpoint}/{agent_id}", data=transform_agents_from_flat_agent_spec(data))
28
96
  return AgentUpsertResponse.model_validate(response)
29
97
 
30
98
  def delete(self, agent_id: str) -> dict:
@@ -35,14 +103,14 @@ class AgentClient(BaseAPIClient):
35
103
 
36
104
  def get_drafts_by_names(self, agent_names: List[str]) -> List[dict]:
37
105
  formatted_agent_names = [f"names={x}" for x in agent_names]
38
- return self._get(f"{self.base_endpoint}?{'&'.join(formatted_agent_names)}&include_hidden=true")
106
+ return transform_agents_to_flat_agent_spec(self._get(f"{self.base_endpoint}?{'&'.join(formatted_agent_names)}&include_hidden=true"))
39
107
 
40
108
  def get_draft_by_id(self, agent_id: str) -> List[dict]:
41
109
  if agent_id is None:
42
110
  return ""
43
111
  else:
44
112
  try:
45
- agent = self._get(f"{self.base_endpoint}/{agent_id}")
113
+ agent = transform_agents_to_flat_agent_spec(self._get(f"{self.base_endpoint}/{agent_id}"))
46
114
  return agent
47
115
  except ClientAPIException as e:
48
116
  if e.response.status_code == 404 and "not found with the given name" in e.response.text:
@@ -51,5 +119,5 @@ class AgentClient(BaseAPIClient):
51
119
 
52
120
  def get_drafts_by_ids(self, agent_ids: List[str]) -> List[dict]:
53
121
  formatted_agent_ids = [f"ids={x}" for x in agent_ids]
54
- return self._get(f"{self.base_endpoint}?{'&'.join(formatted_agent_ids)}&include_hidden=true")
122
+ return transform_agents_to_flat_agent_spec(self._get(f"{self.base_endpoint}?{'&'.join(formatted_agent_ids)}&include_hidden=true"))
55
123
 
@@ -36,6 +36,8 @@ class BaseAPIClient:
36
36
 
37
37
  if not self.is_local:
38
38
  self.base_url = f"{self.base_url}/v1/orchestrate"
39
+ else:
40
+ self.base_url = f"{self.base_url}/v1"
39
41
 
40
42
  def _get_headers(self) -> dict:
41
43
  headers = {}
@@ -46,7 +48,6 @@ class BaseAPIClient:
46
48
  return headers
47
49
 
48
50
  def _get(self, path: str, params: dict = None, data=None, return_raw=False) -> dict:
49
-
50
51
  url = f"{self.base_url}{path}"
51
52
  response = requests.get(url, headers=self._get_headers(), params=params, data=data, verify=self.verify)
52
53
  self._check_response(response)
@@ -1,11 +1,12 @@
1
1
  from typing import List
2
2
 
3
+ from ibm_cloud_sdk_core.authenticators import MCSPAuthenticator
3
4
  from pydantic import BaseModel, ValidationError
4
5
  from typing import Optional
5
6
 
6
7
  from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient, ClientAPIException
7
8
  from ibm_watsonx_orchestrate.agent_builder.connections.types import ConnectionEnvironment, ConnectionPreference, ConnectionAuthType, ConnectionSecurityScheme, IdpConfigData, AppConfigData, ConnectionType
8
- from ibm_watsonx_orchestrate.client.utils import is_cpd_env
9
+ from ibm_watsonx_orchestrate.client.utils import is_cpd_env, is_local_dev
9
10
 
10
11
  import logging
11
12
  logger = logging.getLogger(__name__)
@@ -42,6 +43,12 @@ class GetConnectionResponse(BaseModel):
42
43
 
43
44
 
44
45
  class ConnectionsClient(BaseAPIClient):
46
+ def __init__(self, base_url: str, api_key: str = None, is_local: bool = False, verify: str = None, authenticator: MCSPAuthenticator = None):
47
+ super(ConnectionsClient, self).__init__(base_url, api_key, is_local, verify, authenticator)
48
+ if is_local_dev(base_url):
49
+ self.base_url = f"{base_url.rstrip('/')}/api/v1/orchestrate"
50
+ else:
51
+ self.base_url = f"{base_url.rstrip('/')}/v1/orchestrate"
45
52
  """
46
53
  Client to handle CRUD operations for Connections endpoint
47
54
  """
@@ -77,6 +84,8 @@ class ConnectionsClient(BaseAPIClient):
77
84
  else f"/connections/applications?include_details=true"
78
85
  )
79
86
  res = self._get(path)
87
+ import json
88
+ json.dumps(res)
80
89
  return [ListConfigsResponse.model_validate(conn) for conn in res.get("applications", [])]
81
90
  except ValidationError as e:
82
91
  logger.error("Recieved unexpected response from server")
@@ -107,25 +116,25 @@ class ConnectionsClient(BaseAPIClient):
107
116
 
108
117
  # POST /api/v1/connections/applications/{app_id}/configs/{env}/credentials
109
118
  # POST /api/v1/connections/applications/{app_id}/configs/{env}/runtime_credentials
110
- def create_credentials(self, app_id: str, env: ConnectionEnvironment, payload: dict, use_sso: bool) -> None:
111
- if use_sso:
119
+ def create_credentials(self, app_id: str, env: ConnectionEnvironment, payload: dict, use_app_credentials: bool) -> None:
120
+ if use_app_credentials:
112
121
  self._post(f"/connections/applications/{app_id}/configs/{env}/credentials", data=payload)
113
122
  else:
114
123
  self._post(f"/connections/applications/{app_id}/configs/{env}/runtime_credentials", data=payload)
115
124
 
116
125
  # PATCH /api/v1/connections/applications/{app_id}/configs/{env}/credentials
117
126
  # PATCH /api/v1/connections/applications/{app_id}/configs/{env}/runtime_credentials
118
- def update_credentials(self, app_id: str, env: ConnectionEnvironment, payload: dict, use_sso: bool) -> None:
119
- if use_sso:
127
+ def update_credentials(self, app_id: str, env: ConnectionEnvironment, payload: dict, use_app_credentials: bool) -> None:
128
+ if use_app_credentials:
120
129
  self._patch(f"/connections/applications/{app_id}/configs/{env}/credentials", data=payload)
121
130
  else:
122
131
  self._patch(f"/connections/applications/{app_id}/configs/{env}/runtime_credentials", data=payload)
123
132
 
124
133
  # GET /api/v1/connections/applications/{app_id}/configs/credentials?env={env}
125
134
  # GET /api/v1/connections/applications/{app_id}/configs/runtime_credentials?env={env}
126
- def get_credentials(self, app_id: str, env: ConnectionEnvironment, use_sso: bool) -> dict:
135
+ def get_credentials(self, app_id: str, env: ConnectionEnvironment, use_app_credentials: bool) -> dict:
127
136
  try:
128
- if use_sso:
137
+ if use_app_credentials:
129
138
  path = (
130
139
  f"/connections/applications/{app_id}/credentials?env={env}"
131
140
  if is_cpd_env(self.base_url)
@@ -146,8 +155,8 @@ class ConnectionsClient(BaseAPIClient):
146
155
 
147
156
  # DELETE /api/v1/connections/applications/{app_id}/configs/{env}/credentials
148
157
  # DELETE /api/v1/connections/applications/{app_id}/configs/{env}/runtime_credentials
149
- def delete_credentials(self, app_id: str, env: ConnectionEnvironment, use_sso: bool) -> None:
150
- if use_sso:
158
+ def delete_credentials(self, app_id: str, env: ConnectionEnvironment, use_app_credentials: bool) -> None:
159
+ if use_app_credentials:
151
160
  self._delete(f"/connections/applications/{app_id}/configs/{env}/credentials")
152
161
  else:
153
162
  self._delete(f"/connections/applications/{app_id}/configs/{env}/runtime_credentials")
@@ -17,14 +17,16 @@ def _get_connections_manager_url() -> str:
17
17
  url_parts = url.split(":")
18
18
  url_parts[-1] = str(LOCAL_CONNECTION_MANAGER_PORT)
19
19
  url = ":".join(url_parts)
20
- url = url + "/api/v1/orchestrate"
21
20
  return url
22
21
  return None
23
22
 
24
23
  def get_connections_client() -> ConnectionsClient:
25
24
  return instantiate_client(client=ConnectionsClient, url=_get_connections_manager_url())
26
25
 
27
- def get_connection_type(security_scheme: ConnectionSecurityScheme, auth_type: ConnectionAuthType) -> ConnectionType:
26
+ def get_connection_type(security_scheme: ConnectionSecurityScheme, auth_type: ConnectionAuthType) -> ConnectionType | None:
27
+ if security_scheme is None and auth_type is None:
28
+ return None
29
+
28
30
  if security_scheme != ConnectionSecurityScheme.OAUTH2:
29
31
  return ConnectionType(security_scheme)
30
32
  return ConnectionType(auth_type)
@@ -14,7 +14,7 @@ DEFAULT_TENANT = {
14
14
  DEFAULT_USER = {"username": "wxo.archer@ibm.com", "password": "watsonx"}
15
15
  DEFAULT_LOCAL_SERVICE_URL = "http://localhost:4321"
16
16
  DEFAULT_LOCAL_AUTH_ENDPOINT = f"{DEFAULT_LOCAL_SERVICE_URL}/api/v1/auth/token"
17
- DEFAULT_LOCAL_TENANT_URL = f"{DEFAULT_LOCAL_SERVICE_URL}/tenants"
17
+ DEFAULT_LOCAL_TENANT_URL = f"{DEFAULT_LOCAL_SERVICE_URL}/api/v1/tenants"
18
18
  DEFAULT_LOCAL_TENANT_AUTH_ENDPOINT = "{}/api/v1/auth/token?tenant_id={}"
19
19
 
20
20
 
@@ -51,13 +51,13 @@ class ServiceInstance(BaseServiceInstance):
51
51
  def _create_token(self) -> str:
52
52
  if not self._credentials.auth_type:
53
53
  if ".cloud.ibm.com" in self._credentials.url:
54
- logger.warning("Using IBM IAM Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of 'ibm_iam' or 'mscp' or 'cpd")
54
+ logger.warning("Using IBM IAM Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of 'ibm_iam' or 'mcsp' or 'cpd")
55
55
  return self._authenticate(EnvironmentAuthType.IBM_CLOUD_IAM)
56
56
  elif is_cpd_env(self._credentials.url):
57
- logger.warning("Using CPD Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of 'ibm_iam' or 'mscp' or 'cpd")
57
+ logger.warning("Using CPD Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of 'ibm_iam' or 'mcsp' or 'cpd")
58
58
  return self._authenticate(EnvironmentAuthType.CPD)
59
59
  else:
60
- logger.warning("Using MCSP Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of 'ibm_iam' or 'mscp' or 'cpd' ")
60
+ logger.warning("Using MCSP Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of 'ibm_iam' or 'mcsp' or 'cpd' ")
61
61
  return self._authenticate(EnvironmentAuthType.MCSP)
62
62
  else:
63
63
  return self._authenticate(self._credentials.auth_type)
@@ -29,12 +29,17 @@ class TempusClient(BaseAPIClient):
29
29
  authenticator=authenticator
30
30
  )
31
31
 
32
+ def get_tempus_endpoint(self) -> str:
33
+ """
34
+ Returns the Tempus endpoint URL
35
+ """
36
+ return self.base_url
32
37
  def create_update_flow_model(self, flow_id: str, model: dict) -> dict:
33
- return self._post(f"/v1/flow-models/{flow_id}", data=model)
38
+ return self._post(f"/flow-models/{flow_id}", data=model)
34
39
 
35
40
  def run_flow(self, flow_id: str, input: dict) -> dict:
36
- return self._post(f"/v1/flows/{flow_id}/versions/TIP/run", data=input)
41
+ return self._post(f"/flows/{flow_id}/versions/TIP/run", data=input)
37
42
 
38
43
  def arun_flow(self, flow_id: str, input: dict) -> dict:
39
- return self._post(f"/v1/flows/{flow_id}/versions/TIP/run/async", data=input)
44
+ return self._post(f"/flows/{flow_id}/versions/TIP/run/async", data=input)
40
45
 
@@ -47,6 +47,16 @@ def is_local_dev(url: str | None = None) -> bool:
47
47
 
48
48
  return False
49
49
 
50
+ def is_ibm_cloud():
51
+ cfg = Config()
52
+ active_env = cfg.read(CONTEXT_SECTION_HEADER, CONTEXT_ACTIVE_ENV_OPT)
53
+ url = cfg.get(ENVIRONMENTS_SECTION_HEADER, active_env, ENV_WXO_URL_OPT)
54
+
55
+ if url.__contains__("cloud.ibm.com"):
56
+ return True
57
+ return False
58
+
59
+
50
60
  def is_cpd_env(url: str) -> bool:
51
61
  if url.lower().startswith("https://cpd"):
52
62
  return True