ibm-watsonx-orchestrate 1.9.0b1__py3-none-any.whl → 1.10.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.
Files changed (34) hide show
  1. ibm_watsonx_orchestrate/__init__.py +2 -1
  2. ibm_watsonx_orchestrate/agent_builder/agents/types.py +2 -0
  3. ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +2 -2
  4. ibm_watsonx_orchestrate/agent_builder/models/types.py +5 -0
  5. ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py +19 -7
  6. ibm_watsonx_orchestrate/agent_builder/tools/types.py +5 -3
  7. ibm_watsonx_orchestrate/agent_builder/voice_configurations/__init__.py +1 -0
  8. ibm_watsonx_orchestrate/agent_builder/voice_configurations/types.py +98 -0
  9. ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +20 -0
  10. ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +170 -1
  11. ibm_watsonx_orchestrate/cli/commands/connections/connections_controller.py +5 -2
  12. ibm_watsonx_orchestrate/cli/commands/copilot/copilot_controller.py +103 -20
  13. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +19 -12
  14. ibm_watsonx_orchestrate/cli/commands/models/model_provider_mapper.py +17 -13
  15. ibm_watsonx_orchestrate/cli/commands/server/server_command.py +17 -4
  16. ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +6 -1
  17. ibm_watsonx_orchestrate/cli/commands/voice_configurations/voice_configurations_command.py +58 -0
  18. ibm_watsonx_orchestrate/cli/commands/voice_configurations/voice_configurations_controller.py +173 -0
  19. ibm_watsonx_orchestrate/cli/main.py +2 -0
  20. ibm_watsonx_orchestrate/client/agents/agent_client.py +64 -1
  21. ibm_watsonx_orchestrate/client/connections/connections_client.py +14 -2
  22. ibm_watsonx_orchestrate/client/copilot/cpe/copilot_cpe_client.py +5 -3
  23. ibm_watsonx_orchestrate/client/voice_configurations/voice_configurations_client.py +75 -0
  24. ibm_watsonx_orchestrate/docker/compose-lite.yml +23 -2
  25. ibm_watsonx_orchestrate/docker/default.env +16 -12
  26. ibm_watsonx_orchestrate/flow_builder/flows/__init__.py +2 -2
  27. ibm_watsonx_orchestrate/flow_builder/flows/flow.py +29 -24
  28. ibm_watsonx_orchestrate/flow_builder/types.py +109 -17
  29. ibm_watsonx_orchestrate/flow_builder/utils.py +7 -3
  30. {ibm_watsonx_orchestrate-1.9.0b1.dist-info → ibm_watsonx_orchestrate-1.10.0b0.dist-info}/METADATA +1 -1
  31. {ibm_watsonx_orchestrate-1.9.0b1.dist-info → ibm_watsonx_orchestrate-1.10.0b0.dist-info}/RECORD +34 -29
  32. {ibm_watsonx_orchestrate-1.9.0b1.dist-info → ibm_watsonx_orchestrate-1.10.0b0.dist-info}/WHEEL +0 -0
  33. {ibm_watsonx_orchestrate-1.9.0b1.dist-info → ibm_watsonx_orchestrate-1.10.0b0.dist-info}/entry_points.txt +0 -0
  34. {ibm_watsonx_orchestrate-1.9.0b1.dist-info → ibm_watsonx_orchestrate-1.10.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,26 @@
1
1
  from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient, ClientAPIException
2
2
  from typing_extensions import List, Optional
3
+ from enum import Enum
4
+
3
5
  from ibm_watsonx_orchestrate.client.utils import is_local_dev
4
6
  from pydantic import BaseModel
7
+ import time
8
+ import logging
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ POLL_INTERVAL = 2
13
+ MAX_RETRIES = 10
14
+
15
+ class ReleaseMode(str, Enum):
16
+ DEPLOY = "deploy"
17
+ UNDEPLOY = "undeploy"
18
+
19
+ class ReleaseStatus(str, Enum):
20
+ SUCCESS = "success"
21
+ NONE = "none"
22
+ FAILED = "failed"
23
+ IN_PROGRESS = "in_progress"
5
24
 
6
25
  def transform_agents_from_flat_agent_spec(agents: dict | list[dict] ) -> dict | list[dict]:
7
26
  if isinstance(agents,list):
@@ -83,7 +102,6 @@ class AgentClient(BaseAPIClient):
83
102
  super().__init__(*args, **kwargs)
84
103
  self.base_endpoint = "/orchestrate/agents" if is_local_dev(self.base_url) else "/agents"
85
104
 
86
-
87
105
  def create(self, payload: dict) -> AgentUpsertResponse:
88
106
  response = self._post(self.base_endpoint, data=transform_agents_from_flat_agent_spec(payload))
89
107
  return AgentUpsertResponse.model_validate(response)
@@ -120,4 +138,49 @@ class AgentClient(BaseAPIClient):
120
138
  def get_drafts_by_ids(self, agent_ids: List[str]) -> List[dict]:
121
139
  formatted_agent_ids = [f"ids={x}" for x in agent_ids]
122
140
  return transform_agents_to_flat_agent_spec(self._get(f"{self.base_endpoint}?{'&'.join(formatted_agent_ids)}&include_hidden=true"))
141
+
142
+ def poll_release_status(self, agent_id: str, environment_id: str, mode: str = "deploy") -> bool:
143
+ expected_status = {
144
+ ReleaseMode.DEPLOY: ReleaseStatus.SUCCESS,
145
+ ReleaseMode.UNDEPLOY: ReleaseStatus.NONE
146
+ }[mode]
147
+
148
+ for attempt in range(MAX_RETRIES):
149
+ try:
150
+ response = self._get(
151
+ f"{self.base_endpoint}/{agent_id}/releases/status?environment_id={environment_id}"
152
+ )
153
+ except Exception as e:
154
+ logger.error(f"Polling for Deployment/Undeployment failed on attempt {attempt + 1}: {e}")
155
+ return False
156
+
157
+ if not isinstance(response, dict):
158
+ logger.warning(f"Invalid response format: {response}")
159
+ return False
160
+
161
+ status = response.get("deployment_status")
162
+
163
+ if status == expected_status:
164
+ return True
165
+ elif status == "failed":
166
+ return False
167
+ elif status == "in_progress":
168
+ pass
169
+
170
+ time.sleep(POLL_INTERVAL)
171
+
172
+ logger.warning(f"{mode.capitalize()} status polling timed out")
173
+ return False
174
+
175
+ def deploy(self, agent_id: str, environment_id: str) -> bool:
176
+ self._post(f"{self.base_endpoint}/{agent_id}/releases", data={"environment_id": environment_id})
177
+ return self.poll_release_status(agent_id, environment_id, mode=ReleaseMode.DEPLOY)
178
+
179
+ def undeploy(self, agent_id: str, version: str, environment_id: str) -> bool:
180
+ self._post(f"{self.base_endpoint}/{agent_id}/releases/{version}/undeploy")
181
+ return self.poll_release_status(agent_id, environment_id, mode=ReleaseMode.UNDEPLOY)
182
+
183
+ def get_environments_for_agent(self, agent_id: str):
184
+ return self._get(f"{self.base_endpoint}/{agent_id}/environment")
185
+
123
186
 
@@ -3,6 +3,7 @@ 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
6
7
 
7
8
  from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient, ClientAPIException
8
9
  from ibm_watsonx_orchestrate.agent_builder.connections.types import ConnectionEnvironment, ConnectionPreference, ConnectionAuthType, ConnectionSecurityScheme, IdpConfigData, AppConfigData, ConnectionType
@@ -12,12 +13,23 @@ import logging
12
13
  logger = logging.getLogger(__name__)
13
14
 
14
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 = 'oauth2_password'
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
+
15
27
  class ListConfigsResponse(BaseModel):
16
28
  connection_id: str = None,
17
29
  app_id: str = None
18
30
  name: str = None
19
31
  security_scheme: ConnectionSecurityScheme | None = None,
20
- auth_type: ConnectionAuthType | None = None,
32
+ auth_type: FetchConfigAuthTypes | None = None,
21
33
  environment: ConnectionEnvironment | None = None,
22
34
  preference: ConnectionPreference | None = None,
23
35
  credentials_entered: bool | None = False
@@ -28,7 +40,7 @@ class GetConfigResponse(BaseModel):
28
40
  app_id: str = None
29
41
  environment: ConnectionEnvironment = None
30
42
  preference: ConnectionPreference = None
31
- auth_type: ConnectionAuthType | None = None
43
+ auth_type: FetchConfigAuthTypes | None = None
32
44
  sso: bool = None
33
45
  security_scheme: ConnectionSecurityScheme = None
34
46
  server_url: str | None = None
@@ -21,13 +21,15 @@ class CPEClient(BaseAPIClient):
21
21
  }
