ibm-watsonx-orchestrate 1.1.0__py3-none-any.whl → 1.2.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.
@@ -5,7 +5,7 @@
5
5
 
6
6
  pkg_name = "ibm-watsonx-orchestrate"
7
7
 
8
- __version__ = "1.1.0"
8
+ __version__ = "1.2.0"
9
9
 
10
10
 
11
11
 
@@ -1,5 +1,5 @@
1
1
  from enum import Enum
2
- from typing import List, Any, Dict, Literal, Optional
2
+ from typing import List, Any, Dict, Literal, Optional, Union
3
3
 
4
4
  from pydantic import BaseModel, model_validator, ConfigDict, Field, AliasChoices
5
5
 
@@ -14,7 +14,7 @@ class ToolPermission(str, Enum):
14
14
  class JsonSchemaObject(BaseModel):
15
15
  model_config = ConfigDict(extra='allow')
16
16
 
17
- type: Optional[Literal['object', 'string', 'number', 'integer', 'boolean', 'array', 'null']] = None
17
+ type: Optional[Union[Literal['object', 'string', 'number', 'integer', 'boolean', 'array', 'null'], List[Literal['object', 'string', 'number', 'integer', 'boolean', 'array', 'null']]]] = None
18
18
  title: str | None = None
19
19
  description: str | None = None
20
20
  properties: Optional[Dict[str, 'JsonSchemaObject']] = None
@@ -34,13 +34,19 @@ class JsonSchemaObject(BaseModel):
34
34
  aliasName: str | None = None
35
35
  "Runtime feature where the sdk can provide the original name of a field before prefixing"
36
36
 
37
+ @model_validator(mode='after')
38
+ def normalize_type_field(self) -> 'JsonSchemaObject':
39
+ if isinstance(self.type, list):
40
+ self.type = self.type[0]
41
+ return self
42
+
37
43
 
38
44
  class ToolRequestBody(BaseModel):
39
45
  model_config = ConfigDict(extra='allow')
40
46
 
41
47
  type: Literal['object']
42
48
  properties: Dict[str, JsonSchemaObject]
43
- required: List[str]
49
+ required: Optional[List[str]] = None
44
50
 
45
51
 
46
52
  class ToolResponseBody(BaseModel):
@@ -52,7 +58,7 @@ class ToolResponseBody(BaseModel):
52
58
  items: JsonSchemaObject = None
53
59
  uniqueItems: bool = None
54
60
  anyOf: List['JsonSchemaObject'] = None
55
- required: List[str] = None
61
+ required: Optional[List[str]] = None
56
62
 
57
63
  class OpenApiSecurityScheme(BaseModel):
58
64
  type: Literal['apiKey', 'http', 'oauth2', 'openIdConnect']
@@ -128,6 +134,10 @@ class SkillToolBinding(BaseModel):
128
134
  class ClientSideToolBinding(BaseModel):
129
135
  pass
130
136
 
137
+ class McpToolBinding(BaseModel):
138
+ server_url: Optional[str] = None
139
+ source: str
140
+ connections: Dict[str, str]
131
141
 
132
142
  class ToolBinding(BaseModel):
133
143
  openapi: OpenApiToolBinding = None
@@ -135,6 +145,7 @@ class ToolBinding(BaseModel):
135
145
  wxflows: WxFlowsToolBinding = None
136
146
  skill: SkillToolBinding = None
137
147
  client_side: ClientSideToolBinding = None
148
+ mcp: McpToolBinding = None
138
149
 
139
150
  @model_validator(mode='after')
140
151
  def validate_binding_type(self) -> 'ToolBinding':
@@ -143,7 +154,8 @@ class ToolBinding(BaseModel):
143
154
  self.python is not None,
144
155
  self.wxflows is not None,
145
156
  self.skill is not None,
146
- self.client_side is not None
157
+ self.client_side is not None,
158
+ self.mcp is not None
147
159
  ]
148
160
  if sum(bindings) == 0:
149
161
  raise ValueError("One binding must be set")
@@ -159,4 +171,5 @@ class ToolSpec(BaseModel):
159
171
  input_schema: ToolRequestBody = None
160
172
  output_schema: ToolResponseBody = None
161
173
  binding: ToolBinding = None
174
+ toolkit_id: str | None = None
162
175
 
