ibm-watsonx-orchestrate 1.5.0b1__py3-none-any.whl → 1.6.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 (57) hide show
  1. ibm_watsonx_orchestrate/__init__.py +1 -1
  2. ibm_watsonx_orchestrate/agent_builder/agents/__init__.py +1 -1
  3. ibm_watsonx_orchestrate/agent_builder/agents/types.py +53 -3
  4. ibm_watsonx_orchestrate/agent_builder/model_policies/types.py +1 -1
  5. ibm_watsonx_orchestrate/agent_builder/models/types.py +0 -1
  6. ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py +41 -3
  7. ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py +2 -1
  8. ibm_watsonx_orchestrate/agent_builder/tools/types.py +7 -0
  9. ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +18 -1
  10. ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +97 -3
  11. ibm_watsonx_orchestrate/cli/commands/channels/webchat/channels_webchat_controller.py +0 -1
  12. ibm_watsonx_orchestrate/cli/commands/connections/connections_controller.py +1 -1
  13. ibm_watsonx_orchestrate/cli/commands/environment/environment_command.py +29 -4
  14. ibm_watsonx_orchestrate/cli/commands/environment/environment_controller.py +74 -8
  15. ibm_watsonx_orchestrate/cli/commands/environment/types.py +1 -0
  16. ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_command.py +224 -0
  17. ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_controller.py +158 -0
  18. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_command.py +2 -2
  19. ibm_watsonx_orchestrate/cli/commands/models/model_provider_mapper.py +31 -25
  20. ibm_watsonx_orchestrate/cli/commands/models/models_command.py +6 -6
  21. ibm_watsonx_orchestrate/cli/commands/models/models_controller.py +17 -8
  22. ibm_watsonx_orchestrate/cli/commands/server/server_command.py +25 -17
  23. ibm_watsonx_orchestrate/cli/commands/server/types.py +2 -1
  24. ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_controller.py +0 -3
  25. ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +14 -12
  26. ibm_watsonx_orchestrate/cli/config.py +2 -0
  27. ibm_watsonx_orchestrate/cli/main.py +6 -0
  28. ibm_watsonx_orchestrate/client/agents/agent_client.py +14 -8
  29. ibm_watsonx_orchestrate/client/agents/assistant_agent_client.py +3 -3
  30. ibm_watsonx_orchestrate/client/agents/external_agent_client.py +2 -2
  31. ibm_watsonx_orchestrate/client/base_api_client.py +9 -9
  32. ibm_watsonx_orchestrate/client/connections/connections_client.py +32 -6
  33. ibm_watsonx_orchestrate/client/connections/utils.py +1 -1
  34. ibm_watsonx_orchestrate/client/credentials.py +4 -0
  35. ibm_watsonx_orchestrate/client/model_policies/model_policies_client.py +2 -2
  36. ibm_watsonx_orchestrate/client/service_instance.py +42 -1
  37. ibm_watsonx_orchestrate/client/utils.py +27 -2
  38. ibm_watsonx_orchestrate/docker/compose-lite.yml +27 -17
  39. ibm_watsonx_orchestrate/docker/default.env +21 -15
  40. ibm_watsonx_orchestrate/{experimental/flow_builder → flow_builder}/flows/__init__.py +3 -2
  41. ibm_watsonx_orchestrate/flow_builder/flows/decorators.py +77 -0
  42. ibm_watsonx_orchestrate/{experimental/flow_builder → flow_builder}/flows/events.py +6 -1
  43. ibm_watsonx_orchestrate/{experimental/flow_builder → flow_builder}/flows/flow.py +70 -87
  44. ibm_watsonx_orchestrate/{experimental/flow_builder → flow_builder}/types.py +15 -6
  45. ibm_watsonx_orchestrate/flow_builder/utils.py +185 -0
  46. {ibm_watsonx_orchestrate-1.5.0b1.dist-info → ibm_watsonx_orchestrate-1.6.0b0.dist-info}/METADATA +2 -1
  47. {ibm_watsonx_orchestrate-1.5.0b1.dist-info → ibm_watsonx_orchestrate-1.6.0b0.dist-info}/RECORD +55 -53
  48. ibm_watsonx_orchestrate/experimental/flow_builder/flows/decorators.py +0 -144
  49. ibm_watsonx_orchestrate/experimental/flow_builder/utils.py +0 -115
  50. /ibm_watsonx_orchestrate/{experimental/flow_builder → flow_builder}/__init__.py +0 -0
  51. /ibm_watsonx_orchestrate/{experimental/flow_builder → flow_builder}/data_map.py +0 -0
  52. /ibm_watsonx_orchestrate/{experimental/flow_builder → flow_builder}/flows/constants.py +0 -0
  53. /ibm_watsonx_orchestrate/{experimental/flow_builder → flow_builder}/node.py +0 -0
  54. /ibm_watsonx_orchestrate/{experimental/flow_builder → flow_builder}/resources/flow_status.openapi.yml +0 -0
  55. {ibm_watsonx_orchestrate-1.5.0b1.dist-info → ibm_watsonx_orchestrate-1.6.0b0.dist-info}/WHEEL +0 -0
  56. {ibm_watsonx_orchestrate-1.5.0b1.dist-info → ibm_watsonx_orchestrate-1.6.0b0.dist-info}/entry_points.txt +0 -0
  57. {ibm_watsonx_orchestrate-1.5.0b1.dist-info → ibm_watsonx_orchestrate-1.6.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -20,6 +20,7 @@ from typing_extensions import Self
20
20
  from pydantic import BaseModel, Field, SerializeAsAny
21
21
  import yaml
22
22
  from ibm_watsonx_orchestrate.agent_builder.tools.python_tool import PythonTool
23
+ from ibm_watsonx_orchestrate.client.tools.tool_client import ToolClient
23
24
  from ibm_watsonx_orchestrate.client.tools.tempus_client import TempusClient
24
25
  from ibm_watsonx_orchestrate.client.utils import instantiate_client
25
26
  from ..types import (
@@ -36,7 +37,7 @@ from ..types import (
36
37
  )
37
38
 
38
39
  from ..data_map import DataMap
39
- from ..utils import _get_json_schema_obj, get_valid_name, import_flow_model
40
+ from ..utils import _get_json_schema_obj, get_valid_name, import_flow_model, _get_tool_request_body, _get_tool_response_body
40
41
 
41
42
  from .events import StreamConsumer
42
43
 
@@ -70,6 +71,7 @@ class Flow(Node):
70
71
  metadata: dict[str, str] = {}
71
72
  parent: Any = None
72
73
  _sequence_id: int = 0 # internal-id
74
+ _tool_client: ToolClient = None
73
75
 
74
76
  def __init__(self, **kwargs):
75
77
  super().__init__(**kwargs)
@@ -77,6 +79,9 @@ class Flow(Node):
77
79
  # extract data schemas
78
80
  self._refactor_node_to_schemaref(self)
79
81
 
82
+ # get Tool Client
83
+ self._tool_client = instantiate_client(ToolClient)
84
+
80
85
  def _find_topmost_flow(self) -> Self:
81
86
  if self.parent:
82
87
  return self.parent._find_topmost_flow()
@@ -104,7 +109,27 @@ class Flow(Node):
104
109
  # if there is already a schema with the same name, return it
105
110
  if title:
106
111
  if title in top_flow.schemas:
107
- return top_flow.schemas[title]
112
+ existing_schema = top_flow.schemas[title]
113
+ # we need a deep compare if the incoming schema and existing_schema is the same
114
+ # pydantic suppport nested comparison by default
115
+
116
+ schema.title = title
117
+
118
+ if schema == existing_schema:
119
+ return existing_schema
120
+ # we need to do a deep compare
121
+ incoming_model = schema.model_dump(exclude_none=True, exclude_unset=True)
122
+ existing_model = existing_schema.model_dump(exclude_none=True, exclude_unset=True)
123
+
124
+ # log the model
125
+ # logger.info(f"incoming_model: {incoming_model}")
126
+ # logger.info(f"existing_model: {existing_model}")
127
+
128
+ if incoming_model == existing_model:
129
+ return existing_schema
130
+
131
+ # else we need a new name, and create a new schema
132
+ title = title + "_" + str(self._next_sequence_id())
108
133
 
109
134
  # otherwise, create a deep copy of the schema, add it to the dictionary and return it
110
135
  if schema:
@@ -122,7 +147,7 @@ class Flow(Node):
122
147
  elif schema.aliasName:
123
148
  title = get_valid_name(schema.aliasName)
124
149
  else:
125
- title = "bo_" + uuid.uuid4().hex
150
+ title = "bo_" + str(self._next_sequence_id())
126
151
 
127
152
  if new_schema.type == "object":
128
153
  # iterate the properties and add schema recursively
@@ -193,7 +218,7 @@ class Flow(Node):
193
218
  required= spec.output_schema.required)
194
219
  spec.output_schema = self._add_schema_ref(json_obj, f"{spec.name}_output")
195
220
  elif spec.output_schema.type == "array":
196
- if spec.output_schema.items.type == "object":
221
+ if hasattr(spec.output_schema, "items") and hasattr(spec.output_schema.items, "type") and spec.output_schema.items.type == "object":
197
222
  schema_ref = self._add_schema_ref(spec.output_schema.items)
198
223
  spec.output_schema.items = JsonSchemaObjectRef(ref=f"{schema_ref.ref}")
199
224
 
@@ -265,23 +290,30 @@ class Flow(Node):
265
290
 
266
291
  if isinstance(tool, str):
267
292
  name = name if name is not None and name != "" else tool
268
- input_schema_obj = _get_json_schema_obj(parameter_name = "input", type_def = input_schema)
269
- output_schema_obj = _get_json_schema_obj("output", output_schema)
293
+
294
+ if input_schema is None and output_schema is None:
295
+ # try to retrieve the schema from server
296
+ tool_specs: List[dict] = self._tool_client.get_draft_by_name(name)
297
+ if (tool_specs is None) or (len(tool_specs) == 0):
298
+ raise ValueError(f"tool '{name}' not found")
299
+
300
+ # use the first spec
301
+ tool_spec: ToolSpec = ToolSpec.model_validate(tool_specs[0])
302
+ # just pick the first one that is found
303
+ if hasattr(tool_spec, "input_schema"):
304
+ input_schema_obj = _get_json_schema_obj("input", tool_spec.input_schema, True)
305
+ if hasattr(tool_spec, "output_schema"):
306
+ output_schema_obj = _get_json_schema_obj("output", tool_spec.output_schema)
307
+ else:
308
+ input_schema_obj = _get_json_schema_obj("input", input_schema)
309
+ output_schema_obj = _get_json_schema_obj("output", output_schema)
270
310
 
271
311
  toolnode_spec = ToolNodeSpec(type = "tool",
272
312
  name = name,
273
313
  display_name = display_name,
274
314
  description = description,
275
- input_schema=ToolRequestBody(
276
- type=input_schema_obj.type,
277
- properties=input_schema_obj.properties,
278
- required=input_schema_obj.required,
279
- ) if input_schema is not None else None,
280
- output_schema=ToolResponseBody(
281
- type=output_schema_obj.type,
282
- properties=output_schema_obj.properties,
283
- required=output_schema_obj.required
284
- ) if output_schema is not None else None,
315
+ input_schema= _get_tool_request_body(input_schema_obj),
316
+ output_schema= _get_tool_response_body(output_schema_obj),
285
317
  output_schema_object = output_schema_obj,
286
318
  tool = tool)
287
319
 
@@ -341,16 +373,8 @@ class Flow(Node):
341
373
  agent=agent,
342
374
  message=message,
343
375
  guidelines=guidelines,
344
- input_schema=ToolRequestBody(
345
- type=input_schema_obj.type,
346
- properties=input_schema_obj.properties,
347
- required=input_schema_obj.required,
348
- ),
349
- output_schema=ToolResponseBody(
350
- type=output_schema_obj.type,
351
- properties=output_schema_obj.properties,
352
- required=output_schema_obj.required
353
- ),
376
+ input_schema=_get_tool_request_body(input_schema_obj),
377
+ output_schema=_get_tool_response_body(output_schema_obj),
354
378
  output_schema_object = output_schema_obj
355
379
  )