22
22
 
23
23
 
24
- def submit_pre_cpe_chat(self, user_message: str | None =None, tools: Dict[str, Any] = None, agents: Dict[str, Any] = None) -> dict:
24
+ def submit_pre_cpe_chat(self, user_message: str | None =None, tools: Dict[str, Any] = None, collaborators: Dict[str, Any] = None, knowledge_bases: Dict[str, Any] = None, selected:bool=False) -> dict:
25
25
  payload = {
26
26
  "message": user_message,
27
27
  "tools": tools,
28
- "agents": agents,
28
+ "collaborators": collaborators,
29
+ "knowledge_bases": knowledge_bases,
29
30
  "chat_id": self.chat_id,
30
- "chat_model_name": self.chat_model_name
31
+ "chat_model_name": self.chat_model_name,
32
+ 'selected':selected
31
33
  }
32
34
 
33
35
  response = self._post_nd_json("/wxo-cpe/create-agent", data=payload)
@@ -0,0 +1,75 @@
1
+ from pydantic import ValidationError
2
+ from ibm_watsonx_orchestrate.agent_builder.voice_configurations import VoiceConfiguration
3
+ from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient, ClientAPIException
4
+ from ibm_watsonx_orchestrate.client.client_errors import MissingArgument
5
+
6
+ import logging
7
+ logger = logging.getLogger(__name__)
8
+
9
+ class VoiceConfigurationsClient(BaseAPIClient):
10
+
11
+ def create(self, voice_config: VoiceConfiguration) -> dict:
12
+ return self._post("/voice_configurations", data=voice_config.model_dump(exclude_none=True))
13
+
14
+
15
+ def update(self, voice_config_id: str, voice_config: VoiceConfiguration) -> dict:
16
+ if voice_config_id in [None,""]:
17
+ raise MissingArgument("voice_config_id")
18
+ return self._patch(f"/voice_configurations/{voice_config_id}", data=voice_config.model_dump(exclude_none=True))
19
+
20
+
21
+ def get_by_id(self, voice_config_id: str) -> dict | None:
22
+ if voice_config_id in [None,""]:
23
+ raise MissingArgument("voice_config_id")
24
+
25
+ try:
26
+ response = self._get(f"/voice_configurations/{voice_config_id}")
27
+ return VoiceConfiguration.model_validate(response)
28
+
29
+ except ClientAPIException as e:
30
+ if e.response.status_code == 404:
31
+ return None
32
+ raise e
33
+
34
+ except ValidationError as e:
35
+ logger.error("Recieved unexpected response from server")
36
+ raise e
37
+
38
+ def get_by_name(self, name: str) -> list[VoiceConfiguration]:
39
+ return self.get_by_names([name])
40
+
41
+ def get_by_names(self, names: list[str]) -> list[VoiceConfiguration]:
42
+ # server will implement query by name, below can be uncommented then
43
+ # formatted_config_names = [f"names={n}" for n in names]
44
+ # return self._get(f"/voice_configurations?{"&".join(formatted_config_names)}")
45
+ config_list = self.list()
46
+ filtered_list = [cfg for cfg in config_list if cfg.name in names]
47
+
48
+ try:
49
+ return [ VoiceConfiguration.model_validate(cfg) for cfg in filtered_list ]
50
+ except ValidationError as e:
51
+ logger.error("Recieved unexpected response from server")
52
+ raise e
53
+
54
+
55
+
56
+
57
+ def delete(self, voice_config_id: str) -> None:
58
+ if voice_config_id in [None,""]:
59
+ raise MissingArgument("voice_config_id")
60
+ self._delete(f"/voice_configurations/{voice_config_id}")
61
+
62
+
63
+ def list(self) -> list[dict]:
64
+ try:
65
+ response = self._get("/voice_configurations")
66
+ return [VoiceConfiguration.model_validate(x) for x in response.get('voice_configurations',[])]
67
+
68
+ except ClientAPIException as e:
69
+ if e.response.status_code == 404:
70
+ return []
71
+ raise e
72
+
73
+ except ValidationError as e:
74
+ logger.error("Recieved unexpected response from server")
75
+ raise e
@@ -102,6 +102,7 @@ services:
102
102
  DISMISS_NOTIFICATION_TIMEOUT: 10000