@@ -0,0 +1,71 @@
1
+ import typer
2
+ from typing import List
3
+ from typing_extensions import Annotated, Optional
4
+ from ibm_watsonx_orchestrate.cli.commands.toolkit.toolkit_controller import ToolkitController, ToolkitKind
5
+
6
+ toolkits_app = typer.Typer(no_args_is_help=True)
7
+
8
+ @toolkits_app.command(name="import")
9
+ def import_toolkit(
10
+ kind: Annotated[
11
+ ToolkitKind,
12
+ typer.Option("--kind", "-k", help="Kind of toolkit, currently only MCP is supported"),
13
+ ],
14
+ name: Annotated[
15
+ str,
16
+ typer.Option("--name", "-n", help="Name of the toolkit"),
17
+ ],
18
+ description: Annotated[
19
+ str,
20
+ typer.Option("--description", help="Description of the toolkit"),
21
+ ],
22
+ package_root: Annotated[
23
+ str,
24
+ typer.Option("--package-root", "-p", help="Root directory of the MCP server package"),
25
+ ],
26
+ command: Annotated[
27
+ str,
28
+ typer.Option(
29
+ "--command",
30
+ help="Command to start the MCP server. Can be a string (e.g. 'node dist/index.js --transport stdio') "
31
+ "or a JSON-style list of arguments (e.g. '[\"node\", \"dist/index.js\", \"--transport\", \"stdio\"]'). "
32
+ "The first argument will be used as the executable, the rest as its arguments."
33
+ ),
34
+ ],
35
+ tools: Annotated[
36
+ Optional[str],
37
+ typer.Option("--tools", "-t", help="Comma-separated list of tools to import. Or you can use `*` to use all tools"),
38
+ ] = None,
39
+ app_id: Annotated[
40
+ List[str],
41
+ typer.Option(
42
+ "--app-id", "-a",
43
+ help='The app id of the connection to associate with this tool. A application connection represents the server authentication credentials needed to connect to this tool. Only type key_value is currently supported for MCP.'
44
+ )
45
+ ] = None
46
+ ):
47
+ if tools == "*":
48
+ tool_list = ["*"] # Wildcard to use all tools
49
+ elif tools:
50
+ tool_list = [tool.strip() for tool in tools.split(",")]
51
+ else:
52
+ tool_list = None
53
+
54
+ toolkit_controller = ToolkitController(
55
+ kind=kind,
56
+ name=name,
57
+ description=description,
58
+ package_root=package_root,
59
+ command=command,
60
+ )
61
+ toolkit_controller.import_toolkit(tools=tool_list, app_id=app_id)
62
+
63
+ @toolkits_app.command(name="remove")
64
+ def remove_toolkit(
65
+ name: Annotated[
66
+ str,
67
+ typer.Option("--name", "-n", help="Name of the toolkit you wish to remove"),
68
+ ],
69
+ ):
70
+ toolkit_controller = ToolkitController()
71
+ toolkit_controller.remove_toolkit(name=name)
@@ -0,0 +1,212 @@
1
+ import os
2
+ import zipfile
3
+ import tempfile
4
+ from typing import List, Optional
5
+ from enum import Enum
6
+ import logging
7
+ import sys
8
+ import re
9
+ import requests
10
+ from ibm_watsonx_orchestrate.client.toolkit.toolkit_client import ToolKitClient
11
+ from ibm_watsonx_orchestrate.client.utils import instantiate_client
12
+ from ibm_watsonx_orchestrate.utils.utils import sanatize_app_id
13
+ from ibm_watsonx_orchestrate.client.connections import get_connections_client
14
+ import typer
15
+ import json
16
+ from rich.console import Console
17
+ from rich.progress import Progress, SpinnerColumn, TextColumn
18
+ from ibm_watsonx_orchestrate.client.utils import is_local_dev
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ class ToolkitKind(str, Enum):
23
+ MCP = "mcp"
24
+
25
+ def get_connection_id(app_id: str) -> str:
26
+ connections_client = get_connections_client()
27
+ existing_draft_configuration = connections_client.get_config(app_id=app_id, env='draft')
28
+ existing_live_configuration = connections_client.get_config(app_id=app_id, env='live')
29
+
30
+ for config in [existing_draft_configuration, existing_live_configuration]:
31
+ if config and config.security_scheme != 'key_value_creds':
32
+ logger.error("Only key_value credentials are currently supported")
33
+ exit(1)
34
+ connection_id = None
35
+ if app_id is not None:
36
+ connection = connections_client.get(app_id=app_id)
37
+ if not connection:
38
+ logger.error(f"No connection exists with the app-id '{app_id}'")
39
+ exit(1)
40
+ connection_id = connection.connection_id
41
+ return connection_id
42
+
43
+ def validate_params(kind: str):
44
+ if kind != ToolkitKind.MCP:
45
+ raise ValueError(f"Unsupported toolkit kind: {kind}")
46
+
47
+
48
+ class ToolkitController:
49
+ def __init__(
50
+ self,
51
+ kind: ToolkitKind = None,
52
+ name: str = None,
53
+ description: str = None,
54
+ package_root: str = None,
55
+ command: str = None,
56
+ ):
57
+ self.kind = kind
58
+ self.name = name
59
+ self.description = description
60
+ self.package_root = package_root
61
+ self.command = command
62
+ self.client = None
63
+
64
+ def get_client(self) -> ToolKitClient:
65
+ if not self.client:
66
+ self.client = instantiate_client(ToolKitClient)
67
+ return self.client
68
+
69
+ def import_toolkit(self, tools: Optional[List[str]] = None, app_id: Optional[List[str]] = None):
70
+ if not is_local_dev():
71
+ logger.error("This functionality is only available for Local Environments")
72
+ sys.exit(1)
73
+
74
+ if app_id and isinstance(app_id, str):
75
+ app_id = [app_id]
76
+ elif not app_id:
77
+ app_id = []
78
+
79
+ validate_params(kind=self.kind)
80
+
81
+ remapped_connections = self._remap_connections(app_id)
82
+
83
+ client = self.get_client()
84
+ draft_toolkits = client.get_draft_by_name(toolkit_name=self.name)
85
+ if len(draft_toolkits) > 0:
86
+ logger.error(f"Existing toolkit found with name '{self.name}'. Failed to create toolkit.")
87
+ sys.exit(1)
88
+
89
+ with tempfile.TemporaryDirectory() as tmpdir:
90
+ # Handle zip file or directory
91
+ if self.package_root.endswith(".zip") and os.path.isfile(self.package_root):
92
+ zip_file_path = self.package_root
93
+ else:
94
+ zip_file_path = os.path.join(tmpdir, os.path.basename(f"{self.package_root.rstrip(os.sep)}.zip"))
95
+ with zipfile.ZipFile(zip_file_path, "w", zipfile.ZIP_DEFLATED) as mcp_zip_tool_artifacts:
96
+ self._populate_zip(self.package_root, mcp_zip_tool_artifacts)
97
+
98
+ try:
99
+ command_parts = json.loads(self.command)
100
+ if not isinstance(command_parts, list):
101
+ raise ValueError("JSON command must be a list of strings")
102
+ except (json.JSONDecodeError, ValueError):
103
+ command_parts = self.command.split()
104
+
105
+ command = command_parts[0]
106
+ args = command_parts[1:]
107
+
108
+ console = Console()
109
+ # List tools if not provided
110
+ if tools is None:
111
+ with Progress(
112
+ SpinnerColumn(spinner_name="dots"),
113
+ TextColumn("[progress.description]{task.description}"),
114
+ transient=True,
115
+ console=console,
116
+ ) as progress:
117
+ progress.add_task(description="No tools specified, retrieving all tools from provided MCP server", total=None)
118
+ tools = self.get_client().list_tools(
119
+ zip_file_path=zip_file_path,
120
+ command=command,
121
+ args=args,
122
+ )
123
+ # Normalize tools to a list of tool names
124
+ tools = [
125
+ tool["name"] if isinstance(tool, dict) and "name" in tool else tool
126
+ for tool in tools
127
+ ]
128
+
129
+
130
+ logger.info("✅ The following tools will be imported:")
131
+ for tool in tools:
132
+ console.print(f" • {tool}")
133
+
134
+
135
+ # Create toolkit metadata
136
+ payload = {
137
+ "name": self.name,
138
+ "description": self.description,
139
+ "mcp": {
140
+ "source": "files",
141
+ "command": command,
142
+ "args": args,
143
+ "tools": tools,
144
+ "connections": remapped_connections,
145
+ }
146
+ }
147
+ toolkit = self.get_client().create_toolkit(payload)
148
+ toolkit_id = toolkit["id"]
149
+
150
+ console = Console()
151
+ # Upload zip file
152
+ with Progress(
153
+ SpinnerColumn(spinner_name="dots"),
154
+ TextColumn("[progress.description]{task.description}"),
155
+ transient=True,
156
+ console=console,
157
+ ) as progress:
158
+ progress.add_task(description="Uploading toolkit zip file...", total=None)
159
+ self.get_client().upload(toolkit_id=toolkit_id, zip_file_path=zip_file_path)
160
+ logger.info(f"Successfully imported tool kit {self.name}")
161
+
162
+ def _populate_zip(self, package_root: str, zipfile: zipfile.ZipFile) -> str:
163
+ for root, _, files in os.walk(package_root):
164
+ for file in files:
165
+ full_path = os.path.join(root, file)
166
+ relative_path = os.path.relpath(full_path, start=package_root)
167
+ zipfile.write(full_path, arcname=relative_path)
168
+ return zipfile
169
+
170
+ def _remap_connections(self, app_ids: List[str]):
171
+ app_id_dict = {}
172
+ for app_id in app_ids:
173
+ split_pattern = re.compile(r"(?<!\\)=")
174
+ split_id = re.split(split_pattern, app_id)
175
+ split_id = [x.replace("\\=", "=") for x in split_id]
176
+ if len(split_id) == 2:
177
+ runtime_id, local_id = split_id
178
+ elif len(split_id) == 1:
179
+ runtime_id = split_id[0]
180
+ local_id = split_id[0]
181
+ else:
182
+ raise typer.BadParameter(f"The provided --app-id '{app_id}' is not valid. This is likely caused by having mutliple equal signs, please use '\\=' to represent a literal '=' character")
183
+
184
+ if not len(runtime_id.strip()) or not len(local_id.strip()):
185
+ raise typer.BadParameter(f"The provided --app-id '{app_id}' is not valid. --app-id cannot be empty or whitespace")
186
+
187
+ runtime_id = sanatize_app_id(runtime_id)
188
+ app_id_dict[runtime_id] = get_connection_id(local_id)
189
+
190
+ return app_id_dict
191
+
192
+
193
+ def remove_toolkit(self, name: str):
194
+ if not is_local_dev():
195
+ logger.error("This functionality is only available for Local Environments")
196
+ sys.exit(1)
197
+ try:
198
+ client = self.get_client()
199
+ draft_toolkits = client.get_draft_by_name(toolkit_name=name)
200
+ if len(draft_toolkits) > 1:
201
+ logger.error(f"Multiple existing toolkits found with name '{name}'. Failed to remove toolkit")
202
+ sys.exit(1)
203
+ if len(draft_toolkits) > 0:
204
+ draft_toolkit = draft_toolkits[0]
205
+ toolkit_id = draft_toolkit.get("id")
206
+ self.get_client().delete(toolkit_id=toolkit_id)
207
+ logger.info(f"Successfully removed tool {name}")
208
+ else:
209
+ logger.warning(f"No toolkit named '{name}' found")
210
+ except requests.HTTPError as e:
211
+ logger.error(e.response.text)
212
+ exit(1)
@@ -27,8 +27,9 @@ from ibm_watsonx_orchestrate.cli.config import Config, CONTEXT_SECTION_HEADER, C
27
27
  DEFAULT_CONFIG_FILE_CONTENT
