ibm-watsonx-orchestrate 1.6.2__py3-none-any.whl → 1.7.0b1__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 (44) hide show
  1. ibm_watsonx_orchestrate/__init__.py +2 -1
  2. ibm_watsonx_orchestrate/agent_builder/agents/agent.py +3 -3
  3. ibm_watsonx_orchestrate/agent_builder/agents/assistant_agent.py +3 -2
  4. ibm_watsonx_orchestrate/agent_builder/agents/external_agent.py +3 -2
  5. ibm_watsonx_orchestrate/agent_builder/agents/types.py +9 -8
  6. ibm_watsonx_orchestrate/agent_builder/connections/connections.py +4 -3
  7. ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base_requests.py +1 -22
  8. ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +1 -17
  9. ibm_watsonx_orchestrate/agent_builder/tools/base_tool.py +2 -1
  10. ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py +14 -13
  11. ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py +136 -92
  12. ibm_watsonx_orchestrate/agent_builder/tools/types.py +10 -9
  13. ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +7 -7
  14. ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +4 -3
  15. ibm_watsonx_orchestrate/cli/commands/environment/environment_controller.py +5 -5
  16. ibm_watsonx_orchestrate/cli/commands/environment/types.py +2 -0
  17. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_command.py +0 -18
  18. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +33 -19
  19. ibm_watsonx_orchestrate/cli/commands/models/models_command.py +1 -1
  20. ibm_watsonx_orchestrate/cli/commands/server/server_command.py +100 -36
  21. ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_command.py +1 -1
  22. ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +11 -4
  23. ibm_watsonx_orchestrate/cli/config.py +3 -3
  24. ibm_watsonx_orchestrate/cli/init_helper.py +10 -1
  25. ibm_watsonx_orchestrate/cli/main.py +2 -0
  26. ibm_watsonx_orchestrate/client/knowledge_bases/knowledge_base_client.py +1 -1
  27. ibm_watsonx_orchestrate/client/local_service_instance.py +3 -1
  28. ibm_watsonx_orchestrate/client/service_instance.py +33 -7
  29. ibm_watsonx_orchestrate/docker/compose-lite.yml +177 -2
  30. ibm_watsonx_orchestrate/docker/default.env +22 -2
  31. ibm_watsonx_orchestrate/flow_builder/flows/__init__.py +3 -1
  32. ibm_watsonx_orchestrate/flow_builder/flows/decorators.py +4 -2
  33. ibm_watsonx_orchestrate/flow_builder/flows/events.py +10 -9
  34. ibm_watsonx_orchestrate/flow_builder/flows/flow.py +91 -20
  35. ibm_watsonx_orchestrate/flow_builder/node.py +12 -1
  36. ibm_watsonx_orchestrate/flow_builder/types.py +169 -16
  37. ibm_watsonx_orchestrate/flow_builder/utils.py +120 -5
  38. ibm_watsonx_orchestrate/utils/exceptions.py +23 -0
  39. {ibm_watsonx_orchestrate-1.6.2.dist-info → ibm_watsonx_orchestrate-1.7.0b1.dist-info}/METADATA +2 -4
  40. {ibm_watsonx_orchestrate-1.6.2.dist-info → ibm_watsonx_orchestrate-1.7.0b1.dist-info}/RECORD +43 -43
  41. ibm_watsonx_orchestrate/flow_builder/resources/flow_status.openapi.yml +0 -66
  42. {ibm_watsonx_orchestrate-1.6.2.dist-info → ibm_watsonx_orchestrate-1.7.0b1.dist-info}/WHEEL +0 -0
  43. {ibm_watsonx_orchestrate-1.6.2.dist-info → ibm_watsonx_orchestrate-1.7.0b1.dist-info}/entry_points.txt +0 -0
  44. {ibm_watsonx_orchestrate-1.6.2.dist-info → ibm_watsonx_orchestrate-1.7.0b1.dist-info}/licenses/LICENSE +0 -0
@@ -15,6 +15,7 @@ import logging
15
15
  import copy
16
16
  import uuid
17
17
  import pytz
18
+ import os
18
19
 
19
20
  from typing_extensions import Self
20
21
  from pydantic import BaseModel, Field, SerializeAsAny
@@ -25,11 +26,13 @@ from ibm_watsonx_orchestrate.client.tools.tempus_client import TempusClient
25
26
  from ibm_watsonx_orchestrate.client.utils import instantiate_client