103
103
  STANDALONE: "true"
104
104
  STREAM_TIMEOUT: ${STREAM_TIMEOUT:-120000}
105
+ WXO_VOICE_URL: http://localhost:9876/v1
105
106
  command: "./StartServer.sh"
106
107
  ports:
107
108
  - "3000:4002"
@@ -122,6 +123,11 @@ services:
122
123
  CONNECTIONS_MANAGER_ENDPOINT: http://wxo-server-connection-manager:3001
123
124
  AGENT_OPS_API_KEY: ${AGENTOPS_API_KEY}
124
125
  AGENTS_OPS_RUNTIME_ENDPOINT: https://frontend-server:443
126
+ DPI_WO_WDU_SERVER_ENDPOINT: https://wxo-doc-processing-service:8080
127
+ DPI_RAG_SERVER_ENDPOINT: https://wxo-doc-processing-llm-service:8083
128
+ IBM_DPS_CACHE_SERVICE: https://wxo-doc-processing-cache:8080
129
+ DOCPROC_BASE_URL: http://wxo-doc-processing-infra-standalone:9080
130
+ BUILDER_ASYNC_CALLBACK_ENDPOINT: http://wxo-builder:4025
125
131
  IS_OBSERVABILITY_FEATURE_ENABLED: "true"