28
28
  from ibm_watsonx_orchestrate.agent_builder.connections import ConnectionSecurityScheme, ExpectedCredentials
29
29
  from ibm_watsonx_orchestrate.client.tools.tool_client import ToolClient
30
+ from ibm_watsonx_orchestrate.client.toolkit.toolkit_client import ToolKitClient
30
31
  from ibm_watsonx_orchestrate.client.connections import get_connections_client, get_connection_type
31
- from ibm_watsonx_orchestrate.client.utils import instantiate_client
32
+ from ibm_watsonx_orchestrate.client.utils import instantiate_client, is_local_dev
32
33
  from ibm_watsonx_orchestrate.utils.utils import sanatize_app_id
33
34
 
34
35
  from ibm_watsonx_orchestrate import __version__
@@ -41,6 +42,7 @@ __supported_characters_pattern = re.compile("^(\\w|_)+$")
41
42
  class ToolKind(str, Enum):
42
43
  openapi = "openapi"
43
44
  python = "python"
45
+ mcp = "mcp"
44
46
  # skill = "skill"
45
47
 
46
48
  def validate_app_ids(kind: ToolKind, **args) -> None:
@@ -368,21 +370,24 @@ class ToolsController:
368
370
  response = self.get_client().get()
369
371
  tool_specs = [ToolSpec.model_validate(tool) for tool in response]
