ibm-watsonx-orchestrate 1.9.0b2__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 (23) 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/tools/python_tool.py +19 -7
  4. ibm_watsonx_orchestrate/agent_builder/voice_configurations/__init__.py +1 -0
  5. ibm_watsonx_orchestrate/agent_builder/voice_configurations/types.py +98 -0
  6. ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +20 -0
  7. ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +170 -1
  8. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +19 -12
  9. ibm_watsonx_orchestrate/cli/commands/server/server_command.py +17 -4
  10. ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +5 -0
  11. ibm_watsonx_orchestrate/cli/commands/voice_configurations/voice_configurations_command.py +58 -0
  12. ibm_watsonx_orchestrate/cli/commands/voice_configurations/voice_configurations_controller.py +173 -0
  13. ibm_watsonx_orchestrate/cli/main.py +2 -0
  14. ibm_watsonx_orchestrate/client/agents/agent_client.py +64 -1
  15. ibm_watsonx_orchestrate/client/voice_configurations/voice_configurations_client.py +75 -0
  16. ibm_watsonx_orchestrate/docker/compose-lite.yml +15 -1
  17. ibm_watsonx_orchestrate/docker/default.env +9 -6
  18. ibm_watsonx_orchestrate/flow_builder/flows/flow.py +24 -19
  19. {ibm_watsonx_orchestrate-1.9.0b2.dist-info → ibm_watsonx_orchestrate-1.10.0b0.dist-info}/METADATA +1 -1
  20. {ibm_watsonx_orchestrate-1.9.0b2.dist-info → ibm_watsonx_orchestrate-1.10.0b0.dist-info}/RECORD +23 -18
  21. {ibm_watsonx_orchestrate-1.9.0b2.dist-info → ibm_watsonx_orchestrate-1.10.0b0.dist-info}/WHEEL +0 -0
  22. {ibm_watsonx_orchestrate-1.9.0b2.dist-info → ibm_watsonx_orchestrate-1.10.0b0.dist-info}/entry_points.txt +0 -0
  23. {ibm_watsonx_orchestrate-1.9.0b2.dist-info → ibm_watsonx_orchestrate-1.10.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -5,7 +5,8 @@
5
5
 
6
6
  pkg_name = "ibm-watsonx-orchestrate"
7
7
 
8
- __version__ = "1.9.0b2"
8
+ __version__ = "1.10.0b0"
9
+
9
10
 
10
11
 
11
12
 
@@ -77,6 +77,8 @@ class BaseAgentSpec(BaseModel):
77
77
  description: Annotated[str, Field(json_schema_extra={"min_length_str":1})]
78
78
  context_access_enabled: bool = True
79
79
  context_variables: Optional[List[str]] = []
80
+ voice_configuration_id: Optional[str] = None
81
+ voice_configuration: Optional[str] = None
80
82
 
81
83
  def dump_spec(self, file: str) -> None:
82
84
  dumped = self.model_dump(mode='json', exclude_unset=True, exclude_none=True)
@@ -187,15 +187,27 @@ def _fix_optional(schema):
187
187
  replacements = {}
188
188
  if schema.required is None:
189
189
  schema.required = []
190
-
191
190
  for k, v in schema.properties.items():
191
+ # Simple null type & required -> not required
192
192
  if v.type == 'null' and k in schema.required:
193
193
  not_required.append(k)
194
- if v.anyOf is not None and next(filter(lambda x: x.type == 'null', v.anyOf)) and k in schema.required:
195
- v.anyOf = list(filter(lambda x: x.type != 'null', v.anyOf))
196
- if len(v.anyOf) == 1:
197
- replacements[k] = v.anyOf[0]
198
- not_required.append(k)
194
+ # Optional with null & required
195
+ if v.anyOf is not None and [x for x in v.anyOf if x.type == 'null']:
196
+ if k in schema.required:
197
+ # required with default -> not required
198
+ # required without default -> required & remove null from union
199
+ if v.default:
200
+ not_required.append(k)
201
+ else:
202
+ v.anyOf = list(filter(lambda x: x.type != 'null', v.anyOf))
203
+ if len(v.anyOf) == 1:
204
+ replacements[k] = v.anyOf[0]
205
+ else:
206
+ # not required with default -> no change
207
+ # not required without default -> means default input is 'None'
208
+ v.default = v.default if v.default else 'null'
209
+
210
+
199
211
  schema.required = list(filter(lambda x: x not in not_required, schema.required if schema.required is not None else []))
200
212
  for k, v in replacements.items():
201
213
  combined = {
@@ -204,7 +216,7 @@ def _fix_optional(schema):
204
216
  }
205
217
  schema.properties[k] = JsonSchemaObject(**combined)
206
218
  schema.properties[k].anyOf = None
207
-
219
+
208
220
  for k in schema.properties.keys():
209
221
  if schema.properties[k].type == 'object':
210
222
  schema.properties[k] = _fix_optional(schema.properties[k])
@@ -0,0 +1 @@
1
+ from .types import VoiceConfiguration
@@ -0,0 +1,98 @@
1
+ import json
2
+ from typing import Annotated, Optional, List, Dict
3
+ from pydantic import BaseModel, Field, model_validator
4
+
5
+ def _validate_exactly_one_of_fields(object: BaseModel, object_name: str, fields: list[str]):
6
+ present_fields = [getattr(object,field) for field in fields if getattr(object,field) is not None]
7
+
8
+ if len(present_fields) != 1:
9
+ raise ValueError(f"{object_name} requires exactly one of {','.join(fields)}")
10
+
11
+
12
+ def _validate_language_uniqueness(config: BaseModel):
13
+ if hasattr(config,'language') and hasattr(config,'additional_languages'):
14
+ if config.language and config.additional_languages and config.language in config.additional_languages:
15
+ raise ValueError(f"Language '{config.language}' cannot be in both the default language and additional_languages")
16
+
17
+
18
+ class WatsonSTTConfig(BaseModel):
19
+ api_url: Annotated[str, Field(min_length=1,max_length=2048)]
20
+ api_key: Optional[Annotated[str, Field(min_length=1,max_length=2048)]] = None
21
+ bearer_token: Optional[Annotated[str, Field(min_length=1,max_length=2048)]] = None
22
+ model: Annotated[str, Field(min_length=1,max_length=256)]
23
+
24
+ class EmotechSTTConfig(BaseModel):
25
+ api_key: Annotated[str,Field(min_length=1,max_length=2048)]
26
+ api_url: Annotated[str,Field(min_length=1,max_length=2048)]
27
+
28
+
29
+ class SpeechToTextConfig(BaseModel):
30
+ provider: Annotated[str, Field(min_length=1,max_length=128)]
31
+ watson_stt_config: Optional[WatsonSTTConfig] = None
32
+ emotech_stt_config: Optional[EmotechSTTConfig] = None
33
+
34
+ @model_validator(mode='after')
35
+ def validate_providers(self):
36
+ _validate_exactly_one_of_fields(self,'SpeechToTextConfig',['watson_stt_config','emotech_stt_config'])
37
+ return self
38
+
39
+ class WatsonTTSConfig(BaseModel):
40
+ api_url: Annotated[str, Field(min_length=1,max_length=2048)]
41
+ api_key: Optional[Annotated[str, Field(min_length=1,max_length=2048)]] = None
42
+ bearer_token: Optional[Annotated[str, Field(min_length=1,max_length=2048)]] = None
43
+ voice: Annotated[str, Field(min_length=1,max_length=128)]
44
+ rate_percentage: Optional[int] = None
45
+ pitch_percentage: Optional[int] = None
46
+ language: Optional[str] = None
47
+
48
+ class EmotechTTSConfig(BaseModel):
49
+ api_url: Annotated[str, Field(min_length=1,max_length=2048)]
50
+ api_key: Annotated[str, Field(min_length=1,max_length=2048)]
51
+ voice: Optional[Annotated[str, Field(min_length=1,max_length=128)]]
52
+
53
+ class TextToSpeechConfig(BaseModel):
54
+ provider: Annotated[str, Field(min_length=1,max_length=128)]
55
+ watson_tts_config: Optional[WatsonTTSConfig] = None
56
+ emotech_tts_config: Optional[EmotechTTSConfig] = None
57
+
58
+ @model_validator(mode='after')
59
+ def validate_providers(self):
60
+ _validate_exactly_one_of_fields(self,'TextToSpeechConfig',['watson_tts_config','emotech_tts_config'])
61
+ return self
62
+
63
+ class AdditionalProperties(BaseModel):
64
+ speech_to_text: Optional[SpeechToTextConfig] = None
65
+ text_to_speech: Optional[TextToSpeechConfig] = None
66
+
67
+ class DTMFInput(BaseModel):
68
+ inter_digit_timeout_ms: Optional[int] = None
69
+ termination_key: Optional[str] = None
70
+ maximum_count: Optional[int] = None
71
+ ignore_speech: Optional[bool] = None
72
+
73
+ class AttachedAgent(BaseModel):
74
+ id: str
75
+ name: Optional[str] = None
76
+ display_name: Optional[str] = None
77
+
78
+ class VoiceConfiguration(BaseModel):
79
+ name: Annotated[str, Field(min_length=1,max_length=128)]
80
+ speech_to_text: SpeechToTextConfig
81
+ text_to_speech: TextToSpeechConfig
82
+ language: Optional[Annotated[str,Field(min_length=2,max_length=16)]] = None
83
+ additional_languages: Optional[dict[str,AdditionalProperties]] = None
84
+ dtmf_input: Optional[DTMFInput] = None
85
+ voice_configuration_id: Optional[str] = None
86
+ tenant_id: Optional[Annotated[str, Field(min_length=1,max_length=128)]] = None
87
+ attached_agents: Optional[list[AttachedAgent]] = None
88
+
89
+ @model_validator(mode='after')
90
+ def validate_language(self):
91
+ _validate_language_uniqueness(self)
92
+ return self
93
+
94
+ def dumps_spec(self) -> str:
95
+ dumped = self.model_dump(mode='json', exclude_none=True)
96
+ return json.dumps(dumped, indent=2)
97
+
98
+
@@ -253,3 +253,23 @@ def export_agent(
253
253
  ):
254
254
  agents_controller = AgentsController()
255
255
  agents_controller.export_agent(name=name, kind=kind, output_path=output_file, agent_only_flag=agent_only_flag)
256
+
257
+ @agents_app.command(name="deploy", help="Deploy Agent")
258
+ def deploy_agent(
259
+ name: Annotated[
260
+ str,
261
+ typer.Option("--name", "-n", help="Name of the agent you wish to deploy"),
262
+ ]
263
+ ):
264
+ agents_controller = AgentsController()
265
+ agents_controller.deploy_agent(name=name)
266
+
267
+ @agents_app.command(name="undeploy", help="Undeploy Agent")
268
+ def undeploy_agent(
269
+ name: Annotated[
270
+ str,
271
+ typer.Option("--name", "-n", help="Name of the agent you wish to undeploy"),
272
+ ]
273
+ ):
274
+ agents_controller = AgentsController()
275
+ agents_controller.undeploy_agent(name=name)
@@ -29,13 +29,17 @@ from ibm_watsonx_orchestrate.client.agents.agent_client import AgentClient, Agen
29
29
  from ibm_watsonx_orchestrate.client.agents.external_agent_client import ExternalAgentClient
30
30
  from ibm_watsonx_orchestrate.client.agents.assistant_agent_client import AssistantAgentClient
31
31
  from ibm_watsonx_orchestrate.client.tools.tool_client import ToolClient
32
+ from ibm_watsonx_orchestrate.client.voice_configurations.voice_configurations_client import VoiceConfigurationsClient
32
33
  from ibm_watsonx_orchestrate.utils.exceptions import BadRequest
33
34
  from ibm_watsonx_orchestrate.client.connections import get_connections_client
34
35
  from ibm_watsonx_orchestrate.client.knowledge_bases.knowledge_base_client import KnowledgeBaseClient
35
36
 
36
- from ibm_watsonx_orchestrate.client.utils import instantiate_client
37
+ from ibm_watsonx_orchestrate.client.utils import instantiate_client, is_local_dev
37
38
  from ibm_watsonx_orchestrate.utils.utils import check_file_in_zip
38
39
 
40
+ from rich.console import Console
41
+ from rich.progress import Progress, SpinnerColumn, TextColumn
42
+
39
43
  logger = logging.getLogger(__name__)
40
44
 
41
45
  # Helper generic type for any agent
@@ -197,6 +201,7 @@ def get_app_id_from_conn_id(conn_id: str) -> str:
197
201
  exit(1)
198
202
  return app_id
199
203
 
204
+
200
205
  def get_agent_details(name: str, client: AgentClient | ExternalAgentClient | AssistantAgentClient) -> dict:
201
206
  agent_specs = client.get_draft_by_name(name)
202
207
  if len(agent_specs) > 1:
@@ -219,6 +224,7 @@ class AgentsController:
219
224
  self.assistant_client = None
220
225
  self.tool_client = None
221
226
  self.knowledge_base_client = None
227
+ self.voice_configuration_client = None
222
228
 
223
229
  def get_native_client(self):
224
230
  if not self.native_client:
@@ -245,6 +251,11 @@ class AgentsController:
245
251
  self.knowledge_base_client = instantiate_client(KnowledgeBaseClient)
246
252
  return self.knowledge_base_client
247
253
 
254
+ def get_voice_configuration_client(self):
255
+ if not self.voice_configuration_client:
256
+ self.voice_configuration_client = instantiate_client(VoiceConfigurationsClient)
257
+ return self.voice_configuration_client
258
+
248
259
  @staticmethod
249
260
  def import_agent(file: str, app_id: str) -> List[Agent | ExternalAgent | AssistantAgent]:
250
261
  agents = parse_file(file)
@@ -520,6 +531,38 @@ class AgentsController:
520
531
  guideline.tool = name
521
532
 
522
533
  return ref_agent
534
+
535
+ def get_voice_config_name_from_id(self, voice_config_id: str) -> str | None:
536
+ client = self.get_voice_configuration_client()
537
+ config = client.get_by_id(voice_config_id)
538
+ return config.name if config else None
539
+
540
+ def get_voice_config_id_from_name(self, voice_config_name: str) -> str | None:
541
+ client = self.get_voice_configuration_client()
542
+ configs = client.get_by_name(voice_config_name)
543
+
544
+ if len(configs) == 0:
545
+ logger.error(f"No voice_configs with the name '{voice_config_name}' found. Failed to get config")
546
+ sys.exit(1)
547
+
548
+ if len(configs) > 1:
549
+ logger.error(f"Multiple voice_configs with the name '{voice_config_name}' found. Failed to get config")
550
+ sys.exit(1)
551
+
552
+ return configs[0].voice_configuration_id
553
+
554
+
555
+ def reference_voice_config(self,agent: Agent):
556
+ deref_agent = deepcopy(agent)
557
+ deref_agent.voice_configuration = self.get_voice_config_name_from_id(agent.voice_configuration_id)
558
+ del deref_agent.voice_configuration_id
559
+ return deref_agent
560
+
561
+ def dereference_voice_config(self,agent: Agent):
562
+ ref_agent = deepcopy(agent)
563
+ ref_agent.voice_configuration_id = self.get_voice_config_id_from_name(agent.voice_configuration)
564
+ del ref_agent.voice_configuration
565
+ return ref_agent
523
566
 
524
567
  @staticmethod
525
568
  def dereference_app_id(agent: ExternalAgent | AssistantAgent) -> ExternalAgent | AssistantAgent:
@@ -540,7 +583,18 @@ class AgentsController:
540
583
  agent.config.connection_id = None
541
584
 
542
585
  return agent
586
+
587
+ def dereference_common_agent_dependencies(self, agent: AnyAgentT) -> AnyAgentT:
588
+ if agent.voice_configuration:
589
+ agent = self.dereference_voice_config(agent)
590
+
591
+ return agent
592
+
593
+ def reference_common_agent_dependencies(self, agent: AnyAgentT) -> AnyAgentT:
594
+ if agent.voice_configuration_id:
595
+ agent = self.reference_voice_config(agent)
543
596
 
597
+ return agent
544
598
 
545
599
  def dereference_native_agent_dependencies(self, agent: Agent) -> Agent:
546
600
  if agent.collaborators and len(agent.collaborators):
@@ -584,6 +638,8 @@ class AgentsController:
584
638
 
585
639
  # Convert all names used in an agent to the corresponding ids
586
640
  def dereference_agent_dependencies(self, agent: AnyAgentT) -> AnyAgentT:
641
+
642
+ agent = self.dereference_common_agent_dependencies(agent)
587
643
  if isinstance(agent, Agent):
588
644
  return self.dereference_native_agent_dependencies(agent)
589
645
  if isinstance(agent, ExternalAgent) or isinstance(agent, AssistantAgent):
@@ -591,6 +647,8 @@ class AgentsController:
591
647
 
592
648
  # Convert all ids used in an agent to the corresponding names
593
649
  def reference_agent_dependencies(self, agent: AnyAgentT) -> AnyAgentT:
650
+
651
+ agent = self.reference_common_agent_dependencies(agent)
594
652
  if isinstance(agent, Agent):
595
653
  return self.reference_native_agent_dependencies(agent)
596
654
  if isinstance(agent, ExternalAgent) or isinstance(agent, AssistantAgent):
@@ -1111,3 +1169,114 @@ class AgentsController:
1111
1169
  logger.info(f"Successfully wrote agents and tools to '{output_path}'")
1112
1170
  zip_file_out.close()
1113
1171
 
1172
+
1173
+ def deploy_agent(self, name: str):
1174
+ if is_local_dev():
1175
+ logger.error("Agents cannot be deployed in Developer Edition")
1176
+ sys.exit(1)
1177
+ native_client = self.get_native_client()
1178
+ external_client = self.get_external_client()
1179
+ assistant_client = self.get_assistant_client()
1180
+
1181
+ existing_native_agents = native_client.get_draft_by_name(name)
1182
+ existing_external_agents = external_client.get_draft_by_name(name)
1183
+ existing_assistant_agents = assistant_client.get_draft_by_name(name)
1184
+
1185
+ if len(existing_native_agents) == 0 and (len(existing_external_agents) >= 1 or len(existing_assistant_agents) >= 1):
1186
+ logger.error(f"No native agent found with name '{name}'. Only Native Agents can be deployed to a Live Environment")
1187
+ sys.exit(1)
1188
+ if len(existing_native_agents) > 1:
1189
+ logger.error(f"Multiple native agents with the name '{name}' found. Failed to get agent")
1190
+ sys.exit(1)
1191
+ if len(existing_native_agents) == 0:
1192
+ logger.error(f"No native agents with the name '{name}' found. Failed to get agent")
1193
+ sys.exit(1)
1194
+
1195
+
1196
+ agent_details = existing_native_agents[0]
1197
+ agent_id = agent_details.get("id")
1198
+
1199
+ environments = native_client.get_environments_for_agent(agent_id)
1200
+
1201
+ live_environment = [env for env in environments if env.get("name") == "live"]
1202
+ if live_environment is None:
1203
+ logger.error("No live environment found for this tenant")
1204
+ sys.exit(1)
1205
+
1206
+ live_env_id = live_environment[0].get("id")
1207
+
1208
+ console = Console()
1209
+ with Progress(
1210
+ SpinnerColumn(spinner_name="dots"),
1211
+ TextColumn("[progress.description]{task.description}"),
1212
+ transient=True,
1213
+ console=console,
1214
+ ) as progress:
1215
+ progress.add_task(description="Deploying agent to Live envrionment", total=None)
1216
+
1217
+ status = native_client.deploy(agent_id, live_env_id)
1218
+
1219
+ if status:
1220
+ logger.info(f"Successfully deployed agent {name}")
1221
+ else:
1222
+ logger.error(f"Error deploying agent {name}")
1223
+
1224
+ def undeploy_agent(self, name: str):
1225
+ if is_local_dev():
1226
+ logger.error("Agents cannot be undeployed in Developer Edition")
1227
+ sys.exit(1)
1228
+
1229
+ native_client = self.get_native_client()
1230
+ external_client = self.get_external_client()
1231
+ assistant_client = self.get_assistant_client()
1232
+
1233
+ existing_native_agents = native_client.get_draft_by_name(name)
1234
+ existing_external_agents = external_client.get_draft_by_name(name)
1235
+ existing_assistant_agents = assistant_client.get_draft_by_name(name)
1236
+
1237
+ if len(existing_native_agents) == 0 and (len(existing_external_agents) >= 1 or len(existing_assistant_agents) >= 1):
1238
+ logger.error(f"No native agent found with name '{name}'. Only Native Agents can be undeployed from a Live Environment")
1239
+ sys.exit(1)
1240
+ if len(existing_native_agents) > 1:
1241
+ logger.error(f"Multiple native agents with the name '{name}' found. Failed to get agent")
1242
+ sys.exit(1)
1243
+ if len(existing_native_agents) == 0:
1244
+ logger.error(f"No native agents with the name '{name}' found. Failed to get agent")
1245
+ sys.exit(1)
1246
+
1247
+ agent_details = existing_native_agents[0]
1248
+ agent_id = agent_details.get("id")
1249
+
1250
+ environments = native_client.get_environments_for_agent(agent_id)
1251
+ live_environment = [env for env in environments if env.get("name") == "live"]
1252
+ if live_environment is None:
1253
+ logger.error("No live environment found for this tenant")
1254
+ sys.exit(1)
1255
+ version_id = live_environment[0].get("current_version")
1256
+
1257
+ if version_id is None:
1258
+ agent_name = agent_details.get("name")
1259
+ logger.error(f"Agent {agent_name} does not exist in a Live environment")
1260
+ sys.exit(1)
1261
+
1262
+ draft_environment = [env for env in environments if env.get("name") == "draft"]
1263
+ if draft_environment is None:
1264
+ logger.error("No draft environment found for this tenant")
1265
+ sys.exit(1)
1266
+ draft_env_id = draft_environment[0].get("id")
1267
+
1268
+ console = Console()
1269
+ with Progress(
1270
+ SpinnerColumn(spinner_name="dots"),
1271
+ TextColumn("[progress.description]{task.description}"),
1272
+ transient=True,
1273
+ console=console,
1274
+ ) as progress:
1275
+ progress.add_task(description="Undeploying agent to Draft envrionment", total=None)
1276
+
1277
+ status = native_client.undeploy(agent_id, version_id, draft_env_id)
1278
+ if status:
1279
+ logger.info(f"Successfully undeployed agent {name}")
1280
+ else:
1281
+ logger.error(f"Error undeploying agent {name}")
1282
+
@@ -72,6 +72,21 @@ class KnowledgeBaseController:
72
72
 
73
73
  knowledge_bases = parse_file(file=file)
74
74
 
75
+ if app_id:
76
+ connections_client = get_connections_client()
77
+ connection_id = None
78
+
79
+ connections = connections_client.get_draft_by_app_id(app_id=app_id)
80
+ if not connections:
81
+ logger.error(f"No connection exists with the app-id '{app_id}'")
82
+ exit(1)
83
+
84
+ connection_id = connections.connection_id
85
+
86
+ for kb in knowledge_bases:
87
+ if kb.conversational_search_tool and kb.conversational_search_tool.index_config and len(kb.conversational_search_tool.index_config) > 0:
88
+ kb.conversational_search_tool.index_config[0].connection_id = connection_id
89
+
75
90
  existing_knowledge_bases = client.get_by_names([kb.name for kb in knowledge_bases])
76
91
 
77
92
  for kb in knowledge_bases:
@@ -97,18 +112,10 @@ class KnowledgeBaseController:
97
112
  if len(kb.conversational_search_tool.index_config) != 1:
98
113
  raise ValueError(f"Must provide exactly one conversational_search_tool.index_config. Provided {len(kb.conversational_search_tool.index_config)}.")
99
114
 
100
-
101
- if app_id:
102
- connections_client = get_connections_client()
103
- connection_id = None
104
- if app_id is not None:
105
- connections = connections_client.get_draft_by_app_id(app_id=app_id)
106
- if not connections:
107
- logger.error(f"No connection exists with the app-id '{app_id}'")
108
- exit(1)
109
-
110
- connection_id = connections.connection_id
111
- kb.conversational_search_tool.index_config[0].connection_id = connection_id
115
+ if (kb.conversational_search_tool.index_config[0].milvus or \
116
+ kb.conversational_search_tool.index_config[0].elastic_search) and \
117
+ not kb.conversational_search_tool.index_config[0].connection_id:
118
+ raise ValueError(f"Must provide credentials (via --app-id) when using milvus or elastic_search.")
112
119
 
113
120
  kb.prioritize_built_in_index = False
114
121
  client.create(payload=kb.model_dump(exclude_none=True))
@@ -370,9 +370,13 @@ def get_persisted_user_env() -> dict | None:
370
370
  user_env = cfg.get(USER_ENV_CACHE_HEADER) if cfg.get(USER_ENV_CACHE_HEADER) else None
371
371
  return user_env
372
372
 
373
- def run_compose_lite(final_env_file: Path, experimental_with_langfuse=False, experimental_with_ibm_telemetry=False, with_doc_processing=False) -> None:
374
-
375
-
373
+ def run_compose_lite(
374
+ final_env_file: Path,
375
+ experimental_with_langfuse=False,
376
+ experimental_with_ibm_telemetry=False,
377
+ with_doc_processing=False,
378
+ with_voice=False
379
+ ) -> None:
376
380
  compose_path = get_compose_file()
377
381
 
378
382
  compose_command = ensure_docker_compose_installed()
@@ -408,6 +412,8 @@ def run_compose_lite(final_env_file: Path, experimental_with_langfuse=False, exp
408
412
  profiles.append("ibm-telemetry")
409
413
  if with_doc_processing:
410
414
  profiles.append("docproc")
415
+ if with_voice:
416
+ profiles.append("voice")
411
417
 
412
418
  command = compose_command[:]
413
419
  for profile in profiles:
@@ -855,6 +861,11 @@ def server_start(
855
861
  '--compose-file', '-f',
856
862
  help='Provide the path to a custom docker-compose file to use instead of the default compose file'
857
863
  ),
864
+ with_voice: bool = typer.Option(
865
+ False,
866
+ '--with-voice', '-v',
867
+ help='Enable voice controller to interact with the chat via voice channels'
868
+ ),
858
869
  ):
859
870
  confirm_accepts_license_agreement(accept_terms_and_conditions)
860
871
 
@@ -896,6 +907,7 @@ def server_start(
896
907
  if experimental_with_ibm_telemetry:
897
908
  merged_env_dict['USE_IBM_TELEMETRY'] = 'true'
898
909
 
910
+
899
911
  try:
900
912
  dev_edition_source = get_dev_edition_source(merged_env_dict)
901
913
  docker_login_by_dev_edition_source(merged_env_dict, dev_edition_source)
@@ -908,7 +920,8 @@ def server_start(
908
920
  run_compose_lite(final_env_file=final_env_file,
909
921
  experimental_with_langfuse=experimental_with_langfuse,
910
922
  experimental_with_ibm_telemetry=experimental_with_ibm_telemetry,
911
- with_doc_processing=with_doc_processing)
923
+ with_doc_processing=with_doc_processing,
924
+ with_voice=with_voice)
912
925
 
913
926
  run_db_migration()
914
927
 
@@ -464,6 +464,11 @@ The [bold]flow tool[/bold] is being imported from [green]`{file}`[/green].
464
464
  continue
465
465
 
466
466
  model = obj().to_json()
467
+ # Ensure metadata exists and is correct
468
+ if "metadata" not in model or not isinstance(model["metadata"], dict):
469
+ model["metadata"] = {}
470
+ if "source_kind" not in model["metadata"]:
471
+ model["metadata"]["source_kind"] = "adk/python"
467
472
  break
468
473
 
469
474
  elif file_path.suffix.lower() == ".json":
@@ -0,0 +1,58 @@
1
+ import sys
2
+ from typing import Annotated
3
+ import typer
4
+ import logging
5
+
6
+ from ibm_watsonx_orchestrate.cli.commands.voice_configurations.voice_configurations_controller import VoiceConfigurationsController
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ voice_configurations_app = typer.Typer(no_args_is_help=True)
11
+
12
+ @voice_configurations_app.command(name="import", help="Import a voice configuration into the active environment from a file")
13
+ def import_voice_config(
14
+ file: Annotated[
15
+ str,
16
+ typer.Option(
17
+ "--file",
18
+ "-f",
19
+ help="YAML file with voice configuraton definition"
20
+ )
21
+ ],
22
+ ):
23
+ voice_config_controller = VoiceConfigurationsController()
24
+ imported_config = voice_config_controller.import_voice_config(file)
25
+ voice_config_controller.publish_or_update_voice_config(imported_config)
26
+
27
+ @voice_configurations_app.command(name="remove", help="Remove a voice configuration from the active environment")
28
+ def remove_voice_config(
29
+ voice_config_name: Annotated[
30
+ str,
31
+ typer.Option(
32
+ "--name",
33
+ "-n",
34
+ help="name of the voice configuration to remove"
35
+ )
36
+ ] = None,
37
+ ):
38
+ voice_config_controller = VoiceConfigurationsController()
39
+ if voice_config_name:
40
+ voice_config_controller.remove_voice_config_by_name(voice_config_name)
41
+ else:
42
+ raise TypeError("You must specify the name of a voice configuration")
43
+
44
+
45
+
46
+ @voice_configurations_app.command(name="list", help="List all voice configurations in the active environment")
47
+ def list_voice_configs(
48
+ verbose: Annotated[
49
+ bool,
50
+ typer.Option(
51
+ "--verbose",
52
+ "-v",
53
+ help="List full details of all voice configurations in json format"
54
+ )
55
+ ] = False,
56
+ ):
57
+ voice_config_controller = VoiceConfigurationsController()
58
+ voice_config_controller.list_voice_configs(verbose)
@@ -0,0 +1,173 @@
1
+ import json
2
+ import sys
3
+ import rich
4
+ import yaml
5
+ import logging
6
+ from ibm_watsonx_orchestrate.agent_builder.voice_configurations import VoiceConfiguration
7
+ from ibm_watsonx_orchestrate.client.utils import instantiate_client
8
+ from ibm_watsonx_orchestrate.client.voice_configurations.voice_configurations_client import VoiceConfigurationsClient
9
+ from ibm_watsonx_orchestrate.utils.exceptions import BadRequest
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ class VoiceConfigurationsController:
14
+
15
+ def __init__(self):
16
+ self.voice_configs_client = None
17
+
18
+ def get_voice_configurations_client(self):
19
+ if not self.voice_configs_client:
20
+ self.voice_configs_client = instantiate_client(VoiceConfigurationsClient)
21
+ return self.voice_configs_client
22
+
23
+
24
+ def import_voice_config(self, file: str) -> VoiceConfiguration:
25
+
26
+ if file.endswith('.yaml') or file.endswith('.yml'):
27
+ with open(file, 'r') as f:
28
+ content = yaml.load(f, Loader=yaml.SafeLoader)
29
+
30
+ elif file.endswith(".json"):
31
+ with open(file, 'r') as f:
32
+ content = json.load(f)
33
+
34
+ else:
35
+ raise BadRequest("file must end in .yaml, .yml or .json")
36
+
37
+ return VoiceConfiguration.model_validate(content)
38
+
39
+
40
+ def fetch_voice_configs(self) -> list[VoiceConfiguration]:
41
+ client = self.get_voice_configurations_client()
42
+ res = client.list()
43
+
44
+ voice_configs = []
45
+
46
+ for config in res:
47
+ try:
48
+ voice_configs.append(VoiceConfiguration.model_validate(config))
49
+ except:
50
+ name = config.get('name', None)
51
+ logger.error(f"Config '{name}' could not be parsed")
52
+
53
+ return voice_configs
54
+
55
+ def get_voice_config(self, voice_config_id: str) -> VoiceConfiguration | None:
56
+ client = self.get_voice_configurations_client()
57
+ return client.get(voice_config_id)
58
+
59
+ def get_voice_config_by_name(self, voice_config_name: str) -> VoiceConfiguration | None:
60
+ client = self.get_voice_configurations_client()
61
+ configs = client.get_by_name(voice_config_name)
62
+ if len(configs) == 0:
63
+ logger.error(f"No voice_configs with the name '{voice_config_name}' found. Failed to get config")
64
+ sys.exit(1)
65
+
66
+ if len(configs) > 1:
67
+ logger.error(f"Multiple voice_configs with the name '{voice_config_name}' found. Failed to get config")
68
+ sys.exit(1)
69
+
70
+ return configs[0]
71
+
72
+ def list_voice_configs(self, verbose: bool) -> None:
73
+ voice_configs = self.fetch_voice_configs()
74
+
75
+ if verbose:
76
+ json_configs = [json.loads(x.dumps_spec()) for x in voice_configs]
77
+ rich.print_json(json.dumps(json_configs, indent=4))
78
+ else:
79
+ config_table = rich.table.Table(
80
+ show_header=True,
81
+ header_style="bold white",
82
+ title="Voice Configurations",
83
+ show_lines=True
84
+ )
85
+
86
+ column_args={
87
+ "Name" : {"overflow": "fold"},
88
+ "ID" : {"overflow": "fold"},
89
+ "STT Provider" : {"overflow": "fold"},
90
+ "TTS Provider" : {"overflow": "fold"},
91
+ "Attached Agents" : {}
92
+ }
93
+
94
+ for column in column_args:
95
+ config_table.add_column(column, **column_args[column])
96
+
97
+ for config in voice_configs:
98
+ attached_agents = [x.display_name or x.name or x.id for x in config.attached_agents]
99
+ config_table.add_row(
100
+ config.name,
101
+ config.voice_configuration_id,
102
+ config.speech_to_text.provider,
103
+ config.text_to_speech.provider,
104
+ ",".join(attached_agents)
105
+ )
106
+
107
+ rich.print(config_table)
108
+
109
+
110
+ def create_voice_config(self, voice_config: VoiceConfiguration) -> str | None:
111
+ client = self.get_voice_configurations_client()
112
+ res = client.create(voice_config)
113
+ config_id = res.get("id",None)
114
+ if config_id:
115
+ logger.info(f"Sucessfully created voice config '{voice_config['name']}'. id: '{config_id}'")
116
+
117
+ return config_id
118
+
119
+
120
+ def update_voice_config_by_id(self, voice_config_id: str, voice_config: VoiceConfiguration) -> str | None:
121
+ client = self.get_voice_configurations_client()
122
+ res = client.update(voice_config_id,voice_config)
123
+ config_id = res.get("id",None)
124
+ if config_id:
125
+ logger.info(f"Sucessfully updated voice config '{voice_config['name']}'. id: '{config_id}'")
126
+
127
+ return config_id
128
+
129
+ def update_voice_config_by_name(self, voice_config_name: str, voice_config: VoiceConfiguration) -> str | None:
130
+ client = self.get_voice_configurations_client()
131
+ existing_config = client.get_by_name(voice_config_name)
132
+
133
+ if existing_config and len(existing_config) > 0:
134
+ config_id = existing_config[0].voice_configuration_id
135
+ client.update(config_id,voice_config)
136
+ else:
137
+ logger.warning(f"Voice config '{voice_config_name}' not found, creating new config instead")
138
+ config_id = self.create_voice_config(voice_config)
139
+
140
+ return config_id
141
+
142
+ def publish_or_update_voice_config(self, voice_config: VoiceConfiguration) -> str | None:
143
+ client = self.get_voice_configurations_client()
144
+ voice_config_name = voice_config.name
145
+ existing_config = client.get_by_name(voice_config_name)
146
+
147
+ if existing_config and len(existing_config) > 0:
148
+ config_id = existing_config[0].voice_configuration_id
149
+ client.update(config_id,voice_config)
150
+ else:
151
+ client.create(voice_config)
152
+
153
+ def remove_voice_config_by_id(self, voice_config_id: str) -> None:
154
+ client = self.get_voice_configurations_client()
155
+ client.delete(voice_config_id)
156
+ logger.info(f"Sucessfully deleted voice config '{voice_config_id}'")
157
+
158
+ def remove_voice_config_by_name(self, voice_config_name: str) -> None:
159
+ client = self.get_voice_configurations_client()
160
+ voice_config = self.get_voice_config_by_name(voice_config_name)
161
+ if voice_config:
162
+ client.delete(voice_config.voice_configuration_id)
163
+ logger.info(f"Sucessfully deleted voice config '{voice_config_name}'")
164
+ else:
165
+ logger.info(f"Voice config '{voice_config_name}' not found")
166
+
167
+
168
+
169
+
170
+
171
+
172
+
173
+
@@ -16,6 +16,7 @@ from ibm_watsonx_orchestrate.cli.commands.knowledge_bases.knowledge_bases_comman
16
16
  from ibm_watsonx_orchestrate.cli.commands.toolkit.toolkit_command import toolkits_app
17
17
  from ibm_watsonx_orchestrate.cli.commands.evaluations.evaluations_command import evaluation_app
18
18
  from ibm_watsonx_orchestrate.cli.commands.copilot.copilot_command import copilot_app
19
+ from ibm_watsonx_orchestrate.cli.commands.voice_configurations.voice_configurations_command import voice_configurations_app
19
20
  from ibm_watsonx_orchestrate.cli.init_helper import init_callback
20
21
 
21
22
  import urllib3
@@ -35,6 +36,7 @@ app.add_typer(tools_app, name="tools", help='Interact with the tools in your act
35
36
  app.add_typer(toolkits_app, name="toolkits", help="Interact with the toolkits in your active env")
36
37
  app.add_typer(knowledge_bases_app, name="knowledge-bases", help="Upload knowledge your agents can search through to your active env")
37
38
  app.add_typer(connections_app, name="connections", help='Interact with the agents in your active env')
39
+ app.add_typer(voice_configurations_app, name="voice-configs", help="Configure voice providers to enable voice interaction with your agents")
38
40
  app.add_typer(server_app, name="server", help='Manipulate your local Orchestrate Developer Edition server [requires entitlement]')
39
41
  app.add_typer(chat_app, name="chat", help='Launch the chat ui for your local Developer Edition server [requires entitlement]')
40
42
  app.add_typer(models_app, name="models", help='List the available large language models (llms) that can be used in your agent definitions')
@@ -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
 
@@ -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"
@@ -342,6 +343,7 @@ services:
342
343
  AGENTOPS_API_KEY_AUTH_ENABLED: ${AGENTOPS_API_KEY_AUTH_ENABLED:-false}
343
344
  AGENTOPS_API_KEY: ${AGENTOPS_API_KEY}
344
345
  ES_HOST: http://elasticsearch:9200
346
+ ORIGIN_HEADER: "internal"
345
347
 
346
348
  wxo-server-worker:
347
349
  image: ${WORKER_REGISTRY:-us.icr.io/watson-orchestrate-private}/wxo-server-conversation_controller:${WORKER_TAG:-latest}
@@ -517,7 +519,7 @@ services:
517
519
  # IBM AGENT-OPS
518
520
  ########################
519
521
  elasticsearch:
520
- image: docker.elastic.co/elasticsearch/elasticsearch:8.17.5
522
+ image: docker.elastic.co/elasticsearch/elasticsearch:8.19.0
521
523
  profiles: [ibm-telemetry]
522
524
  environment:
523
525
  - discovery.type=single-node
@@ -794,6 +796,7 @@ services:
794
796
  WO_INSTANCE: ${WO_INSTANCE}
795
797
  AUTHORIZATION_URL: ${AUTHORIZATION_URL}
796
798
  WO_AUTH_TYPE: ${WO_AUTH_TYPE}
799
+ FLOW_CALLBACK_REQUEST_RETRIES: "1"
797
800
  healthcheck:
798
801
  test: curl -k http://localhost:9044/readiness --fail
799
802
  interval: 30s
@@ -1092,6 +1095,17 @@ services:
1092
1095
  exit 0;
1093
1096
  "
1094
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
+
1095
1109
  volumes:
1096
1110
  tools:
1097
1111
  driver: local
@@ -57,10 +57,10 @@ EVENT_BROKER_TTL="3600"
57
57
  REGISTRY_URL=
58
58
 
59
59
 
60
- SERVER_TAG=01-08-2025
60
+ SERVER_TAG=13-08-2025-b01e43e00a7c900f92057d63520be08f965b6b24
61
61
  SERVER_REGISTRY=
62
62
 
63
- WORKER_TAG=01-08-2025-v2
63
+ WORKER_TAG=13-08-2025-b01e43e00a7c900f92057d63520be08f965b6b24
64
64
  WORKER_REGISTRY=
65
65
 
66
66
  AI_GATEWAY_TAG=01-08-2025-v1
@@ -82,16 +82,16 @@ UITAG=31-07-2025
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=31-07-2025-d7145cb
91
+ BUILDER_TAG=12-08-2025-8f00997
92
92
  BUILDER_REGISTRY=
93
93
 
94
- FLOW_RUNTIME_TAG=01-08-2025
94
+ FLOW_RUNTIME_TAG=11-08-2025
95
95
  FLOW_RUMTIME_REGISTRY=
96
96
 
97
97
 
@@ -107,6 +107,9 @@ SOCKET_HANDLER_REGISTRY=
107
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=
@@ -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:
@@ -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 and (spec.input_schema.type == "object" or spec.input_schema.type == "array") :
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'''
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ibm-watsonx-orchestrate
3
- Version: 1.9.0b2
3
+ Version: 1.10.0b0
4
4
  Summary: IBM watsonx.orchestrate SDK
5
5
  Author-email: IBM <support@ibm.com>
6
6
  License: MIT License
@@ -1,10 +1,10 @@
1
- ibm_watsonx_orchestrate/__init__.py,sha256=qSsB-sI25lOiixDR8S9s8PpP3iB7TKKGjtr405WOdZI,427
1
+ ibm_watsonx_orchestrate/__init__.py,sha256=CE0F2w_9jm2X8mCofA2JwrXdBnhedrW0zMrgrGnZNy8,429
2
2
  ibm_watsonx_orchestrate/agent_builder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  ibm_watsonx_orchestrate/agent_builder/agents/__init__.py,sha256=lmZwaiWXD4Ea19nrMwZXaqCxFMG29xNS8vUoZtK3yI4,392
4
4
  ibm_watsonx_orchestrate/agent_builder/agents/agent.py,sha256=W0uya81fQPrYZFaO_tlsxBL56Bfpw0xrqdxQJhAZ6XI,983
5
5
  ibm_watsonx_orchestrate/agent_builder/agents/assistant_agent.py,sha256=NnWThJ2N8HUOD9IDL6ZhtTKyLMHSacJCpxDNityRmgY,1051
6
6
  ibm_watsonx_orchestrate/agent_builder/agents/external_agent.py,sha256=7HzEFjd7JiiRTgvA1RVA3M0-Mr42FTQnOtGMII5ufk0,1045
7
- ibm_watsonx_orchestrate/agent_builder/agents/types.py,sha256=kXbWWz4iHo-knQ5OZRZW7kc8rTwfOzPVn7iyndk5cDs,13013
7
+ ibm_watsonx_orchestrate/agent_builder/agents/types.py,sha256=al_nz27cPqD1QLnamMgd8mkgUiATECaEptKttlkf0wU,13108
8
8
  ibm_watsonx_orchestrate/agent_builder/agents/webchat_customizations/__init__.py,sha256=5TXa8UqKUAlDo4hTbE5S9OPEkQhxXhPJHJC4pEe8U00,92
9
9
  ibm_watsonx_orchestrate/agent_builder/agents/webchat_customizations/prompts.py,sha256=jNVF_jgz1Dmt7-RxAceAS0XWXk_fx9h3sS_fGrvZT28,941
10
10
  ibm_watsonx_orchestrate/agent_builder/agents/webchat_customizations/welcome_content.py,sha256=U76wZrblSXx4qv7phcPYs3l8SiFzwZ5cJ74u8Y2iYhU,608
@@ -24,16 +24,18 @@ ibm_watsonx_orchestrate/agent_builder/tools/__init__.py,sha256=adkYX0wgB-RKFCUBw
24
24
  ibm_watsonx_orchestrate/agent_builder/tools/base_tool.py,sha256=0vwMIAyKyB8v1QmNrubLy8Al58g3qT78EUgrmOjegoI,1220
25
25
  ibm_watsonx_orchestrate/agent_builder/tools/flow_tool.py,sha256=DJWYVmIjw1O_cbzPpwU0a_vIZGvo0mj8UsjW9zkKMlA,2589
26
26
  ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py,sha256=h9ma18GUQHt88UIakd6QZHic822bAXzYokh6sfqAMZk,19410
27
- ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py,sha256=FDY-y9jy4bNy88i7JG85Zj_YrxnPfJPD0DjwvL9ITxc,12012
27
+ ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py,sha256=cdgu-v1oRR9RUbMNKEklOzO1z3nNZpDZPSMwnJEvuIY,12533
28
28
  ibm_watsonx_orchestrate/agent_builder/tools/types.py,sha256=m2t4uXNp0DVwVFzd0Jf_se8tz6V8FzM5fYgFs7AlvHU,8251
29
29
  ibm_watsonx_orchestrate/agent_builder/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ ibm_watsonx_orchestrate/agent_builder/voice_configurations/__init__.py,sha256=W3-T8PH_KuONsCQPILQAvW2Nz25p7dp9AZbWHWGJXhA,37
31
+ ibm_watsonx_orchestrate/agent_builder/voice_configurations/types.py,sha256=lSS1yiDzVAMkEdOwcI4_1DUuRnu_6oc8XupQtoE4Fvs,3989
30
32
  ibm_watsonx_orchestrate/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
33
  ibm_watsonx_orchestrate/cli/config.py,sha256=iXjDxymWhAmLSUu6eh7zJR20dYZDzbxcU5VoBdh3VIw,8318
32
34
  ibm_watsonx_orchestrate/cli/init_helper.py,sha256=qxnKdFcPtGsV_6RqP_IuLshRxgB004SxzDAkBTExA-4,1675
33
- ibm_watsonx_orchestrate/cli/main.py,sha256=eE71dfx-EdDYHymYDqXcTjyMXpX0b3cueXOxCI2YLLs,3119
35
+ ibm_watsonx_orchestrate/cli/main.py,sha256=Jrp0d40-RAtpzsGnf45dpPxJi3d7snod5BCnFMh4foU,3384
34
36
  ibm_watsonx_orchestrate/cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py,sha256=Zif3bH3X7j5qf7MWGcr53mJ2oEKXNRHeg3ZHeoHTIqQ,8984
36
- ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py,sha256=gEsuG3Mbz6shuv8yB2C-dPcqM5mMXUAVO0YniNNOMk0,48379
37
+ ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py,sha256=sC3kRKO5ZvDGUSy5CBnS3C-qU6bG9kxG-u42KcpVYuQ,9574
38
+ ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py,sha256=W1xeg4UQZ5fEudlvYfppw9PyfwTcQq-woWpHPZorUPc,55704
37
39
  ibm_watsonx_orchestrate/cli/commands/channels/channels_command.py,sha256=fVIFhPUTPdxsxIE10nWL-W5wvBR-BS8V8D6r__J8R98,822
38
40
  ibm_watsonx_orchestrate/cli/commands/channels/channels_controller.py,sha256=WjQxwJujvo28SsWgfJSXIpkcgniKcskJ2arL4MOz0Ys,455
39
41
  ibm_watsonx_orchestrate/cli/commands/channels/types.py,sha256=hMFvWPr7tAmDrhBqtzfkCsrubX3lsU6lapTSOFsUbHM,475
@@ -51,12 +53,12 @@ ibm_watsonx_orchestrate/cli/commands/environment/types.py,sha256=X6jEnyBdxakromA
51
53
  ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_command.py,sha256=nOVxeZSTp1bfV_l_06B6x6wfNeusNAr5KImJYkwGWx8,14298
52
54
  ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_controller.py,sha256=dZEAD0rIS9DQjWD2-i6367RjNd2PWB3Fm_DDk25toBg,7855
53
55
  ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_command.py,sha256=hOzRcGVoqq7dTc4bSregKxH-kYbrVqaFdhBLawqnRNo,2668
54
- ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py,sha256=M19rRMFEEcAarPVw4fOr3S94Se1ZEo4hD7sCUr0xYTI,10115
56
+ ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py,sha256=AyGc2JudpHaSf0ktHSVRkPVsaejWL8KH7Dv0XgEGZ1U,10531
55
57
  ibm_watsonx_orchestrate/cli/commands/login/login_command.py,sha256=xArMiojoozg7Exn6HTpbTcjDO2idZRA-y0WV-_Ic1Sk,651
56
58
  ibm_watsonx_orchestrate/cli/commands/models/model_provider_mapper.py,sha256=mbvBR5o9M7W6OpTZyd6TtSEOIXq07dPYz4hv5zDrsd0,8129
57
59
  ibm_watsonx_orchestrate/cli/commands/models/models_command.py,sha256=PW-PIM5Nq0qdCopWjANGBWEuEoA3NLTFThYrN8ggGCI,6425
58
60
  ibm_watsonx_orchestrate/cli/commands/models/models_controller.py,sha256=eZSYQUg9TL_-8lgcPVpKIx7MtOE7K_NCvZW9Y9YsFA0,18466
59
- ibm_watsonx_orchestrate/cli/commands/server/server_command.py,sha256=9VM9i2g54UwK0wsQ7z1m3KSDItoAslSk693YKGXPq0s,43309
61
+ ibm_watsonx_orchestrate/cli/commands/server/server_command.py,sha256=VhNfBPVXtTjMt-wKLtlXhWu5YB9TQ7BTnBrxWUDZdU4,43636
60
62
  ibm_watsonx_orchestrate/cli/commands/server/types.py,sha256=UCrgGErbSVBnOzesfjrIii4tTCuVfWemYz5AKGZX0oA,4213
61
63
  ibm_watsonx_orchestrate/cli/commands/settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
64
  ibm_watsonx_orchestrate/cli/commands/settings/settings_command.py,sha256=CzXRkd-97jXyS6LtaaNtMah-aZu0919dYl-mDwzGThc,344
@@ -67,8 +69,10 @@ ibm_watsonx_orchestrate/cli/commands/settings/observability/langfuse/langfuse_co
67
69
  ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_command.py,sha256=KI96Yexq4ZkM-VxcW88oMszjnOxbdU7quSxFtvf_ry4,4367
68
70
  ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_controller.py,sha256=3Jykp5-DzMM_At8kYJto171V6LesaqKdsk2nfg1TRpk,11949
69
71
  ibm_watsonx_orchestrate/cli/commands/tools/tools_command.py,sha256=Cuo1ZvlfsymojqbadCqdwwS0HUjaWpe2XQrV70g61_s,3943
70
- ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py,sha256=4VnV_rm7uXA7gtAfmqdw7igb5tkVGyhcsoni21jMDFA,40407
72
+ ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py,sha256=_iXQx_Ycno4bpgI0HuJNsf2yYNP_8o6AOCUlBavBHEE,40720
71
73
  ibm_watsonx_orchestrate/cli/commands/tools/types.py,sha256=_md0GEa_cTH17NO_moWDY_LNdFvyEFQ1UVB9_FltYiA,173
74
+ ibm_watsonx_orchestrate/cli/commands/voice_configurations/voice_configurations_command.py,sha256=q4KHWQ-LZbp31e2ytihX1OuyAPS4-nRinmc-eMXC0l0,1783
75
+ ibm_watsonx_orchestrate/cli/commands/voice_configurations/voice_configurations_controller.py,sha256=JbAc_CY0woHY8u7qrnOcb9O2FgECzkzeMirxFhRoep8,5884
72
76
  ibm_watsonx_orchestrate/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
77
  ibm_watsonx_orchestrate/client/base_api_client.py,sha256=cSbQb7-K9KoMeFLEIiYGho4cQqf8TcPyYklju9X4cC4,5214
74
78
  ibm_watsonx_orchestrate/client/base_service_instance.py,sha256=sM_r7bln9BpgEOhaJMdFI9-je3T7GLQxLduk-in0oRY,235
@@ -78,7 +82,7 @@ ibm_watsonx_orchestrate/client/credentials.py,sha256=gDVeeQZDdbbjJiO1EI61yx2oRgT
78
82
  ibm_watsonx_orchestrate/client/local_service_instance.py,sha256=dt7vfLnjgt7mT8wSq8SJZndNTwsPzhb0XDhcnPUPFpU,3524
79
83
  ibm_watsonx_orchestrate/client/service_instance.py,sha256=fp3Lc4yQf4zTkxVS5WnIAkrHT0xG_a5i44qcLeQkaa4,6600
80
84
  ibm_watsonx_orchestrate/client/utils.py,sha256=MUw11r0_wYv3RdF6-1BmSxcwYNF6q2_Urte7tMYTKVA,5836
81
- ibm_watsonx_orchestrate/client/agents/agent_client.py,sha256=v1ZqXKj3p5JeJyOLpV84UUI2LY8XPjW1ejo5yzkeiMQ,4708
85
+ ibm_watsonx_orchestrate/client/agents/agent_client.py,sha256=ObAoh5g4zvB1mZiA6xotvs4b1FGE1r92kkb2C7juGn8,6890
82
86
  ibm_watsonx_orchestrate/client/agents/assistant_agent_client.py,sha256=1JQN0E4T_uz5V0LM-LD1ahNu2KCeFBjXAr8WCiP9mkE,1745
83
87
  ibm_watsonx_orchestrate/client/agents/external_agent_client.py,sha256=iQ44XBdC4rYfS-zFn4St1xC5y5gf5SNqKHzMNQcFDZc,1808
84
88
  ibm_watsonx_orchestrate/client/analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -96,8 +100,9 @@ ibm_watsonx_orchestrate/client/models/models_client.py,sha256=ZvP3iPgUFw_SXp41kJ
96
100
  ibm_watsonx_orchestrate/client/toolkit/toolkit_client.py,sha256=TLFNS39EeBD_t4Y-rX9sGp4sWBDr--mE5qVtHq8Q2hk,3121
97
101
  ibm_watsonx_orchestrate/client/tools/tempus_client.py,sha256=24fKDZUOVHBW-Vj4mubnpnUmab5LgGn8u5hOVyJaozI,1804
98
102
  ibm_watsonx_orchestrate/client/tools/tool_client.py,sha256=d3i3alVwa0TCN72w9sWOrM20GCbNmnpTnqEOJVbBIFM,1994
99
- ibm_watsonx_orchestrate/docker/compose-lite.yml,sha256=Mg8XqWLTxQS7Xl_tP32MGvkvEFyGiwEnug5FWyVjV7g,43616
100
- ibm_watsonx_orchestrate/docker/default.env,sha256=wdBu50CfT3wg9FSJ9ekW6_OGtQVNGk4nrdfOgR_6Hk0,6128
103
+ ibm_watsonx_orchestrate/client/voice_configurations/voice_configurations_client.py,sha256=M5xIPLiVNpP-zxQw8CTNT9AiBjeXXmJiNaE142e2A3E,2682
104
+ ibm_watsonx_orchestrate/docker/compose-lite.yml,sha256=LdAwhDdslZ5SHxV5CGRjJliEeAfuk6N8a_Xv-4QWSNc,44133
105
+ ibm_watsonx_orchestrate/docker/default.env,sha256=13lliXku5OQsZo2vRPEv6ev0k6GpVRErRQKWNY_o6IY,6234
101
106
  ibm_watsonx_orchestrate/docker/proxy-config-single.yaml,sha256=WEbK4ENFuTCYhzRu_QblWp1_GMARgZnx5vReQafkIG8,308
102
107
  ibm_watsonx_orchestrate/docker/start-up.sh,sha256=LTtwHp0AidVgjohis2LXGvZnkFQStOiUAxgGABOyeUI,1811
103
108
  ibm_watsonx_orchestrate/docker/sdk/ibm_watsonx_orchestrate-0.6.0-py3-none-any.whl,sha256=Hi3-owh5OM0Jz2ihX9nLoojnr7Ky1TV-GelyqLcewLE,2047417
@@ -112,7 +117,7 @@ ibm_watsonx_orchestrate/flow_builder/flows/__init__.py,sha256=jbCklY7l08KG8cbFih
112
117
  ibm_watsonx_orchestrate/flow_builder/flows/constants.py,sha256=-TGneZyjA4YiAtJJK7OmmjDHYQC4mw2e98MPAZqiB50,324
113
118
  ibm_watsonx_orchestrate/flow_builder/flows/decorators.py,sha256=lr4qSWq5PWqlGFf4fzUQZCVQDHBYflrYwZ24S89Aq80,2794
114
119
  ibm_watsonx_orchestrate/flow_builder/flows/events.py,sha256=VyaBm0sADwr15LWfKbcBQS1M80NKqzYDj3UlW3OpOf4,2984
115
- ibm_watsonx_orchestrate/flow_builder/flows/flow.py,sha256=uAk_uS2i1zH3toJQw7I_5zaSPVOdvAyEitGdM0rxrJI,56949
120
+ ibm_watsonx_orchestrate/flow_builder/flows/flow.py,sha256=WPAdXML0o9RuHZE0ajBBOrorS6YmJd8RhV3-FMbDifM,57304
116
121
  ibm_watsonx_orchestrate/run/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
117
122
  ibm_watsonx_orchestrate/run/connections.py,sha256=K-65GXPA8GEsVmRdPfMe_LB2G9RfXQUr95wvRUOhkS4,1828
118
123
  ibm_watsonx_orchestrate/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -121,8 +126,8 @@ ibm_watsonx_orchestrate/utils/utils.py,sha256=U7z_2iASoFiZ2zM0a_2Mc2Y-P5oOT7hOwu
121
126
  ibm_watsonx_orchestrate/utils/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
122
127
  ibm_watsonx_orchestrate/utils/logging/logger.py,sha256=FzeGnidXAjC7yHrvIaj4KZPeaBBSCniZFlwgr5yV3oA,1037
123
128
  ibm_watsonx_orchestrate/utils/logging/logging.yaml,sha256=9_TKfuFr1barnOKP0fZT5D6MhddiwsXVTFjtRbcOO5w,314
124
- ibm_watsonx_orchestrate-1.9.0b2.dist-info/METADATA,sha256=vUmVmbH5irCvOWWPEcc9ckYadyPb1xtRTbGvAaq8K5o,1362
125
- ibm_watsonx_orchestrate-1.9.0b2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
126
- ibm_watsonx_orchestrate-1.9.0b2.dist-info/entry_points.txt,sha256=SfIT02-Jen5e99OcLhzbcM9Bdyf8SGVOCtnSplgZdQI,69
127
- ibm_watsonx_orchestrate-1.9.0b2.dist-info/licenses/LICENSE,sha256=Shgxx7hTdCOkiVRmfGgp_1ISISrwQD7m2f0y8Hsapl4,1083
128
- ibm_watsonx_orchestrate-1.9.0b2.dist-info/RECORD,,
129
+ ibm_watsonx_orchestrate-1.10.0b0.dist-info/METADATA,sha256=j9LQ4nwtXYKwZJGT48LSfws9sC84DZ9tHXHki4E-a9s,1363
130
+ ibm_watsonx_orchestrate-1.10.0b0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
131
+ ibm_watsonx_orchestrate-1.10.0b0.dist-info/entry_points.txt,sha256=SfIT02-Jen5e99OcLhzbcM9Bdyf8SGVOCtnSplgZdQI,69
132
+ ibm_watsonx_orchestrate-1.10.0b0.dist-info/licenses/LICENSE,sha256=Shgxx7hTdCOkiVRmfGgp_1ISISrwQD7m2f0y8Hsapl4,1083
133
+ ibm_watsonx_orchestrate-1.10.0b0.dist-info/RECORD,,