126
132
  ALLOW_INSECURE_TLS: "true"
127
133
  command: 'npm start'
@@ -165,7 +171,7 @@ services:
165
171
  "
166
172
 
167
173
  wxo-milvus-standalone:
168
- image: ${OPENSOURCE_REGISTRY_PROXY:-docker.io}/milvusdb/milvus:v2.5.6
174
+ image: ${OPENSOURCE_REGISTRY_PROXY:-docker.io}/milvusdb/milvus:v2.5.15
169
175
  command: ["milvus", "run", "standalone"]
170
176
  security_opt:
171
177
  - seccomp:unconfined
@@ -337,6 +343,7 @@ services:
337
343
  AGENTOPS_API_KEY_AUTH_ENABLED: ${AGENTOPS_API_KEY_AUTH_ENABLED:-false}
338
344
  AGENTOPS_API_KEY: ${AGENTOPS_API_KEY}
339
345
  ES_HOST: http://elasticsearch:9200
346
+ ORIGIN_HEADER: "internal"
340
347
 
341
348
  wxo-server-worker:
342
349
  image: ${WORKER_REGISTRY:-us.icr.io/watson-orchestrate-private}/wxo-server-conversation_controller:${WORKER_TAG:-latest}
@@ -512,7 +519,7 @@ services:
512
519
  # IBM AGENT-OPS