370
372
  tools = [BaseTool(spec=spec) for spec in tool_specs]
371
-
372
373
 
373
374
  if verbose:
374
375
  tools_list = []
375
376
  for tool in tools:
376
-
377
377
  tools_list.append(json.loads(tool.dumps_spec()))
378
378
 
379
379
  rich.print(JSON(json.dumps(tools_list, indent=4)))
380
380
  else:
381
381
  table = rich.table.Table(show_header=True, header_style="bold white", show_lines=True)
382
- columns = ["Name", "Description", "Permission", "Type", "App ID"]
382
+ columns = ["Name", "Description", "Permission", "Type", "Toolkit", "App ID"]
383
383
  for column in columns:
384
384
  table.add_column(column)
385
-
385
+
386
+ connections_client = get_connections_client()
387
+ connections = connections_client.list()
388
+
389
+ connections_dict = {conn.connection_id: conn for conn in connections}
390
+
386
391
  for tool in tools:
387
392
  tool_binding = tool.__tool_spec__.binding
388
393
 
@@ -394,25 +399,49 @@ class ToolsController:
394
399
  elif tool_binding.python is not None and hasattr(tool_binding.python, "connections") and tool_binding.python.connections is not None:
395
400
  for conn in tool_binding.python.connections:
396
401
  connection_ids.append(tool_binding.python.connections[conn])
397
-
398
- connections_client = get_connections_client()
402
+ elif tool_binding.mcp is not None and hasattr(tool_binding.mcp, "connections"):
403
+ for conn in tool_binding.mcp.connections:
404
+ connection_ids.append(tool_binding.mcp.connections[conn])
405
+
406
+
399
407
  app_ids = []
