ibm-watsonx-orchestrate 1.10.2__py3-none-any.whl → 1.11.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.
- ibm_watsonx_orchestrate/__init__.py +2 -1
- ibm_watsonx_orchestrate/agent_builder/agents/types.py +13 -0
- ibm_watsonx_orchestrate/agent_builder/connections/types.py +53 -6
- ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +25 -10
- ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +10 -2
- ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +404 -173
- ibm_watsonx_orchestrate/cli/commands/connections/connections_command.py +33 -4
- ibm_watsonx_orchestrate/cli/commands/connections/connections_controller.py +62 -6
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_controller.py +6 -2
- ibm_watsonx_orchestrate/cli/commands/environment/environment_command.py +1 -1
- ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_command.py +174 -2
- ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_controller.py +93 -9
- ibm_watsonx_orchestrate/cli/commands/server/server_command.py +0 -3
- ibm_watsonx_orchestrate/cli/commands/server/types.py +15 -7
- ibm_watsonx_orchestrate/client/base_api_client.py +31 -10
- ibm_watsonx_orchestrate/client/connections/connections_client.py +14 -0
- ibm_watsonx_orchestrate/client/service_instance.py +19 -34
- ibm_watsonx_orchestrate/client/utils.py +3 -1
- ibm_watsonx_orchestrate/docker/compose-lite.yml +16 -11
- ibm_watsonx_orchestrate/docker/default.env +15 -13
- ibm_watsonx_orchestrate/flow_builder/data_map.py +4 -1
- ibm_watsonx_orchestrate/flow_builder/flows/flow.py +117 -7
- ibm_watsonx_orchestrate/flow_builder/node.py +76 -5
- ibm_watsonx_orchestrate/flow_builder/types.py +344 -10
- {ibm_watsonx_orchestrate-1.10.2.dist-info → ibm_watsonx_orchestrate-1.11.0.dist-info}/METADATA +2 -2
- {ibm_watsonx_orchestrate-1.10.2.dist-info → ibm_watsonx_orchestrate-1.11.0.dist-info}/RECORD +29 -29
- {ibm_watsonx_orchestrate-1.10.2.dist-info → ibm_watsonx_orchestrate-1.11.0.dist-info}/WHEEL +0 -0
- {ibm_watsonx_orchestrate-1.10.2.dist-info → ibm_watsonx_orchestrate-1.11.0.dist-info}/entry_points.txt +0 -0
- {ibm_watsonx_orchestrate-1.10.2.dist-info → ibm_watsonx_orchestrate-1.11.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,9 +1,22 @@
|
|
1
1
|
import json
|
2
|
-
|
2
|
+
from ibm_watsonx_orchestrate.utils.exceptions import BadRequest
|
3
3
|
import requests
|
4
4
|
from abc import ABC, abstractmethod
|
5
5
|
from ibm_cloud_sdk_core.authenticators import MCSPAuthenticator
|
6
6
|
from typing_extensions import List
|
7
|
+
from contextlib import contextmanager
|
8
|
+
|
9
|
+
@contextmanager
|
10
|
+
def ssl_handler():
|
11
|
+
try:
|
12
|
+
yield
|
13
|
+
except requests.exceptions.SSLError as e:
|
14
|
+
error_message = str(e)
|
15
|
+
if "self-signed certificate in certificate chain" in error_message:
|
16
|
+
reason = "[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain"
|
17
|
+
else:
|
18
|
+
reason = error_message
|
19
|
+
raise BadRequest(f"SSL handshake failed for request '{e.request.path_url}'. Reason: '{reason}'")
|
7
20
|
|
8
21
|
|
9
22
|
class ClientAPIException(requests.HTTPError):
|
@@ -50,7 +63,8 @@ class BaseAPIClient:
|
|
50
63
|
|
51
64
|
def _get(self, path: str, params: dict = None, data=None, return_raw=False) -> dict:
|
52
65
|
url = f"{self.base_url}{path}"
|
53
|
-
|
66
|
+
with ssl_handler():
|
67
|
+
response = requests.get(url, headers=self._get_headers(), params=params, data=data, verify=self.verify)
|
54
68
|
self._check_response(response)
|
55
69
|
if not return_raw:
|
56
70
|
return response.json()
|
@@ -59,13 +73,15 @@ class BaseAPIClient:
|
|
59
73
|
|
60
74
|
def _post(self, path: str, data: dict = None, files: dict = None) -> dict:
|
61
75
|
url = f"{self.base_url}{path}"
|
62
|
-
|
76
|
+
with ssl_handler():
|
77
|
+
response = requests.post(url, headers=self._get_headers(), json=data, files=files, verify=self.verify)
|
63
78
|
self._check_response(response)
|
64
79
|
return response.json() if response.text else {}
|
65
80
|
|
66
81
|
def _post_nd_json(self, path: str, data: dict = None, files: dict = None) -> List[dict]:
|
67
82
|
url = f"{self.base_url}{path}"
|
68
|
-
|
83
|
+
with ssl_handler():
|
84
|
+
response = requests.post(url, headers=self._get_headers(), json=data, files=files)
|
69
85
|
self._check_response(response)
|
70
86
|
|
71
87
|
res = []
|
@@ -76,33 +92,38 @@ class BaseAPIClient:
|
|
76
92
|
|
77
93
|
def _post_form_data(self, path: str, data: dict = None, files: dict = None) -> dict:
|
78
94
|
url = f"{self.base_url}{path}"
|
79
|
-
|
80
|
-
|
95
|
+
with ssl_handler():
|
96
|
+
# Use data argument instead of json so data is encoded as application/x-www-form-urlencoded
|
97
|
+
response = requests.post(url, headers=self._get_headers(), data=data, files=files, verify=self.verify)
|
81
98
|
self._check_response(response)
|
82
99
|
return response.json() if response.text else {}
|
83
100
|
|
84
101
|
def _put(self, path: str, data: dict = None) -> dict:
|
85
102
|
|
86
103
|
url = f"{self.base_url}{path}"
|
87
|
-
|
104
|
+
with ssl_handler():
|
105
|
+
response = requests.put(url, headers=self._get_headers(), json=data, verify=self.verify)
|
88
106
|
self._check_response(response)
|
89
107
|
return response.json() if response.text else {}
|
90
108
|
|
91
109
|
def _patch(self, path: str, data: dict = None) -> dict:
|
92
110
|
url = f"{self.base_url}{path}"
|
93
|
-
|
111
|
+
with ssl_handler():
|
112
|
+
response = requests.patch(url, headers=self._get_headers(), json=data, verify=self.verify)
|
94
113
|
self._check_response(response)
|
95
114
|
return response.json() if response.text else {}
|
96
115
|
|
97
116
|
def _patch_form_data(self, path: str, data: dict = None, files = None) -> dict:
|
98
117
|
url = f"{self.base_url}{path}"
|
99
|
-
|
118
|
+
with ssl_handler():
|
119
|
+
response = requests.patch(url, headers=self._get_headers(), data=data, files=files, verify=self.verify)
|
100
120
|
self._check_response(response)
|
101
121
|
return response.json() if response.text else {}
|
102
122
|
|
103
123
|
def _delete(self, path: str, data=None) -> dict:
|
104
124
|
url = f"{self.base_url}{path}"
|
105
|
-
|
125
|
+
with ssl_handler():
|
126
|
+
response = requests.delete(url, headers=self._get_headers(), json=data, verify=self.verify)
|
106
127
|
self._check_response(response)
|
107
128
|
return response.json() if response.text else {}
|
108
129
|
|
@@ -177,3 +177,17 @@ class ConnectionsClient(BaseAPIClient):
|
|
177
177
|
logger.warning(f"Connections not found. Returning connection ID: {conn_id}")
|
178
178
|
return conn_id
|
179
179
|
raise e
|
180
|
+
|
181
|
+
def get_drafts_by_ids(self, conn_ids) -> List[ListConfigsResponse]:
|
182
|
+
try:
|
183
|
+
res = self._get(f"/connections/applications?connectionIds={','.join(conn_ids)}")
|
184
|
+
import json
|
185
|
+
json.dumps(res)
|
186
|
+
return [ListConfigsResponse.model_validate(conn) for conn in res.get("applications", [])]
|
187
|
+
except ValidationError as e:
|
188
|
+
logger.error("Recieved unexpected response from server")
|
189
|
+
raise e
|
190
|
+
except ClientAPIException as e:
|
191
|
+
if e.response.status_code == 404:
|
192
|
+
return []
|
193
|
+
raise e
|
@@ -10,7 +10,7 @@ from ibm_cloud_sdk_core.authenticators import MCSPV2Authenticator
|
|
10
10
|
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator
|
11
11
|
from ibm_cloud_sdk_core.authenticators import CloudPakForDataAuthenticator
|
12
12
|
|
13
|
-
from ibm_watsonx_orchestrate.client.utils import check_token_validity, is_cpd_env
|
13
|
+
from ibm_watsonx_orchestrate.client.utils import check_token_validity, is_cpd_env, is_ibm_cloud_platform
|
14
14
|
from ibm_watsonx_orchestrate.client.base_service_instance import BaseServiceInstance
|
15
15
|
from ibm_watsonx_orchestrate.cli.commands.environment.types import EnvironmentAuthType
|
16
16
|
|
@@ -21,14 +21,6 @@ from ibm_watsonx_orchestrate.client.client_errors import (
|
|
21
21
|
import logging
|
22
22
|
logger = logging.getLogger(__name__)
|
23
23
|
|
24
|
-
from ibm_watsonx_orchestrate.cli.config import (
|
25
|
-
Config,
|
26
|
-
CONTEXT_SECTION_HEADER,
|
27
|
-
CONTEXT_ACTIVE_ENV_OPT,
|
28
|
-
ENVIRONMENTS_SECTION_HEADER,
|
29
|
-
ENV_WXO_URL_OPT
|
30
|
-
)
|
31
|
-
|
32
24
|
class ServiceInstance(BaseServiceInstance):
|
33
25
|
"""Connect, get details, and check usage of a Watson Machine Learning service instance."""
|
34
26
|
|
@@ -50,29 +42,28 @@ class ServiceInstance(BaseServiceInstance):
|
|
50
42
|
return self._client.token
|
51
43
|
|
52
44
|
def _create_token(self) -> str:
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
45
|
+
inferred_auth_type = None
|
46
|
+
if is_ibm_cloud_platform(self._credentials.url):
|
47
|
+
inferred_auth_type = EnvironmentAuthType.IBM_CLOUD_IAM
|
48
|
+
elif is_cpd_env(self._credentials.url):
|
49
|
+
inferred_auth_type = EnvironmentAuthType.CPD
|
50
|
+
else:
|
51
|
+
inferred_auth_type = EnvironmentAuthType.MCSP
|
52
|
+
|
53
|
+
if self._credentials.auth_type:
|
54
|
+
if self._credentials.auth_type != inferred_auth_type:
|
55
|
+
logger.warning(f"Overriding the default authentication type '{inferred_auth_type}' for url '{self._credentials.url}' with '{self._credentials.auth_type.lower()}'")
|
56
|
+
auth_type = self._credentials.auth_type.lower()
|
57
|
+
else:
|
58
|
+
inferred_type_options = [t for t in EnvironmentAuthType if t != inferred_auth_type]
|
59
|
+
logger.warning(f"Using '{inferred_auth_type}' Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of {', '.join(inferred_type_options[:-1])} or {inferred_type_options[-1]}")
|
60
|
+
auth_type = inferred_auth_type
|
61
|
+
|
67
62
|
if auth_type == "mcsp":
|
68
63
|
try:
|
69
64
|
return self._authenticate(EnvironmentAuthType.MCSP_V1)
|
70
65
|
except:
|
71
66
|
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)
|
76
67
|
else:
|
77
68
|
return self._authenticate(auth_type)
|
78
69
|
|
@@ -100,13 +91,7 @@ class ServiceInstance(BaseServiceInstance):
|
|
100
91
|
if self._credentials.iam_url is not None:
|
101
92
|
url = self._credentials.iam_url
|
102
93
|
else:
|
103
|
-
|
104
|
-
env_cfg = cfg.get(ENVIRONMENTS_SECTION_HEADER)
|
105
|
-
matching_wxo_url = next(
|
106
|
-
(env_config['wxo_url'] for env_config in env_cfg.values() if 'bypass_ssl' in env_config and 'verify' in env_config),
|
107
|
-
None
|
108
|
-
)
|
109
|
-
base_url = matching_wxo_url.split("/orchestrate")[0]
|
94
|
+
base_url = self._credentials.url.split("/orchestrate")[0]
|
110
95
|
url = f"{base_url}/icp4d-api"
|
111
96
|
|
112
97
|
password = self._credentials.password if self._credentials.password is not None else None
|
@@ -66,7 +66,7 @@ def is_ibm_cloud_platform(url:str | None = None) -> bool:
|
|
66
66
|
if url is None:
|
67
67
|
url = get_current_env_url()
|
68
68
|
|
69
|
-
if
|
69
|
+
if ".cloud.ibm.com" in url:
|
70
70
|
return True
|
71
71
|
return False
|
72
72
|
|
@@ -161,6 +161,8 @@ def instantiate_client(client: type[T] , url: str | None=None) -> T:
|
|
161
161
|
client_instance = client(base_url=url, api_key=token, is_local=is_local_dev(url), verify=False)
|
162
162
|
elif verify is not None:
|
163
163
|
client_instance = client(base_url=url, api_key=token, is_local=is_local_dev(url), verify=verify)
|
164
|
+
else:
|
165
|
+
client_instance = client(base_url=url, api_key=token, is_local=is_local_dev(url))
|
164
166
|
else:
|
165
167
|
client_instance = client(base_url=url, api_key=token, is_local=is_local_dev(url))
|
166
168
|
|
@@ -62,6 +62,7 @@ services:
|
|
62
62
|
WATSONX_API_KEY: ${WATSONX_APIKEY}
|
63
63
|
WATSONX_URL: ${WATSONX_URL}
|
64
64
|
WATSONX_SPACE_ID: ${WATSONX_SPACE_ID}
|
65
|
+
NODE_TLS_REJECT_UNAUTHORIZED: ${AI_GATEWAY_TLS_REJECT_UNAUTHORIZED}
|
65
66
|
|
66
67
|
wxo-agent-gateway:
|
67
68
|
image: ${AGENT_GATEWAY_REGISTRY:-us.icr.io/watson-orchestrate-private}/wxo-agent-gateway:${AGENT_GATEWAY_TAG:-latest}
|
@@ -132,7 +133,7 @@ services:
|
|
132
133
|
DOCPROC_ENABLED: ${DOCPROC_ENABLED:-false}
|
133
134
|
IS_OBSERVABILITY_FEATURE_ENABLED: "true"
|
134
135
|
ALLOW_INSECURE_TLS: "true"
|
135
|
-
ENABLE_EDIT_PROMPTS: "
|
136
|
+
ENABLE_EDIT_PROMPTS: "false"
|
136
137
|
LANGFLOW_ENABLED: ${LANGFLOW_ENABLED:-false}
|
137
138
|
command: 'npm start'
|
138
139
|
ports:
|
@@ -327,6 +328,7 @@ services:
|
|
327
328
|
ENABLE_WEBHOOKS: false
|
328
329
|
DISABLE_JSON_LOG_CELERY: true
|
329
330
|
WXO_DEPLOYMENT_PLATFORM: saas
|
331
|
+
CPD_VERIFY: ${CPD_VERIFY}
|
330
332
|
CONNECTION_MANAGER_URL: http://wxo-server-connection-manager:3001
|
331
333
|
CHANNEL_SESSION_REDIS_URL: redis://wxo-server-redis:6379/5
|
332
334
|
WXO_MILVUS_URI: http://wxo-milvus-standalone:19530
|
@@ -376,6 +378,7 @@ services:
|
|
376
378
|
IS_WXO_LITE: "TRUE"
|
377
379
|
TRM_BASE_URL: http://tools-runtime-manager:8080
|
378
380
|
AGENT_STEP_DETAILS: redis://wxo-server-redis:6379/0
|
381
|
+
RECURSION_LIMIT: ${RECURSION_LIMIT}
|
379
382
|
AGENT_GATEWAY_URI: http://wxo-agent-gateway:8989
|
380
383
|
JWT_SECRET: ${JWT_SECRET}
|
381
384
|
POSTGRES_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@wxo-server-db:5432/postgres
|
@@ -446,6 +449,7 @@ services:
|
|
446
449
|
IBM_TELEMETRY_TRACER_ENDPOINT: http://jaeger:4318/v1/traces
|
447
450
|
USE_IBM_TELEMETRY: ${USE_IBM_TELEMETRY:-false}
|
448
451
|
WXO_DEPLOYMENT_PLATFORM: saas
|
452
|
+
CPD_VERIFY: ${CPD_VERIFY}
|
449
453
|
CALLBACK_HOST_URL: ${CALLBACK_HOST_URL:-http://wxo-server:4321}
|
450
454
|
LANGFLOW_ENABLED: ${LANGFLOW_ENABLED:-false}
|
451
455
|
|
@@ -653,6 +657,7 @@ services:
|
|
653
657
|
- WATSONX_URL=${WATSONX_URL}
|
654
658
|
- PROXY_SERVER_URL=http://jaeger-proxy:9201
|
655
659
|
- WXO_DEPLOYMENT_PLATFORM=saas
|
660
|
+
- CPD_VERIFY=${CPD_VERIFY}
|
656
661
|
- TENANT_API_KEY=${AGENTOPS_API_KEY}
|
657
662
|
- TENANT_CONFIG_URL=http://wxo-server:4321
|
658
663
|
- TENANT_DEFAULT_USERNAME=${ES_USERNAME}
|
@@ -882,18 +887,18 @@ services:
|
|
882
887
|
PREFLIGHT_MAX_SIZE: 10Mb
|
883
888
|
PREFLIGHT_MAX_PAGES: 600
|
884
889
|
RUNTIME_LIBRARY: "watson_doc_understanding"
|
885
|
-
WXAI_API_KEY: ${WXAI_API_KEY:-}
|
886
|
-
WXAI_IMAGE_DESCRIPTION_MODEL_ID: ${WXAI_IMAGE_DESCRIPTION_MODEL_ID:-mistralai/
|
887
|
-
|
888
|
-
WXAI_KVP_MODEL_ID: ${WXAI_KVP_MODEL_ID:-}
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
WXAI_URL: ${WXAI_URL:-}
|
890
|
+
WXAI_API_KEY: ${WXAI_API_KEY:-"gateway"}
|
891
|
+
WXAI_IMAGE_DESCRIPTION_MODEL_ID: ${WXAI_IMAGE_DESCRIPTION_MODEL_ID:-mistralai/mistral-small-3-1-24b-instruct-2503}
|
892
|
+
WXAI_IMAGE_DESCRIPTION_SPACE_ID: ${WATSONX_SPACE_ID:-}
|
893
|
+
WXAI_KVP_MODEL_ID: ${WXAI_KVP_MODEL_ID:-mistralai/mistral-small-3-1-24b-instruct-2503}
|
894
|
+
WXAI_KVP_SPACE_ID: ${WATSONX_SPACE_ID:-}
|
895
|
+
WXAI_SEMANTIC_KVP_MODEL_ID: ${WXAI_SEMANTIC_KVP_MODEL_ID:-mistralai/mistral-small-3-1-24b-instruct-2503}
|
896
|
+
WXAI_SEMANTIC_KVP_SPACE_ID: ${WATSONX_SPACE_ID:-}
|
897
|
+
WXAI_URL: ${AI_GATEWAY_BASE_URL:-}
|
894
898
|
WXAI_USERNAME: ${WXAI_USERNAME:-}
|
895
899
|
WXAI_INSTANCE_ID: ${WXAI_INSTANCE_ID:-}
|
896
|
-
WXAI_VERSION: ${WXAI_VERSION:-
|
900
|
+
WXAI_VERSION: ${WXAI_VERSION:-2023-05-29}
|
901
|
+
WXAI_PROVIDER: ${WXAI_PROVIDER:-gateway}
|
897
902
|
|
898
903
|
profiles:
|
899
904
|
- docproc
|
@@ -36,7 +36,7 @@ DB_ENCRYPTION_KEY=dummy_db_encryption_key
|
|
36
36
|
BASE_URL=dummy_base_url
|
37
37
|
SERVER_TYPE=CELERY
|
38
38
|
SQLALCHEMY_DEBUG=false
|
39
|
-
|
39
|
+
RECURSION_LIMIT=50
|
40
40
|
LANGFUSE_HOST=http://host.docker.internal:3010
|
41
41
|
LANGFUSE_EMAIL=orchestrate@ibm.com
|
42
42
|
LANGFUSE_USERNAME=orchestrate
|
@@ -58,10 +58,10 @@ REGISTRY_URL=
|
|
58
58
|
|
59
59
|
|
60
60
|
|
61
|
-
SERVER_TAG=
|
61
|
+
SERVER_TAG=09-09-2025-cff47f4
|
62
62
|
SERVER_REGISTRY=
|
63
63
|
|
64
|
-
WORKER_TAG=
|
64
|
+
WORKER_TAG=09-09-2025-cff47f4
|
65
65
|
WORKER_REGISTRY=
|
66
66
|
|
67
67
|
AI_GATEWAY_TAG=20-08-2025-9ed6d40
|
@@ -73,9 +73,9 @@ AGENT_GATEWAY_REGISTRY=
|
|
73
73
|
DB_REGISTRY=
|
74
74
|
# If you build multiarch set all three of these to the same, we have a pr against main
|
75
75
|
# to not have this separation, but we can merge it later
|
76
|
-
DBTAG=
|
77
|
-
AMDDBTAG=
|
78
|
-
ARM64DBTAG=
|
76
|
+
DBTAG=04-09-2025-48c39
|
77
|
+
AMDDBTAG=04-09-2025-48c39
|
78
|
+
ARM64DBTAG=04-09-2025-48c39
|
79
79
|
|
80
80
|
UI_REGISTRY=
|
81
81
|
UITAG=29-08-2025
|
@@ -92,7 +92,7 @@ TR_REGISTRY=
|
|
92
92
|
BUILDER_TAG=27-08-2025-7432aca
|
93
93
|
BUILDER_REGISTRY=
|
94
94
|
|
95
|
-
FLOW_RUNTIME_TAG=
|
95
|
+
FLOW_RUNTIME_TAG=01-09-2025-8e6fcfc
|
96
96
|
FLOW_RUMTIME_REGISTRY=
|
97
97
|
|
98
98
|
|
@@ -105,7 +105,7 @@ JAEGER_PROXY_REGISTRY=
|
|
105
105
|
SOCKET_HANDLER_TAG=29-05-2025
|
106
106
|
SOCKET_HANDLER_REGISTRY=
|
107
107
|
|
108
|
-
CPE_TAG=
|
108
|
+
CPE_TAG=29-08-2025-e612bea
|
109
109
|
CPE_REGISTRY=
|
110
110
|
|
111
111
|
VOICE_CONTROLLER_TAG=12-08-2025
|
@@ -115,17 +115,19 @@ LANGFLOW_TAG=
|
|
115
115
|
LANGFLOW_IMAGE=
|
116
116
|
|
117
117
|
# IBM Document Processing
|
118
|
-
WDU_TAG=2.
|
118
|
+
WDU_TAG=2.7.0
|
119
119
|
WDU_REGISTRY=
|
120
120
|
|
121
|
-
DOCPROC_DPS_TAG=
|
122
|
-
DOCPROC_LLMSERVICE_TAG=
|
123
|
-
DOCPROC_CACHE_TAG=
|
124
|
-
DOCPROC_DPI_TAG=
|
121
|
+
DOCPROC_DPS_TAG=20250904-181617-287-1ca2bf1
|
122
|
+
DOCPROC_LLMSERVICE_TAG=20250904-main-136-b94360e
|
123
|
+
DOCPROC_CACHE_TAG=20250902-master-83-748d456
|
124
|
+
DOCPROC_DPI_TAG=20250905-092707-286-8c6b8f92
|
125
125
|
DOCPROC_REGISTRY=
|
126
126
|
|
127
127
|
# END -- IMAGE REGISTRIES AND TAGS
|
128
128
|
|
129
|
+
CPD_VERIFY=true
|
130
|
+
AI_GATEWAY_TLS_REJECT_UNAUTHORIZED=1
|
129
131
|
TAVILY_API_KEY=dummy_tavily_api_key
|
130
132
|
PREFERRED_MODELS=meta-llama/llama-3-2-90b-vision-instruct,meta-llama/llama-3-405b-instruct
|
131
133
|
INCOMPATIBLE_MODELS=flan,embedding,cross-encoder,tinytimemixers
|
@@ -12,7 +12,10 @@ class DataMap(BaseModel):
|
|
12
12
|
def to_json(self) -> dict[str, Any]:
|
13
13
|
model_spec = {}
|
14
14
|
if self.maps and len(self.maps) > 0:
|
15
|
-
model_spec["maps"] = [
|
15
|
+
model_spec["maps"] = []
|
16
|
+
for assignment in self.maps:
|
17
|
+
model_spec["maps"].append(assignment.model_dump())
|
18
|
+
return model_spec
|
16
19
|
|
17
20
|
def add(self, line: Assignment) -> Self:
|
18
21
|
self.maps.append(line)
|
@@ -25,7 +25,7 @@ from ibm_watsonx_orchestrate.client.tools.tool_client import ToolClient
|
|
25
25
|
from ibm_watsonx_orchestrate.client.tools.tempus_client import TempusClient
|
26
26
|
from ibm_watsonx_orchestrate.client.utils import instantiate_client
|
27
27
|
from ..types import (
|
28
|
-
DocProcKVPSchema, EndNodeSpec, Expression, ForeachPolicy, ForeachSpec, LoopSpec, BranchNodeSpec, MatchPolicy, PlainTextReadingOrder, PromptLLMParameters, PromptNodeSpec, TimerNodeSpec,
|
28
|
+
DocProcKVPSchema, Assignment, Conditions, EndNodeSpec, Expression, ForeachPolicy, ForeachSpec, LoopSpec, BranchNodeSpec, MatchPolicy, NodeIdCondition, PlainTextReadingOrder, PromptExample, PromptLLMParameters, PromptNodeSpec, TimerNodeSpec,
|
29
29
|
StartNodeSpec, ToolSpec, JsonSchemaObject, ToolRequestBody, ToolResponseBody, UserFieldKind, UserFieldOption, UserFlowSpec, UserNodeSpec, WaitPolicy,
|
30
30
|
DocProcSpec, TextExtractionResponse, DocProcInput, DecisionsNodeSpec, DecisionsRule, DocExtSpec, File, DocumentClassificationResponse, DocClassifierSpec, DocumentProcessingCommonInput
|
31
31
|
)
|
@@ -64,7 +64,7 @@ class FlowEdge(BaseModel):
|
|
64
64
|
|
65
65
|
class Flow(Node):
|
66
66
|
'''Flow represents a flow that will be run by wxO Flow engine.'''
|
67
|
-
output_map: DataMap | None = None
|
67
|
+
output_map: dict[str, DataMap] | None = None
|
68
68
|
nodes: dict[str, SerializeAsAny[Node]] = {}
|
69
69
|
edges: List[FlowEdge] = []
|
70
70
|
schemas: dict[str, JsonSchemaObject] = {}
|
@@ -401,6 +401,7 @@ class Flow(Node):
|
|
401
401
|
display_name: str|None=None,
|
402
402
|
system_prompt: str | list[str] | None = None,
|
403
403
|
user_prompt: str | list[str] | None = None,
|
404
|
+
prompt_examples: list[PromptExample] | None = None,
|
404
405
|
llm: str | None = None,
|
405
406
|
llm_parameters: PromptLLMParameters | None = None,
|
406
407
|
description: str | None = None,
|
@@ -422,6 +423,7 @@ class Flow(Node):
|
|
422
423
|
description=description,
|
423
424
|
system_prompt=system_prompt,
|
424
425
|
user_prompt=user_prompt,
|
426
|
+
prompt_examples=prompt_examples,
|
425
427
|
llm=llm,
|
426
428
|
llm_parameters=llm_parameters,
|
427
429
|
input_schema=_get_tool_request_body(input_schema_obj),
|
@@ -438,7 +440,7 @@ class Flow(Node):
|
|
438
440
|
node = self._add_node(node)
|
439
441
|
return cast(PromptNode, node)
|
440
442
|
|
441
|
-
def
|
443
|
+
def docclassifier(self,
|
442
444
|
name: str,
|
443
445
|
llm : str = "watsonx/meta-llama/llama-3-2-90b-vision-instruct",
|
444
446
|
version: str = "TIP",
|
@@ -605,6 +607,7 @@ class Flow(Node):
|
|
605
607
|
display_name: str|None=None,
|
606
608
|
description: str | None = None,
|
607
609
|
input_map: DataMap = None,
|
610
|
+
document_structure: bool = False,
|
608
611
|
kvp_schemas: list[DocProcKVPSchema] = None,
|
609
612
|
enable_hw: bool = False) -> DocProcNode:
|
610
613
|
|
@@ -629,6 +632,7 @@ class Flow(Node):
|
|
629
632
|
output_schema=_get_tool_response_body(output_schema_obj),
|
630
633
|
output_schema_object = output_schema_obj,
|
631
634
|
task=task,
|
635
|
+
document_structure=document_structure,
|
632
636
|
plain_text_reading_order=plain_text_reading_order,
|
633
637
|
enable_hw=enable_hw,
|
634
638
|
kvp_schemas=kvp_schemas
|
@@ -721,7 +725,7 @@ class Flow(Node):
|
|
721
725
|
'''Create a single node flow with an automatic START and END node.'''
|
722
726
|
return self.sequence(START, node, END)
|
723
727
|
|
724
|
-
def branch(self, evaluator: Union[Callable, Expression]) ->
|
728
|
+
def branch(self, evaluator: Union[Callable, Expression, Conditions]) -> 'Branch':
|
725
729
|
'''Create a BRANCH node'''
|
726
730
|
e = evaluator
|
727
731
|
if isinstance(evaluator, Callable):
|
@@ -735,11 +739,19 @@ class Flow(Node):
|
|
735
739
|
# e = new_script_spec
|
736
740
|
elif isinstance(evaluator, str):
|
737
741
|
e = Expression(expression=evaluator)
|
742
|
+
elif isinstance(evaluator, list):
|
743
|
+
e = Conditions(conditions=evaluator)
|
738
744
|
|
739
745
|
spec = BranchNodeSpec(name = "branch_" + str(self._next_sequence_id()), evaluator=e)
|
740
746
|
branch_node = Branch(spec = spec, containing_flow=self)
|
741
747
|
return cast(Branch, self._add_node(branch_node))
|
742
748
|
|
749
|
+
def conditions(self) -> 'Branch':
|
750
|
+
'''Create a Branch node with empty Conditions evaluator (if-else)'''
|
751
|
+
spec = BranchNodeSpec(name = "branch_" + str(self._next_sequence_id()), evaluator=Conditions(conditions=[]))
|
752
|
+
branch_conditions_node = Branch(spec = spec, containing_flow=self)
|
753
|
+
return cast(Branch, self._add_node(branch_conditions_node))
|
754
|
+
|
743
755
|
def wait_for(self, *args) -> "Wait":
|
744
756
|
'''Wait for all incoming nodes to complete.'''
|
745
757
|
raise ValueError("Not implemented yet.")
|
@@ -754,6 +766,77 @@ class Flow(Node):
|
|
754
766
|
|
755
767
|
# return cast(Wait, self.node(wait_node))
|
756
768
|
|
769
|
+
def map_flow_output_with_variable(self, target_output_variable: str, variable: str, default_value: str = None) -> Self:
|
770
|
+
if self.output_map and "spec" in self.output_map:
|
771
|
+
maps = self.output_map["spec"].maps or []
|
772
|
+
else:
|
773
|
+
maps = []
|
774
|
+
|
775
|
+
curr_map_metadata = {
|
776
|
+
"assignmentType": "variable"
|
777
|
+
}
|
778
|
+
|
779
|
+
target_variable = "flow.output." + target_output_variable
|
780
|
+
value_expression = "flow." + variable
|
781
|
+
|
782
|
+
if default_value:
|
783
|
+
maps.append(Assignment(target_variable=target_variable, value_expression=value_expression, default_value=default_value, metadata=curr_map_metadata))
|
784
|
+
else:
|
785
|
+
maps.append(Assignment(target_variable=target_variable, value_expression=value_expression, metadata=curr_map_metadata))
|
786
|
+
|
787
|
+
flow_output_map_spec = DataMap(maps=maps)
|
788
|
+
|
789
|
+
if self.output_map and "spec" in self.output_map:
|
790
|
+
self.output_map["spec"] = flow_output_map_spec
|
791
|
+
else:
|
792
|
+
self.output_map = {"spec": flow_output_map_spec}
|
793
|
+
return self
|
794
|
+
|
795
|
+
def map_output(self, output_variable: str, expression: str, default_value: str = None) -> Self:
|
796
|
+
if self.output_map and "spec" in self.output_map:
|
797
|
+
maps = self.output_map["spec"].maps or []
|
798
|
+
else:
|
799
|
+
maps = []
|
800
|
+
|
801
|
+
curr_map_metadata = {
|
802
|
+
"assignmentType": "pyExpression"
|
803
|
+
}
|
804
|
+
|
805
|
+
target_variable = "flow.output." + output_variable
|
806
|
+
value_expression = expression
|
807
|
+
|
808
|
+
if default_value:
|
809
|
+
maps.append(Assignment(target_variable=target_variable, value_expression=value_expression, default_value=default_value, metadata=curr_map_metadata))
|
810
|
+
else:
|
811
|
+
maps.append(Assignment(target_variable=target_variable, value_expression=value_expression, metadata=curr_map_metadata))
|
812
|
+
|
813
|
+
flow_output_map_spec = DataMap(maps=maps)
|
814
|
+
|
815
|
+
if self.output_map and "spec" in self.output_map:
|
816
|
+
self.output_map["spec"] = flow_output_map_spec
|
817
|
+
else:
|
818
|
+
self.output_map = {"spec": flow_output_map_spec}
|
819
|
+
return self
|
820
|
+
|
821
|
+
def map_flow_output_with_none(self, target_output_variable: str) -> Self:
|
822
|
+
if self.output_map and "spec" in self.output_map:
|
823
|
+
maps = self.output_map["spec"].maps or []
|
824
|
+
else:
|
825
|
+
maps = []
|
826
|
+
|
827
|
+
|
828
|
+
target_variable = "flow.output." + target_output_variable
|
829
|
+
|
830
|
+
maps.append(Assignment(target_variable=target_variable, value_expression=None))
|
831
|
+
|
832
|
+
flow_output_map_spec = DataMap(maps=maps)
|
833
|
+
|
834
|
+
if self.output_map and "spec" in self.output_map:
|
835
|
+
self.output_map["spec"] = flow_output_map_spec
|
836
|
+
else:
|
837
|
+
self.output_map = {"spec": flow_output_map_spec}
|
838
|
+
return self
|
839
|
+
|
757
840
|
|
758
841
|
def foreach(self, item_schema: type[BaseModel],
|
759
842
|
input_schema: type[BaseModel] |None=None,
|
@@ -814,8 +897,6 @@ class Flow(Node):
|
|
814
897
|
input_schema: type[BaseModel] |None=None,
|
815
898
|
output_schema: type[BaseModel] |None=None) -> "UserFlow": # return a UserFlow object
|
816
899
|
|
817
|
-
raise ValueError("userflow is NOT supported yet and it's interface will change.")
|
818
|
-
|
819
900
|
output_schema_obj = _get_json_schema_obj("output", output_schema)
|
820
901
|
input_schema_obj = _get_json_schema_obj("input", input_schema)
|
821
902
|
|
@@ -917,6 +998,11 @@ class Flow(Node):
|
|
917
998
|
for key, value in self.metadata.items():
|
918
999
|
metadata_dict[key] = value
|
919
1000
|
flow_dict["metadata"] = metadata_dict
|
1001
|
+
|
1002
|
+
if self.output_map and "spec" in self.output_map:
|
1003
|
+
flow_dict["output_map"] = {
|
1004
|
+
"spec": self.output_map["spec"].to_json()
|
1005
|
+
}
|
920
1006
|
return flow_dict
|
921
1007
|
|
922
1008
|
def _get_node_id(self, node: Union[str, Node]) -> str:
|
@@ -1226,6 +1312,27 @@ class Branch(FlowControl):
|
|
1226
1312
|
raise ValueError("Cannot have custom label __default__. Use default() instead.")
|
1227
1313
|
|
1228
1314
|
return self._add_case(label, node)
|
1315
|
+
|
1316
|
+
def condition(self, to_node: Node, expression: str="", default: bool=False) -> Self:
|
1317
|
+
'''
|
1318
|
+
Add a condition to this branch node.
|
1319
|
+
|
1320
|
+
Parameters:
|
1321
|
+
expression (str): The expression of this condition.
|
1322
|
+
to_node (Node): The node to go to when expression is evaluated to true.
|
1323
|
+
default (bool): The condition is the default (else) case.
|
1324
|
+
'''
|
1325
|
+
|
1326
|
+
node_id = self.containing_flow._get_node_id(to_node)
|
1327
|
+
if default:
|
1328
|
+
condition = NodeIdCondition(node_id=node_id, default=default)
|
1329
|
+
else:
|
1330
|
+
condition = NodeIdCondition(expression=expression, node_id=node_id, default=default)
|
1331
|
+
|
1332
|
+
self.spec.evaluator.conditions.append(condition)
|
1333
|
+
self.containing_flow.edge(self, to_node)
|
1334
|
+
|
1335
|
+
return self
|
1229
1336
|
|
1230
1337
|
def default(self, node: Node) -> Self:
|
1231
1338
|
'''
|
@@ -1445,13 +1552,14 @@ class UserFlow(Flow):
|
|
1445
1552
|
kind: UserFieldKind = UserFieldKind.Text,
|
1446
1553
|
display_name: str | None = None,
|
1447
1554
|
description: str | None = None,
|
1555
|
+
direction: str | None = None,
|
1448
1556
|
default: Any | None = None,
|
1449
1557
|
text: str = None, # The text used to ask question to the user, e.g. 'what is your name?'
|
1450
1558
|
option: UserFieldOption | None = None,
|
1451
1559
|
is_list: bool = False,
|
1452
1560
|
min: Any | None = None,
|
1453
1561
|
max: Any | None = None,
|
1454
|
-
input_map: DataMap = None,
|
1562
|
+
input_map: DataMap | None= None,
|
1455
1563
|
custom: dict[str, Any] = {}) -> UserNode:
|
1456
1564
|
'''create a node in the flow'''
|
1457
1565
|
# create a json schema object based on the single field
|
@@ -1483,6 +1591,8 @@ class UserFlow(Flow):
|
|
1483
1591
|
description = description,
|
1484
1592
|
default = default,
|
1485
1593
|
text = text,
|
1594
|
+
direction = direction,
|
1595
|
+
input_map = input_map,
|
1486
1596
|
option = option,
|
1487
1597
|
is_list = is_list,
|
1488
1598
|
min = min,
|