513
520
  ########################
514
521
  elasticsearch:
515
- image: docker.elastic.co/elasticsearch/elasticsearch:8.17.5
522
+ image: docker.elastic.co/elasticsearch/elasticsearch:8.19.0
516
523
  profiles: [ibm-telemetry]
517
524
  environment:
518
525
  - discovery.type=single-node
@@ -549,6 +556,7 @@ services:
549
556
  - TENANT_DEFAULT_PASSWORD=${ES_PASSWORD}
550
557
  - TENANT_DEFAULT_HOSTNAME=http://elasticsearch:9200
551
558
  - DEFAULT_TENANT_ID=${DEFAULT_TENANT_ID}
559
+ - FORCE_SINGLE_TENANT=${FORCE_SINGLE_TENANT:-true}
552
560
  ports:
553
561
  - "9202:9201" # Expose proxy on host port 9202
554
562
  networks:
@@ -644,6 +652,7 @@ services:
644
652
  - INBOUND_API_KEY=${AGENTOPS_API_KEY}
645
653
  - DEFAULT_TENANT_ID=${DEFAULT_TENANT_ID}
646
654
  - TENANT_DEFAULT_HOSTNAME=http://elasticsearch:9200
655
+ - FORCE_SINGLE_TENANT=${FORCE_SINGLE_TENANT:-true}
647
656
  ports:
648
657
  - "8765:443"
649
658
  networks:
@@ -787,6 +796,7 @@ services:
787
796
  WO_INSTANCE: ${WO_INSTANCE}
788
797
  AUTHORIZATION_URL: ${AUTHORIZATION_URL}
789
798
  WO_AUTH_TYPE: ${WO_AUTH_TYPE}
799
+ FLOW_CALLBACK_REQUEST_RETRIES: "1"
790
800
  healthcheck:
791
801
  test: curl -k http://localhost:9044/readiness --fail
792
802
  interval: 30s
@@ -1085,6 +1095,17 @@ services:
1085
1095
  exit 0;
1086
1096
  "
1087
1097
 
1098
+ wxo-server-voice:
1099
+ image: ${VOICE_CONTROLLER_REGISTRY:-us.icr.io/watson-orchestrate-private}/wxo-server-voice:${VOICE_CONTROLLER_TAG:-latest}
1100
+ profiles: [voice]
1101
+ restart: unless-stopped
1102
+ ports:
1103
+ - 9876:8765
1104
+ environment:
1105
+ WXO_BASE_DOMAIN_URL: "http://wxo-server:4321"
1106
+ AI_GATEWAY_URL: "http://ai-gateway:8787"
1107
+ WXO_ORIGIN_HEADER: "internal"
1108
+
1088
1109
  volumes:
1089
1110
  tools:
1090
1111
  driver: local
@@ -57,13 +57,13 @@ EVENT_BROKER_TTL="3600"
57
57
  REGISTRY_URL=
58
58
 
59
59
 
60
- SERVER_TAG=31-07-2025
60
+ SERVER_TAG=13-08-2025-b01e43e00a7c900f92057d63520be08f965b6b24
61
61
  SERVER_REGISTRY=
62
62
 
63
- WORKER_TAG=31-07-2025
63
+ WORKER_TAG=13-08-2025-b01e43e00a7c900f92057d63520be08f965b6b24
64
64
  WORKER_REGISTRY=
65
65
 
66
- AI_GATEWAY_TAG=21-07-2025
66
+ AI_GATEWAY_TAG=01-08-2025-v1
67
67
  AI_GATEWAY_REGISTRY=
68
68
 
69
69
  AGENT_GATEWAY_TAG=29-07-2025