400
408
  for connection_id in connection_ids:
401
- app_id = str(connections_client.get_draft_by_id(connection_id))
409
+ connection = connections_dict.get(connection_id)
410
+ if connection:
411
+ app_id = str(connection.app_id or connection.connection_id)
412
+ elif connection_id:
413
+ app_id = str(connection_id)
414
+ else:
415
+ app_id = ""
402
416
  app_ids.append(app_id)
403
417
 
404
418
  if tool_binding.python is not None:
405
419
  tool_type=ToolKind.python
406
420
  elif tool_binding.openapi is not None:
407
421
  tool_type=ToolKind.openapi
422
+ elif tool_binding.mcp is not None:
423
+ tool_type=ToolKind.mcp
408
424
  else:
409
425
  tool_type="Unknown"
410
426
 
427
+ toolkit_name = ""
428
+
429
+ if is_local_dev():
430
+ toolkit_client = instantiate_client(ToolKitClient)
431
+ if tool.__tool_spec__.toolkit_id:
432
+ toolkit = toolkit_client.get_draft_by_id(tool.__tool_spec__.toolkit_id)
433
+ if isinstance(toolkit, dict) and "name" in toolkit:
434
+ toolkit_name = toolkit["name"]
435
+ elif toolkit:
436
+ toolkit_name = str(toolkit)
437
+
438
+
411
439
  table.add_row(
412
440
  tool.__tool_spec__.name,
413
441
  tool.__tool_spec__.description,
414
442
  tool.__tool_spec__.permission,
415
443
  tool_type,
444
+ toolkit_name,
416
445
  ", ".join(app_ids),
417
446
  )
418
447
 
@@ -11,6 +11,7 @@ from ibm_watsonx_orchestrate.cli.commands.models.models_command import models_ap
11
11
  from ibm_watsonx_orchestrate.cli.commands.environment.environment_command import environment_app
12
12
  from ibm_watsonx_orchestrate.cli.commands.channels.channels_command import channel_app
13
13
  from ibm_watsonx_orchestrate.cli.commands.knowledge_bases.knowledge_bases_command import knowledge_bases_app
14
+ from ibm_watsonx_orchestrate.cli.commands.toolkit.toolkit_command import toolkits_app
14
15
 
