ibm-watsonx-orchestrate 1.8.0b0__py3-none-any.whl → 1.8.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 (27) hide show
  1. ibm_watsonx_orchestrate/__init__.py +1 -1
  2. ibm_watsonx_orchestrate/agent_builder/agents/types.py +12 -0
  3. ibm_watsonx_orchestrate/agent_builder/connections/types.py +14 -2
  4. ibm_watsonx_orchestrate/cli/commands/channels/types.py +15 -2
  5. ibm_watsonx_orchestrate/cli/commands/channels/webchat/channels_webchat_controller.py +5 -4
  6. ibm_watsonx_orchestrate/cli/commands/connections/connections_command.py +14 -6
  7. ibm_watsonx_orchestrate/cli/commands/connections/connections_controller.py +6 -8
  8. ibm_watsonx_orchestrate/cli/commands/copilot/copilot_controller.py +111 -36
  9. ibm_watsonx_orchestrate/cli/commands/copilot/copilot_server_controller.py +23 -7
  10. ibm_watsonx_orchestrate/cli/commands/environment/types.py +1 -1
  11. ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_command.py +16 -6
  12. ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_controller.py +20 -2
  13. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +10 -8
  14. ibm_watsonx_orchestrate/cli/commands/server/server_command.py +1 -9
  15. ibm_watsonx_orchestrate/client/copilot/cpe/copilot_cpe_client.py +2 -1
  16. ibm_watsonx_orchestrate/client/utils.py +5 -4
  17. ibm_watsonx_orchestrate/docker/compose-lite.yml +9 -2
  18. ibm_watsonx_orchestrate/docker/default.env +6 -5
  19. ibm_watsonx_orchestrate/flow_builder/flows/__init__.py +8 -5
  20. ibm_watsonx_orchestrate/flow_builder/flows/flow.py +47 -7
  21. ibm_watsonx_orchestrate/flow_builder/node.py +7 -1
  22. ibm_watsonx_orchestrate/flow_builder/types.py +168 -65
  23. {ibm_watsonx_orchestrate-1.8.0b0.dist-info → ibm_watsonx_orchestrate-1.8.0b1.dist-info}/METADATA +2 -2
  24. {ibm_watsonx_orchestrate-1.8.0b0.dist-info → ibm_watsonx_orchestrate-1.8.0b1.dist-info}/RECORD +27 -27
  25. {ibm_watsonx_orchestrate-1.8.0b0.dist-info → ibm_watsonx_orchestrate-1.8.0b1.dist-info}/WHEEL +0 -0
  26. {ibm_watsonx_orchestrate-1.8.0b0.dist-info → ibm_watsonx_orchestrate-1.8.0b1.dist-info}/entry_points.txt +0 -0
  27. {ibm_watsonx_orchestrate-1.8.0b0.dist-info → ibm_watsonx_orchestrate-1.8.0b1.dist-info}/licenses/LICENSE +0 -0
@@ -3,12 +3,12 @@ import os.path
3
3
  from typing import List, Dict, Optional, Tuple
4
4
  import csv
5
5
  from pathlib import Path
6
- import rich
6
+ import sys
7
7
  from wxo_agentic_evaluation import main as evaluate
8
8
  from wxo_agentic_evaluation.tool_planner import build_snapshot
9
9
  from wxo_agentic_evaluation.analyze_run import analyze
10
10
  from wxo_agentic_evaluation.batch_annotate import generate_test_cases_from_stories
11
- from wxo_agentic_evaluation.arg_configs import TestConfig, AuthConfig, LLMUserConfig, ChatRecordingConfig, AnalyzeConfig
11
+ from wxo_agentic_evaluation.arg_configs import TestConfig, AuthConfig, LLMUserConfig, ChatRecordingConfig, AnalyzeConfig, ProviderConfig
12
12
  from wxo_agentic_evaluation.record_chat import record_chats