356
380
 
@@ -391,16 +415,8 @@ class Flow(Node):
391
415
  user_prompt=user_prompt,
392
416
  llm=llm,
393
417
  llm_parameters=llm_parameters,
394
- input_schema=ToolRequestBody(
395
- type=input_schema_obj.type,
396
- properties=input_schema_obj.properties,
397
- required=input_schema_obj.required,
398
- ),
399
- output_schema=ToolResponseBody(
400
- type=output_schema_obj.type,
401
- properties=output_schema_obj.properties,
402
- required=output_schema_obj.required
403
- ),
418
+ input_schema=_get_tool_request_body(input_schema_obj),
419
+ output_schema=_get_tool_response_body(output_schema_obj),
404
420
  output_schema_object = output_schema_obj
405
421
  )
406
422
 
@@ -543,21 +559,13 @@ class Flow(Node):
543
559
  },
544
560
  required = ["items"])
545
561
 
562
+ new_foreach_item_schema = self._add_schema(foreach_item_schema)
546
563
  spec = ForeachSpec(name = "foreach_" + str(self._next_sequence_id()),
547
- input_schema=ToolRequestBody(
548
- type=input_schema_obj.type,
549
- properties=input_schema_obj.properties,
550
- required=input_schema_obj.required,
551
- ),
552
- output_schema=ToolResponseBody(
553
- type=output_schema_obj.type,
554
- properties=output_schema_obj.properties,
555
- required=output_schema_obj.required
556
- ) if output_schema_obj is not None else None,
557
- item_schema = foreach_item_schema)
564
+ input_schema=_get_tool_request_body(input_schema_obj),
565
+ output_schema=_get_tool_response_body(output_schema_obj),
566
+ item_schema = new_foreach_item_schema)
558
567
  foreach_obj = Foreach(spec = spec, parent = self)