@@ -77,36 +77,39 @@ AMDDBTAG=29-07-2025-9f3661b
77
77
  ARM64DBTAG=29-07-2025-9f3661b
78
78
 
79
79
  UI_REGISTRY=
80
- UITAG=23-07-2025
80
+ UITAG=31-07-2025
81
81
 
82
82
  CM_REGISTRY=
83
83
  CM_TAG=24-07-2025
84
84
 
85
- TRM_TAG=23-07-2025-3c60549f0bac275de3e5736265a3fd49cdd3a203
85
+ TRM_TAG=12-08-2025-5f816b3d5981afdd65fa55401d70e5aa5f734fc4
86
86
  TRM_REGISTRY=
87
87
 
88
- TR_TAG=23-07-2025-3c60549f0bac275de3e5736265a3fd49cdd3a203
88
+ TR_TAG=14-08-2025-7b2f9c9
89
89
  TR_REGISTRY=
90
90
 
91
- BUILDER_TAG=29-07-2025
91
+ BUILDER_TAG=12-08-2025-8f00997
92
92
  BUILDER_REGISTRY=
93
93
 
94
- FLOW_RUNTIME_TAG=28-07-2025-v2
94
+ FLOW_RUNTIME_TAG=11-08-2025
95
95
  FLOW_RUMTIME_REGISTRY=
96
96
 
97
97
 
98
- AGENT_ANALYTICS_TAG=02-07-2025-v1
98
+ AGENT_ANALYTICS_TAG=05-08-2025
99
99
  AGENT_ANALYTICS_REGISTRY=
100
100
 
101
- JAEGER_PROXY_TAG=01-07-2025
101
+ JAEGER_PROXY_TAG=23-07-2025
102
102
  JAEGER_PROXY_REGISTRY=
103
103
 
104
104
  SOCKET_HANDLER_TAG=29-05-2025
105
105
  SOCKET_HANDLER_REGISTRY=
106
106
 
107
- CPE_TAG=17-07-2025
107
+ CPE_TAG=06-08-2025-b0a20ad
108
108
  CPE_REGISTRY=
109
109
 
110
+ VOICE_CONTROLLER_TAG=12-08-2025
111
+ VOICE_CONTROLLER_REGISTRY=
112
+
110
113
  # IBM Document Processing
111
114
  WDU_TAG=2.5.0
112
115
  WDU_REGISTRY=
@@ -114,7 +117,7 @@ WDU_REGISTRY=
114
117
  DOCPROC_DPS_TAG=20250721-164412-250-503756a
115
118
  DOCPROC_LLMSERVICE_TAG=20250725-100249-111-51d3e51
116
119
  DOCPROC_CACHE_TAG=20250723-100852-70-9edc1ab
117
- DOCPROC_DPI_TAG=20250710-195309-241-8f9fb6d7
120
+ DOCPROC_DPI_TAG=20250731-155328-257-06879e86
118
121
  DOCPROC_REGISTRY=
119
122
 
120
123
  # END -- IMAGE REGISTRIES AND TAGS
@@ -182,6 +185,7 @@ CALLBACK_HOST_URL=
182
185
 
183
186
  AGENTOPS_API_KEY_AUTH_ENABLED=true
184
187
  AGENTOPS_API_KEY=qwertyuiop
188
+ FORCE_SINGLE_TENANT=true
185
189
 
186
190
  RUNTIME_MANAGER_API_KEY=example
187
191
 
@@ -1,6 +1,6 @@
1
1
  from .constants import START, END, RESERVED
2
2
 
3
- from ..types import FlowContext, TaskData, TaskEventType, File, DecisionsCondition, DecisionsRule
3
+ from ..types import FlowContext, TaskData, TaskEventType, DocProcInput, DecisionsCondition, DecisionsRule
4
4
  from ..node import UserNode, AgentNode, StartNode, EndNode, PromptNode, ToolNode, DecisionsNode