15
16
  app = typer.Typer(
16
17
  no_args_is_help=True,
@@ -20,10 +21,11 @@ app.add_typer(login_app)
20
21
  app.add_typer(environment_app, name="env", help='Add, remove, or select the activate env other commands will interact with (either your local server or a production instance)')
21
22
  app.add_typer(agents_app, name="agents", help='Interact with the agents in your active env')
22
23
  app.add_typer(tools_app, name="tools", help='Interact with the tools in your active env')
24
+ app.add_typer(toolkits_app, name="toolkits", help="Interact with the toolkits in your active env")
23
25
  app.add_typer(knowledge_bases_app, name="knowledge-bases", help="Upload knowledge your agents can search through to your active env")
24
26
  app.add_typer(connections_app, name="connections", help='Interact with the agents in your active env')
25
- app.add_typer(server_app, name="server", help='Manipulate your local Orchestrate Developer Edition server [requires an Entitlement]')
26
- app.add_typer(chat_app, name="chat", help='Launch the chat ui for your local Developer Edition server [requires docker pull credentials]')
27
+ app.add_typer(server_app, name="server", help='Manipulate your local Orchestrate Developer Edition server [requires entitlement]')
28
+ app.add_typer(chat_app, name="chat", help='Launch the chat ui for your local Developer Edition server [requires entitlement]')
27
29
  app.add_typer(models_app, name="models", help='List the available large language models (llms) that can be used in your agent definitions')
28
30
  app.add_typer(channel_app, name="channels", help="Configure channels where your agent can exist on (such as embedded webchat)")
29
31
  app.add_typer(settings_app, name="settings", help='Configure the settings for your active env')
@@ -0,0 +1,81 @@
1
+ from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient, ClientAPIException
2
+ from typing_extensions import List
3
+ import os
4
+ import json
5
+
6
+ class ToolKitClient(BaseAPIClient):
7
+ # POST /toolkits/prepare/list-tools
8
+ def list_tools(self, zip_file_path: str, command: str, args: List[str]) -> List[str]:
9
+ """
10
+ List the available tools inside the MCP server
11
+ """
12
+
13
+ filename = os.path.basename(zip_file_path)
14
+
15
+ list_toolkit_obj = {
16
+ "source": "files",
17
+ "command": command,
18
+ "args": args,
19
+ }
20
+
21
+ with open(zip_file_path, "rb") as f:
22
+ files = {
23
+ "list_toolkit_obj": (None, json.dumps(list_toolkit_obj), "application/json"),
24
+ "file": (filename, f, "application/zip"),
25
+ }
26
+
27
+ response = self._post("/orchestrate/toolkits/prepare/list-tools", files=files)
28
+
29
+ return response.get("tools", [])
30
+
31
+
32
+ # POST /api/v1/orchestrate/toolkits
33
+ def create_toolkit(self, payload) -> dict:
34
+ """
35
+ Creates new toolkit metadata
36
+ """
37
+ try:
38
+ return self._post("/orchestrate/toolkits", data=payload)
39
+ except ClientAPIException as e:
40
+ if e.response.status_code == 400 and "already exists" in e.response.text:
41
+ raise ClientAPIException(
42
+ status_code=400,
43
+ message=f"There is already a Toolkit with the same name that exists for this tenant."
44
+ )
45
+ raise(e)
46
+
47
+ # POST /toolkits/{toolkit-id}/upload
48
+ def upload(self, toolkit_id: str, zip_file_path: str) -> dict:
49
+ """
50
+ Upload zip file to the toolkit.
51
+ """
52
+ filename = os.path.basename(zip_file_path)
53
+ with open(zip_file_path, "rb") as f:
54
+ files = {
55
+ "file": (filename, f, "application/zip", {"Expires": "0"})
56
+ }
57
+ return self._post(f"/orchestrate/toolkits/{toolkit_id}/upload", files=files)
58
+
59
+ # DELETE /toolkits/{toolkit-id}
60
+ def delete(self, toolkit_id: str) -> dict:
61
+ return self._delete(f"/orchestrate/toolkits/{toolkit_id}")
62
+
63
+ def get_draft_by_name(self, toolkit_name: str) -> List[dict]:
64
+ return self.get_drafts_by_names([toolkit_name])
65
+
66
+ def get_drafts_by_names(self, toolkit_names: List[str]) -> List[dict]:
67
+ formatted_toolkit_names = [f"names={x}" for x in toolkit_names]
68
+ return self._get(f"/orchestrate/toolkits?{'&'.join(formatted_toolkit_names)}")
69
+
70
+ def get_draft_by_id(self, toolkit_id: str) -> dict:
71
+ if toolkit_id is None:
72
+ return ""
73
+ else:
74
+ try:
75
+ toolkit = self._get(f"/orchestrate/toolkits/{toolkit_id}")
76
+ return toolkit
77
+ except ClientAPIException as e:
78
+ if e.response.status_code == 404 and "not found with the given name" in e.response.text:
79
+ return ""
80
+ raise(e)
81
+
@@ -595,3 +595,4 @@ volumes:
595
595
  networks:
596
596
  default:
597
597
  name: wxo-server
598
+
@@ -49,18 +49,18 @@ EVENT_BROKER_TTL="-1"
49
49
  # THE VALUES FOR REGISTRY_URL AND *_REGISTRY ARE NOT SET HERE; THEY ARE EITHER PROVIDED BY THE USER OR DETERMINED AT RUNTIME BASED ON WO_DEVELOPER_EDITION_SOURCE.
50
50
  REGISTRY_URL=
51
51
 
52
- SERVER_TAG=02-05-2025
52
+ SERVER_TAG=03-05-2025
53
53
  SERVER_REGISTRY=
54
54
 
55
- WORKER_TAG=01-05-2025
55
+ WORKER_TAG=03-05-2025
56
56
  WORKER_REGISTRY=
57
57
 
58
58
  DB_REGISTRY=
59
59
  # If you build multiarch set all three of these to the same, we have a pr against main
60
60
  # to not have this separation, but we can merge it later
61
- DBTAG=01-05-2025
62
- AMDDBTAG=01-05-2025
63
- ARM64DBTAG=01-05-2025
61
+ DBTAG=02-05-2025
62
+ AMDDBTAG=02-05-2025
63
+ ARM64DBTAG=02-05-2025
64
64
 
65
65
  UI_REGISTRY=
66
66
  UITAG=01-05-2025
@@ -68,10 +68,10 @@ UITAG=01-05-2025
68
68
  CM_REGISTRY=
69
69
  CM_TAG=30-04-2025
70
70
 
71
- TRM_TAG=01-05-2025
71
+ TRM_TAG=02-05-2025
72
72
  TRM_REGISTRY=
73
73
 
74
- TR_TAG=01-05-2025
74
+ TR_TAG=02-05-2025
75
75
  TR_REGISTRY=
76
76
 
77
77
  BUILDER_REGISTRY=
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ibm-watsonx-orchestrate
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: IBM watsonx.orchestrate SDK
5
5
  Author-email: IBM <support@ibm.com>
6
6
  License: MIT License
@@ -1,4 +1,4 @@
1
- ibm_watsonx_orchestrate/__init__.py,sha256=91p2cfiBwxyluyBwQs2LrncECszeZvkEMadULPl-Q1o,425
1
+ ibm_watsonx_orchestrate/__init__.py,sha256=QYUW_dfyamR2JbmoR89HSm5oP6_1q9B_-nA05eXp5BI,425
2
2
  ibm_watsonx_orchestrate/agent_builder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  ibm_watsonx_orchestrate/agent_builder/agents/__init__.py,sha256=v4G0MGh11eOCkUJP_4AMOcFgzW14oE41G3iFp7G2vvw,376
4
4
  ibm_watsonx_orchestrate/agent_builder/agents/agent.py,sha256=PcBg2dRi-IOzvl24u8fa3B0jLaM5hzgkpTS8k56L9Ag,919
@@ -15,12 +15,12 @@ ibm_watsonx_orchestrate/agent_builder/tools/__init__.py,sha256=adkYX0wgB-RKFCUBw
15
15
  ibm_watsonx_orchestrate/agent_builder/tools/base_tool.py,sha256=unJBOJUY8DAq3T3YX5d1H5KehJUCjObAdpGLVoWIfzw,1156
16
16
  ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py,sha256=GD2yQkBSRHcnyq1LqA3crfo05rruqCj9vBS9xVDR2r0,14738
17
17
  ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py,sha256=gh38bToYRnoqmsoR7pHCwqQTI6ENbxOPOV5aVSqECrg,8359
18
- ibm_watsonx_orchestrate/agent_builder/tools/types.py,sha256=lA_BxWR_vQ0KTGYh3V0LPtNx9jG-IhwuptFf-VzhZAk,5280
18
+ ibm_watsonx_orchestrate/agent_builder/tools/types.py,sha256=LcMEWTUYx_bAKK_gA8Uav-hSU5qsKl4_lJ7oWYgdfLc,5813
19
19
  ibm_watsonx_orchestrate/agent_builder/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  ibm_watsonx_orchestrate/agent_builder/utils/pydantic_utils.py,sha256=QEanM6FpkmntvS02whdhWx1d4v6zT_1l9ipEbfTgHs8,7623
21
21
  ibm_watsonx_orchestrate/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  ibm_watsonx_orchestrate/cli/config.py,sha256=C_iSP6WSb5SO6cVPTueQ9lEX16gYOLXD12cKXD6w_KQ,8168
23
- ibm_watsonx_orchestrate/cli/main.py,sha256=I_7qACre-Q6yr4FXPD_BLgJYHRqXo_NDXA5aS9rQUqA,2352
23
+ ibm_watsonx_orchestrate/cli/main.py,sha256=Tey0gm3_z4JUbrKMSVFh_PEbCQqUGCohvnHRAU73KQA,2522
24
24
  ibm_watsonx_orchestrate/cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py,sha256=GVHM1wv28zGzOcc__3BXHt0Y5NXZuOr7PH8exwVqn-o,6640
26
26
  ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py,sha256=mOoBwLltv5dpGdawoyXClUIsO3bIpZLOXrIXZ7Cney4,27920
@@ -46,8 +46,10 @@ ibm_watsonx_orchestrate/cli/commands/settings/observability/__init__.py,sha256=4
46
46
  ibm_watsonx_orchestrate/cli/commands/settings/observability/observability_command.py,sha256=TAkpKwoqocsShSgEeR6LzHCzJx16VDQ6cYsbpljxeqI,372
47
47
  ibm_watsonx_orchestrate/cli/commands/settings/observability/langfuse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
48
  ibm_watsonx_orchestrate/cli/commands/settings/observability/langfuse/langfuse_command.py,sha256=Wa0L8E44EdxH9LdOvmnluLk_ApJVfTLauNOC1kV4W8k,6515
49
+ ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_command.py,sha256=hsVazoC25DUHtCFt3nmzkF5Eai59-tnNIJR51Emjk0E,2494
50
+ ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_controller.py,sha256=QRVyfXJAOHA4D5pgVcq7m4TnKwSSwwtyns3_rKc1Jcc,8499
49
51
  ibm_watsonx_orchestrate/cli/commands/tools/tools_command.py,sha256=2GK5AKwEYXsOZaASG15J8yNIPakI0NYkSXBTkeuHj6s,3343
50
- ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py,sha256=69gypDPFh-mOdRl-3xExu6rIzcnxKZyKsCr_ZD4qTwo,26292
52
+ ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py,sha256=i6JmF0sObMmIVWuWH2DVCirKLLW-NkFtj7n06dg3Yos,27688
51
53
  ibm_watsonx_orchestrate/cli/commands/tools/types.py,sha256=_md0GEa_cTH17NO_moWDY_LNdFvyEFQ1UVB9_FltYiA,173
52
54
  ibm_watsonx_orchestrate/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
55
  ibm_watsonx_orchestrate/client/base_api_client.py,sha256=0ozLUudIrQH0RTdKaX0Y5c35FRNZPTemaAp71AsoMsQ,4410
@@ -68,9 +70,10 @@ ibm_watsonx_orchestrate/client/connections/__init__.py,sha256=u821r2ZiYXLYNTknxd
68
70
  ibm_watsonx_orchestrate/client/connections/connections_client.py,sha256=aenYRfywM0lPlJWipFuy-Dnp2ljmpeP11oaWvPd69sA,7053
69
71
  ibm_watsonx_orchestrate/client/connections/utils.py,sha256=XqgYoiehiqIZ-LnduIAiJ9DIIF7IR7QvkKTc9784Ii0,1326
70
72
  ibm_watsonx_orchestrate/client/knowledge_bases/knowledge_base_client.py,sha256=U-pG_H0I8992f0V13Li_e1dksKp54MrYX3X9bvr-09w,2181
73
+ ibm_watsonx_orchestrate/client/toolkit/toolkit_client.py,sha256=1ZcOcjULV8xXqEVQpuDx_Nhh3dOFC2DA-RXMddl-wH0,2974
71
74
  ibm_watsonx_orchestrate/client/tools/tool_client.py,sha256=pEKOBH488YbLVc71ucucX0rr8YoulvDCxejuyWd0K8s,1588
72
- ibm_watsonx_orchestrate/docker/compose-lite.yml,sha256=OTiVtPVrgKOyY9YuEvkkR_dytbhqKfSiKM3yMxahlS0,24683
73
- ibm_watsonx_orchestrate/docker/default.env,sha256=yV2hRFzpCNYs8UVMHQ0HA__rlITmJObXJvF2PjwvoUo,4609
75
+ ibm_watsonx_orchestrate/docker/compose-lite.yml,sha256=Br6wNrRNYVYeIpc4q_Usc-ENK0RzccE-67GSlYhwggw,24688
76
+ ibm_watsonx_orchestrate/docker/default.env,sha256=kuLLcXI24G4plLW8_7Af9K8VEVpt_zAhCOL1p-FVqS0,4609
74
77
  ibm_watsonx_orchestrate/docker/start-up.sh,sha256=LTtwHp0AidVgjohis2LXGvZnkFQStOiUAxgGABOyeUI,1811
75
78
  ibm_watsonx_orchestrate/docker/sdk/ibm_watsonx_orchestrate-0.6.0-py3-none-any.whl,sha256=Hi3-owh5OM0Jz2ihX9nLoojnr7Ky1TV-GelyqLcewLE,2047417
76
79
  ibm_watsonx_orchestrate/docker/sdk/ibm_watsonx_orchestrate-0.6.0.tar.gz,sha256=e5T-q7XPAtiCyQljwZp6kk3Q_4Tg6y5sijHTkscmqqQ,2025466
@@ -82,8 +85,8 @@ ibm_watsonx_orchestrate/utils/utils.py,sha256=3JWk1J9A04yVZeetE3TQH82I53Sn20KvU_
82
85
  ibm_watsonx_orchestrate/utils/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
86
  ibm_watsonx_orchestrate/utils/logging/logger.py,sha256=FzeGnidXAjC7yHrvIaj4KZPeaBBSCniZFlwgr5yV3oA,1037
84
87
  ibm_watsonx_orchestrate/utils/logging/logging.yaml,sha256=9_TKfuFr1barnOKP0fZT5D6MhddiwsXVTFjtRbcOO5w,314
85
- ibm_watsonx_orchestrate-1.1.0.dist-info/METADATA,sha256=Zk_dfo1Tu9kjiiJLQDIY1YLIkFlAQW1DwLVSdJamLWg,1251
86
- ibm_watsonx_orchestrate-1.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
87
- ibm_watsonx_orchestrate-1.1.0.dist-info/entry_points.txt,sha256=SfIT02-Jen5e99OcLhzbcM9Bdyf8SGVOCtnSplgZdQI,69
88
- ibm_watsonx_orchestrate-1.1.0.dist-info/licenses/LICENSE,sha256=Shgxx7hTdCOkiVRmfGgp_1ISISrwQD7m2f0y8Hsapl4,1083
89
- ibm_watsonx_orchestrate-1.1.0.dist-info/RECORD,,
88
+ ibm_watsonx_orchestrate-1.2.0.dist-info/METADATA,sha256=ecvKQFIj26BZT-5M9fPjcfs371MR-NQIV89qrn6GNRQ,1251
89
+ ibm_watsonx_orchestrate-1.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
90
+ ibm_watsonx_orchestrate-1.2.0.dist-info/entry_points.txt,sha256=SfIT02-Jen5e99OcLhzbcM9Bdyf8SGVOCtnSplgZdQI,69
91
+ ibm_watsonx_orchestrate-1.2.0.dist-info/licenses/LICENSE,sha256=Shgxx7hTdCOkiVRmfGgp_1ISISrwQD7m2f0y8Hsapl4,1083
92
+ ibm_watsonx_orchestrate-1.2.0.dist-info/RECORD,,