559
568
  foreach_node = self._add_node(foreach_obj)
560
- self._add_schema(foreach_item_schema)
561
569
 
562
570
  return cast(Flow, foreach_node)
563
571
 
@@ -580,16 +588,8 @@ class Flow(Node):
580
588
 
581
589
  loop_spec = LoopSpec(name = "loop_" + str(self._next_sequence_id()),
582
590
  evaluator = e,
583
- input_schema=ToolRequestBody(
584
- type=input_schema_obj.type,
585
- properties=input_schema_obj.properties,
586
- required=input_schema_obj.required,
587
- ) if input_schema_obj is not None else None,
588
- output_schema=ToolResponseBody(
589
- type=output_schema_obj.type,
590
- properties=output_schema_obj.properties,
591
- required=output_schema_obj.required
592
- ) if output_schema_obj is not None else None)
591
+ input_schema=_get_tool_request_body(input_schema_obj),
592
+ output_schema=_get_tool_response_body(output_schema_obj))
593
593
  while_loop = Loop(spec = loop_spec, parent = self)
594
594
  while_node = self._add_node(while_loop)
595
595
  return cast(Loop, while_node)
@@ -599,23 +599,15 @@ class Flow(Node):
599
599
  input_schema: type[BaseModel] |None=None,
600
600
  output_schema: type[BaseModel] |None=None) -> "UserFlow": # return a UserFlow object