26
27
  from ..types import (
27
28
  EndNodeSpec, Expression, ForeachPolicy, ForeachSpec, LoopSpec, BranchNodeSpec, MatchPolicy, PromptLLMParameters, PromptNodeSpec,
28
- StartNodeSpec, ToolSpec, JsonSchemaObject, ToolRequestBody, ToolResponseBody, UserFieldKind, UserFieldOption, UserFlowSpec, UserNodeSpec, WaitPolicy
29
+ StartNodeSpec, ToolSpec, JsonSchemaObject, ToolRequestBody, ToolResponseBody, UserFieldKind, UserFieldOption, UserFlowSpec, UserNodeSpec, WaitPolicy,
30
+ DocProcSpec, TextExtractionResponse, KVPInvoicesExtractionResponse, KVPUtilityBillsExtractionResponse,
31
+ DocumentContent
29
32
  )
30
33
  from .constants import CURRENT_USER, START, END, ANY_USER
31
34
  from ..node import (
32
- EndNode, Node, PromptNode, StartNode, UserNode, AgentNode, DataMap, ToolNode
35
+ EndNode, Node, PromptNode, StartNode, UserNode, AgentNode, DataMap, ToolNode, DocProcNode
33
36
  )
34
37
  from ..types import (
35
38
  AgentNodeSpec, extract_node_spec, FlowContext, FlowEventType, FlowEvent, FlowSpec,
@@ -114,7 +117,7 @@ class Flow(Node):
114
117
  # pydantic suppport nested comparison by default
115
118
 
116
119
  schema.title = title
117
-
120
+
118
121
  if schema == existing_schema:
119
122
  return existing_schema
120
123
  # we need to do a deep compare
@@ -354,6 +357,7 @@ class Flow(Node):
354
357
  name: str,
355
358
  agent: str,
356
359
  display_name: str|None=None,
360
+ title: str | None = None,
357
361
  message: str | None = "Follow the agent instructions.",
358
362
  description: str | None = None,
359
363
  input_schema: type[BaseModel]|None = None,
@@ -371,6 +375,7 @@ class Flow(Node):
371
375
  display_name=display_name,
372
376
  description=description,
373
377
  agent=agent,
378
+ title=title,
374
379
  message=message,
375
380
  guidelines=guidelines,
376
381
  input_schema=_get_tool_request_body(input_schema_obj),
@@ -428,6 +433,50 @@ class Flow(Node):
428
433
  # add the node to the list of node
429
434
  node = self._add_node(node)
430
435
  return cast(PromptNode, node)
436
+
437
+ def docproc(self,
438
+ name: str,
439
+ task: str,
440
+ display_name: str|None=None,
441
+ description: str | None = None,
442
+ input_map: DataMap = None) -> DocProcNode:
443
+
444
+ if name is None :
445
+ raise ValueError("name must be provided.")
446
+
447
+ if task is None:
448
+ raise ValueError("task must be provided.")
449
+
450
+ output_schema_dict = {
451
+ "text_extraction" : TextExtractionResponse,
452
+ "kvp_invoices_extraction" : KVPInvoicesExtractionResponse,
453
+ "kvp_utility_bills_extraction" : KVPUtilityBillsExtractionResponse
454
+ }
455
+ # create input spec
456
+ input_schema_obj = _get_json_schema_obj(parameter_name = "input", type_def = DocumentContent)
457
+ output_schema_obj = _get_json_schema_obj("output", output_schema_dict[task])
458
+ if "$defs" in output_schema_obj.model_extra:
459
+ output_schema_obj.model_extra.pop("$defs")
460
+ # Create the docproc spec
461
+ task_spec = DocProcSpec(
462
+ name=name,
463
+ display_name=display_name if display_name is not None else name,
464
+ description=description,
465
+ input_schema=_get_tool_request_body(input_schema_obj),
466
+ output_schema=_get_tool_response_body(output_schema_obj),
467
+ output_schema_object = output_schema_obj,
468
+ task=task
469
+ )
470
+
471
+ node = DocProcNode(spec=task_spec)
472
+ # setup input map
473
+ if input_map:
474
+ node.input_map = self._get_data_map(input_map)
475
+
476
+ # add the node to the list of node
477
+ node = self._add_node(node)
478
+ return cast(DocProcNode, node)
479
+
431
480
 
432
481
  def node_exists(self, node: Union[str, Node]):
433
482
 
@@ -921,7 +970,8 @@ class FlowFactory(BaseModel):
921
970
  description: str|None=None,
922
971
  initiators: Sequence[str]|None=None,
923
972
  input_schema: type[BaseModel]|None=None,
924
- output_schema: type[BaseModel]|None=None) -> Flow:
973
+ output_schema: type[BaseModel]|None=None,
974
+ schedulable: bool=False) -> Flow:
925
975
  if isinstance(name, Callable):