13
13
  from wxo_agentic_evaluation.external_agent.external_validate import ExternalAgentValidation
14
14
  from wxo_agentic_evaluation.external_agent.performance_test import ExternalAgentPerformanceTest
@@ -41,12 +41,26 @@ class EvaluationsController:
41
41
  def evaluate(self, config_file: Optional[str] = None, test_paths: Optional[str] = None, output_dir: Optional[str] = None) -> None:
42
42
  url, tenant_name, token = self._get_env_config()
43
43
 
44
+ if "WATSONX_SPACE_ID" in os.environ and "WATSONX_APIKEY" in os.environ:
45
+ provider = "watsonx"
46
+ elif "WO_INSTANCE" in os.environ and "WO_API_KEY" in os.environ:
47
+ provider = "model_proxy"
48
+ else:
49
+ logger.error(
50
+ "No provider found. Please either provide a config_file or set either WATSONX_SPACE_ID and WATSONX_APIKEY or WO_INSTANCE and WO_API_KEY in your system environment variables."
51
+ )
52
+ sys.exit(1)
53
+
44
54
  config_data = {
45
55
  "wxo_lite_version": __version__,
46
56
  "auth_config": AuthConfig(
47
57
  url=url,
48
58
  tenant_name=tenant_name,
49
59
  token=token
60
+ ),
61
+ "provider_config": ProviderConfig(
62
+ provider=provider,
63
+ model_id="meta-llama/llama-3-405b-instruct",
50
64
  )
51
65
  }
52
66
 
@@ -62,6 +76,10 @@ class EvaluationsController:
62
76
  if "llm_user_config" in file_config:
63
77
  llm_config_data = file_config.pop("llm_user_config")
64
78
  config_data["llm_user_config"] = LLMUserConfig(**llm_config_data)
79
+
80
+ if "provider_config" in file_config:
81
+ provider_config_data = file_config.pop("provider_config")
82
+ config_data["provider_config"] = ProviderConfig(**provider_config_data)
65
83
 
66
84
  config_data.update(file_config)
67
85
 
@@ -71,6 +71,7 @@ class KnowledgeBaseController:
71
71
  client = self.get_client()
72
72
 
73
73
  knowledge_bases = parse_file(file=file)
74
+
74
75
  existing_knowledge_bases = client.get_by_names([kb.name for kb in knowledge_bases])
75
76
 
76
77
  for kb in knowledge_bases:
@@ -137,23 +138,24 @@ class KnowledgeBaseController:
137
138
 
138
139
  def update_knowledge_base(
139
140
  self, knowledge_base_id: str, kb: KnowledgeBase, file_dir: str
140
- ) -> None:
141
- filtered_files = []
142
-
141
+ ) -> None:
143
142
  if kb.documents:
144
143
  status = self.get_client().status(knowledge_base_id)
145
144
  existing_docs = [doc.get("metadata", {}).get("original_file_name", "") for doc in status.get("documents", [])]
146
145
 
146
+ removed_docs = existing_docs[:]
147
147
  for filepath in kb.documents:
148
148
  filename = get_file_name(filepath)
149
149
 
150
150
  if filename in existing_docs:
151
- logger.warning(f'Document \"{filename}\" already exists in knowledge base, skipping.')
152
- else:
153
- filtered_files.append(filepath)
151
+ logger.warning(f'Document \"{filename}\" already exists in knowledge base. Updating...')
152
+ removed_docs.remove(filename)
153
+
154
+ for filename in removed_docs:
155
+ logger.warning(f'Document \"{filename}\" removed from knowledge base.')
156
+
154
157
 
155
- if filtered_files:
156
- files = [('files', (get_file_name(file_path), open(get_relative_file_path(file_path, file_dir), 'rb'))) for file_path in filtered_files]
158
+ files = [('files', (get_file_name(file_path), open(get_relative_file_path(file_path, file_dir), 'rb'))) for file_path in kb.documents]
157
159
 