601
601
 
602
- logger.warning("userflow is NOT working yet.")
602
+ raise ValueError("userflow is NOT supported yet and it's interface will change.")
603
603
 
604
604
  output_schema_obj = _get_json_schema_obj("output", output_schema)
605
605
  input_schema_obj = _get_json_schema_obj("input", input_schema)
606
606
 
607
607
  spec = UserFlowSpec(name = "userflow_" + str(self._next_sequence_id()),
608
- input_schema=ToolRequestBody(
609
- type=input_schema_obj.type,
610
- properties=input_schema_obj.properties,
611
- required=input_schema_obj.required,
612
- ) if input_schema_obj is not None else None,
613
- output_schema=ToolResponseBody(
614
- type=output_schema_obj.type,
615
- properties=output_schema_obj.properties,
616
- required=output_schema_obj.required
617
- ) if output_schema_obj is not None else None,
618
- owners = owners)
608
+ input_schema=_get_tool_request_body(input_schema_obj),
609
+ output_schema=_get_tool_response_body(output_schema_obj),
610
+ owners = owners)
619
611
  userflow_obj = UserFlow(spec = spec, parent = self)
620
612
  userflow_node = self._add_node(userflow_obj)
621
613
 
@@ -822,7 +814,7 @@ class FlowRun(BaseModel):
822
814
  def _on_flow_end(self, event:FlowEvent):