926
976
  flow_spec = getattr(name, "__flow_spec__", None)
927
977
  if not flow_spec:
@@ -942,7 +992,8 @@ class FlowFactory(BaseModel):
942
992
  initiators=initiators,
943
993
  input_schema=_get_tool_request_body(input_schema_obj),
944
994
  output_schema=_get_tool_response_body(output_schema_obj),
945
- output_schema_object = output_schema_obj
995
+ output_schema_object = output_schema_obj,
996
+ schedulable=schedulable,
946
997
  )
947
998
 
948
999
  return Flow(spec = flow_spec)
@@ -1228,10 +1279,12 @@ class UserFlow(Flow):
1228
1279
  kind: UserFieldKind = UserFieldKind.Text,
1229
1280
  display_name: str | None = None,
1230
1281
  description: str | None = None,
1231
- owners: list[str] = [],
1232
1282
  default: Any | None = None,
1233
- text: str = None,
1283
+ text: str = None, # The text used to ask question to the user, e.g. 'what is your name?'
1234
1284
  option: UserFieldOption | None = None,
1285
+ is_list: bool = False,
1286
+ min: Any | None = None,
1287
+ max: Any | None = None,
1235
1288
  input_map: DataMap = None,
1236
1289
  custom: dict[str, Any] = {}) -> UserNode:
1237
1290
  '''create a node in the flow'''
@@ -1246,20 +1299,42 @@ class UserFlow(Flow):
1246
1299
  schema_obj.properties = {}
1247
1300
  schema_obj.properties[name] = UserFieldKind.convert_kind_to_schema_property(kind, name, description, default, option, custom)
1248
1301
 
1249
- return self.user(name,
1250
- display_name=display_name,
1251
- description=description,
1252
- owners=owners,
1253
- text=text,
1254
- output_schema=schema_obj,
1255
- input_map=input_map)
1302
+ task_spec = UserNodeSpec(
1303
+ name=name,
1304
+ display_name=display_name,
1305
+ description=description,
1306
+ owners=[CURRENT_USER],
1307
+ input_schema=_get_tool_request_body(schema_obj),
1308
+ output_schema=_get_tool_response_body(schema_obj),
1309
+ text=text,
1310
+ output_schema_object = schema_obj
1311
+ )
1312
+
1313
+ node = UserNode(spec = task_spec)
1314
+ node.field(name = name,
1315
+ kind = kind,
1316
+ display_name = display_name,
1317
+ description = description,
1318
+ default = default,
1319
+ text = text,
1320
+ option = option,
1321
+ is_list = is_list,
1322
+ min = min,
1323
+ max = max,
1324
+ custom = custom)
1325
+
1326
+ # setup input map
1327
+ if input_map:
1328
+ node.input_map = self._get_data_map(input_map)
1329
+
1330
+ node = self._add_node(node)
1331
+ return cast(UserNode, node)
1256
1332
 
