ibm-watsonx-orchestrate 1.12.0b0__py3-none-any.whl → 1.13.0b0__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.
- ibm_watsonx_orchestrate/__init__.py +2 -1
- ibm_watsonx_orchestrate/agent_builder/agents/types.py +5 -5
- ibm_watsonx_orchestrate/agent_builder/connections/types.py +34 -3
- ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +11 -2
- ibm_watsonx_orchestrate/agent_builder/models/types.py +18 -1
- ibm_watsonx_orchestrate/agent_builder/toolkits/base_toolkit.py +1 -1
- ibm_watsonx_orchestrate/agent_builder/toolkits/types.py +14 -2
- ibm_watsonx_orchestrate/agent_builder/tools/__init__.py +1 -1
- ibm_watsonx_orchestrate/agent_builder/tools/base_tool.py +1 -1
- ibm_watsonx_orchestrate/agent_builder/tools/langflow_tool.py +61 -1
- ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py +6 -0
- ibm_watsonx_orchestrate/agent_builder/tools/types.py +21 -3
- ibm_watsonx_orchestrate/agent_builder/voice_configurations/__init__.py +1 -1
- ibm_watsonx_orchestrate/agent_builder/voice_configurations/types.py +11 -0
- ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +29 -53
- ibm_watsonx_orchestrate/cli/commands/connections/connections_command.py +2 -2
- ibm_watsonx_orchestrate/cli/commands/connections/connections_controller.py +56 -30
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_command.py +25 -2
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_controller.py +249 -14
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_server_controller.py +4 -4
- ibm_watsonx_orchestrate/cli/commands/environment/environment_command.py +5 -1
- ibm_watsonx_orchestrate/cli/commands/environment/environment_controller.py +6 -3
- ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_command.py +3 -2
- ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_controller.py +1 -1
- ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +45 -16
- ibm_watsonx_orchestrate/cli/commands/models/model_provider_mapper.py +23 -4
- ibm_watsonx_orchestrate/cli/commands/models/models_command.py +2 -2
- ibm_watsonx_orchestrate/cli/commands/models/models_controller.py +29 -10
- ibm_watsonx_orchestrate/cli/commands/partners/offering/partners_offering_controller.py +21 -4
- ibm_watsonx_orchestrate/cli/commands/partners/offering/types.py +7 -15
- ibm_watsonx_orchestrate/cli/commands/partners/partners_command.py +1 -1
- ibm_watsonx_orchestrate/cli/commands/server/server_command.py +30 -20
- ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_command.py +2 -2
- ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_controller.py +139 -27
- ibm_watsonx_orchestrate/cli/commands/tools/tools_command.py +2 -2
- ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +79 -36
- ibm_watsonx_orchestrate/cli/commands/voice_configurations/voice_configurations_controller.py +23 -11
- ibm_watsonx_orchestrate/cli/common.py +26 -0
- ibm_watsonx_orchestrate/cli/config.py +33 -2
- ibm_watsonx_orchestrate/client/connections/connections_client.py +1 -14
- ibm_watsonx_orchestrate/client/copilot/cpe/copilot_cpe_client.py +34 -1
- ibm_watsonx_orchestrate/client/knowledge_bases/knowledge_base_client.py +6 -2
- ibm_watsonx_orchestrate/client/model_policies/model_policies_client.py +1 -1
- ibm_watsonx_orchestrate/client/models/models_client.py +1 -1
- ibm_watsonx_orchestrate/client/threads/threads_client.py +34 -0
- ibm_watsonx_orchestrate/client/utils.py +29 -7
- ibm_watsonx_orchestrate/docker/compose-lite.yml +58 -8
- ibm_watsonx_orchestrate/docker/default.env +26 -17
- ibm_watsonx_orchestrate/flow_builder/flows/decorators.py +10 -2
- ibm_watsonx_orchestrate/flow_builder/flows/flow.py +90 -16
- ibm_watsonx_orchestrate/flow_builder/node.py +14 -2
- ibm_watsonx_orchestrate/flow_builder/types.py +57 -3
- ibm_watsonx_orchestrate/langflow/__init__.py +0 -0
- ibm_watsonx_orchestrate/langflow/langflow_utils.py +195 -0
- ibm_watsonx_orchestrate/langflow/lfx_deps.py +84 -0
- ibm_watsonx_orchestrate/utils/async_helpers.py +31 -0
- ibm_watsonx_orchestrate/utils/docker_utils.py +1177 -33
- ibm_watsonx_orchestrate/utils/environment.py +165 -20
- ibm_watsonx_orchestrate/utils/exceptions.py +1 -1
- ibm_watsonx_orchestrate/utils/tokens.py +51 -0
- ibm_watsonx_orchestrate/utils/utils.py +63 -4
- {ibm_watsonx_orchestrate-1.12.0b0.dist-info → ibm_watsonx_orchestrate-1.13.0b0.dist-info}/METADATA +2 -2
- {ibm_watsonx_orchestrate-1.12.0b0.dist-info → ibm_watsonx_orchestrate-1.13.0b0.dist-info}/RECORD +66 -59
- {ibm_watsonx_orchestrate-1.12.0b0.dist-info → ibm_watsonx_orchestrate-1.13.0b0.dist-info}/WHEEL +0 -0
- {ibm_watsonx_orchestrate-1.12.0b0.dist-info → ibm_watsonx_orchestrate-1.13.0b0.dist-info}/entry_points.txt +0 -0
- {ibm_watsonx_orchestrate-1.12.0b0.dist-info → ibm_watsonx_orchestrate-1.13.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -1,8 +1,8 @@
|
|
1
1
|
import os
|
2
2
|
import zipfile
|
3
3
|
import tempfile
|
4
|
-
from typing import List, Optional
|
5
|
-
from
|
4
|
+
from typing import List, Optional, Any
|
5
|
+
from pydantic import BaseModel
|
6
6
|
import logging
|
7
7
|
import sys
|
8
8
|
import re
|
@@ -10,7 +10,7 @@ import requests
|
|
10
10
|
from ibm_watsonx_orchestrate.client.toolkit.toolkit_client import ToolKitClient
|
11
11
|
from ibm_watsonx_orchestrate.client.tools.tool_client import ToolClient
|
12
12
|
from ibm_watsonx_orchestrate.agent_builder.toolkits.base_toolkit import BaseToolkit, ToolkitSpec
|
13
|
-
from ibm_watsonx_orchestrate.agent_builder.toolkits.types import ToolkitKind, Language, ToolkitSource, ToolkitTransportKind
|
13
|
+
from ibm_watsonx_orchestrate.agent_builder.toolkits.types import ToolkitKind, Language, ToolkitSource, ToolkitTransportKind, ToolkitListEntry
|
14
14
|
from ibm_watsonx_orchestrate.client.utils import instantiate_client
|
15
15
|
from ibm_watsonx_orchestrate.utils.utils import sanitize_app_id
|
16
16
|
from ibm_watsonx_orchestrate.client.connections import get_connections_client
|
@@ -18,7 +18,7 @@ import typer
|
|
18
18
|
import json
|
19
19
|
from rich.console import Console
|
20
20
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
21
|
-
from ibm_watsonx_orchestrate.
|
21
|
+
from ibm_watsonx_orchestrate.cli.common import ListFormats, rich_table_to_markdown
|
22
22
|
from rich.json import JSON
|
23
23
|
import rich
|
24
24
|
import rich.table
|
@@ -264,8 +264,119 @@ class ToolkitController:
|
|
264
264
|
except requests.HTTPError as e:
|
265
265
|
logger.error(e.response.text)
|
266
266
|
exit(1)
|
267
|
+
|
268
|
+
def _lookup_toolkit_resource_value(
|
269
|
+
self,
|
270
|
+
toolkit: BaseToolkit,
|
271
|
+
lookup_table: dict[str, str],
|
272
|
+
target_attr: str,
|
273
|
+
target_attr_display_name: str
|
274
|
+
) -> List[str] | str | None:
|
275
|
+
"""
|
276
|
+
Using a lookup table convert all the strings in a given field of an agent into their equivalent in the lookup table
|
277
|
+
Example: lookup_table={1: obj1, 2: obj2} agent=Toolkit(tools=[1,2]) return. [obj1, obj2]
|
278
|
+
|
279
|
+
Args:
|
280
|
+
toolkit: A toolkit
|
281
|
+
lookup_table: A dictionary that maps one value to another
|
282
|
+
target_attr: The field to convert on the provided agent
|
283
|
+
target_attr_display_name: The name of the field to be displayed in the event of an error
|
284
|
+
"""
|
285
|
+
attr_value = getattr(toolkit, target_attr, None)
|
286
|
+
if not attr_value:
|
287
|
+
return
|
288
|
+
|
289
|
+
if isinstance(attr_value, list):
|
290
|
+
new_resource_list=[]
|
291
|
+
for value in attr_value:
|
292
|
+
if value in lookup_table:
|
293
|
+
new_resource_list.append(lookup_table[value])
|
294
|
+
else:
|
295
|
+
logger.warning(f"{target_attr_display_name} with ID '{value}' not found. Returning {target_attr_display_name} ID")
|
296
|
+
new_resource_list.append(value)
|
297
|
+
return new_resource_list
|
298
|
+
else:
|
299
|
+
if attr_value in lookup_table:
|
300
|
+
return lookup_table[attr_value]
|
301
|
+
else:
|
302
|
+
logger.warning(f"{target_attr_display_name} with ID '{attr_value}' not found. Returning {target_attr_display_name} ID")
|
303
|
+
return attr_value
|
304
|
+
|
305
|
+
def _construct_lut_toolkit_resource(self, resource_list: List[dict], key_attr: str, value_attr) -> dict:
|
306
|
+
"""
|
307
|
+
Given a list of dictionaries build a key -> value look up table
|
308
|
+
Example [{id: 1, name: obj1}, {id: 2, name: obj2}] return {1: obj1, 2: obj2}
|
309
|
+
|
310
|
+
Args:
|
311
|
+
resource_list: A list of dictionries from which to build the lookup table from
|
312
|
+
key_attr: The name of the field whose value will form the key of the lookup table
|
313
|
+
value_attrL The name of the field whose value will form the value of the lookup table
|
314
|
+
|
315
|
+
Returns:
|
316
|
+
A lookup table
|
317
|
+
"""
|
318
|
+
lut = {}
|
319
|
+
for resource in resource_list:
|
320
|
+
if isinstance(resource, BaseModel):
|
321
|
+
resource = resource.model_dump()
|
322
|
+
lut[resource.get(key_attr, None)] = resource.get(value_attr, None)
|
323
|
+
return lut
|
324
|
+
|
325
|
+
def _batch_request_resource(self, client_fn, ids, batch_size=50) -> List[dict]:
|
326
|
+
resources = []
|
327
|
+
for i in range(0, len(ids), batch_size):
|
328
|
+
chunk = ids[i:i + batch_size]
|
329
|
+
resources += (client_fn(chunk))
|
330
|
+
return resources
|
331
|
+
|
332
|
+
def _get_all_unique_toolkit_resources(self, toolkits: List[BaseToolkit], target_attr: str) -> List[str]:
|
333
|
+
"""
|
334
|
+
Given a list of toolkits get all the unique values of a certain field
|
335
|
+
Example: tk1.tools = [1 ,2 ,3] and tk2.tools = [2, 4, 5] then return [1, 2, 3, 4, 5]
|
336
|
+
Example: tk1.id = "123" and tk2.id = "456" then return ["123", "456"]
|
337
|
+
|
338
|
+
Args:
|
339
|
+
toolkits: List of toolkits
|
340
|
+
target_attr: The name of the field to access and get unique elements
|
341
|
+
|
342
|
+
Returns:
|
343
|
+
A list of unique elements from across all toolkits
|
344
|
+
"""
|
345
|
+
all_ids = set()
|
346
|
+
for toolkit in toolkits:
|
347
|
+
attr_value = getattr(toolkit, target_attr, None)
|
348
|
+
if attr_value:
|
349
|
+
if isinstance(attr_value, list):
|
350
|
+
all_ids.update(attr_value)
|
351
|
+
else:
|
352
|
+
all_ids.add(attr_value)
|
353
|
+
return list(all_ids)
|
354
|
+
|
355
|
+
def _bulk_resolve_toolkit_tools(self, toolkits: List[BaseToolkit]) -> List[BaseToolkit]:
|
356
|
+
new_toolkit_specs = [tk.__toolkit_spec__ for tk in toolkits].copy()
|
357
|
+
all_tools_ids = self._get_all_unique_toolkit_resources(new_toolkit_specs, "tools")
|
358
|
+
if not all_tools_ids:
|
359
|
+
return toolkits
|
360
|
+
|
361
|
+
tool_client = instantiate_client(ToolClient)
|
362
|
+
|
363
|
+
all_tools = self._batch_request_resource(tool_client.get_drafts_by_ids, all_tools_ids)
|
364
|
+
|
365
|
+
tool_lut = self._construct_lut_toolkit_resource(all_tools, "id", "name")
|
366
|
+
|
367
|
+
new_toolkits = []
|
368
|
+
for toolkit_spec in new_toolkit_specs:
|
369
|
+
tool_names = self._lookup_toolkit_resource_value(toolkit_spec, tool_lut, "tools", "Tool")
|
370
|
+
if tool_names:
|
371
|
+
toolkit_spec.tools = tool_names
|
372
|
+
new_toolkits.append(BaseToolkit(toolkit_spec))
|
373
|
+
return new_toolkits
|
374
|
+
|
375
|
+
def list_toolkits(self, verbose=False, format: ListFormats| None = None) -> List[dict[str, Any]] | List[ToolkitListEntry] | str | None:
|
376
|
+
if verbose and format:
|
377
|
+
logger.error("For toolkits list, `--verbose` and `--format` are mutually exclusive options")
|
378
|
+
sys.exit(1)
|
267
379
|
|
268
|
-
def list_toolkits(self, verbose=False):
|
269
380
|
client = self.get_client()
|
270
381
|
response = client.get()
|
271
382
|
toolkit_spec = [ToolkitSpec.model_validate(toolkit) for toolkit in response]
|
@@ -276,7 +387,10 @@ class ToolkitController:
|
|
276
387
|
for toolkit in toolkits:
|
277
388
|
tools_list.append(json.loads(toolkit.dumps_spec()))
|
278
389
|
rich.print(JSON(json.dumps(tools_list, indent=4)))
|
390
|
+
return tools_list
|
279
391
|
else:
|
392
|
+
toolkit_details = []
|
393
|
+
|
280
394
|
table = rich.table.Table(show_header=True, header_style="bold white", show_lines=True)
|
281
395
|
column_args = {
|
282
396
|
"Name": {"overflow": "fold"},
|
@@ -288,23 +402,14 @@ class ToolkitController:
|
|
288
402
|
for column in column_args:
|
289
403
|
table.add_column(column,**column_args[column])
|
290
404
|
|
291
|
-
tools_client = instantiate_client(ToolClient)
|
292
|
-
|
293
405
|
connections_client = get_connections_client()
|
294
406
|
connections = connections_client.list()
|
295
407
|
|
296
408
|
connections_dict = {conn.connection_id: conn for conn in connections}
|
297
409
|
|
298
|
-
|
299
|
-
tool_ids = toolkit.__toolkit_spec__.tools or []
|
300
|
-
tool_names = []
|
301
|
-
if len(tool_ids) == 0:
|
302
|
-
logger.warning("This toolkit contains no tools.")
|
303
|
-
|
304
|
-
for tool_id in tool_ids:
|
305
|
-
tool = tools_client.get_draft_by_id(tool_id)
|
306
|
-
tool_names.append(tool["name"])
|
410
|
+
resolved_toolkits = self._bulk_resolve_toolkit_tools(toolkits)
|
307
411
|
|
412
|
+
for toolkit in resolved_toolkits:
|
308
413
|
app_ids = []
|
309
414
|
connection_ids = toolkit.__toolkit_spec__.mcp.connections.values()
|
310
415
|
|
@@ -317,15 +422,22 @@ class ToolkitController:
|
|
317
422
|
else:
|
318
423
|
app_id = ""
|
319
424
|
app_ids.append(app_id)
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
toolkit.__toolkit_spec__.
|
325
|
-
|
326
|
-
toolkit.__toolkit_spec__.description,
|
327
|
-
", ".join(tool_names),
|
328
|
-
", ".join(app_ids),
|
425
|
+
|
426
|
+
entry = ToolkitListEntry(
|
427
|
+
name = toolkit.__toolkit_spec__.name,
|
428
|
+
description = toolkit.__toolkit_spec__.description,
|
429
|
+
tools = toolkit.__toolkit_spec__.tools,
|
430
|
+
app_ids = app_ids
|
329
431
|
)
|
330
|
-
|
331
|
-
|
432
|
+
if format == ListFormats.JSON:
|
433
|
+
toolkit_details.append(entry)
|
434
|
+
else:
|
435
|
+
table.add_row(*entry.get_row_details())
|
436
|
+
|
437
|
+
match format:
|
438
|
+
case ListFormats.JSON:
|
439
|
+
return toolkit_details
|
440
|
+
case ListFormats.Table:
|
441
|
+
return rich_table_to_markdown(table)
|
442
|
+
case _:
|
443
|
+
rich.print(table)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import typer
|
2
2
|
from typing import List
|
3
|
-
from typing_extensions import Annotated
|
3
|
+
from typing_extensions import Annotated, Optional
|
4
4
|
from ibm_watsonx_orchestrate.cli.commands.tools.tools_controller import ToolsController, ToolKind
|
5
5
|
tools_app= typer.Typer(no_args_is_help=True)
|
6
6
|
|
@@ -34,7 +34,7 @@ def tool_import(
|
|
34
34
|
)
|
35
35
|
] = None,
|
36
36
|
requirements_file: Annotated[
|
37
|
-
str,
|
37
|
+
Optional[str],
|
38
38
|
typer.Option(
|
39
39
|
"--requirements-file",
|
40
40
|
"-r",
|
@@ -1,5 +1,4 @@
|
|
1
1
|
import logging
|
2
|
-
import asyncio
|
3
2
|
import importlib
|
4
3
|
import inspect
|
5
4
|
import sys
|
@@ -11,29 +10,28 @@ import zipfile
|
|
11
10
|
from enum import Enum
|
12
11
|
from os import path
|
13
12
|
from pathlib import Path
|
14
|
-
from typing import Iterable, List
|
13
|
+
from typing import Iterable, List, Any, Optional, cast
|
15
14
|
import rich
|
16
15
|
import json
|
17
|
-
from rich.json import JSON
|
18
16
|
import glob
|
19
17
|
|
20
18
|
import rich.table
|
21
19
|
import typer
|
22
20
|
|
23
|
-
from rich.console import Console
|
24
21
|
from rich.panel import Panel
|
25
22
|
|
26
|
-
from ibm_watsonx_orchestrate.agent_builder.tools import BaseTool, ToolSpec
|
23
|
+
from ibm_watsonx_orchestrate.agent_builder.tools import BaseTool, ToolSpec, ToolListEntry
|
27
24
|
from ibm_watsonx_orchestrate.agent_builder.tools.flow_tool import create_flow_json_tool
|
28
|
-
from ibm_watsonx_orchestrate.agent_builder.tools.langflow_tool import create_langflow_tool
|
25
|
+
from ibm_watsonx_orchestrate.agent_builder.tools.langflow_tool import LangflowTool, create_langflow_tool
|
29
26
|
from ibm_watsonx_orchestrate.agent_builder.tools.openapi_tool import create_openapi_json_tools_from_uri,create_openapi_json_tools_from_content
|
30
27
|
from ibm_watsonx_orchestrate.cli.commands.models.models_controller import ModelHighlighter
|
31
28
|
from ibm_watsonx_orchestrate.cli.commands.tools.types import RegistryType
|
32
29
|
from ibm_watsonx_orchestrate.cli.commands.connections.connections_controller import configure_connection, remove_connection, add_connection
|
30
|
+
from ibm_watsonx_orchestrate.cli.common import ListFormats, rich_table_to_markdown
|
33
31
|
from ibm_watsonx_orchestrate.agent_builder.connections.types import ConnectionType, ConnectionEnvironment, ConnectionPreference
|
34
32
|
from ibm_watsonx_orchestrate.cli.config import Config, CONTEXT_SECTION_HEADER, CONTEXT_ACTIVE_ENV_OPT, \
|
35
33
|
PYTHON_REGISTRY_HEADER, PYTHON_REGISTRY_TYPE_OPT, PYTHON_REGISTRY_TEST_PACKAGE_VERSION_OVERRIDE_OPT, \
|
36
|
-
DEFAULT_CONFIG_FILE_CONTENT
|
34
|
+
DEFAULT_CONFIG_FILE_CONTENT, PYTHON_REGISTRY_SKIP_VERSION_CHECK_OPT
|
37
35
|
from ibm_watsonx_orchestrate.agent_builder.connections import ConnectionSecurityScheme, ExpectedCredentials
|
38
36
|
from ibm_watsonx_orchestrate.flow_builder.flows.decorators import FlowWrapper
|
39
37
|
from ibm_watsonx_orchestrate.client.tools.tool_client import ToolClient
|
@@ -42,6 +40,7 @@ from ibm_watsonx_orchestrate.client.connections import get_connections_client, g
|
|
42
40
|
from ibm_watsonx_orchestrate.client.utils import instantiate_client, is_local_dev
|
43
41
|
from ibm_watsonx_orchestrate.flow_builder.utils import import_flow_support_tools
|
44
42
|
from ibm_watsonx_orchestrate.utils.utils import sanitize_app_id
|
43
|
+
from ibm_watsonx_orchestrate.utils.async_helpers import run_coroutine_sync
|
45
44
|
from ibm_watsonx_orchestrate.utils.exceptions import BadRequest
|
46
45
|
from ibm_watsonx_orchestrate.client.tools.tempus_client import TempusClient
|
47
46
|
|
@@ -56,6 +55,11 @@ DEFAULT_LANGFLOW_TOOL_REQUIREMENTS = [
|
|
56
55
|
"lfx==0.1.8"
|
57
56
|
]
|
58
57
|
|
58
|
+
DEFAULT_LANGFLOW_RUNNER_MODULES = [
|
59
|
+
"lfx",
|
60
|
+
"lfx-nightly"
|
61
|
+
]
|
62
|
+
|
59
63
|
class ToolKind(str, Enum):
|
60
64
|
openapi = "openapi"
|
61
65
|
python = "python"
|
@@ -615,7 +619,7 @@ def get_whl_in_registry(registry_url: str, version: str) -> str| None:
|
|
615
619
|
return wheel_file
|
616
620
|
|
617
621
|
class ToolsController:
|
618
|
-
def __init__(self, tool_kind: ToolKind = None, file: str = None, requirements_file: str = None):
|
622
|
+
def __init__(self, tool_kind: ToolKind = None, file: str = None, requirements_file: Optional[str] = None):
|
619
623
|
self.client = None
|
620
624
|
self.tool_kind = tool_kind
|
621
625
|
self.file = file
|
@@ -651,14 +655,14 @@ class ToolsController:
|
|
651
655
|
app_id = app_id[0]
|
652
656
|
connection = connections_client.get_draft_by_app_id(app_id=app_id)
|
653
657
|
connection_id = connection.connection_id
|
654
|
-
tools =
|
658
|
+
tools = run_coroutine_sync(import_openapi_tool(file=args["file"], connection_id=connection_id))
|
655
659
|
case "flow":
|
656
|
-
tools =
|
660
|
+
tools = run_coroutine_sync(import_flow_tool(file=args["file"]))
|
657
661
|
case "skill":
|
658
662
|
tools = []
|
659
663
|
logger.warning("Skill Import not implemented yet")
|
660
664
|
case "langflow":
|
661
|
-
tools =
|
665
|
+
tools = run_coroutine_sync(import_langflow_tool(file=args["file"],app_id=args.get('app_id',None)))
|
662
666
|
case _:
|
663
667
|
raise BadRequest("Invalid kind selected")
|
664
668
|
|
@@ -669,14 +673,18 @@ class ToolsController:
|
|
669
673
|
yield tool
|
670
674
|
|
671
675
|
|
672
|
-
def list_tools(self, verbose=False):
|
676
|
+
def list_tools(self, verbose=False, format: ListFormats| None = None) -> List[dict[str, Any]] | str | None:
|
677
|
+
if verbose and format:
|
678
|
+
logger.error("For tools list, `--verbose` and `--format` are mutually exclusive options")
|
679
|
+
sys.exit(1)
|
680
|
+
|
673
681
|
response = self.get_client().get()
|
674
682
|
tool_specs = []
|
675
683
|
parse_errors = []
|
676
684
|
|
677
685
|
for tool in response:
|
678
686
|
try:
|
679
|
-
tool_specs.append(ToolSpec.model_validate(tool))
|
687
|
+
tool_specs.append(ToolSpec.model_validate(tool, context="list"))
|
680
688
|
except Exception as e:
|
681
689
|
name = tool.get('name', None)
|
682
690
|
parse_errors.append([
|
@@ -693,12 +701,19 @@ class ToolsController:
|
|
693
701
|
tools_list.append(json.loads(tool.dumps_spec()))
|
694
702
|
|
695
703
|
rich.print_json(json.dumps(tools_list, indent=4))
|
704
|
+
return tools_list
|
696
705
|
else:
|
706
|
+
tool_details = []
|
707
|
+
|
708
|
+
connections_client = get_connections_client()
|
709
|
+
connections = connections_client.list()
|
710
|
+
|
711
|
+
connections_dict = {conn.connection_id: conn for conn in connections}
|
712
|
+
|
697
713
|
table = rich.table.Table(show_header=True, header_style="bold white", show_lines=True)
|
698
714
|
column_args = {
|
699
715
|
"Name": {"overflow": "fold"},
|
700
716
|
"Description": {},
|
701
|
-
"Permission": {},
|
702
717
|
"Type": {},
|
703
718
|
"Toolkit": {},
|
704
719
|
"App ID": {"overflow": "fold"}
|
@@ -706,11 +721,6 @@ class ToolsController:
|
|
706
721
|
for column in column_args:
|
707
722
|
table.add_column(column,**column_args[column])
|
708
723
|
|
709
|
-
connections_client = get_connections_client()
|
710
|
-
connections = connections_client.list()
|
711
|
-
|
712
|
-
connections_dict = {conn.connection_id: conn for conn in connections}
|
713
|
-
|
714
724
|
for tool in tools:
|
715
725
|
tool_binding = tool.__tool_spec__.binding
|
716
726
|
|
@@ -768,22 +778,31 @@ class ToolsController:
|
|
768
778
|
toolkit_name = toolkit["name"]
|
769
779
|
elif toolkit:
|
770
780
|
toolkit_name = str(toolkit)
|
771
|
-
|
772
781
|
|
773
|
-
|
774
|
-
tool.__tool_spec__.name,
|
775
|
-
tool.__tool_spec__.description,
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
", ".join(app_ids),
|
782
|
+
entry = ToolListEntry(
|
783
|
+
name=tool.__tool_spec__.name,
|
784
|
+
description=tool.__tool_spec__.description,
|
785
|
+
type=tool_type,
|
786
|
+
toolkit=toolkit_name,
|
787
|
+
app_ids=app_ids
|
780
788
|
)
|
781
789
|
|
782
|
-
|
790
|
+
if format == ListFormats.JSON:
|
791
|
+
tool_details.append(entry)
|
792
|
+
else:
|
793
|
+
table.add_row(*entry.get_row_details())
|
794
|
+
|
795
|
+
match format:
|
796
|
+
case ListFormats.JSON:
|
797
|
+
return tool_details
|
798
|
+
case ListFormats.Table:
|
799
|
+
return rich_table_to_markdown(table)
|
800
|
+
case _:
|
801
|
+
rich.print(table)
|
783
802
|
|
784
|
-
|
785
|
-
|
786
|
-
|
803
|
+
for error in parse_errors:
|
804
|
+
for l in error:
|
805
|
+
logger.error(l)
|
787
806
|
|
788
807
|
def get_all_tools(self) -> dict:
|
789
808
|
return {entry["name"]: entry["id"] for entry in self.get_client().get()}
|
@@ -851,15 +870,18 @@ class ToolsController:
|
|
851
870
|
|
852
871
|
cfg = Config()
|
853
872
|
registry_type = cfg.read(PYTHON_REGISTRY_HEADER, PYTHON_REGISTRY_TYPE_OPT) or DEFAULT_CONFIG_FILE_CONTENT[PYTHON_REGISTRY_HEADER][PYTHON_REGISTRY_TYPE_OPT]
|
873
|
+
skip_version_check = cfg.read(PYTHON_REGISTRY_HEADER, PYTHON_REGISTRY_SKIP_VERSION_CHECK_OPT) or DEFAULT_CONFIG_FILE_CONTENT[PYTHON_REGISTRY_HEADER][PYTHON_REGISTRY_SKIP_VERSION_CHECK_OPT]
|
854
874
|
|
855
875
|
version = __version__
|
856
876
|
if registry_type == RegistryType.LOCAL:
|
877
|
+
logger.warning(f"Using a local registry which is for development purposes only")
|
857
878
|
requirements.append(f"/packages/ibm_watsonx_orchestrate-0.6.0-py3-none-any.whl\n")
|
858
879
|
elif registry_type == RegistryType.PYPI:
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
880
|
+
if not skip_version_check:
|
881
|
+
wheel_file = get_whl_in_registry(registry_url='https://pypi.org/simple/ibm-watsonx-orchestrate', version=version)
|
882
|
+
if not wheel_file:
|
883
|
+
logger.error(f"Could not find ibm-watsonx-orchestrate@{version} on https://pypi.org/project/ibm-watsonx-orchestrate")
|
884
|
+
exit(1)
|
863
885
|
requirements.append(f"ibm-watsonx-orchestrate=={version}\n")
|
864
886
|
elif registry_type == RegistryType.TESTPYPI:
|
865
887
|
override_version = cfg.get(PYTHON_REGISTRY_HEADER, PYTHON_REGISTRY_TEST_PACKAGE_VERSION_OVERRIDE_OPT) or version
|
@@ -890,13 +912,34 @@ class ToolsController:
|
|
890
912
|
tool_path = Path(self.file)
|
891
913
|
zip_tool_artifacts.write(tool_path, arcname=f"{tool_path.stem}.json")
|
892
914
|
|
893
|
-
requirements =
|
915
|
+
requirements = []
|
894
916
|
|
895
917
|
if self.requirements_file:
|
896
918
|
requirements_file_path = Path(self.requirements_file)
|
897
919
|
requirements.extend(
|
898
920
|
get_requirement_lines(requirements_file=requirements_file_path, remove_trailing_newlines=False)
|
899
921
|
)
|
922
|
+
|
923
|
+
langflowTool = cast(LangflowTool, tool)
|
924
|
+
# if there are additional requriements from the langflow model, we should add it to the requirement set
|
925
|
+
if langflowTool.requirements and len(langflowTool.requirements) > 0:
|
926
|
+
requirements.extend(langflowTool.requirements)
|
927
|
+
|
928
|
+
# now check if the requirements contain modules listed in DEFAULT_LANGFLOW_RUNNER_MODULES
|
929
|
+
# if it is needed, we are assuming the user wants to override the default langflow module
|
930
|
+
# with a specific version
|
931
|
+
runner_overridden = False
|
932
|
+
for r in requirements:
|
933
|
+
# get the module name from the requirements
|
934
|
+
module_name = r.strip().split('==')[0].split('=')[0].split('>=')[0].split('<=')[0].split('~=')[0].lower()
|
935
|
+
if not module_name.startswith('#'):
|
936
|
+
if module_name in DEFAULT_LANGFLOW_RUNNER_MODULES:
|
937
|
+
runner_overridden = True
|
938
|
+
|
939
|
+
if not runner_overridden:
|
940
|
+
# add the default runner to the top of requirement list
|
941
|
+
requirements = DEFAULT_LANGFLOW_TOOL_REQUIREMENTS + list(requirements)
|
942
|
+
|
900
943
|
requirements_content = '\n'.join(requirements) + '\n'
|
901
944
|
zip_tool_artifacts.writestr("requirements.txt",requirements_content)
|
902
945
|
zip_tool_artifacts.writestr("bundle-format", "2.0.0\n")
|
ibm_watsonx_orchestrate/cli/commands/voice_configurations/voice_configurations_controller.py
CHANGED
@@ -3,10 +3,12 @@ import sys
|
|
3
3
|
import rich
|
4
4
|
import yaml
|
5
5
|
import logging
|
6
|
-
from
|
6
|
+
from typing import Optional, List, Any
|
7
|
+
from ibm_watsonx_orchestrate.agent_builder.voice_configurations import VoiceConfiguration, VoiceConfigurationListEntry
|
7
8
|
from ibm_watsonx_orchestrate.client.utils import instantiate_client
|
8
9
|
from ibm_watsonx_orchestrate.client.voice_configurations.voice_configurations_client import VoiceConfigurationsClient
|
9
10
|
from ibm_watsonx_orchestrate.utils.exceptions import BadRequest
|
11
|
+
from ibm_watsonx_orchestrate.cli.common import ListFormats, rich_table_to_markdown
|
10
12
|
|
11
13
|
logger = logging.getLogger(__name__)
|
12
14
|
|
@@ -69,12 +71,13 @@ class VoiceConfigurationsController:
|
|
69
71
|
|
70
72
|
return configs[0]
|
71
73
|
|
72
|
-
def list_voice_configs(self, verbose: bool) -> None:
|
74
|
+
def list_voice_configs(self, verbose: bool, format: Optional[ListFormats]=None) -> List[dict[str | Any]] | List[VoiceConfigurationListEntry] | str | None:
|
73
75
|
voice_configs = self.fetch_voice_configs()
|
74
76
|
|
75
77
|
if verbose:
|
76
78
|
json_configs = [json.loads(x.dumps_spec()) for x in voice_configs]
|
77
79
|
rich.print_json(json.dumps(json_configs, indent=4))
|
80
|
+
return json_configs
|
78
81
|
else:
|
79
82
|
config_table = rich.table.Table(
|
80
83
|
show_header=True,
|
@@ -94,18 +97,27 @@ class VoiceConfigurationsController:
|
|
94
97
|
for column in column_args:
|
95
98
|
config_table.add_column(column, **column_args[column])
|
96
99
|
|
100
|
+
config_details = []
|
101
|
+
|
97
102
|
for config in voice_configs:
|
98
|
-
attached_agents = [x.
|
99
|
-
|
100
|
-
config.name,
|
101
|
-
config.voice_configuration_id,
|
102
|
-
config.speech_to_text.provider,
|
103
|
-
config.text_to_speech.provider,
|
104
|
-
|
103
|
+
attached_agents = [x.name or x.id for x in config.attached_agents]
|
104
|
+
entry = VoiceConfigurationListEntry(
|
105
|
+
name=config.name,
|
106
|
+
id=config.voice_configuration_id,
|
107
|
+
speech_to_text_provider=config.speech_to_text.provider,
|
108
|
+
text_to_speech_provider=config.text_to_speech.provider,
|
109
|
+
attached_agents=attached_agents
|
105
110
|
)
|
111
|
+
config_details.append(entry)
|
112
|
+
config_table.add_row(*entry.get_row_details())
|
106
113
|
|
107
|
-
|
108
|
-
|
114
|
+
match format:
|
115
|
+
case ListFormats.JSON:
|
116
|
+
return config_details
|
117
|
+
case ListFormats.Table:
|
118
|
+
return rich_table_to_markdown(config_table)
|
119
|
+
case _:
|
120
|
+
rich.print(config_table)
|
109
121
|
|
110
122
|
def create_voice_config(self, voice_config: VoiceConfiguration) -> str | None:
|
111
123
|
client = self.get_voice_configurations_client()
|
@@ -0,0 +1,26 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
from rich.table import Table
|
3
|
+
|
4
|
+
class ListFormats(str, Enum):
|
5
|
+
Table = "table"
|
6
|
+
JSON = "json"
|
7
|
+
|
8
|
+
def __str__(self):
|
9
|
+
return self.value
|
10
|
+
|
11
|
+
def __repr__(self):
|
12
|
+
return repr(self.value)
|
13
|
+
|
14
|
+
def rich_table_to_markdown(table: Table) -> str:
|
15
|
+
headers = [column.header for column in table.columns]
|
16
|
+
cols = [[cell for cell in col.cells] for col in table.columns]
|
17
|
+
rows = list(map(list, zip(*cols)))
|
18
|
+
|
19
|
+
# Header row
|
20
|
+
md = "| " + " | ".join(headers) + " |\n"
|
21
|
+
# Separator row
|
22
|
+
md += "| " + " | ".join(["---"] * len(headers)) + " |\n"
|
23
|
+
# # Data rows
|
24
|
+
for row in rows:
|
25
|
+
md += "| " + " | ".join(row) + " |\n"
|
26
|
+
return md
|
@@ -22,6 +22,7 @@ AUTH_MCSP_TOKEN_OPT = "wxo_mcsp_token"
|
|
22
22
|
AUTH_MCSP_TOKEN_EXPIRY_OPT = "wxo_mcsp_token_expiry"
|
23
23
|
CONTEXT_ACTIVE_ENV_OPT = "active_environment"
|
24
24
|
PYTHON_REGISTRY_TYPE_OPT = "type"
|
25
|
+
PYTHON_REGISTRY_SKIP_VERSION_CHECK_OPT = "skip_version_check"
|
25
26
|
PYTHON_REGISTRY_TEST_PACKAGE_VERSION_OVERRIDE_OPT = "test_package_version_override"
|
26
27
|
ENV_WXO_URL_OPT = "wxo_url"
|
27
28
|
ENV_IAM_URL_OPT = "iam_url"
|
@@ -40,7 +41,8 @@ DEFAULT_CONFIG_FILE_CONTENT = {
|
|
40
41
|
CONTEXT_SECTION_HEADER: {CONTEXT_ACTIVE_ENV_OPT: None},
|
41
42
|
PYTHON_REGISTRY_HEADER: {
|
42
43
|
PYTHON_REGISTRY_TYPE_OPT: str(RegistryType.PYPI),
|
43
|
-
PYTHON_REGISTRY_TEST_PACKAGE_VERSION_OVERRIDE_OPT: None
|
44
|
+
PYTHON_REGISTRY_TEST_PACKAGE_VERSION_OVERRIDE_OPT: None,
|
45
|
+
PYTHON_REGISTRY_SKIP_VERSION_CHECK_OPT: False
|
44
46
|
},
|
45
47
|
ENVIRONMENTS_SECTION_HEADER: {
|
46
48
|
PROTECTED_ENV_NAME: {
|
@@ -85,7 +87,8 @@ def _check_if_auth_config_file(folder, file):
|
|
85
87
|
|
86
88
|
def clear_protected_env_credentials_token():
|
87
89
|
auth_cfg = Config(config_file_folder=AUTH_CONFIG_FILE_FOLDER, config_file=AUTH_CONFIG_FILE)
|
88
|
-
auth_cfg.
|
90
|
+
if auth_cfg.exists(AUTH_SECTION_HEADER, PROTECTED_ENV_NAME, AUTH_MCSP_TOKEN_OPT):
|
91
|
+
auth_cfg.delete(AUTH_SECTION_HEADER, PROTECTED_ENV_NAME, AUTH_MCSP_TOKEN_OPT)
|
89
92
|
|
90
93
|
|
91
94
|
class ConfigFileTypes(str, Enum):
|
@@ -230,3 +233,31 @@ class Config:
|
|
230
233
|
|
231
234
|
with open(self.config_file_path, 'w') as conf_file:
|
232
235
|
yaml.dump(deletion_data, conf_file, allow_unicode=True)
|
236
|
+
|
237
|
+
def exists(self, *args) -> bool:
|
238
|
+
"""
|
239
|
+
Determines if an item of arbitrary depth exists in the config file.
|
240
|
+
Takes an arbitrary number of args. Uses the args in order
|
241
|
+
as keys to access deeper sections of the config and then deleting the last specified key.
|
242
|
+
"""
|
243
|
+
if len(args) < 1:
|
244
|
+
raise BadRequest("Config.delete() requires at least one positional argument")
|
245
|
+
|
246
|
+
config_data = {}
|
247
|
+
try:
|
248
|
+
with open(self.config_file_path, 'r') as conf_file:
|
249
|
+
config_data = yaml_safe_load(conf_file) or {}
|
250
|
+
except FileNotFoundError:
|
251
|
+
pass
|
252
|
+
|
253
|
+
expression = "config_data"
|
254
|
+
for key in args:
|
255
|
+
temp = eval(expression)
|
256
|
+
|
257
|
+
if not isinstance(temp, dict) or key not in temp:
|
258
|
+
return False
|
259
|
+
|
260
|
+
else:
|
261
|
+
expression += f"['{key}']"
|
262
|
+
|
263
|
+
return True
|
@@ -3,27 +3,14 @@ from typing import List
|
|
3
3
|
from ibm_cloud_sdk_core.authenticators import MCSPAuthenticator
|
4
4
|
from pydantic import BaseModel, ValidationError
|
5
5
|
from typing import Optional
|
6
|
-
from enum import Enum
|
7
6
|
|
8
7
|
from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient, ClientAPIException
|
9
|
-
from ibm_watsonx_orchestrate.agent_builder.connections.types import ConnectionEnvironment, ConnectionPreference, ConnectionConfiguration, ConnectionAuthType, ConnectionSecurityScheme, IdpConfigData, AppConfigData, ConnectionType
|
8
|
+
from ibm_watsonx_orchestrate.agent_builder.connections.types import ConnectionEnvironment, ConnectionPreference, ConnectionConfiguration, ConnectionAuthType, ConnectionSecurityScheme, IdpConfigData, AppConfigData, ConnectionType, FetchConfigAuthTypes
|
10
9
|
from ibm_watsonx_orchestrate.client.utils import is_cpd_env, is_local_dev
|
11
10
|
|
12
11
|
import logging
|
13
12
|
logger = logging.getLogger(__name__)
|
14
13
|
|
15
|
-
|
16
|
-
class FetchConfigAuthTypes(str, Enum):
|
17
|
-
BASIC_AUTH = ConnectionType.BASIC_AUTH.value
|
18
|
-
BEARER_TOKEN = ConnectionType.BEARER_TOKEN.value
|
19
|
-
API_KEY_AUTH = ConnectionType.API_KEY_AUTH.value
|
20
|
-
OAUTH2_AUTH_CODE = ConnectionType.OAUTH2_AUTH_CODE.value
|
21
|
-
OAUTH2_IMPLICIT = 'oauth2_implicit'
|
22
|
-
OAUTH2_PASSWORD = ConnectionType.OAUTH2_PASSWORD.value
|
23
|
-
OAUTH2_CLIENT_CREDS = ConnectionType.OAUTH2_CLIENT_CREDS.value
|
24
|
-
OAUTH_ON_BEHALF_OF_FLOW = ConnectionType.OAUTH_ON_BEHALF_OF_FLOW.value
|
25
|
-
KEY_VALUE = ConnectionType.KEY_VALUE.value
|
26
|
-
|
27
14
|
class ListConfigsResponse(BaseModel):
|
28
15
|
connection_id: str = None,
|
29
16
|
app_id: str = None
|