158
160
  kb.prioritize_built_in_index = True
159
161
  payload = kb.model_dump(exclude_none=True);
@@ -46,15 +46,7 @@ _ALWAYS_UNSET: set[str] = {
46
46
 
47
47
  def define_saas_wdu_runtime(value: str = "none") -> None:
48
48
  cfg = Config()
49
-
50
- current_config_file_values = cfg.get(USER_ENV_CACHE_HEADER)
51
- current_config_file_values["SAAS_WDU_RUNTIME"] = value
52
-
53
- cfg.save(
54
- {
55
- USER_ENV_CACHE_HEADER: current_config_file_values
56
- }
57
- )
49
+ cfg.write(USER_ENV_CACHE_HEADER,"SAAS_WDU_RUNTIME",value)
58
50
 
59
51
  def ensure_docker_installed() -> None:
60
52
  try:
@@ -21,10 +21,11 @@ class CPEClient(BaseAPIClient):
21
21
  }
22
22
 
23
23
 
24
- def submit_pre_cpe_chat(self, user_message: str | None =None, tools: Dict[str, Any] = None) -> dict:
24
+ def submit_pre_cpe_chat(self, user_message: str | None =None, tools: Dict[str, Any] = None, agents: Dict[str, Any] = None) -> dict:
25
25
  payload = {
26
26
  "message": user_message,
27
27
  "tools": tools,
28
+ "agents": agents,
28
29
  "chat_id": self.chat_id,
29
30
  "chat_model_name": self.chat_model_name
30
31
  }
