ibm-watsonx-orchestrate 1.7.0a0__py3-none-any.whl → 1.8.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 +1 -1
- ibm_watsonx_orchestrate/agent_builder/agents/agent.py +3 -3
- ibm_watsonx_orchestrate/agent_builder/agents/assistant_agent.py +3 -2
- ibm_watsonx_orchestrate/agent_builder/agents/external_agent.py +3 -2
- ibm_watsonx_orchestrate/agent_builder/agents/types.py +26 -9
- ibm_watsonx_orchestrate/agent_builder/agents/webchat_customizations/prompts.py +1 -0
- ibm_watsonx_orchestrate/agent_builder/connections/connections.py +25 -10
- ibm_watsonx_orchestrate/agent_builder/connections/types.py +5 -9
- ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base_requests.py +1 -22
- ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +1 -17
- ibm_watsonx_orchestrate/agent_builder/tools/base_tool.py +2 -1
- ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py +14 -13
- ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py +136 -92
- ibm_watsonx_orchestrate/agent_builder/tools/types.py +10 -9
- ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +7 -7
- ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +35 -7
- ibm_watsonx_orchestrate/cli/commands/channels/webchat/channels_webchat_controller.py +33 -23
- ibm_watsonx_orchestrate/cli/commands/chat/chat_command.py +2 -0
- ibm_watsonx_orchestrate/cli/commands/connections/connections_controller.py +6 -4
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_command.py +65 -0
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_controller.py +293 -0
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_server_controller.py +154 -0
- ibm_watsonx_orchestrate/cli/commands/environment/environment_controller.py +6 -6
- ibm_watsonx_orchestrate/cli/commands/environment/types.py +2 -0
- ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_command.py +118 -30
- ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_controller.py +22 -9
- ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_command.py +0 -18
- ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +33 -19
- ibm_watsonx_orchestrate/cli/commands/models/models_command.py +1 -1
- ibm_watsonx_orchestrate/cli/commands/server/server_command.py +66 -9
- ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_command.py +1 -1
- ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +93 -14
- ibm_watsonx_orchestrate/cli/config.py +3 -3
- ibm_watsonx_orchestrate/cli/init_helper.py +10 -1
- ibm_watsonx_orchestrate/cli/main.py +5 -0
- ibm_watsonx_orchestrate/client/base_api_client.py +12 -0
- ibm_watsonx_orchestrate/client/copilot/cpe/copilot_cpe_client.py +66 -0
- ibm_watsonx_orchestrate/client/knowledge_bases/knowledge_base_client.py +1 -1
- ibm_watsonx_orchestrate/client/local_service_instance.py +3 -1
- ibm_watsonx_orchestrate/client/service_instance.py +33 -7
- ibm_watsonx_orchestrate/client/utils.py +48 -9
- ibm_watsonx_orchestrate/docker/compose-lite.yml +16 -4
- ibm_watsonx_orchestrate/docker/default.env +25 -15
- ibm_watsonx_orchestrate/flow_builder/flows/__init__.py +3 -1
- ibm_watsonx_orchestrate/flow_builder/flows/decorators.py +4 -2
- ibm_watsonx_orchestrate/flow_builder/flows/events.py +10 -9
- ibm_watsonx_orchestrate/flow_builder/flows/flow.py +91 -20
- ibm_watsonx_orchestrate/flow_builder/node.py +12 -1
- ibm_watsonx_orchestrate/flow_builder/types.py +169 -16
- ibm_watsonx_orchestrate/flow_builder/utils.py +121 -6
- ibm_watsonx_orchestrate/utils/exceptions.py +23 -0
- {ibm_watsonx_orchestrate-1.7.0a0.dist-info → ibm_watsonx_orchestrate-1.8.0b0.dist-info}/METADATA +5 -5
- {ibm_watsonx_orchestrate-1.7.0a0.dist-info → ibm_watsonx_orchestrate-1.8.0b0.dist-info}/RECORD +56 -52
- ibm_watsonx_orchestrate/flow_builder/resources/flow_status.openapi.yml +0 -66
- {ibm_watsonx_orchestrate-1.7.0a0.dist-info → ibm_watsonx_orchestrate-1.8.0b0.dist-info}/WHEEL +0 -0
- {ibm_watsonx_orchestrate-1.7.0a0.dist-info → ibm_watsonx_orchestrate-1.8.0b0.dist-info}/entry_points.txt +0 -0
- {ibm_watsonx_orchestrate-1.7.0a0.dist-info → ibm_watsonx_orchestrate-1.8.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -41,7 +41,7 @@ from ibm_watsonx_orchestrate.client.connections import get_connections_client, g
|
|
41
41
|
from ibm_watsonx_orchestrate.client.utils import instantiate_client, is_local_dev
|
42
42
|
from ibm_watsonx_orchestrate.flow_builder.utils import import_flow_support_tools
|
43
43
|
from ibm_watsonx_orchestrate.utils.utils import sanatize_app_id
|
44
|
-
from ibm_watsonx_orchestrate.
|
44
|
+
from ibm_watsonx_orchestrate.utils.exceptions import BadRequest
|
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 = {
|
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
|
-
|
93
|
-
|
94
|
-
|
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 = {
|
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
|
-
|
196
|
-
|
197
|
-
|
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,7 +400,14 @@ 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. This tool can query the status of a flow tool instance.
|
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].
|
404
|
+
|
405
|
+
[bold cyan]Experimental Features - Scheduling Flows and Agents: [/bold cyan]
|
406
|
+
- You can now schedule any Flows to be run on a later time. Just include the [bold green]"schedulable=True"[/bold green] attribute in the @flow decorator.
|
407
|
+
- Once enabled, you can schedule a flow by saying something like: [bold green]Can you schedule the flow <flow_name> to run everyday at 7am EST for 3 times?[/bold green]
|
408
|
+
- To schedule an agent, see the example in [bold green]examples/flow_builder/agent_scheduler[/bold green]. Use that to import the [bold green]agent_run[/bold green] tool to your agent.
|
409
|
+
- Use [bold green]agent_run[/bold green] tool to schedule an agent. For example: [bold green]Can you schedule the agent <agent_name> to run every weekday at 8am UK time?[/bold green]
|
410
|
+
- In scheduling, it is important to mention timezone or UTC time (also known as Greenwich Mean Time or Coordinated Universal Time) will be used.
|
349
411
|
|
350
412
|
"""
|
351
413
|
|
@@ -422,7 +484,7 @@ The [bold]flow tool[/bold] is being imported from [green]`{file}`[/green].
|
|
422
484
|
permission="read_only",
|
423
485
|
flow_model=model)
|
424
486
|
|
425
|
-
tools = import_flow_support_tools()
|
487
|
+
tools = import_flow_support_tools(model=model)
|
426
488
|
|
427
489
|
tools.append(tool)
|
428
490
|
|
@@ -499,7 +561,7 @@ class ToolsController:
|
|
499
561
|
tools = []
|
500
562
|
logger.warning("Skill Import not implemented yet")
|
501
563
|
case _:
|
502
|
-
raise
|
564
|
+
raise BadRequest("Invalid kind selected")
|
503
565
|
|
504
566
|
for tool in tools:
|
505
567
|
yield tool
|
@@ -507,7 +569,20 @@ class ToolsController:
|
|
507
569
|
|
508
570
|
def list_tools(self, verbose=False):
|
509
571
|
response = self.get_client().get()
|
510
|
-
tool_specs = [
|
572
|
+
tool_specs = []
|
573
|
+
parse_errors = []
|
574
|
+
|
575
|
+
for tool in response:
|
576
|
+
try:
|
577
|
+
tool_specs.append(ToolSpec.model_validate(tool))
|
578
|
+
except Exception as e:
|
579
|
+
name = tool.get('name', None)
|
580
|
+
parse_errors.append([
|
581
|
+
f"Tool '{name}' could not be parsed",
|
582
|
+
json.dumps(tool),
|
583
|
+
e
|
584
|
+
])
|
585
|
+
|
511
586
|
tools = [BaseTool(spec=spec) for spec in tool_specs]
|
512
587
|
|
513
588
|
if verbose:
|
@@ -596,6 +671,10 @@ class ToolsController:
|
|
596
671
|
|
597
672
|
rich.print(table)
|
598
673
|
|
674
|
+
for error in parse_errors:
|
675
|
+
for l in error:
|
676
|
+
logger.error(l)
|
677
|
+
|
599
678
|
def get_all_tools(self) -> dict:
|
600
679
|
return {entry["name"]: entry["id"] for entry in self.get_client().get()}
|
601
680
|
|
@@ -6,6 +6,7 @@ from copy import deepcopy
|
|
6
6
|
from ibm_watsonx_orchestrate.cli.commands.tools.types import RegistryType
|
7
7
|
from ibm_watsonx_orchestrate.utils.utils import yaml_safe_load
|
8
8
|
from enum import Enum
|
9
|
+
from ibm_watsonx_orchestrate.utils.exceptions import BadRequest
|
9
10
|
|
10
11
|
# Section Headers
|
11
12
|
AUTH_SECTION_HEADER = "auth"
|
@@ -82,7 +83,6 @@ def _check_if_default_config_file(folder, file):
|
|
82
83
|
def _check_if_auth_config_file(folder, file):
|
83
84
|
return folder == AUTH_CONFIG_FILE_FOLDER and file == AUTH_CONFIG_FILE
|
84
85
|
|
85
|
-
|
86
86
|
def clear_protected_env_credentials_token():
|
87
87
|
auth_cfg = Config(config_file_folder=AUTH_CONFIG_FILE_FOLDER, config_file=AUTH_CONFIG_FILE)
|
88
88
|
auth_cfg.delete(AUTH_SECTION_HEADER, PROTECTED_ENV_NAME, AUTH_MCSP_TOKEN_OPT)
|
@@ -91,7 +91,7 @@ def clear_protected_env_credentials_token():
|
|
91
91
|
class ConfigFileTypes(str, Enum):
|
92
92
|
AUTH = 'auth'
|
93
93
|
CONFIG = 'config'
|
94
|
-
|
94
|
+
DOCPROC_FEATURE_CONF= 'docproc_feature'
|
95
95
|
|
96
96
|
class Config:
|
97
97
|
|
@@ -209,7 +209,7 @@ class Config:
|
|
209
209
|
as keys to access deeper sections of the config and then deleting the last specified key.
|
210
210
|
"""
|
211
211
|
if len(args) < 1:
|
212
|
-
raise
|
212
|
+
raise BadRequest("Config.delete() requires at least one positional argument")
|
213
213
|
|
214
214
|
config_data = {}
|
215
215
|
try:
|
@@ -4,6 +4,7 @@ from typing import Optional
|
|
4
4
|
from rich import print as pprint
|
5
5
|
from dotenv import dotenv_values
|
6
6
|
import typer
|
7
|
+
import sys
|
7
8
|
|
8
9
|
from ibm_watsonx_orchestrate.cli.config import Config, PYTHON_REGISTRY_HEADER, \
|
9
10
|
PYTHON_REGISTRY_TEST_PACKAGE_VERSION_OVERRIDE_OPT
|
@@ -29,7 +30,6 @@ def version_callback(checkVersion: bool=True):
|
|
29
30
|
|
30
31
|
raise typer.Exit()
|
31
32
|
|
32
|
-
|
33
33
|
|
34
34
|
def init_callback(
|
35
35
|
ctx: typer.Context,
|
@@ -38,6 +38,15 @@ def init_callback(
|
|
38
38
|
"--version",
|
39
39
|
help="Show the installed version of the ADK and Developer Edition Tags",
|
40
40
|
callback=version_callback
|
41
|
+
),
|
42
|
+
debug: Optional[bool] = typer.Option(
|
43
|
+
False,
|
44
|
+
"--debug",
|
45
|
+
help="Enable debug mode"
|
41
46
|
)
|
42
47
|
):
|
48
|
+
if debug:
|
49
|
+
sys.tracebacklimit = 40
|
50
|
+
else:
|
51
|
+
sys.tracebacklimit = 0
|
43
52
|
pass
|
@@ -1,4 +1,6 @@
|
|
1
|
+
|
1
2
|
import typer
|
3
|
+
import sys
|
2
4
|
|
3
5
|
from ibm_watsonx_orchestrate.cli.commands.connections.connections_command import connections_app
|
4
6
|
from ibm_watsonx_orchestrate.cli.commands.login.login_command import login_app
|
@@ -13,6 +15,7 @@ from ibm_watsonx_orchestrate.cli.commands.channels.channels_command import chann
|
|
13
15
|
from ibm_watsonx_orchestrate.cli.commands.knowledge_bases.knowledge_bases_command import knowledge_bases_app
|
14
16
|
from ibm_watsonx_orchestrate.cli.commands.toolkit.toolkit_command import toolkits_app
|
15
17
|
from ibm_watsonx_orchestrate.cli.commands.evaluations.evaluations_command import evaluation_app
|
18
|
+
from ibm_watsonx_orchestrate.cli.commands.copilot.copilot_command import copilot_app
|
16
19
|
from ibm_watsonx_orchestrate.cli.init_helper import init_callback
|
17
20
|
|
18
21
|
import urllib3
|
@@ -24,6 +27,7 @@ app = typer.Typer(
|
|
24
27
|
pretty_exceptions_enable=False,
|
25
28
|
callback=init_callback
|
26
29
|
)
|
30
|
+
|
27
31
|
app.add_typer(login_app)
|
28
32
|
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)')
|
29
33
|
app.add_typer(agents_app, name="agents", help='Interact with the agents in your active env')
|
@@ -36,6 +40,7 @@ app.add_typer(chat_app, name="chat", help='Launch the chat ui for your local Dev
|
|
36
40
|
app.add_typer(models_app, name="models", help='List the available large language models (llms) that can be used in your agent definitions')
|
37
41
|
app.add_typer(channel_app, name="channels", help="Configure channels where your agent can exist on (such as embedded webchat)")
|
38
42
|
app.add_typer(evaluation_app, name="evaluations", help='Evaluate the performance of your agents in your active env')
|
43
|
+
app.add_typer(copilot_app, name="copilot", help='Access AI powered assistance to help refine your agents')
|
39
44
|
app.add_typer(settings_app, name="settings", help='Configure the settings for your active env')
|
40
45
|
|
41
46
|
if __name__ == "__main__":
|
@@ -3,6 +3,7 @@ import json
|
|
3
3
|
import requests
|
4
4
|
from abc import ABC, abstractmethod
|
5
5
|
from ibm_cloud_sdk_core.authenticators import MCSPAuthenticator
|
6
|
+
from typing_extensions import List
|
6
7
|
|
7
8
|
|
8
9
|
class ClientAPIException(requests.HTTPError):
|
@@ -62,6 +63,17 @@ class BaseAPIClient:
|
|
62
63
|
self._check_response(response)
|
63
64
|
return response.json() if response.text else {}
|
64
65
|
|
66
|
+
def _post_nd_json(self, path: str, data: dict = None, files: dict = None) -> List[dict]:
|
67
|
+
url = f"{self.base_url}{path}"
|
68
|
+
response = requests.post(url, headers=self._get_headers(), json=data, files=files)
|
69
|
+
self._check_response(response)
|
70
|
+
|
71
|
+
res = []
|
72
|
+
if response.text:
|
73
|
+
for line in response.text.splitlines():
|
74
|
+
res.append(json.loads(line))
|
75
|
+
return res
|
76
|
+
|
65
77
|
def _post_form_data(self, path: str, data: dict = None, files: dict = None) -> dict:
|
66
78
|
url = f"{self.base_url}{path}"
|
67
79
|
# Use data argument instead of json so data is encoded as application/x-www-form-urlencoded
|
@@ -0,0 +1,66 @@
|
|
1
|
+
from typing import Dict, Any
|
2
|
+
from uuid import uuid4
|
3
|
+
|
4
|
+
from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient
|
5
|
+
|
6
|
+
|
7
|
+
class CPEClient(BaseAPIClient):
|
8
|
+
"""
|
9
|
+
Client to handle CRUD operations for Conversational Prompt Engineering Service
|
10
|
+
"""
|
11
|
+
|
12
|
+
def __init__(self, *args, **kwargs):
|
13
|
+
self.chat_id = str(uuid4())
|
14
|
+
super().__init__(*args, **kwargs)
|
15
|
+
self.base_url = kwargs.get("base_url", self.base_url)
|
16
|
+
self.chat_model_name = 'llama-3-3-70b-instruct'
|
17
|
+
|
18
|
+
def _get_headers(self) -> dict:
|
19
|
+
return {
|
20
|
+
"chat_id": self.chat_id
|
21
|
+
}
|
22
|
+
|
23
|
+
|
24
|
+
def submit_pre_cpe_chat(self, user_message: str | None =None, tools: Dict[str, Any] = None) -> dict:
|
25
|
+
payload = {
|
26
|
+
"message": user_message,
|
27
|
+
"tools": tools,
|
28
|
+
"chat_id": self.chat_id,
|
29
|
+
"chat_model_name": self.chat_model_name
|
30
|
+
}
|
31
|
+
|
32
|
+
response = self._post_nd_json("/wxo-cpe/create-agent", data=payload)
|
33
|
+
|
34
|
+
if response:
|
35
|
+
return response[-1]
|
36
|
+
|
37
|
+
|
38
|
+
def init_with_context(self, model: str | None = None, context_data: Dict[str, Any] = None) -> dict:
|
39
|
+
payload = {
|
40
|
+
"context_data": context_data,
|
41
|
+
"chat_id": self.chat_id
|
42
|
+
}
|
43
|
+
|
44
|
+
if model:
|
45
|
+
payload["target_model_name"] = model
|
46
|
+
|
47
|
+
response = self._post_nd_json("/wxo-cpe/init_cpe_from_wxo", data=payload)
|
48
|
+
|
49
|
+
if response:
|
50
|
+
return response[-1]
|
51
|
+
|
52
|
+
|
53
|
+
def invoke(self, prompt: str, model: str | None = None, context_data: Dict[str, Any] = None) -> dict:
|
54
|
+
payload = {
|
55
|
+
"prompt": prompt,
|
56
|
+
"context_data": context_data,
|
57
|
+
"chat_id": self.chat_id
|
58
|
+
}
|
59
|
+
|
60
|
+
if model:
|
61
|
+
payload["target_model_name"] = model
|
62
|
+
|
63
|
+
response = self._post_nd_json("/wxo-cpe/invoke", data=payload)
|
64
|
+
|
65
|
+
if response:
|
66
|
+
return response[-1]
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient
|
1
|
+
from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient, ClientAPIException
|
2
2
|
import json
|
3
3
|
from typing_extensions import List
|
4
4
|
from ibm_watsonx_orchestrate.client.utils import is_local_dev
|
@@ -2,6 +2,8 @@ from ibm_watsonx_orchestrate.client.base_service_instance import BaseServiceInst
|
|
2
2
|
import logging
|
3
3
|
import requests
|
4
4
|
from ibm_watsonx_orchestrate.client.credentials import Credentials
|
5
|
+
import json
|
6
|
+
import base64
|
5
7
|
|
6
8
|
logger = logging.getLogger(__name__)
|
7
9
|
|
@@ -11,7 +13,7 @@ DEFAULT_TENANT = {
|
|
11
13
|
"tags": ["test"]
|
12
14
|
}
|
13
15
|
|
14
|
-
DEFAULT_USER =
|
16
|
+
DEFAULT_USER = json.loads(base64.b64decode('eyJ1c2VybmFtZSI6ICJ3eG8uYXJjaGVyQGlibS5jb20iLCJwYXNzd29yZCI6ICJ3YXRzb254In0='))
|
15
17
|
DEFAULT_LOCAL_SERVICE_URL = "http://localhost:4321"
|
16
18
|
DEFAULT_LOCAL_AUTH_ENDPOINT = f"{DEFAULT_LOCAL_SERVICE_URL}/api/v1/auth/token"
|
17
19
|
DEFAULT_LOCAL_TENANT_URL = f"{DEFAULT_LOCAL_SERVICE_URL}/api/v1/tenants"
|
@@ -6,6 +6,7 @@
|
|
6
6
|
from __future__ import annotations
|
7
7
|
|
8
8
|
from ibm_cloud_sdk_core.authenticators import MCSPAuthenticator
|
9
|
+
from ibm_cloud_sdk_core.authenticators import MCSPV2Authenticator
|
9
10
|
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator
|
10
11
|
from ibm_cloud_sdk_core.authenticators import CloudPakForDataAuthenticator
|
11
12
|
|
@@ -51,24 +52,47 @@ class ServiceInstance(BaseServiceInstance):
|
|
51
52
|
def _create_token(self) -> str:
|
52
53
|
if not self._credentials.auth_type:
|
53
54
|
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 '
|
55
|
+
logger.warning("Using IBM IAM Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of 'mcsp', 'mcsp_v1', 'mcsp_v2' or 'cpd' ")
|
55
56
|
return self._authenticate(EnvironmentAuthType.IBM_CLOUD_IAM)
|
56
57
|
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'
|
58
|
+
logger.warning("Using CPD Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of 'ibm_iam', 'mcsp', 'mcsp_v1' or 'mcsp_v2' ")
|
58
59
|
return self._authenticate(EnvironmentAuthType.CPD)
|
59
60
|
else:
|
60
|
-
logger.warning("Using MCSP Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of 'ibm_iam'
|
61
|
-
|
61
|
+
logger.warning("Using MCSP Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of 'ibm_iam', 'mcsp_v1', 'mcsp_v2' or 'cpd' ")
|
62
|
+
try:
|
63
|
+
return self._authenticate(EnvironmentAuthType.MCSP_V1)
|
64
|
+
except:
|
65
|
+
return self._authenticate(EnvironmentAuthType.MCSP_V2)
|
66
|
+
auth_type = self._credentials.auth_type.lower()
|
67
|
+
if auth_type == "mcsp":
|
68
|
+
try:
|
69
|
+
return self._authenticate(EnvironmentAuthType.MCSP_V1)
|
70
|
+
except:
|
71
|
+
return self._authenticate(EnvironmentAuthType.MCSP_V2)
|
72
|
+
elif auth_type == "mcsp_v1":
|
73
|
+
return self._authenticate(EnvironmentAuthType.MCSP_V1)
|
74
|
+
elif auth_type == "mcsp_v2":
|
75
|
+
return self._authenticate(EnvironmentAuthType.MCSP_V2)
|
62
76
|
else:
|
63
|
-
return self._authenticate(
|
77
|
+
return self._authenticate(auth_type)
|
64
78
|
|
65
79
|
def _authenticate(self, auth_type: str) -> str:
|
66
80
|
"""Handles authentication based on the auth_type."""
|
67
81
|
try:
|
68
82
|
match auth_type:
|
69
|
-
case EnvironmentAuthType.MCSP:
|
83
|
+
case EnvironmentAuthType.MCSP | EnvironmentAuthType.MCSP_V1:
|
70
84
|
url = self._credentials.iam_url if self._credentials.iam_url is not None else "https://iam.platform.saas.ibm.com"
|
71
85
|
authenticator = MCSPAuthenticator(apikey=self._credentials.api_key, url=url)
|
86
|
+
case EnvironmentAuthType.MCSP_V2:
|
87
|
+
url = self._credentials.iam_url if self._credentials.iam_url is not None else "https://account-iam.platform.saas.ibm.com"
|
88
|
+
wxo_url = self._credentials.url
|
89
|
+
instance_id = wxo_url.split("instances/")[1]
|
90
|
+
authenticator = MCSPV2Authenticator(
|
91
|
+
apikey=self._credentials.api_key,
|
92
|
+
url=url,
|
93
|
+
scope_collection_type="services",
|
94
|
+
scope_id=instance_id
|
95
|
+
)
|
72
96
|
case EnvironmentAuthType.IBM_CLOUD_IAM:
|
73
97
|
authenticator = IAMAuthenticator(apikey=self._credentials.api_key, url=self._credentials.iam_url)
|
74
98
|
case EnvironmentAuthType.CPD:
|
@@ -100,8 +124,10 @@ class ServiceInstance(BaseServiceInstance):
|
|
100
124
|
raise ClientError(f"Unsupported authentication type: {auth_type}")
|
101
125
|
|
102
126
|
return authenticator.token_manager.get_token()
|
127
|
+
|
103
128
|
except Exception as e:
|
104
|
-
raise ClientError(f"Error getting {auth_type.upper()} Token",
|
129
|
+
raise ClientError(f"Error getting {auth_type.upper()} Token", logg_messages=False)
|
130
|
+
|
105
131
|
|
106
132
|
|
107
133
|
def _is_token_refresh_possible(self) -> bool:
|
@@ -21,17 +21,32 @@ from typing import TypeVar
|
|
21
21
|
import os
|
22
22
|
import jwt
|
23
23
|
import time
|
24
|
+
import sys
|
24
25
|
|
25
26
|
logger = logging.getLogger(__name__)
|
26
27
|
LOCK = Lock()
|
27
28
|
T = TypeVar("T", bound=BaseAPIClient)
|
28
29
|
|
30
|
+
def get_current_env_url() -> str:
|
31
|
+
cfg = Config()
|
32
|
+
active_env = cfg.read(CONTEXT_SECTION_HEADER, CONTEXT_ACTIVE_ENV_OPT)
|
33
|
+
return cfg.get(ENVIRONMENTS_SECTION_HEADER, active_env, ENV_WXO_URL_OPT)
|
34
|
+
|
35
|
+
def get_cpd_instance_id_from_url(url: str | None = None) -> str:
|
36
|
+
if url is None:
|
37
|
+
url = get_current_env_url()
|
38
|
+
|
39
|
+
if not is_cpd_env(url):
|
40
|
+
logger.error(f"The host {url} is not a CPD instance")
|
41
|
+
sys.exit(1)
|
42
|
+
|
43
|
+
url_fragments = url.split('/')
|
44
|
+
return url_fragments[-1] if url_fragments[-1] else url_fragments[-2]
|
45
|
+
|
29
46
|
|
30
47
|
def is_local_dev(url: str | None = None) -> bool:
|
31
48
|
if url is None:
|
32
|
-
|
33
|
-
active_env = cfg.read(CONTEXT_SECTION_HEADER, CONTEXT_ACTIVE_ENV_OPT)
|
34
|
-
url = cfg.get(ENVIRONMENTS_SECTION_HEADER, active_env, ENV_WXO_URL_OPT)
|
49
|
+
url = get_current_env_url()
|
35
50
|
|
36
51
|
if url.startswith("http://localhost"):
|
37
52
|
return True
|
@@ -47,21 +62,45 @@ def is_local_dev(url: str | None = None) -> bool:
|
|
47
62
|
|
48
63
|
return False
|
49
64
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
53
|
-
|
65
|
+
def is_cpd_env(url: str | None = None) -> bool:
|
66
|
+
if url is None:
|
67
|
+
url = get_current_env_url()
|
68
|
+
|
69
|
+
if url.lower().startswith("https://cpd"):
|
70
|
+
return True
|
71
|
+
return False
|
72
|
+
|
73
|
+
def is_saas_env():
|
74
|
+
return is_ga_platform() or is_ibm_cloud_platform()
|
75
|
+
|
76
|
+
def is_ibm_cloud_platform(url:str | None = None) -> bool:
|
77
|
+
if url is None:
|
78
|
+
url = get_current_env_url()
|
54
79
|
|
55
80
|
if url.__contains__("cloud.ibm.com"):
|
56
81
|
return True
|
57
82
|
return False
|
58
83
|
|
84
|
+
def is_ga_platform(url: str | None = None) -> bool:
|
85
|
+
if url is None:
|
86
|
+
url = get_current_env_url()
|
59
87
|
|
60
|
-
|
61
|
-
if url.lower().startswith("https://cpd"):
|
88
|
+
if url.__contains__("orchestrate.ibm.com"):
|
62
89
|
return True
|
63
90
|
return False
|
64
91
|
|
92
|
+
|
93
|
+
def get_environment() -> str:
|
94
|
+
if is_local_dev():
|
95
|
+
return "local"
|
96
|
+
if is_cpd_env():
|
97
|
+
return "cpd"
|
98
|
+
if is_ibm_cloud_platform():
|
99
|
+
return "ibmcloud"
|
100
|
+
if is_ga_platform():
|
101
|
+
return "ga"
|
102
|
+
return None
|
103
|
+
|
65
104
|
def check_token_validity(token: str) -> bool:
|
66
105
|
try:
|
67
106
|
token_claimset = jwt.decode(token, options={"verify_signature": False})
|
@@ -120,7 +120,7 @@ services:
|
|
120
120
|
TEMPUS_RUNTIME_ENDPOINT: http://wxo-tempus-runtime:9044
|
121
121
|
CONNECTIONS_MANAGER_ENDPOINT: http://wxo-server-connection-manager:3001
|
122
122
|
AGENT_OPS_API_KEY: ${AGENTOPS_API_KEY}
|
123
|
-
AGENTS_OPS_RUNTIME_ENDPOINT: https://
|
123
|
+
AGENTS_OPS_RUNTIME_ENDPOINT: https://frontend-server:443
|
124
124
|
IS_OBSERVABILITY_FEATURE_ENABLED: "true"
|
125
125
|
ALLOW_INSECURE_TLS: "true"
|
126
126
|
command: 'npm start'
|
@@ -751,8 +751,8 @@ services:
|
|
751
751
|
SERVER_HTTP_PORT: "9044"
|
752
752
|
SERVER_CONTEXT_PATH:
|
753
753
|
SERVER_JWK_URL: https://wo-ibm-dev.verify.ibm.com/v1.0/endpoint/default/jwks
|
754
|
-
SERVER_STANDALONE_TASK_CALLBACK_URL: http://
|
755
|
-
SERVER_FLOW_CALLBACK_URL: http://
|
754
|
+
SERVER_STANDALONE_TASK_CALLBACK_URL: http://wxo-tempus-runtime:9044/task/callback/{thread_id}/{requestId}
|
755
|
+
SERVER_FLOW_CALLBACK_URL: http://wxo-tempus-runtime:9044/v1/flows/{flow_instance_id}/{task_instance_id}/resume/async
|
756
756
|
JWT_SECRET: ${JWT_SECRET}
|
757
757
|
SERVER_INTERNAL_PROTOCOL: http
|
758
758
|
SERVER_INTERNAL_HOSTNAME: wxo-tempus-runtime
|
@@ -782,6 +782,7 @@ services:
|
|
782
782
|
LOG_LEVEL: info
|
783
783
|
DISABLE_FLOW_BINDING: true
|
784
784
|
DOCPROC_ENABLED: ${DOCPROC_ENABLED:-false}
|
785
|
+
DOCPROC_BASE_URL: http://wxo-doc-processing-infra-standalone:9080
|
785
786
|
WO_API_KEY: ${WO_API_KEY}
|
786
787
|
WO_INSTANCE: ${WO_INSTANCE}
|
787
788
|
AUTHORIZATION_URL: ${AUTHORIZATION_URL}
|
@@ -795,6 +796,16 @@ services:
|
|
795
796
|
- 9044:9044
|
796
797
|
depends_on:
|
797
798
|
- wxo-server-db
|
799
|
+
|
800
|
+
cpe:
|
801
|
+
image: ${CPE_REGISTRY:-us.icr.io/watson-orchestrate-private}/wxo-prompt-optimizer:${CPE_TAG:-latest}
|
802
|
+
platform: linux/amd64
|
803
|
+
restart: unless-stopped
|
804
|
+
environment:
|
805
|
+
WATSONX_APIKEY: ${WATSONX_APIKEY}
|
806
|
+
WATSONX_SPACE_ID: ${WATSONX_SPACE_ID}
|
807
|
+
ports:
|
808
|
+
- 8081:8080
|
798
809
|
|
799
810
|
########################
|
800
811
|
# DOCPROC dependencies
|
@@ -863,7 +874,7 @@ services:
|
|
863
874
|
condition: service_completed_successfully
|
864
875
|
|
865
876
|
wdu-model-copy:
|
866
|
-
image: ${WDU_REGISTRY:-us.icr.io/watson-orchestrate-private}/document-processing/wdu-
|
877
|
+
image: ${WDU_REGISTRY:-us.icr.io/watson-orchestrate-private}/document-processing/wdu-models:${WDU_TAG:-2.4.0}
|
867
878
|
platform: linux/amd64
|
868
879
|
user: root
|
869
880
|
profiles:
|
@@ -902,6 +913,7 @@ services:
|
|
902
913
|
DPI_WO_WDU_SERVER_ENDPOINT: https://wxo-doc-processing-service:8080
|
903
914
|
# DPI_RAG_SERVER_ENDPOINT: https://wxo-doc-processing-llm-service:8083
|
904
915
|
DISABLE_TLS: true
|
916
|
+
STANDALONE_DPI: "true"
|
905
917
|
depends_on:
|
906
918
|
wxo-doc-processing-dpi-minio-init:
|
907
919
|
condition: service_completed_successfully
|