1257
1333
  def user(
1258
1334
  self,
1259
1335
  name: str | None = None,
1260
1336
  display_name: str | None = None,
1261
1337
  description: str | None = None,
1262
- owners: list[str] = [],
1263
1338
  text: str | None = None,
1264
1339
  output_schema: type[BaseModel] | JsonSchemaObject| None = None,
1265
1340
  input_map: DataMap = None,
@@ -1273,16 +1348,12 @@ class UserFlow(Flow):
1273
1348
  # input and output is always the same in an user node
1274
1349
  output_schema_obj = output_schema_obj
1275
1350
 
1276
- # identify owner
1277
- if not owners:
1278
- owners = [ANY_USER]
1279
-
1280
1351
  # Create the tool spec
1281
1352
  task_spec = UserNodeSpec(
1282
1353
  name=name,
1283
1354
  display_name=display_name,
1284
1355
  description=description,
1285
- owners=owners,
1356
+ owners=[CURRENT_USER],
1286
1357
  input_schema=_get_tool_request_body(output_schema_obj),
1287
1358
  output_schema=_get_tool_response_body(output_schema_obj),
1288
1359
  text=text,
@@ -5,7 +5,7 @@ import uuid
5
5
  import yaml
6
6
  from pydantic import BaseModel, Field, SerializeAsAny
7
7
 
8
- from .types import EndNodeSpec, NodeSpec, AgentNodeSpec, PromptNodeSpec, StartNodeSpec, ToolNodeSpec, UserFieldKind, UserFieldOption, UserNodeSpec
8
+ from .types import EndNodeSpec, NodeSpec, AgentNodeSpec, PromptNodeSpec, StartNodeSpec, ToolNodeSpec, UserFieldKind, UserFieldOption, UserNodeSpec, DocProcSpec
9
9
  from .data_map import DataMap
10
10
 
11
11
  class Node(BaseModel):
@@ -78,6 +78,8 @@ class UserNode(Node):
78
78
  description: str | None = None,
79
79
  default: Any | None = None,
80
80
  option: UserFieldOption | None = None,
81
+ min: Any | None = None,
82
+ max: Any | None = None,
81
83
  is_list: bool = False,
82
84
  custom: dict[str, Any] | None = None,
83
85
  widget: str | None = None):
@@ -88,6 +90,8 @@ class UserNode(Node):
88
90
  description=description,
89
91
  default=default,
90
92
  option=option,
93
+ min=min,
94
+ max=max,
91
95
  is_list=is_list,
92
96
  custom=custom,
93
97
  widget=widget)
@@ -105,6 +109,13 @@ class PromptNode(Node):
105
109
 
106
110
  def get_spec(self) -> PromptNodeSpec:
107
111
  return cast(PromptNodeSpec, self.spec)
112
+
113
+ class DocProcNode(Node):
114
+ def __repr__(self):
115
+ return f"DocProcNode(name='{self.spec.name}', description='{self.spec.description}')"
116
+
117
+ def get_spec(self) -> DocProcSpec:
118
+ return cast(DocProcSpec, self.spec)
108
119
 
109
120
  class NodeInstance(BaseModel):
110
121
  node: Node
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass
2
- from enum import Enum
2
+ from enum import Enum, StrEnum, auto
3
3
  import inspect
4
4
  import logging
5
5
  from typing import (
@@ -116,7 +116,7 @@ def _to_json_from_output_schema(schema: Union[ToolResponseBody, SchemaRef]) -> d
116
116
  return model_spec
117
117
 
118
118
  class NodeSpec(BaseModel):
119
- kind: Literal["node", "tool", "user", "agent", "flow", "start", "decisions", "prompt", "branch", "wait", "foreach", "loop", "userflow", "end"] = "node"
119
+ kind: Literal["node", "tool", "user", "agent", "flow", "start", "decisions", "prompt", "branch", "wait", "foreach", "loop", "userflow", "end", "docproc" ] = "node"
120
120
  name: str
121
121
  display_name: str | None = None
122
122
  description: str | None = None
@@ -162,6 +162,26 @@ class NodeSpec(BaseModel):
162
162
 
163
163
  return model_spec
164
164
 
165
+ class DocProcTask(StrEnum):
166
+ '''
167
+ Possible names for the Document processing task parameter
168
+ '''
169
+ text_extraction = auto()
170
+ kvp_invoices_extraction = auto()
171
+ kvp_utility_bills_extraction = auto()
172
+
173
+ class DocProcSpec(NodeSpec):
174
+ task: DocProcTask = Field(description='The document processing operation name', default=DocProcTask.text_extraction)
175
+
176
+ def __init__(self, **data):
177
+ super().__init__(**data)
178
+ self.kind = "docproc"
179
+
180
+ def to_json(self) -> dict[str, Any]:
181
+ model_spec = super().to_json()
182
+ model_spec["task"] = self.task
183
+ return model_spec
184
+
165
185
  class StartNodeSpec(NodeSpec):
166
186
  def __init__(self, **data):
167
187
  super().__init__(**data)
@@ -296,6 +316,8 @@ class UserField(BaseModel):
296
316
  description: str | None = None
297
317
  default: Any | None = None
298
318
  option: UserFieldOption | None = None
319
+ min: Any | None = None,
320
+ max: Any | None = None,
299
321
  is_list: bool = False
300
322
  custom: dict[str, Any] | None = None
301
323
  widget: str | None = None
@@ -314,6 +336,10 @@ class UserField(BaseModel):
314
336
  model_spec["description"] = self.description
315
337
  if self.default:
316
338
  model_spec["default"] = self.default
339
+ if self.min:
340
+ model_spec["min"] = self.min
341
+ if self.max:
342
+ model_spec["min"] = self.max
317
343
  if self.is_list:
318
344
  model_spec["is_list"] = self.is_list
319
345
  if self.option:
@@ -356,7 +382,10 @@ class UserNodeSpec(NodeSpec):
356
382
  display_name: str | None = None,
357
383
  description: str | None = None,
358
384
  default: Any | None = None,
359
- option: list[str] | None = None, is_list: bool = False,
385
+ option: list[str] | None = None,
386
+ min: Any | None = None,
387
+ max: Any | None = None,
388
+ is_list: bool = False,
360
389
  custom: dict[str, Any] | None = None,
361
390
  widget: str | None = None):