5
5
 
6
6
  from .flow import Flow, CompiledFlow, FlowRun, FlowEvent, FlowEventType, FlowFactory, MatchPolicy, WaitPolicy, ForeachPolicy, Branch, Foreach, Loop
@@ -16,7 +16,7 @@ __all__ = [
16
16
  "FlowContext",
17
17
  "TaskData",
18
18
  "TaskEventType",
19
- "File",
19
+ "DocProcInput",
20
20
 
21
21
  "DocProcNode",
22
22
  "UserNode",
@@ -18,7 +18,7 @@ import pytz
18
18
  import os
19
19
 
20
20
  from typing_extensions import Self
21
- from pydantic import BaseModel, Field, SerializeAsAny, create_model
21
+ from pydantic import BaseModel, Field, SerializeAsAny, create_model, TypeAdapter
22
22
  import yaml
23
23
  from ibm_watsonx_orchestrate.agent_builder.tools.python_tool import PythonTool
24
24
  from ibm_watsonx_orchestrate.client.tools.tool_client import ToolClient
@@ -27,7 +27,7 @@ from ibm_watsonx_orchestrate.client.utils import instantiate_client
27
27
  from ..types import (
28
28
  EndNodeSpec, Expression, ForeachPolicy, ForeachSpec, LoopSpec, BranchNodeSpec, MatchPolicy, PromptLLMParameters, PromptNodeSpec,
29
29
  StartNodeSpec, ToolSpec, JsonSchemaObject, ToolRequestBody, ToolResponseBody, UserFieldKind, UserFieldOption, UserFlowSpec, UserNodeSpec, WaitPolicy,
30
- DocProcSpec, TextExtractionResponse, File, DecisionsNodeSpec, DecisionsRule, DocExtSpec, LanguageCode
30
+ DocProcSpec, TextExtractionResponse, DocProcInput, DecisionsNodeSpec, DecisionsRule, DocExtSpec, File
31
31
  )
32
32
  from .constants import CURRENT_USER, START, END, ANY_USER
33
33
  from ..node import (
@@ -115,6 +115,10 @@ class Flow(Node):
115
115
  # we need a deep compare if the incoming schema and existing_schema is the same
116
116
  # pydantic suppport nested comparison by default
117
117
 
118
+ if isinstance(schema, dict):
119
+ # recast schema to support direct access
120
+ schema = JsonSchemaObject.model_validate(schema)
121
+
118
122
  schema.title = title
119
123
 
120
124
  if schema == existing_schema:
@@ -190,7 +194,7 @@ class Flow(Node):
190
194
 
191
195
  def _add_schema_ref(self, schema: JsonSchemaObject, title: str = None) -> SchemaRef:
192
196
  '''Create a schema reference'''
193
- if schema and (schema.type == "object" or schema.type == "array"):
197
+ if schema and (schema.type == "object" or schema.type == "array" or schema.type == "string"):
194
198
  new_schema = self._add_schema(schema, title)
195
199
  return SchemaRef(ref=f"#/schemas/{new_schema.title}")
196
200
  raise AssertionError(f"schema is not a complex object: {schema}")
@@ -199,30 +203,31 @@ class Flow(Node):
199
203
  self._refactor_spec_to_schemaref(node.spec)
200
204
 
201
205
  def _refactor_spec_to_schemaref(self, spec: NodeSpec):
202
- if spec.input_schema:
206
+ if spec.input_schema and not isinstance(spec.input_schema, SchemaRef) and (spec.input_schema.type == "object" or spec.input_schema.type == "array") :
203
207
  if isinstance(spec.input_schema, ToolRequestBody):
204
208
  spec.input_schema = self._add_schema_ref(JsonSchemaObject(type = spec.input_schema.type,
205
209
  properties= spec.input_schema.properties,
206
210
  required= spec.input_schema.required),
207
211
  f"{spec.name}_input")
208
- if spec.output_schema_object is not None and spec.output_schema_object.type == "object":
209
- spec.output_schema = self._add_schema_ref(spec.output_schema_object, spec.output_schema_object.title)
210
- spec.output_schema_object = None
211
- elif spec.output_schema is not None:
212
- if isinstance(spec.output_schema, ToolResponseBody):
213
- if spec.output_schema.type == "object":
214
- json_obj = JsonSchemaObject(type = spec.output_schema.type,
215
- description=spec.output_schema.description,
216
- properties= spec.output_schema.properties,
217
- items = spec.output_schema.items,
218
- uniqueItems=spec.output_schema.uniqueItems,
219
- anyOf=spec.output_schema.anyOf,
220
- required= spec.output_schema.required)
221
- spec.output_schema = self._add_schema_ref(json_obj, f"{spec.name}_output")
222
- elif spec.output_schema.type == "array":
223
- if hasattr(spec.output_schema, "items") and hasattr(spec.output_schema.items, "type") and spec.output_schema.items.type == "object":
224
- schema_ref = self._add_schema_ref(spec.output_schema.items)
225
- spec.output_schema.items = JsonSchemaObjectRef(ref=f"{schema_ref.ref}")
212
+ if not isinstance(spec.output_schema, SchemaRef):
213
+ if spec.output_schema_object is not None and spec.output_schema_object.type == "object":
214
+ spec.output_schema = self._add_schema_ref(spec.output_schema_object, spec.output_schema_object.title)
215
+ spec.output_schema_object = None
216
+ elif spec.output_schema is not None:
217
+ if isinstance(spec.output_schema, ToolResponseBody):
218
+ if spec.output_schema.type == "object":
219
+ json_obj = JsonSchemaObject(type = spec.output_schema.type,
220
+ description=spec.output_schema.description,
221
+ properties= spec.output_schema.properties,
222
+ items = spec.output_schema.items,
223
+ uniqueItems=spec.output_schema.uniqueItems,
224
+ anyOf=spec.output_schema.anyOf,
225
+ required= spec.output_schema.required)
226
+ spec.output_schema = self._add_schema_ref(json_obj, f"{spec.name}_output")
227
+ elif spec.output_schema.type == "array":
228
+ if hasattr(spec.output_schema, "items") and hasattr(spec.output_schema.items, "type") and spec.output_schema.items.type == "object":
229
+ schema_ref = self._add_schema_ref(spec.output_schema.items)
230
+ spec.output_schema.items = JsonSchemaObjectRef(ref=f"{schema_ref.ref}")
226
231
 
227
232
  # def refactor_datamap_spec_to_schemaref(self, spec: FnDataMapSpec):
228
233
  # '''TODO'''
@@ -535,7 +540,7 @@ class Flow(Node):
535
540
  "text_extraction" : TextExtractionResponse
536
541
  }
537
542
  # create input spec
538
- input_schema_obj = _get_json_schema_obj(parameter_name = "input", type_def = File)
543
+ input_schema_obj = _get_json_schema_obj(parameter_name = "input", type_def = DocProcInput)
539
544
  output_schema_obj = _get_json_schema_obj("output", output_schema_dict[task])
540
545
  if "$defs" in output_schema_obj.model_extra:
541
546
  output_schema_obj.model_extra.pop("$defs")
@@ -1060,8 +1065,8 @@ class FlowFactory(BaseModel):
1060
1065
  raise ValueError("Only functions with @flow_spec can be used to create a Flow specification.")
1061
1066
  return Flow(spec = flow_spec)
1062
1067
 
1063
- # create input spec
1064
1068
  input_schema_obj = _get_json_schema_obj(parameter_name = "input", type_def = input_schema)
1069
+ # create input spec
1065
1070
  output_schema_obj = _get_json_schema_obj("output", output_schema)
1066
1071
  if initiators is None:
1067
1072
  initiators = []