@@ -16,6 +16,7 @@ from ibm_watsonx_orchestrate.cli.config import (
16
16
  from threading import Lock
17
17
  from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient
18
18
  from ibm_watsonx_orchestrate.utils.utils import yaml_safe_load
19
+ from ibm_watsonx_orchestrate.cli.commands.channels.types import RuntimeEnvironmentType
19
20
  import logging
20
21
  from typing import TypeVar
21
22
  import os
@@ -92,13 +93,13 @@ def is_ga_platform(url: str | None = None) -> bool:
92
93
 
93
94
  def get_environment() -> str:
94
95
  if is_local_dev():
95
- return "local"
96
+ return RuntimeEnvironmentType.LOCAL
96
97
  if is_cpd_env():
97
- return "cpd"
98
+ return RuntimeEnvironmentType.CPD
98
99
  if is_ibm_cloud_platform():
99
- return "ibmcloud"
100
+ return RuntimeEnvironmentType.IBM_CLOUD
100
101
  if is_ga_platform():
101
- return "ga"
102
+ return RuntimeEnvironmentType.AWS
102
103
  return None
103
104
 
104
105
  def check_token_validity(token: str) -> bool:
@@ -46,7 +46,7 @@ services:
46
46
  WXO_SERVER_URL: http://wxo-server:4321
47
47
  MAX_POOL: 60
48
48
  DEPLOYMENT_MODE: laptop
49
- SUFFIXLIST: '["global_05d7ba72", "ibm_184bdbd3"]'
49
+ SUFFIXLIST: ${CM_SUFFIXLIST:-[]}
50
50
  ports:
51
51
  - 3001:3001
52
52
 
@@ -804,6 +804,13 @@ services:
804
804
  environment:
805
805
  WATSONX_APIKEY: ${WATSONX_APIKEY}
806
806
  WATSONX_SPACE_ID: ${WATSONX_SPACE_ID}
807
+ WO_API_KEY: ${WO_API_KEY}
808
+ WO_USERNAME: ${WO_USERNAME}
809
+ WO_PASSWORD: ${WO_PASSWORD}
810
+ WO_INSTANCE: ${WO_INSTANCE}
811
+ USE_SAAS_ML_TOOLS_RUNTIME: ${USE_SAAS_ML_TOOLS_RUNTIME}
812
+ WO_AUTH_TYPE: ${WO_AUTH_TYPE}
813
+ AUTHORIZATION_URL: ${AUTHORIZATION_URL}
807
814
  ports:
808
815
  - 8081:8080
809
816
 
@@ -909,7 +916,7 @@ services:
909
916
  ENRICHMENT_BATCH_SIZE: "1000"
910
917
  CIPHER_AES_REALM_KEY: "dGVzdHRlc3R0ZXN0dGVzdA=="
911
918
  SIDECAR_METERED_ENABLED: "false"
912
- DPI_DEBUG: true
919
+ DPI_DEBUG: "false"
913
920
  DPI_WO_WDU_SERVER_ENDPOINT: https://wxo-doc-processing-service:8080
914
921
  # DPI_RAG_SERVER_ENDPOINT: https://wxo-doc-processing-llm-service:8083
915
922
  DISABLE_TLS: true
@@ -53,10 +53,10 @@ EVENT_BROKER_TTL="-1"
53
53
  REGISTRY_URL=
54
54
 
55
55
 
56
- SERVER_TAG=02-07-2025
56
+ SERVER_TAG=10-07-2025-beb40a3a
57
57
  SERVER_REGISTRY=
58
58
 
59
- WORKER_TAG=02-07-2025
59
+ WORKER_TAG=10-07-2025-beb40a3a
60
60
  WORKER_REGISTRY=
61
61
 
62
62
  AI_GATEWAY_TAG=01-07-2025
@@ -84,10 +84,10 @@ TRM_REGISTRY=
84
84
  TR_TAG=08-07-2025
85
85
  TR_REGISTRY=
86
86
 
87
- BUILDER_TAG=02-07-2025
87
+ BUILDER_TAG=15-07-2025
88
88
  BUILDER_REGISTRY=
89
89
 
90
- FLOW_RUNTIME_TAG=10-07-2025
90
+ FLOW_RUNTIME_TAG=15-07-2025
91
91
  FLOW_RUMTIME_REGISTRY=
92
92
 
93
93
 
@@ -100,7 +100,7 @@ JAEGER_PROXY_REGISTRY=
100
100
  SOCKET_HANDLER_TAG=29-05-2025
101
101
  SOCKET_HANDLER_REGISTRY=
102
102
 
103
- CPE_TAG=08-07-2025
103
+ CPE_TAG=17-07-2025
104
104
  CPE_REGISTRY=
105
105
 
106
106
  # IBM Document Processing
@@ -171,6 +171,7 @@ WO_INSTANCE=
171
171
  AUTHORIZATION_URL=
172
172
  WO_AUTH_TYPE=
173
173
  PYTHONPATH=
174
+ CM_SUFFIXLIST=
174
175
 
175
176
  # Use your machine's local IP address for external async tool communication.
176
177
  CALLBACK_HOST_URL=
@@ -1,6 +1,8 @@
1
1
  from .constants import START, END, RESERVED
2
- from ..types import FlowContext, TaskData, TaskEventType, DocumentContent
3
- from ..node import UserNode, AgentNode, StartNode, EndNode, PromptNode, ToolNode
2
+
3
+ from ..types import FlowContext, TaskData, TaskEventType, File, DecisionsCondition, DecisionsRule
4
+ from ..node import UserNode, AgentNode, StartNode, EndNode, PromptNode, ToolNode, DecisionsNode
5
+
4
6
  from .flow import Flow, CompiledFlow, FlowRun, FlowEvent, FlowEventType, FlowFactory, MatchPolicy, WaitPolicy, ForeachPolicy, Branch, Foreach, Loop
5
7
  from .decorators import flow
6
8
  from ..data_map import Assignment, DataMap
@@ -14,7 +16,7 @@ __all__ = [
14
16
  "FlowContext",
15
17
  "TaskData",
16
18
  "TaskEventType",
17
- "DocumentContent",
19
+ "File",
18
20
 
19
21
  "DocProcNode",
20
22
  "UserNode",
@@ -23,6 +25,7 @@ __all__ = [
23
25
  "EndNode",
24
26
  "PromptNode",
25
27
  "ToolNode",
28
+ "DecisionsNode",
26
29
  "Assignment",
27
30
  "DataMap",
28
31
 
@@ -38,8 +41,8 @@ __all__ = [
38
41
  "Branch",
39
42
  "Foreach",
40
43
  "Loop",
44
+ "DecisionsCondition",
45
+ "DecisionsRule",
41
46
 
42
- "user",
43
- "flow_spec",
44
47
  "flow"
45
48
  ]
@@ -27,12 +27,11 @@ from ibm_watsonx_orchestrate.client.utils import instantiate_client
27
27
  from ..types import (
28
28
  EndNodeSpec, Expression, ForeachPolicy, ForeachSpec, LoopSpec, BranchNodeSpec, MatchPolicy, PromptLLMParameters, PromptNodeSpec,
29
29
  StartNodeSpec, ToolSpec, JsonSchemaObject, ToolRequestBody, ToolResponseBody, UserFieldKind, UserFieldOption, UserFlowSpec, UserNodeSpec, WaitPolicy,
30
- DocProcSpec, TextExtractionResponse, KVPInvoicesExtractionResponse, KVPUtilityBillsExtractionResponse,
31
- DocumentContent
30
+ DocProcSpec, TextExtractionResponse, File, DecisionsNodeSpec, DecisionsRule
32
31
  )
33
32
  from .constants import CURRENT_USER, START, END, ANY_USER
34
33
  from ..node import (
35
- EndNode, Node, PromptNode, StartNode, UserNode, AgentNode, DataMap, ToolNode, DocProcNode
34
+ EndNode, Node, PromptNode, StartNode, UserNode, AgentNode, DataMap, ToolNode, DocProcNode, DecisionsNode
36
35
  )
37
36
  from ..types import (
38
37
  AgentNodeSpec, extract_node_spec, FlowContext, FlowEventType, FlowEvent, FlowSpec,
@@ -434,6 +433,49 @@ class Flow(Node):
434
433
  node = self._add_node(node)
435
434
  return cast(PromptNode, node)
436
435
 
436
+ def decisions(self,
437
+ name: str,
438
+ display_name: str|None=None,
439
+ rules: list[DecisionsRule] | None = None,
440
+ default_actions: dict[str, Any] = None,
441
+ locale: str | None = None,
442
+ description: str | None = None,
443
+ input_schema: type[BaseModel]|None = None,
444
+ output_schema: type[BaseModel]|None=None,
445
+ input_map: DataMap = None) -> PromptNode:
446
+
447
+ if name is None:
448
+ raise ValueError("name must be provided.")
449
+
450
+ if rules is None:
451
+ raise ValueError("rules must be specified.")
452
+
453
+ # create input spec
454
+ input_schema_obj = _get_json_schema_obj(parameter_name = "input", type_def = input_schema)
455
+ output_schema_obj = _get_json_schema_obj("output", output_schema)
456
+
457
+ # Create the tool spec
458
+ task_spec = DecisionsNodeSpec(
459
+ name=name,
460
+ display_name=display_name if display_name is not None else name,
461
+ description=description,
462
+ rules=rules,
463
+ default_actions=default_actions,
464
+ locale=locale,
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
+ )
469
+
470
+ node = DecisionsNode(spec=task_spec)
471
+ # setup input map
472
+ if input_map:
473
+ node.input_map = self._get_data_map(input_map)
474
+
475
+ # add the node to the list of node
476
+ node = self._add_node(node)
477
+ return cast(DecisionsNode, node)
478
+
437
479
  def docproc(self,
438
480
  name: str,
439
481
  task: str,
@@ -448,12 +490,10 @@ class Flow(Node):
448
490
  raise ValueError("task must be provided.")
449
491
 
450
492
  output_schema_dict = {
451
- "text_extraction" : TextExtractionResponse,
452
- "kvp_invoices_extraction" : KVPInvoicesExtractionResponse,
453
- "kvp_utility_bills_extraction" : KVPUtilityBillsExtractionResponse
493
+ "text_extraction" : TextExtractionResponse
454
494
  }
455
495
  # create input spec
456
- input_schema_obj = _get_json_schema_obj(parameter_name = "input", type_def = DocumentContent)
496
+ input_schema_obj = _get_json_schema_obj(parameter_name = "input", type_def = File)
457
497
  output_schema_obj = _get_json_schema_obj("output", output_schema_dict[task])
458
498
  if "$defs" in output_schema_obj.model_extra:
459
499
  output_schema_obj.model_extra.pop("$defs")
@@ -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, DocProcSpec
8
+ from .types import EndNodeSpec, NodeSpec, AgentNodeSpec, PromptNodeSpec, StartNodeSpec, ToolNodeSpec, UserFieldKind, UserFieldOption, UserNodeSpec, DocProcSpec, DecisionsNodeSpec
9
9
  from .data_map import DataMap
10
10
 
11
11
  class Node(BaseModel):
@@ -116,7 +116,13 @@ class DocProcNode(Node):
116
116
 
117
117
  def get_spec(self) -> DocProcSpec:
118
118
  return cast(DocProcSpec, self.spec)
119
+ class DecisionsNode(Node):
120
+ def __repr__(self):
121
+ return f"DecisionsNode(name='{self.spec.name}', description='{self.spec.description}')"
119
122
 
123
+ def get_spec(self) -> DecisionsNodeSpec:
124
+ return cast(DecisionsNodeSpec, self.spec)
125
+
120
126
  class NodeInstance(BaseModel):
121
127
  node: Node
122
128
  id: str # unique id of this task instance
@@ -1,5 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
  from enum import Enum, StrEnum, auto
3
+ from datetime import date
4
+ import numbers
3
5
  import inspect
4
6
  import logging
5
7
  from typing import (
@@ -167,8 +169,6 @@ class DocProcTask(StrEnum):
167
169
  Possible names for the Document processing task parameter
168
170
  '''
169
171
  text_extraction = auto()
170
- kvp_invoices_extraction = auto()
171
- kvp_utility_bills_extraction = auto()
172
172
 
173
173
  class DocProcSpec(NodeSpec):
174
174
  task: DocProcTask = Field(description='The document processing operation name', default=DocProcTask.text_extraction)
@@ -515,6 +515,7 @@ class PromptNodeSpec(NodeSpec):
515
515
  model_spec["llm_parameters"] = self.llm_parameters.to_json()
516
516
 
517
517
  return model_spec
518
+
518
519
 
519
520
  class Expression(BaseModel):
520
521
  '''An expression could return a boolean or a value'''
@@ -743,7 +744,7 @@ class LanguageCode(StrEnum):
743
744
  fr = auto()
744
745
  en_hw = auto()
745
746
 
746
- class DocumentContent(BaseModel):
747
+ class File(BaseModel):
747
748
  '''
748
749
  This class represents the input of a Document processing task.
749
750
 
@@ -752,7 +753,7 @@ class DocumentContent(BaseModel):
752
753
  language (LanguageCode): Optional language code used when processing the input document
753
754
  '''
754
755
  # 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
+ # We need to use bytes here for Chat-with-doc to recognize the input as a File.
756
757
  document_ref: bytes | str = Field(
757
758
  description="Either an ID or a URL identifying the document to be used.",
758
759
  title='Document reference',
@@ -779,73 +780,175 @@ class TextExtractionResponse(BaseModel):
779
780
  '''
780
781
  output: TextExtraction = Field(description='The text extraction response')
781
782
 
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
783
 
784
+ class DecisionsCondition(BaseModel):
785
+ _condition: str | None = None
786
+
787
+ def greater_than(self, value: Union[numbers.Number, date, str]) -> Self:
788
+ self._check_type_is_number_or_date_or_str(value)
789
+ self._condition = f"> {self._format_value(value)}"
790
+ return self
791
+
792
+ def greater_than_or_equal(self, value: Union[numbers.Number, date, str]) -> Self:
793
+ self._check_type_is_number_or_date_or_str(value)
794
+ self._condition = f">= {self._format_value(value)}"
795
+ return self
796
+
797
+ def less_than(self, value: Union[numbers.Number, date, str]) -> Self:
798
+ self._check_type_is_number_or_date_or_str(value)
799
+ self._condition = f"< {self._format_value(value)}"
800
+ return self
801
+
802
+ def less_than_or_equal(self, value: Union[numbers.Number, date, str]) -> Self:
803
+ self._check_type_is_number_or_date_or_str(value)
804
+ self._condition = f"<= {self._format_value(value)}"
805
+ return self
818
806
 
819
- class UtilityBill(BaseModel):
807
+ def equal(self, value: Union[numbers.Number, date, str]) -> Self:
808
+ self._check_type_is_number_or_date_or_str(value)
809
+ self._condition = f"== {self._format_value(value)}"
810
+ return self
811
+
812
+ def not_equal(self, value: Union[numbers.Number, date, str]) -> Self:
813
+ self._check_type_is_number_or_date_or_str(value)
814
+ self._condition = f"== {self._format_value(value)}"
815
+ return self
816
+
817
+ def contains(self, value: str) -> Self:
818
+ self._check_type_is_str(value)
819
+ self._condition = f"contains {self._format_value(value)}"
820
+ return self
821
+
822
+ def not_contains(self, value: str) -> Self:
823
+ self._check_type_is_str(value)
824
+ self._condition = f"doesNotContain {self._format_value(value)}"
825
+ return self
826
+
827
+ def is_in(self, value: str) -> Self:
828
+ self._check_type_is_str(value)
829
+ self._condition = f"in {self._format_value(value)}"
830
+ return self
831
+
832
+ def is_not_in(self, value: str) -> Self:
833
+ self._check_type_is_str(value)
834
+ self._condition = f"notIn {self._format_value(value)}"
835
+ return self
836
+
837
+ def startswith(self, value: str) -> Self:
838
+ self._check_type_is_str(value)
839
+ self._condition = f"startsWith {self._format_value(value)}"
840
+ return self
841
+
842
+ def endswith(self, value: str) -> Self:
843
+ self._check_type_is_str(value)
844
+ self._condition = f"endsWith {self._format_value(value)}"
845
+ return self
846
+
847
+
848
+ def in_range(self, startValue: Union[numbers.Number, date], endValue: Union[numbers.Number, date],
849
+ startsInclusive: bool = False, endsInclusive: bool = False) -> Self:
850
+ self._check_type_is_number_or_date_or_str(startValue)
851
+ self._check_type_is_number_or_date_or_str(endValue)
852
+ if type(startValue) is not type(endValue):
853
+ raise TypeError("startValue and endValue must be of the same type")
854
+ start_op = "[" if startsInclusive else "(" # [ is inclusive, ( is exclusive
855
+ end_op = "]" if endsInclusive else ")"
856
+ self._condition = f"{start_op}{self._format_value(startValue)}:{self._format_value(endValue)}{end_op}"
857
+ return self
858
+
859
+ def _check_type_is_number_or_date(self, value: Union[numbers.Number, date]):
860
+ if not isinstance(value, (numbers.Number, date)):
861
+ raise TypeError("Value must be a number or a date")
862
+
863
+ def _check_type_is_number_or_date_or_str(self, value: Union[numbers.Number, date, str]):
864
+ if not isinstance(value, (numbers.Number, date, str)):
865
+ raise TypeError("Value must be a number or a date or a string")
866
+
867
+ def _check_type_is_str(self, value: str):
868
+ if not isinstance(value, str):
869
+ raise TypeError("Value must be a string")
870
+
871
+ @staticmethod
872
+ def _format_value(value: Union[numbers.Number, date, str]):
873
+ if isinstance(value, numbers.Number):
874
+ return f"{value}"
875
+ if isinstance(value, date):
876
+ return f"\"{value.strftime('%B %d, %Y')}\""
877
+ return f"\"{value}\""
878
+
879
+ def condition(self):
880
+ return self._condition
881
+
882
+
883
+
884
+ class DecisionsRule(BaseModel):
820
885
  '''
821
- This class represents the fields extracted by the "kvp_utility_bills_extraction" document processing (docproc) operation.
886
+ A set of decisions rules.
822
887
  '''
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):
888
+ _conditions: dict[str, str]
889
+ _actions: dict[str, Union[numbers.Number, str]]
890
+
891
+ def __init__(self, **data):
892
+ super().__init__(**data)
893
+ self._conditions = {}
894
+ self._actions = {}
895
+
896
+ def condition(self, key: str, cond: DecisionsCondition) -> Self:
897
+ self._conditions[key] = cond.condition()
898
+ return self
899
+
900
+ def action(self, key: str, value: Union[numbers.Number, date, str]) -> Self:
901
+ if isinstance(value, date):
902
+ self._actions[key] = value.strftime("%B %d, %Y")
903
+ return self
904
+ self._actions[key] = value
905
+ return self
906
+
907
+ def to_json(self) -> dict[str, Any]:
908
+ '''
909
+ Serialize the rules into JSON object
910
+ '''
911
+ model_spec = {}
912
+ if self._conditions:
913
+ model_spec["conditions"] = self._conditions
914
+ if self._actions:
915
+ model_spec["actions"] = self._actions
916
+ return model_spec
917
+
918
+
919
+ class DecisionsNodeSpec(NodeSpec):
840
920
  '''
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
921
+ Node specification for Decision Table
844
922
  '''
845
- output: UtilityBill = Field(
846
- title='Utility bill',
847
- description='The fields extracted from a utility bill document'
848
- )
923
+ locale: str | None = None
924
+ rules: list[DecisionsRule]
925
+ default_actions: dict[str, Union[int, float, complex, str]] | None
926
+
927
+ def __init__(self, **data):
928
+ super().__init__(**data)
929
+ self.kind = "decisions"
930
+
931
+ def default_action(self, key: str, value: Union[int, float, complex, date, str]) -> Self:
932
+ '''
933
+ create a new default action
934
+ '''
935
+ if isinstance(value, date):
936
+ self.default_actions[key] = value.strftime("%B %d, %Y")
937
+ return self
938
+ self.default_actions[key] = value
939
+ return self
940
+
941
+ def to_json(self) -> dict[str, Any]:
942
+ model_spec = super().to_json()
943
+ if self.locale:
944
+ model_spec["locale"] = self.locale
945
+ if self.rules:
946
+ model_spec["rules"] = [rule.to_json() for rule in self.rules]
947
+ if self.default_actions:
948
+ model_spec["default_actions"] = self.default_actions
949
+
950
+ return model_spec
951
+
849
952
 
850
953
  def extract_node_spec(
851
954
  fn: Callable | PythonTool,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ibm-watsonx-orchestrate
3
- Version: 1.8.0b0
3
+ Version: 1.8.0b1
4
4
  Summary: IBM watsonx.orchestrate SDK
5
5
  Author-email: IBM <support@ibm.com>
6
6
  License: MIT License
@@ -11,7 +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.24.2
14
- Requires-Dist: ibm-watsonx-orchestrate-evaluation-framework==1.0.6
14
+ Requires-Dist: ibm-watsonx-orchestrate-evaluation-framework==1.0.8
15
15
  Requires-Dist: jsonref==1.1.0
16
16
  Requires-Dist: jsonschema<5.0.0,>=4.23.0
17
17
  Requires-Dist: langchain-core<=0.3.63