362
391
  userfield = UserField(name=name,
@@ -366,6 +395,8 @@ class UserNodeSpec(NodeSpec):
366
395
  description=description,
367
396
  default=default,
368
397
  option=option,
398
+ min=min,
399
+ max=max,
369
400
  is_list=is_list,
370
401
  custom=custom,
371
402
  widget=widget)
@@ -402,6 +433,8 @@ class UserNodeSpec(NodeSpec):
402
433
  default=prop_schema.default,
403
434
  option=self.setup_field_options(prop_schema.title, prop_schema.enum),
404
435
  is_list=prop_schema.type == "array",
436
+ min=prop_schema.minimum,
437
+ max=prop_schema.maximum,
405
438
  custom=prop_schema.model_extra))
406
439
 
407
440
  def setup_field_options(self, name: str, enums: List[str]) -> UserFieldOption:
@@ -415,6 +448,7 @@ class UserNodeSpec(NodeSpec):
415
448
 
416
449
  class AgentNodeSpec(ToolNodeSpec):
417
450
  message: str | None = Field(default=None, description="The instructions for the task.")
451
+ title: str | None = Field(default=None, description="The title of the message.")
418
452
  guidelines: str | None = Field(default=None, description="The guidelines for the task.")
419
453
  agent: str
420
454
 
@@ -430,6 +464,8 @@ class AgentNodeSpec(ToolNodeSpec):
430
464
  model_spec["guidelines"] = self.guidelines
431
465
  if self.agent:
432
466
  model_spec["agent"] = self.agent
467
+ if self.title:
468
+ model_spec["title"] = self.title
433
469
  return model_spec
434
470
 
435
471
  class PromptLLMParameters(BaseModel):
@@ -553,10 +589,9 @@ class WaitNodeSpec(FlowControlNodeSpec):
553
589
  return my_dict
554
590
 
555
591
  class FlowSpec(NodeSpec):
556
-
557
-
558
592
  # who can initiate the flow
559
593
  initiators: Sequence[str] = [ANY_USER]
594
+ schedulable: bool = False
560
595
 
561
596
  def __init__(self, **kwargs):
562
597
  super().__init__(**kwargs)
@@ -566,6 +601,8 @@ class FlowSpec(NodeSpec):
566
601
  model_spec = super().to_json()
567
602
  if self.initiators:
568
603
  model_spec["initiators"] = self.initiators
604
+
605
+ model_spec["schedulable"] = self.schedulable
569
606
 
570
607
  return model_spec
571
608
 
@@ -631,11 +668,11 @@ class TaskData(NamedTuple):
631
668
 
632
669
  class TaskEventType(Enum):
633
670
 
634
- ON_TASK_WAIT = "on_task_wait" # the task is waiting for inputs before proceeding
635
- ON_TASK_START = "on_task_start"
636
- ON_TASK_END = "on_task_end"
637
- ON_TASK_STREAM = "on_task_stream"
638
- ON_TASK_ERROR = "on_task_error"
671
+ ON_TASK_WAIT = "task:on_task_wait" # the task is waiting for inputs before proceeding
672
+ ON_TASK_START = "task:on_task_start"
673
+ ON_TASK_END = "task:on_task_end"
674
+ ON_TASK_STREAM = "task:on_task_stream"
675
+ ON_TASK_ERROR = "task:on_task_error"
639
676
 
640
677
  class FlowData(BaseModel):
641
678
  '''This class represents the data that is passed between tasks in a flow.'''