823
815
 
824
816
  self.status = FlowRunStatus.COMPLETED
825
- self.output = event.context.data["output"] if "output" in event.context.data else None
817
+ self.output = event.context.data.output
826
818
 
827
819
  if self.debug:
828
820
  logger.debug(f"Flow run `{self.name}`: on_complete handler called. Output: {self.output}")
@@ -938,16 +930,8 @@ class FlowFactory(BaseModel):
938
930
  display_name=display_name,
939
931
  description=description,
940
932
  initiators=initiators,
941
- input_schema=ToolRequestBody(
942
- type=input_schema_obj.type,
943
- properties=input_schema_obj.properties,
944
- required=input_schema_obj.required,
945
- ) if input_schema_obj else None,
946
- output_schema=ToolResponseBody(
947
- type=output_schema_obj.type,
948
- properties=output_schema_obj.properties,
949
- required=output_schema_obj.required
950
- ) if output_schema_obj else None,
933
+ input_schema=_get_tool_request_body(input_schema_obj),
934
+ output_schema=_get_tool_response_body(output_schema_obj),
951
935
  output_schema_object = output_schema_obj
952
936
  )
953
937
 
@@ -974,6 +958,9 @@ class Branch(FlowControl):
974
958
  Returns:
975
959
  Self: The current node.
