ibm-watsonx-orchestrate 1.6.2__py3-none-any.whl → 1.6.4__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.
- ibm_watsonx_orchestrate/__init__.py +2 -1
- ibm_watsonx_orchestrate/agent_builder/agents/agent.py +3 -3
- ibm_watsonx_orchestrate/agent_builder/agents/assistant_agent.py +3 -2
- ibm_watsonx_orchestrate/agent_builder/agents/external_agent.py +3 -2
- ibm_watsonx_orchestrate/agent_builder/agents/types.py +38 -9
- ibm_watsonx_orchestrate/agent_builder/connections/connections.py +4 -3
- ibm_watsonx_orchestrate/agent_builder/connections/types.py +14 -2
- ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base_requests.py +1 -22
- ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +1 -17
- ibm_watsonx_orchestrate/agent_builder/tools/base_tool.py +2 -1
- ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py +75 -24
- ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py +136 -92
- ibm_watsonx_orchestrate/agent_builder/tools/types.py +17 -11
- ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +7 -7
- ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +7 -6
- ibm_watsonx_orchestrate/cli/commands/channels/types.py +15 -2
- ibm_watsonx_orchestrate/cli/commands/channels/webchat/channels_webchat_controller.py +35 -25
- ibm_watsonx_orchestrate/cli/commands/connections/connections_command.py +14 -6
- ibm_watsonx_orchestrate/cli/commands/connections/connections_controller.py +6 -8
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_command.py +65 -0
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_controller.py +368 -0
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_server_controller.py +170 -0
- ibm_watsonx_orchestrate/cli/commands/environment/environment_controller.py +5 -5
- ibm_watsonx_orchestrate/cli/commands/environment/types.py +3 -1
- ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_command.py +102 -37
- ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_controller.py +20 -2
- ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_command.py +0 -18
- ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +36 -20
- ibm_watsonx_orchestrate/cli/commands/models/models_command.py +1 -1
- ibm_watsonx_orchestrate/cli/commands/models/models_controller.py +5 -8
- ibm_watsonx_orchestrate/cli/commands/server/server_command.py +94 -36
- ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_command.py +1 -1
- ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +11 -4
- ibm_watsonx_orchestrate/cli/config.py +3 -3
- ibm_watsonx_orchestrate/cli/init_helper.py +10 -1
- ibm_watsonx_orchestrate/cli/main.py +5 -0
- ibm_watsonx_orchestrate/client/base_api_client.py +12 -0
- ibm_watsonx_orchestrate/client/connections/connections_client.py +5 -30
- ibm_watsonx_orchestrate/client/copilot/cpe/copilot_cpe_client.py +67 -0
- ibm_watsonx_orchestrate/client/knowledge_bases/knowledge_base_client.py +1 -1
- ibm_watsonx_orchestrate/client/local_service_instance.py +3 -1
- ibm_watsonx_orchestrate/client/service_instance.py +33 -7
- ibm_watsonx_orchestrate/client/utils.py +49 -8
- ibm_watsonx_orchestrate/docker/compose-lite.yml +198 -6
- ibm_watsonx_orchestrate/docker/default.env +36 -12
- ibm_watsonx_orchestrate/flow_builder/flows/__init__.py +9 -4
- ibm_watsonx_orchestrate/flow_builder/flows/decorators.py +4 -2
- ibm_watsonx_orchestrate/flow_builder/flows/events.py +10 -9
- ibm_watsonx_orchestrate/flow_builder/flows/flow.py +131 -20
- ibm_watsonx_orchestrate/flow_builder/node.py +18 -1
- ibm_watsonx_orchestrate/flow_builder/types.py +271 -16
- ibm_watsonx_orchestrate/flow_builder/utils.py +120 -6
- ibm_watsonx_orchestrate/utils/exceptions.py +23 -0
- {ibm_watsonx_orchestrate-1.6.2.dist-info → ibm_watsonx_orchestrate-1.6.4.dist-info}/METADATA +3 -7
- {ibm_watsonx_orchestrate-1.6.2.dist-info → ibm_watsonx_orchestrate-1.6.4.dist-info}/RECORD +58 -55
- ibm_watsonx_orchestrate/agent_builder/utils/pydantic_utils.py +0 -149
- ibm_watsonx_orchestrate/flow_builder/resources/flow_status.openapi.yml +0 -66
- {ibm_watsonx_orchestrate-1.6.2.dist-info → ibm_watsonx_orchestrate-1.6.4.dist-info}/WHEEL +0 -0
- {ibm_watsonx_orchestrate-1.6.2.dist-info → ibm_watsonx_orchestrate-1.6.4.dist-info}/entry_points.txt +0 -0
- {ibm_watsonx_orchestrate-1.6.2.dist-info → ibm_watsonx_orchestrate-1.6.4.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,12 @@ 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, File, DecisionsNodeSpec, DecisionsRule
|
29
31
|
)
|
30
32
|
from .constants import CURRENT_USER, START, END, ANY_USER
|
31
33
|
from ..node import (
|
32
|
-
EndNode, Node, PromptNode, StartNode, UserNode, AgentNode, DataMap, ToolNode
|
34
|
+
EndNode, Node, PromptNode, StartNode, UserNode, AgentNode, DataMap, ToolNode, DocProcNode, DecisionsNode
|
33
35
|
)
|
34
36
|
from ..types import (
|
35
37
|
AgentNodeSpec, extract_node_spec, FlowContext, FlowEventType, FlowEvent, FlowSpec,
|
@@ -114,7 +116,7 @@ class Flow(Node):
|
|
114
116
|
# pydantic suppport nested comparison by default
|
115
117
|
|
116
118
|
schema.title = title
|
117
|
-
|
119
|
+
|
118
120
|
if schema == existing_schema:
|
119
121
|
return existing_schema
|
120
122
|
# we need to do a deep compare
|
@@ -354,6 +356,7 @@ class Flow(Node):
|
|
354
356
|
name: str,
|
355
357
|
agent: str,
|
356
358
|
display_name: str|None=None,
|
359
|
+
title: str | None = None,
|
357
360
|
message: str | None = "Follow the agent instructions.",
|
358
361
|
description: str | None = None,
|
359
362
|
input_schema: type[BaseModel]|None = None,
|
@@ -371,6 +374,7 @@ class Flow(Node):
|
|
371
374
|
display_name=display_name,
|
372
375
|
description=description,
|
373
376
|
agent=agent,
|
377
|
+
title=title,
|
374
378
|
message=message,
|
375
379
|
guidelines=guidelines,
|
376
380
|
input_schema=_get_tool_request_body(input_schema_obj),
|
@@ -428,6 +432,91 @@ class Flow(Node):
|
|
428
432
|
# add the node to the list of node
|
429
433
|
node = self._add_node(node)
|
430
434
|
return cast(PromptNode, node)
|
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
|
+
|
479
|
+
def docproc(self,
|
480
|
+
name: str,
|
481
|
+
task: str,
|
482
|
+
display_name: str|None=None,
|
483
|
+
description: str | None = None,
|
484
|
+
input_map: DataMap = None) -> DocProcNode:
|
485
|
+
|
486
|
+
if name is None :
|
487
|
+
raise ValueError("name must be provided.")
|
488
|
+
|
489
|
+
if task is None:
|
490
|
+
raise ValueError("task must be provided.")
|
491
|
+
|
492
|
+
output_schema_dict = {
|
493
|
+
"text_extraction" : TextExtractionResponse
|
494
|
+
}
|
495
|
+
# create input spec
|
496
|
+
input_schema_obj = _get_json_schema_obj(parameter_name = "input", type_def = File)
|
497
|
+
output_schema_obj = _get_json_schema_obj("output", output_schema_dict[task])
|
498
|
+
if "$defs" in output_schema_obj.model_extra:
|
499
|
+
output_schema_obj.model_extra.pop("$defs")
|
500
|
+
# Create the docproc spec
|
501
|
+
task_spec = DocProcSpec(
|
502
|
+
name=name,
|
503
|
+
display_name=display_name if display_name is not None else name,
|
504
|
+
description=description,
|
505
|
+
input_schema=_get_tool_request_body(input_schema_obj),
|
506
|
+
output_schema=_get_tool_response_body(output_schema_obj),
|
507
|
+
output_schema_object = output_schema_obj,
|
508
|
+
task=task
|
509
|
+
)
|
510
|
+
|
511
|
+
node = DocProcNode(spec=task_spec)
|
512
|
+
# setup input map
|
513
|
+
if input_map:
|
514
|
+
node.input_map = self._get_data_map(input_map)
|
515
|
+
|
516
|
+
# add the node to the list of node
|
517
|
+
node = self._add_node(node)
|
518
|
+
return cast(DocProcNode, node)
|
519
|
+
|
431
520
|
|
432
521
|
def node_exists(self, node: Union[str, Node]):
|
433
522
|
|
@@ -921,7 +1010,8 @@ class FlowFactory(BaseModel):
|
|
921
1010
|
description: str|None=None,
|
922
1011
|
initiators: Sequence[str]|None=None,
|
923
1012
|
input_schema: type[BaseModel]|None=None,
|
924
|
-
output_schema: type[BaseModel]|None=None
|
1013
|
+
output_schema: type[BaseModel]|None=None,
|
1014
|
+
schedulable: bool=False) -> Flow:
|
925
1015
|
if isinstance(name, Callable):
|
926
1016
|
flow_spec = getattr(name, "__flow_spec__", None)
|
927
1017
|
if not flow_spec:
|
@@ -942,7 +1032,8 @@ class FlowFactory(BaseModel):
|
|
942
1032
|
initiators=initiators,
|
943
1033
|
input_schema=_get_tool_request_body(input_schema_obj),
|
944
1034
|
output_schema=_get_tool_response_body(output_schema_obj),
|
945
|
-
output_schema_object = output_schema_obj
|
1035
|
+
output_schema_object = output_schema_obj,
|
1036
|
+
schedulable=schedulable,
|
946
1037
|
)
|
947
1038
|
|
948
1039
|
return Flow(spec = flow_spec)
|
@@ -1228,10 +1319,12 @@ class UserFlow(Flow):
|
|
1228
1319
|
kind: UserFieldKind = UserFieldKind.Text,
|
1229
1320
|
display_name: str | None = None,
|
1230
1321
|
description: str | None = None,
|
1231
|
-
owners: list[str] = [],
|
1232
1322
|
default: Any | None = None,
|
1233
|
-
text: str = None,
|
1323
|
+
text: str = None, # The text used to ask question to the user, e.g. 'what is your name?'
|
1234
1324
|
option: UserFieldOption | None = None,
|
1325
|
+
is_list: bool = False,
|
1326
|
+
min: Any | None = None,
|
1327
|
+
max: Any | None = None,
|
1235
1328
|
input_map: DataMap = None,
|
1236
1329
|
custom: dict[str, Any] = {}) -> UserNode:
|
1237
1330
|
'''create a node in the flow'''
|
@@ -1246,20 +1339,42 @@ class UserFlow(Flow):
|
|
1246
1339
|
schema_obj.properties = {}
|
1247
1340
|
schema_obj.properties[name] = UserFieldKind.convert_kind_to_schema_property(kind, name, description, default, option, custom)
|
1248
1341
|
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1342
|
+
task_spec = UserNodeSpec(
|
1343
|
+
name=name,
|
1344
|
+
display_name=display_name,
|
1345
|
+
description=description,
|
1346
|
+
owners=[CURRENT_USER],
|
1347
|
+
input_schema=_get_tool_request_body(schema_obj),
|
1348
|
+
output_schema=_get_tool_response_body(schema_obj),
|
1349
|
+
text=text,
|
1350
|
+
output_schema_object = schema_obj
|
1351
|
+
)
|
1352
|
+
|
1353
|
+
node = UserNode(spec = task_spec)
|
1354
|
+
node.field(name = name,
|
1355
|
+
kind = kind,
|
1356
|
+
display_name = display_name,
|
1357
|
+
description = description,
|
1358
|
+
default = default,
|
1359
|
+
text = text,
|
1360
|
+
option = option,
|
1361
|
+
is_list = is_list,
|
1362
|
+
min = min,
|
1363
|
+
max = max,
|
1364
|
+
custom = custom)
|
1365
|
+
|
1366
|
+
# setup input map
|
1367
|
+
if input_map:
|
1368
|
+
node.input_map = self._get_data_map(input_map)
|
1369
|
+
|
1370
|
+
node = self._add_node(node)
|
1371
|
+
return cast(UserNode, node)
|
1256
1372
|
|
1257
1373
|
def user(
|
1258
1374
|
self,
|
1259
1375
|
name: str | None = None,
|
1260
1376
|
display_name: str | None = None,
|
1261
1377
|
description: str | None = None,
|
1262
|
-
owners: list[str] = [],
|
1263
1378
|
text: str | None = None,
|
1264
1379
|
output_schema: type[BaseModel] | JsonSchemaObject| None = None,
|
1265
1380
|
input_map: DataMap = None,
|
@@ -1273,16 +1388,12 @@ class UserFlow(Flow):
|
|
1273
1388
|
# input and output is always the same in an user node
|
1274
1389
|
output_schema_obj = output_schema_obj
|
1275
1390
|
|
1276
|
-
# identify owner
|
1277
|
-
if not owners:
|
1278
|
-
owners = [ANY_USER]
|
1279
|
-
|
1280
1391
|
# Create the tool spec
|
1281
1392
|
task_spec = UserNodeSpec(
|
1282
1393
|
name=name,
|
1283
1394
|
display_name=display_name,
|
1284
1395
|
description=description,
|
1285
|
-
owners=
|
1396
|
+
owners=[CURRENT_USER],
|
1286
1397
|
input_schema=_get_tool_request_body(output_schema_obj),
|
1287
1398
|
output_schema=_get_tool_response_body(output_schema_obj),
|
1288
1399
|
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, DecisionsNodeSpec
|
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,7 +109,20 @@ 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}')"
|
108
116
|
|
117
|
+
def get_spec(self) -> DocProcSpec:
|
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}')"
|
122
|
+
|
123
|
+
def get_spec(self) -> DecisionsNodeSpec:
|
124
|
+
return cast(DecisionsNodeSpec, self.spec)
|
125
|
+
|
109
126
|
class NodeInstance(BaseModel):
|
110
127
|
node: Node
|
111
128
|
id: str # unique id of this task instance
|
@@ -1,5 +1,7 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
|
-
from enum import Enum
|
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 (
|
@@ -7,7 +9,6 @@ from typing import (
|
|
7
9
|
)
|
8
10
|
|
9
11
|
import docstring_parser
|
10
|
-
from munch import Munch
|
11
12
|
from pydantic import BaseModel, Field
|
12
13
|
|
13
14
|
from langchain_core.tools.base import create_schema_from_function
|
@@ -116,7 +117,7 @@ def _to_json_from_output_schema(schema: Union[ToolResponseBody, SchemaRef]) -> d
|
|
116
117
|
return model_spec
|
117
118
|
|
118
119
|
class NodeSpec(BaseModel):
|
119
|
-
kind: Literal["node", "tool", "user", "agent", "flow", "start", "decisions", "prompt", "branch", "wait", "foreach", "loop", "userflow", "end"] = "node"
|
120
|
+
kind: Literal["node", "tool", "user", "agent", "flow", "start", "decisions", "prompt", "branch", "wait", "foreach", "loop", "userflow", "end", "docproc" ] = "node"
|
120
121
|
name: str
|
121
122
|
display_name: str | None = None
|
122
123
|
description: str | None = None
|
@@ -162,6 +163,24 @@ class NodeSpec(BaseModel):
|
|
162
163
|
|
163
164
|
return model_spec
|
164
165
|
|
166
|
+
class DocProcTask(StrEnum):
|
167
|
+
'''
|
168
|
+
Possible names for the Document processing task parameter
|
169
|
+
'''
|
170
|
+
text_extraction = auto()
|
171
|
+
|
172
|
+
class DocProcSpec(NodeSpec):
|
173
|
+
task: DocProcTask = Field(description='The document processing operation name', default=DocProcTask.text_extraction)
|
174
|
+
|
175
|
+
def __init__(self, **data):
|
176
|
+
super().__init__(**data)
|
177
|
+
self.kind = "docproc"
|
178
|
+
|
179
|
+
def to_json(self) -> dict[str, Any]:
|
180
|
+
model_spec = super().to_json()
|
181
|
+
model_spec["task"] = self.task
|
182
|
+
return model_spec
|
183
|
+
|
165
184
|
class StartNodeSpec(NodeSpec):
|
166
185
|
def __init__(self, **data):
|
167
186
|
super().__init__(**data)
|
@@ -296,6 +315,8 @@ class UserField(BaseModel):
|
|
296
315
|
description: str | None = None
|
297
316
|
default: Any | None = None
|
298
317
|
option: UserFieldOption | None = None
|
318
|
+
min: Any | None = None,
|
319
|
+
max: Any | None = None,
|
299
320
|
is_list: bool = False
|
300
321
|
custom: dict[str, Any] | None = None
|
301
322
|
widget: str | None = None
|
@@ -314,6 +335,10 @@ class UserField(BaseModel):
|
|
314
335
|
model_spec["description"] = self.description
|
315
336
|
if self.default:
|
316
337
|
model_spec["default"] = self.default
|
338
|
+
if self.min:
|
339
|
+
model_spec["min"] = self.min
|
340
|
+
if self.max:
|
341
|
+
model_spec["min"] = self.max
|
317
342
|
if self.is_list:
|
318
343
|
model_spec["is_list"] = self.is_list
|
319
344
|
if self.option:
|
@@ -356,7 +381,10 @@ class UserNodeSpec(NodeSpec):
|
|
356
381
|
display_name: str | None = None,
|
357
382
|
description: str | None = None,
|
358
383
|
default: Any | None = None,
|
359
|
-
option: list[str] | None = None,
|
384
|
+
option: list[str] | None = None,
|
385
|
+
min: Any | None = None,
|
386
|
+
max: Any | None = None,
|
387
|
+
is_list: bool = False,
|
360
388
|
custom: dict[str, Any] | None = None,
|
361
389
|
widget: str | None = None):
|
362
390
|
userfield = UserField(name=name,
|
@@ -366,6 +394,8 @@ class UserNodeSpec(NodeSpec):
|
|
366
394
|
description=description,
|
367
395
|
default=default,
|
368
396
|
option=option,
|
397
|
+
min=min,
|
398
|
+
max=max,
|
369
399
|
is_list=is_list,
|
370
400
|
custom=custom,
|
371
401
|
widget=widget)
|
@@ -402,6 +432,8 @@ class UserNodeSpec(NodeSpec):
|
|
402
432
|
default=prop_schema.default,
|
403
433
|
option=self.setup_field_options(prop_schema.title, prop_schema.enum),
|
404
434
|
is_list=prop_schema.type == "array",
|
435
|
+
min=prop_schema.minimum,
|
436
|
+
max=prop_schema.maximum,
|
405
437
|
custom=prop_schema.model_extra))
|
406
438
|
|
407
439
|
def setup_field_options(self, name: str, enums: List[str]) -> UserFieldOption:
|
@@ -415,6 +447,7 @@ class UserNodeSpec(NodeSpec):
|
|
415
447
|
|
416
448
|
class AgentNodeSpec(ToolNodeSpec):
|
417
449
|
message: str | None = Field(default=None, description="The instructions for the task.")
|
450
|
+
title: str | None = Field(default=None, description="The title of the message.")
|
418
451
|
guidelines: str | None = Field(default=None, description="The guidelines for the task.")
|
419
452
|
agent: str
|
420
453
|
|
@@ -430,6 +463,8 @@ class AgentNodeSpec(ToolNodeSpec):
|
|
430
463
|
model_spec["guidelines"] = self.guidelines
|
431
464
|
if self.agent:
|
432
465
|
model_spec["agent"] = self.agent
|
466
|
+
if self.title:
|
467
|
+
model_spec["title"] = self.title
|
433
468
|
return model_spec
|
434
469
|
|
435
470
|
class PromptLLMParameters(BaseModel):
|
@@ -479,6 +514,7 @@ class PromptNodeSpec(NodeSpec):
|
|
479
514
|
model_spec["llm_parameters"] = self.llm_parameters.to_json()
|
480
515
|
|
481
516
|
return model_spec
|
517
|
+
|
482
518
|
|
483
519
|
class Expression(BaseModel):
|
484
520
|
'''An expression could return a boolean or a value'''
|
@@ -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,227 @@ class Assignment(BaseModel):
|
|
691
728
|
e.g. "node.input.name" or "=f'{node.output.name}_{node.output.id}'"
|
692
729
|
|
693
730
|
'''
|
694
|
-
|
695
|
-
|
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 File(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 File.
|
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
|
+
|
783
|
+
class DecisionsCondition(BaseModel):
|
784
|
+
_condition: str | None = None
|
785
|
+
|
786
|
+
def greater_than(self, value: Union[numbers.Number, date, str]) -> Self:
|
787
|
+
self._check_type_is_number_or_date_or_str(value)
|
788
|
+
self._condition = f"> {self._format_value(value)}"
|
789
|
+
return self
|
790
|
+
|
791
|
+
def greater_than_or_equal(self, value: Union[numbers.Number, date, str]) -> Self:
|
792
|
+
self._check_type_is_number_or_date_or_str(value)
|
793
|
+
self._condition = f">= {self._format_value(value)}"
|
794
|
+
return self
|
795
|
+
|
796
|
+
def less_than(self, value: Union[numbers.Number, date, str]) -> Self:
|
797
|
+
self._check_type_is_number_or_date_or_str(value)
|
798
|
+
self._condition = f"< {self._format_value(value)}"
|
799
|
+
return self
|
800
|
+
|
801
|
+
def less_than_or_equal(self, value: Union[numbers.Number, date, str]) -> Self:
|
802
|
+
self._check_type_is_number_or_date_or_str(value)
|
803
|
+
self._condition = f"<= {self._format_value(value)}"
|
804
|
+
return self
|
805
|
+
|
806
|
+
def equal(self, value: Union[numbers.Number, date, str]) -> Self:
|
807
|
+
self._check_type_is_number_or_date_or_str(value)
|
808
|
+
self._condition = f"== {self._format_value(value)}"
|
809
|
+
return self
|
810
|
+
|
811
|
+
def not_equal(self, value: Union[numbers.Number, date, str]) -> Self:
|
812
|
+
self._check_type_is_number_or_date_or_str(value)
|
813
|
+
self._condition = f"== {self._format_value(value)}"
|
814
|
+
return self
|
815
|
+
|
816
|
+
def contains(self, value: str) -> Self:
|
817
|
+
self._check_type_is_str(value)
|
818
|
+
self._condition = f"contains {self._format_value(value)}"
|
819
|
+
return self
|
820
|
+
|
821
|
+
def not_contains(self, value: str) -> Self:
|
822
|
+
self._check_type_is_str(value)
|
823
|
+
self._condition = f"doesNotContain {self._format_value(value)}"
|
824
|
+
return self
|
825
|
+
|
826
|
+
def is_in(self, value: str) -> Self:
|
827
|
+
self._check_type_is_str(value)
|
828
|
+
self._condition = f"in {self._format_value(value)}"
|
829
|
+
return self
|
830
|
+
|
831
|
+
def is_not_in(self, value: str) -> Self:
|
832
|
+
self._check_type_is_str(value)
|
833
|
+
self._condition = f"notIn {self._format_value(value)}"
|
834
|
+
return self
|
835
|
+
|
836
|
+
def startswith(self, value: str) -> Self:
|
837
|
+
self._check_type_is_str(value)
|
838
|
+
self._condition = f"startsWith {self._format_value(value)}"
|
839
|
+
return self
|
840
|
+
|
841
|
+
def endswith(self, value: str) -> Self:
|
842
|
+
self._check_type_is_str(value)
|
843
|
+
self._condition = f"endsWith {self._format_value(value)}"
|
844
|
+
return self
|
845
|
+
|
846
|
+
|
847
|
+
def in_range(self, startValue: Union[numbers.Number, date], endValue: Union[numbers.Number, date],
|
848
|
+
startsInclusive: bool = False, endsInclusive: bool = False) -> Self:
|
849
|
+
self._check_type_is_number_or_date_or_str(startValue)
|
850
|
+
self._check_type_is_number_or_date_or_str(endValue)
|
851
|
+
if type(startValue) is not type(endValue):
|
852
|
+
raise TypeError("startValue and endValue must be of the same type")
|
853
|
+
start_op = "[" if startsInclusive else "(" # [ is inclusive, ( is exclusive
|
854
|
+
end_op = "]" if endsInclusive else ")"
|
855
|
+
self._condition = f"{start_op}{self._format_value(startValue)}:{self._format_value(endValue)}{end_op}"
|
856
|
+
return self
|
857
|
+
|
858
|
+
def _check_type_is_number_or_date(self, value: Union[numbers.Number, date]):
|
859
|
+
if not isinstance(value, (numbers.Number, date)):
|
860
|
+
raise TypeError("Value must be a number or a date")
|
861
|
+
|
862
|
+
def _check_type_is_number_or_date_or_str(self, value: Union[numbers.Number, date, str]):
|
863
|
+
if not isinstance(value, (numbers.Number, date, str)):
|
864
|
+
raise TypeError("Value must be a number or a date or a string")
|
865
|
+
|
866
|
+
def _check_type_is_str(self, value: str):
|
867
|
+
if not isinstance(value, str):
|
868
|
+
raise TypeError("Value must be a string")
|
869
|
+
|
870
|
+
@staticmethod
|
871
|
+
def _format_value(value: Union[numbers.Number, date, str]):
|
872
|
+
if isinstance(value, numbers.Number):
|
873
|
+
return f"{value}"
|
874
|
+
if isinstance(value, date):
|
875
|
+
return f"\"{value.strftime('%B %d, %Y')}\""
|
876
|
+
return f"\"{value}\""
|
877
|
+
|
878
|
+
def condition(self):
|
879
|
+
return self._condition
|
880
|
+
|
881
|
+
|
882
|
+
|
883
|
+
class DecisionsRule(BaseModel):
|
884
|
+
'''
|
885
|
+
A set of decisions rules.
|
886
|
+
'''
|
887
|
+
_conditions: dict[str, str]
|
888
|
+
_actions: dict[str, Union[numbers.Number, str]]
|
889
|
+
|
890
|
+
def __init__(self, **data):
|
891
|
+
super().__init__(**data)
|
892
|
+
self._conditions = {}
|
893
|
+
self._actions = {}
|
894
|
+
|
895
|
+
def condition(self, key: str, cond: DecisionsCondition) -> Self:
|
896
|
+
self._conditions[key] = cond.condition()
|
897
|
+
return self
|
696
898
|
|
899
|
+
def action(self, key: str, value: Union[numbers.Number, date, str]) -> Self:
|
900
|
+
if isinstance(value, date):
|
901
|
+
self._actions[key] = value.strftime("%B %d, %Y")
|
902
|
+
return self
|
903
|
+
self._actions[key] = value
|
904
|
+
return self
|
905
|
+
|
906
|
+
def to_json(self) -> dict[str, Any]:
|
907
|
+
'''
|
908
|
+
Serialize the rules into JSON object
|
909
|
+
'''
|
910
|
+
model_spec = {}
|
911
|
+
if self._conditions:
|
912
|
+
model_spec["conditions"] = self._conditions
|
913
|
+
if self._actions:
|
914
|
+
model_spec["actions"] = self._actions
|
915
|
+
return model_spec
|
916
|
+
|
917
|
+
|
918
|
+
class DecisionsNodeSpec(NodeSpec):
|
919
|
+
'''
|
920
|
+
Node specification for Decision Table
|
921
|
+
'''
|
922
|
+
locale: str | None = None
|
923
|
+
rules: list[DecisionsRule]
|
924
|
+
default_actions: dict[str, Union[int, float, complex, str]] | None
|
925
|
+
|
926
|
+
def __init__(self, **data):
|
927
|
+
super().__init__(**data)
|
928
|
+
self.kind = "decisions"
|
929
|
+
|
930
|
+
def default_action(self, key: str, value: Union[int, float, complex, date, str]) -> Self:
|
931
|
+
'''
|
932
|
+
create a new default action
|
933
|
+
'''
|
934
|
+
if isinstance(value, date):
|
935
|
+
self.default_actions[key] = value.strftime("%B %d, %Y")
|
936
|
+
return self
|
937
|
+
self.default_actions[key] = value
|
938
|
+
return self
|
939
|
+
|
940
|
+
def to_json(self) -> dict[str, Any]:
|
941
|
+
model_spec = super().to_json()
|
942
|
+
if self.locale:
|
943
|
+
model_spec["locale"] = self.locale
|
944
|
+
if self.rules:
|
945
|
+
model_spec["rules"] = [rule.to_json() for rule in self.rules]
|
946
|
+
if self.default_actions:
|
947
|
+
model_spec["default_actions"] = self.default_actions
|
948
|
+
|
949
|
+
return model_spec
|
950
|
+
|
951
|
+
|
697
952
|
def extract_node_spec(
|
698
953
|
fn: Callable | PythonTool,
|
699
954
|
name: Optional[str] = None,
|