@@ -667,9 +704,9 @@ class FlowContext(BaseModel):
667
704
 
668
705
  class FlowEventType(Enum):
669
706
 
670
- ON_FLOW_START = "on_flow_start"
671
- ON_FLOW_END = "on_flow_end"
672
- ON_FLOW_ERROR = "on_flow_error"
707
+ ON_FLOW_START = "flow:on_flow_start"
708
+ ON_FLOW_END = "flow:on_flow_end"
709
+ ON_FLOW_ERROR = "flow:on_flow_error"
673
710
 
674
711
 
675
712
  @dataclass
@@ -691,9 +728,125 @@ class Assignment(BaseModel):
691
728
  e.g. "node.input.name" or "=f'{node.output.name}_{node.output.id}'"
692
729
 
693
730
  '''
694
- target: str
695
- source: str
696
-
731
+ target_variable: str
732
+ value_expression: str | None = None
733
+ has_no_value: bool = False
734
+ default_value: Any | None = None
735
+ metadata: dict = Field(default_factory=dict[str, Any])
736
+
737
+ class LanguageCode(StrEnum):
738
+ '''
739
+ The ISO-639 language codes understood by Document Processing functions.
740
+ A special 'en_hw' code is used to enable an English handwritten model.
741
+ '''
742
+ en = auto()
743
+ fr = auto()
744
+ en_hw = auto()
745
+
746
+ class DocumentContent(BaseModel):
747
+ '''
748
+ This class represents the input of a Document processing task.
749
+
750
+ Attributes:
751
+ document_ref (bytes|str): This is either a URL to the location of the document bytes or an ID that we use to resolve the location of the document
752
+ language (LanguageCode): Optional language code used when processing the input document
753
+ '''
754
+ # This is declared as bytes but the runtime will understand if a URL is send in as input.
755
+ # We need to use bytes here for Chat-with-doc to recognize the input as a Document.
756
+ document_ref: bytes | str = Field(
757
+ description="Either an ID or a URL identifying the document to be used.",
758
+ title='Document reference',
759
+ default=None,
760
+ json_schema_extra={"format": "binary"})
761
+ language: Optional[LanguageCode] = Field(
762
+ description='Optional language code of the document, defaults to "en"',
763
+ title='Document language code',
764
+ default=LanguageCode.en)
765
+
766
+ class TextExtraction(BaseModel):
767
+ '''
768
+ This class represents the output generated by a "text_extraction" document processing (docproc) operation.
769
+ Attributes:
770
+ text (str): the text extracted from the input document.
771
+ '''
772
+ text: str = Field(description='The text extracted from the input document', title='Text extraction')
773
+
774
+ class TextExtractionResponse(BaseModel):
775
+ '''
776
+ The text extraction operation response.
777
+ Attributes:
778
+ output (TextExtraction): a wrapper for the text extraction response
779
+ '''
780
+ output: TextExtraction = Field(description='The text extraction response')
781
+
782
+ class Invoice(BaseModel):
783
+ '''
784
+ This class represents the fields extracted by the "kvp_invoices_extraction" document processing (docproc) operation.
785
+ '''
786
+ bank_account_number: Optional[str] = Field(title='Bank account number', default=None)
787
+ bank_name: Optional[str] = Field(title='Bank name', default=None)
788
+ bill_to_address: Optional[str] = Field(title='Bill-to address', default=None)
789
+ bill_to_name: Optional[str] = Field(title='Bill-to name', default=None)
790
+ invoice_date: Optional[str] = Field(title='Invoice date', format='date', default=None)
791
+ invoice_number: Optional[str] = Field(title='Invoice number', default=None)
792
+ invoice_total: Optional[float] = Field(title='Invoice total', default=None)
793
+ payment_due_date: Optional[str] = Field(title='Payment due date', format='date', default=None)
794
+ payment_terms: Optional[str] = Field(title='Payment terms', default=None)
795
+ purchase_order_number: Optional[str] = Field(title='Purchase order number', default=None)
796
+ ship_to_address: Optional[str] = Field(title='Ship-to address', default=None)
797
+ ship_to_name: Optional[str] = Field(title='Ship-to name', default=None)
798
+ shipping_amount: Optional[float] = Field(title='Shipping amount', default=None)
799
+ subtotal: Optional[float] = Field(title='Subtotal', default=None)
800
+ tax_amount: Optional[float] = Field(title='Tax amount', default=None)
801
+ tax_rate: Optional[float] = Field(title='Tax rate', default=None)
802
+ tax_type: Optional[str] = Field(title='Tax type', default=None)
803
+ vendor_address: Optional[str] = Field(title='Vendor address', default=None)
804
+ vendor_name: Optional[str] = Field(title='Vendor name', default=None)
805
+
806
+
807
+ class KVPInvoicesExtractionResponse(BaseModel):
808
+ '''
809
+ The response of a "kvp_invoices_extraction" document processing (docproc) operation.
810
+ Attributes:
811
+ invoice: an object with the fields extracted from the input invoice document
812
+ '''
813
+ output: Invoice = Field(
814
+ title='Invoice',
815
+ description='The fields extracted from an invoice document'
816
+ )
817
+
818
+
819
+ class UtilityBill(BaseModel):
820
+ '''
821
+ This class represents the fields extracted by the "kvp_utility_bills_extraction" document processing (docproc) operation.
822
+ '''
823
+ account_number: Optional[str] = Field(title='Account number', default=None)
824
+ amount_due: Optional[float] = Field(title='Amount due', default=None)
825
+ client_number: Optional[str] = Field(title='Client number', default=None)
826
+ company_name: Optional[str] = Field(title='Company name', default=None)
827
+ company_address: Optional[str] = Field(title='Company address', default=None)
828
+ customer_name: Optional[str] = Field(title='Customer name', default=None)
829
+ customer_address: Optional[str] = Field(title='Customer address', default=None)
830
+ due_date: Optional[str] = Field(title='Due date', format='date', default=None)
831
+ payment_received: Optional[float] = Field(title='Payment received', default=None)
832
+ previous_balance: Optional[float] = Field(title='Previous balance', default=None)
833
+ service_address: Optional[str] = Field(title='Service address', default=None)
834
+ statement_date: Optional[str] = Field(title='Statement date', format='date', default=None)
835
+
836
+ #class UtilityBillResponse(BaseModel):
837
+ # field_value: UtilityBill = Field(title='Field value')
838
+
839
+ class KVPUtilityBillsExtractionResponse(BaseModel):
840
+ '''
841
+ The response of a "kvp_utility_bills_extraction" document processing (docproc) operation.
842
+ Attributes:
843
+ utility_bull: an object with the fields extracted from the input utility bill document
844
+ '''
845
+ output: UtilityBill = Field(
846
+ title='Utility bill',
847
+ description='The fields extracted from a utility bill document'
848
+ )
849
+
697
850
  def extract_node_spec(
698
851
  fn: Callable | PythonTool,
699
852
  name: Optional[str] = None,
@@ -87,9 +87,15 @@ def _get_tool_request_body(schema_obj: JsonSchemaObject) -> ToolRequestBody:
87
87
  return None
88
88
 
89
89
  if isinstance(schema_obj, JsonSchemaObject):
90
- request_obj = ToolRequestBody(type='object', properties=schema_obj.properties, required=schema_obj.required)
91
- if schema_obj.model_extra:
92
- request_obj.__pydantic_extra__ = schema_obj.model_extra
90
+ if schema_obj.type == "object":
91
+ request_obj = ToolRequestBody(type='object', properties=schema_obj.properties, required=schema_obj.required)
92
+ if schema_obj.model_extra:
93
+ request_obj.__pydantic_extra__ = schema_obj.model_extra
94
+ else: # we need to wrap a simple type with an object
95
+ request_obj = ToolRequestBody(type='object', properties={}, required=[])
96
+ request_obj.properties["data"] = schema_obj
97
+ if schema_obj.model_extra:
98
+ request_obj.__pydantic_extra__ = schema_obj.model_extra
93
99
 
94
100
  return request_obj
95
101
 
@@ -162,17 +168,27 @@ async def import_flow_model(model):
162
168
 
163
169
  return tool_id
164
170
 
165
- def import_flow_support_tools():
171
+ def import_flow_support_tools(model):
166
172
 
167
173
  if not is_local_dev():
168
174
  # we can't import support tools into non-local environments yet
169
175
  return []
170
176
 
177
+
178
+ schedulable = False
179
+ if "schedulable" in model["spec"]:
180
+ schedulable = model["spec"]["schedulable"]
181
+
171
182
  client = instantiate_client(TempusClient)
172
183
 
173
184
  logger.info(f"Import 'get_flow_status' tool spec...")
174
185
  tools = [create_flow_status_tool("i__get_flow_status_intrinsic_tool__")]
175
186
 
187
+ if schedulable:
188
+ get_schedule_tool = create_get_schedule_tool("i__get_schedule_intrinsic_tool__")
189
+ delete_schedule_tool = create_delete_schedule_tool("i__delete_schedule_intrinsic_tool__")
190
+ tools.extend([get_schedule_tool, delete_schedule_tool])
191
+
176
192
  return tools
177
193
 
178
194
  # Assisted by watsonx Code Assistant
@@ -187,7 +203,7 @@ def create_flow_status_tool(flow_status_tool: str, TEMPUS_ENDPOINT: str="http://
187
203
  )
188
204
 
189
205
  openapi_binding = OpenApiToolBinding(
190
- http_path="/flows",
206
+ http_path="/v1/flows",
191
207
  http_method="GET",
192
208
  security=[],
193
209
  servers=[TEMPUS_ENDPOINT]
@@ -213,3 +229,102 @@ def create_flow_status_tool(flow_status_tool: str, TEMPUS_ENDPOINT: str="http://
213
229
 
214
230
  return OpenAPITool(spec=spec)
215
231
 
232
+
233
+ def create_get_schedule_tool(name: str, TEMPUS_ENDPOINT: str="http://wxo-tempus-runtime:9044") -> dict:
234
+
235
+ spec = ToolSpec(
236
+ name=name,
237
+ description="Use this tool to show the current schedules.",
238
+ permission='read_only',
239
+ display_name= "Get Schedules"
240
+ )
241
+
242
+ openapi_binding = OpenApiToolBinding(
243
+ http_path="/v1/schedules/simple",
244
+ http_method="GET",
245
+ security=[],
246
+ servers=[TEMPUS_ENDPOINT]
247
+ )
248
+
249
+ spec.binding = ToolBinding(openapi=openapi_binding)
250
+ # Input Schema
251
+ properties = {
252
+ "query_schedule_id": {
253
+ "type": "string",
254
+ "title": "schedule_id",
255
+ "description": "Identifies the schedule instance.",
256
+ "in": "query"
257
+ },
258
+ "query_schedule_name": {
259
+ "type": "string",
260
+ "title": "schedule_name",
261
+ "description": "Identifies the schedule name.",
262
+ "in": "query"
263
+ },
264
+ }
265
+
266
+ spec.input_schema = ToolRequestBody(
267
+ type='object',
268
+ properties=properties,
269
+ required=[]
270
+ )
271
+
272
+ response_properties = {
273
+ "schedule_id": {
274
+ "type": "string",
275
+ },
276
+ "schedule_name": {
277
+ "type": "string",
278
+ },
279
+ "schedule_data": {
280
+ "type": "string",
281
+ },
282
+ "schedule_time": {
283
+ "type": "string",
284
+ }
285
+ }
286
+
287
+ spec.output_schema = ToolResponseBody(type='object',
288
+ properties=response_properties,
289
+ description='Return the information about the schedule.')
290
+
291
+ return OpenAPITool(spec=spec)
292
+
293
+
294
+ def create_delete_schedule_tool(name: str, TEMPUS_ENDPOINT: str="http://wxo-tempus-runtime:9044") -> dict:
295
+
296
+ spec = ToolSpec(
297
+ name=name,
298
+ description="Use this tool to delete/remove a schedule based on the schedule_id.",
299
+ permission='read_only',
300
+ display_name= "Delete Schedule"
301
+ )
302
+
303
+ openapi_binding = OpenApiToolBinding(
304
+ http_path="/v1/schedules/{schedule_id}",
305
+ http_method="DELETE",
306
+ security=[],
307
+ servers=[TEMPUS_ENDPOINT]
308
+ )
309
+
310
+ spec.binding = ToolBinding(openapi=openapi_binding)
311
+ # Input Schema
312
+ properties = {
313
+ "path_schedule_id": {
314
+ "type": "string",
315
+ "title": "schedule_id",
316
+ "description": "Identifies the schedule instance.",
317
+ "in": "query"
318
+ }
319
+ }
320
+
321
+ spec.input_schema = ToolRequestBody(
322
+ type='object',
323
+ properties=properties,
324
+ required=[]
325
+ )
326
+
327
+ spec.output_schema = ToolResponseBody(type='object',
328
+ description='Schedule deleted.')
329
+
330
+ return OpenAPITool(spec=spec)