976
960
  '''
961
+ if kind == MatchPolicy.ANY_MATCH:
962
+ raise ValueError("Branch with policy ANY_MATCH is not supported yet.")
963
+
977
964
  self.spec.match_policy = kind
978
965
  return self
979
966
 
@@ -1286,12 +1273,8 @@ class UserFlow(Flow):
1286
1273
  display_name=display_name,
1287
1274
  description=description,
1288
1275
  owners=owners,
1289
- input_schema=None,
1290
- output_schema=ToolResponseBody(
1291
- type=output_schema_obj.type,
1292
- properties=output_schema_obj.properties,
1293
- required=output_schema_obj.required
1294
- ),
1276
+ input_schema=_get_tool_request_body(output_schema_obj),
1277
+ output_schema=_get_tool_response_body(output_schema_obj),
1295
1278
  text=text,
1296
1279
  output_schema_object = output_schema_obj
1297
1280
  )
@@ -14,7 +14,7 @@ from langchain_core.tools.base import create_schema_from_function
14
14
  from langchain_core.utils.json_schema import dereference_refs
15
15
 
16
16
  from ibm_watsonx_orchestrate.agent_builder.tools import PythonTool
17
- from ibm_watsonx_orchestrate.experimental.flow_builder.flows.constants import ANY_USER
17
+ from ibm_watsonx_orchestrate.flow_builder.flows.constants import ANY_USER
18
18
  from ibm_watsonx_orchestrate.agent_builder.tools.types import (
19
19
  ToolSpec, ToolRequestBody, ToolResponseBody, JsonSchemaObject
20
20
  )
@@ -29,9 +29,9 @@ class SchemaRef(BaseModel):
29
29
 
30
30
  ref: str = Field(description="The id of the schema to be used.", serialization_alias="$ref")
31
31
 
32
- def _assign_attribute(obj, attr_name, schema):
32
+ def _assign_attribute(model_spec, attr_name, schema):
33
33
  if hasattr(schema, attr_name) and (getattr(schema, attr_name) is not None):
34
- obj[attr_name] = getattr(schema, attr_name)
34
+ model_spec[attr_name] = getattr(schema, attr_name)
35
35
 
36
36
  def _to_json_from_json_schema(schema: JsonSchemaObject) -> dict[str, Any]:
37
37
  model_spec = {}
@@ -62,12 +62,14 @@ def _to_json_from_json_schema(schema: JsonSchemaObject) -> dict[str, Any]:
62
62
  model_spec["anyOf"] = [_to_json_from_json_schema(schema) for schema in schema.anyOf]
63
63
 
64
64
  _assign_attribute(model_spec, "in_field", schema)
65
+ _assign_attribute(model_spec, "in", schema)
65
66
  _assign_attribute(model_spec, "aliasName", schema)
66
67
 
67
68
  if hasattr(schema, 'model_extra') and schema.model_extra:
68
69
  # for each extra fiels, add it to the model spec
69
70
  for key, value in schema.model_extra.items():
70
- model_spec[key] = value
71
+ if value is not None:
72
+ model_spec[key] = value
71
73
 
72
74
  if isinstance(schema, JsonSchemaObjectRef):
73
75
  model_spec["$ref"] = schema.ref
@@ -635,6 +637,11 @@ class TaskEventType(Enum):
635
637
  ON_TASK_STREAM = "on_task_stream"
636
638
  ON_TASK_ERROR = "on_task_error"
637
639
 
640
+ class FlowData(BaseModel):
641
+ '''This class represents the data that is passed between tasks in a flow.'''
642
+ input: dict[str, Any] = Field(default_factory=dict)
643
+ output: dict[str, Any] = Field(default_factory=dict)
644
+
638
645
  class FlowContext(BaseModel):
639
646
 
640
647
  name: str | None = None # name of the process or task
@@ -642,10 +649,12 @@ class FlowContext(BaseModel):
642
649
  flow_id: str | None = None # id of the flow, this is at the flow definition level
643
650
  instance_id: str | None = None
644
651
  thread_id: str | None = None
652
+ correlation_id: str | None = None
653
+ tenant_id: str | None = None
645
654
  parent_context: Any | None = None
646
655
  child_context: List["FlowContext"] | None = None
647
656
  metadata: dict = Field(default_factory=dict[str, Any])
648
- data: dict = Field(default_factory=dict[str, Any])
657
+ data: Optional[FlowData] = None
649
658
 
650
659
  def get(self, key: str) -> Any:
651
660
 
@@ -655,7 +664,7 @@ class FlowContext(BaseModel):
655
664
  if self.parent_context:
656
665
  pc = cast(FlowContext, self.parent_conetxt)
657
666
  return pc.get(key)
658
-
667
+
659
668
  class FlowEventType(Enum):
660
669
 
661
670
  ON_FLOW_START = "on_flow_start"
@@ -0,0 +1,185 @@
1
+ import inspect
2
+ import json
3
+ from pathlib import Path
4
+ import re
5
+ import logging
6
+ import importlib.resources
7
+ import yaml
8
+
9
+ from pydantic import BaseModel, TypeAdapter
10
+ from typing import types
11
+
12
+ from langchain_core.utils.json_schema import dereference_refs
13
+ import typer
14
+
15
+ from ibm_watsonx_orchestrate.agent_builder.connections.types import ConnectionEnvironment, ConnectionPreference, ConnectionSecurityScheme
16
+ from ibm_watsonx_orchestrate.agent_builder.tools.openapi_tool import create_openapi_json_tools_from_content
17
+ from ibm_watsonx_orchestrate.agent_builder.tools.types import JsonSchemaObject, ToolRequestBody, ToolResponseBody
18
+ from ibm_watsonx_orchestrate.cli.commands.connections.connections_controller import add_connection, configure_connection, set_credentials_connection
19
+ from ibm_watsonx_orchestrate.client.connections.utils import get_connections_client
20
+ from ibm_watsonx_orchestrate.client.tools.tempus_client import TempusClient
21
+ from ibm_watsonx_orchestrate.client.utils import instantiate_client, is_local_dev
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+ def get_valid_name(name: str) -> str:
26
+
27
+ return re.sub('\\W|^(?=\\d)','_', name)
28
+
29
+ def _get_json_schema_obj(parameter_name: str, type_def: type[BaseModel] | ToolRequestBody | ToolResponseBody | None, openapi_decode: bool = False) -> JsonSchemaObject:
30
+ if not type_def or type_def is None or type_def == inspect._empty:
31
+ return None
32
+
33
+ if inspect.isclass(type_def) and issubclass(type_def, BaseModel):
34
+ schema_json = type_def.model_json_schema()
35
+ schema_json = dereference_refs(schema_json)
36
+ schema_obj = JsonSchemaObject(**schema_json)
37
+ if schema_obj.required is None:
38
+ schema_obj.required = []
39
+ return schema_obj
40
+
41
+ if isinstance(type_def, ToolRequestBody) or isinstance(type_def, ToolResponseBody):
42
+ schema_json = type_def.model_dump()
43
+ schema_obj = JsonSchemaObject.model_validate(schema_json)
44
+
45
+ if openapi_decode:
46
+ # during tool import for openapi - we convert header, path and query parameter
47
+ # with a prefix "header_", "path_" and "query_". We need to remove it.
48
+ if schema_obj.type == 'object':
49
+ # for each element in properties, we need to check the key and if it is
50
+ # prefixed with "header_", "path_" and "query_", we need to remove the prefix.
51
+ if hasattr(schema_obj, "properties"):
52
+ new_properties = {}
53
+ for key, value in schema_obj.properties.items():
54
+ if key.startswith('header_'):
55
+ new_properties[key[7:]] = value
56
+ elif key.startswith('path_'):
57
+ new_properties[key[5:]] = value
58
+ elif key.startswith('query_'):
59
+ new_properties[key[6:]] = value
60
+ else:
61
+ new_properties[key] = value
62
+
63
+ schema_obj.properties = new_properties
64
+
65
+ # we also need to go thru required and replace it
66
+ if hasattr(schema_obj, "required"):
67
+ new_required = []
68
+ for item in schema_obj.required:
69
+ if item.startswith('header_'):
70
+ new_required.append(item[7:])
71
+ elif item.startswith('path_'):
72
+ new_required.append(item[5:])
73
+ elif item.startswith('query_'):
74
+ new_required.append(item[6:])
75
+ else:
76
+ new_required.append(item)
77
+ schema_obj.required = new_required
78
+
79
+ return schema_obj
80
+
81
+ # handle the non-obvious cases
82
+ schema_json = TypeAdapter(type_def).json_schema()
83
+ schema_json = dereference_refs(schema_json)
84
+ return JsonSchemaObject.model_validate(schema_json)
85
+
86
+
87
+ def _get_tool_request_body(schema_obj: JsonSchemaObject) -> ToolRequestBody:
88
+ if schema_obj is None:
89
+ return None
90
+
91
+ if isinstance(schema_obj, JsonSchemaObject):
92
+ request_obj = ToolRequestBody(type='object', properties=schema_obj.properties, required=schema_obj.required)
93
+ if schema_obj.model_extra:
94
+ request_obj.__pydantic_extra__ = schema_obj.model_extra
95
+
96
+ return request_obj
97
+
98
+ raise ValueError(f"Invalid schema object: {schema_obj}")
99
+
100
+ def _get_tool_response_body(schema_obj: JsonSchemaObject) -> ToolResponseBody:
101
+ if schema_obj is None:
102
+ return None
103
+
104
+ if isinstance(schema_obj, JsonSchemaObject):
105
+ response_obj = ToolResponseBody(type=schema_obj.type)
106
+ if schema_obj.title:
107
+ response_obj.title = schema_obj.title
108
+ if schema_obj.description:
109
+ response_obj.description = schema_obj.description
110
+ if schema_obj.properties:
111
+ response_obj.properties = schema_obj.properties
112
+ if schema_obj.items:
113
+ response_obj.items = schema_obj.items
114
+ if schema_obj.uniqueItems:
115
+ response_obj.uniqueItems = schema_obj.uniqueItems
116
+ if schema_obj.anyOf:
117
+ response_obj.anyOf = schema_obj.anyOf
118
+ if schema_obj.required:
119
+ response_obj.required = schema_obj.required
120
+
121
+ if schema_obj.model_extra:
122
+ response_obj.__pydantic_extra__ = schema_obj.model_extra
123
+
124
+ return response_obj
125
+
126
+ raise ValueError(f"Invalid schema object: {schema_obj}")
127
+
128
+
129
+ async def import_flow_model(model):
130
+
131
+ if not is_local_dev():
132
+ raise typer.BadParameter(f"Flow tools are only supported in local environment.")
133
+
134
+ if model is None:
135
+ raise typer.BadParameter(f"No model provided.")
136
+
137
+ tools = []
138
+
139
+ flow_id = model["spec"]["name"]
140
+
141
+ tempus_client: TempusClient = instantiate_client(TempusClient)
142
+
143
+ flow_open_api = tempus_client.create_update_flow_model(flow_id=flow_id, model=model)
144
+
145
+ logger.info(f"Flow model `{flow_id}` deployed successfully.")
146
+
147
+ connections_client = get_connections_client()
148
+
149
+ app_id = "flow_tools_app"
150
+ logger.info(f"Creating connection for flow model...")
151
+ existing_app = connections_client.get(app_id=app_id)
152
+ if not existing_app:
153
+ # logger.info(f"Creating app `{app_id}`.")
154
+ add_connection(app_id=app_id)
155
+ # else:
156
+ # logger.info(f"App `{app_id}` already exists.")
157
+
158
+ # logger.info(f"Creating connection for app...")
159
+ configure_connection(
160
+ type=ConnectionPreference.MEMBER,
161
+ app_id=app_id,
162
+ token=connections_client.api_key,
163
+ environment=ConnectionEnvironment.DRAFT,
164
+ security_scheme=ConnectionSecurityScheme.BEARER_TOKEN,
165
+ shared=False
166
+ )
167
+
168
+ set_credentials_connection(app_id=app_id, environment=ConnectionEnvironment.DRAFT, token=connections_client.api_key)
169
+
170
+ connections = connections_client.get_draft_by_app_id(app_id=app_id)
171
+
172
+ # logger.info(f"Connection `{connections.connection_id}` created successfully.")
173
+
174
+ tools = await create_openapi_json_tools_from_content(flow_open_api, connections.connection_id)
175
+
176
+ logger.info(f"Generating 'get_flow_status' tool spec...")
177
+ # Temporary code to deploy a status tool until we have full async support
178
+ with importlib.resources.open_text('ibm_watsonx_orchestrate.flow_builder.resources', 'flow_status.openapi.yml', encoding='utf-8') as f:
179
+ get_status_openapi = f.read()
180
+
181
+ get_flow_status_spec = yaml.safe_load(get_status_openapi)
182
+ tools.extend(await create_openapi_json_tools_from_content(get_flow_status_spec, connections.connection_id))
183
+
184
+
185
+ return tools
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ibm-watsonx-orchestrate
3
- Version: 1.5.0b1
3
+ Version: 1.6.0b0
4
4
  Summary: IBM watsonx.orchestrate SDK
5
5
  Author-email: IBM <support@ibm.com>
6
6
  License: MIT License
@@ -11,6 +11,7 @@ Requires-Dist: click<8.2.0,>=8.0.0
11
11
  Requires-Dist: docstring-parser<1.0,>=0.16
12
12
  Requires-Dist: httpx<1.0.0,>=0.28.1
13
13
  Requires-Dist: ibm-cloud-sdk-core>=3.22.0
14
+ Requires-Dist: ibm-watsonx-orchestrate-evaluation-framework==1.0.2
14
15
  Requires-Dist: jsonref==1.1.0
15
16
  Requires-Dist: jsonschema<5.0.0,>=4.23.0
16
17
  Requires-Dist: langchain-community<1.0.0,>=0.3.12