uipath-langchain 0.0.140__py3-none-any.whl → 0.0.142__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.
Potentially problematic release.
This version of uipath-langchain might be problematic. Click here for more details.
- uipath_langchain/_cli/_runtime/_context.py +1 -7
- uipath_langchain/_cli/_runtime/_input.py +125 -117
- uipath_langchain/_cli/_runtime/_output.py +111 -197
- uipath_langchain/_cli/_runtime/_runtime.py +376 -150
- uipath_langchain/_cli/cli_debug.py +95 -0
- uipath_langchain/_cli/cli_init.py +134 -2
- uipath_langchain/_cli/cli_run.py +30 -17
- uipath_langchain/_resources/AGENTS.md +21 -0
- uipath_langchain/_resources/REQUIRED_STRUCTURE.md +92 -0
- uipath_langchain/middlewares.py +2 -0
- uipath_langchain/py.typed +0 -0
- uipath_langchain/tools/preconfigured.py +2 -2
- {uipath_langchain-0.0.140.dist-info → uipath_langchain-0.0.142.dist-info}/METADATA +2 -2
- {uipath_langchain-0.0.140.dist-info → uipath_langchain-0.0.142.dist-info}/RECORD +17 -13
- {uipath_langchain-0.0.140.dist-info → uipath_langchain-0.0.142.dist-info}/WHEEL +0 -0
- {uipath_langchain-0.0.140.dist-info → uipath_langchain-0.0.142.dist-info}/entry_points.txt +0 -0
- {uipath_langchain-0.0.140.dist-info → uipath_langchain-0.0.142.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Optional
|
|
2
2
|
|
|
3
3
|
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
|
|
4
4
|
from uipath._cli._runtime._contracts import UiPathRuntimeContext
|
|
@@ -7,10 +7,4 @@ from uipath._cli._runtime._contracts import UiPathRuntimeContext
|
|
|
7
7
|
class LangGraphRuntimeContext(UiPathRuntimeContext):
|
|
8
8
|
"""Context information passed throughout the runtime execution."""
|
|
9
9
|
|
|
10
|
-
output: Optional[Any] = None
|
|
11
|
-
state: Optional[Any] = (
|
|
12
|
-
None # TypedDict issue, the actual type is: Optional[langgraph.types.StateSnapshot]
|
|
13
|
-
)
|
|
14
10
|
memory: Optional[AsyncSqliteSaver] = None
|
|
15
|
-
langsmith_tracing_enabled: Union[str, bool, None] = False
|
|
16
|
-
resume_triggers_table: str = "__uipath_resume_triggers"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from typing import Any, Optional, cast
|
|
3
3
|
|
|
4
|
+
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
|
|
4
5
|
from langgraph.types import Command
|
|
5
6
|
from uipath._cli._runtime._contracts import (
|
|
6
7
|
UiPathApiTrigger,
|
|
@@ -17,123 +18,130 @@ from ._exception import LangGraphRuntimeError
|
|
|
17
18
|
logger = logging.getLogger(__name__)
|
|
18
19
|
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
async def get_graph_input(
|
|
22
|
+
context: LangGraphRuntimeContext,
|
|
23
|
+
memory: AsyncSqliteSaver,
|
|
24
|
+
resume_triggers_table: str = "__uipath_resume_triggers",
|
|
25
|
+
) -> Any:
|
|
21
26
|
"""
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
Process the input data for graph execution, handling both fresh starts and resume scenarios.
|
|
28
|
+
|
|
29
|
+
This method determines whether the graph is being executed fresh or resumed from a previous state.
|
|
30
|
+
For fresh executions, it returns the input JSON directly. For resume scenarios, it fetches
|
|
31
|
+
the latest trigger information from the database and constructs a Command object with the
|
|
32
|
+
appropriate resume data.
|
|
33
|
+
|
|
34
|
+
The method handles different types of resume triggers:
|
|
35
|
+
- API triggers: Creates an UiPathApiTrigger with inbox_id and request payload
|
|
36
|
+
- Other triggers: Uses the HitlReader to process the resume data
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
context: The runtime context for the graph execution.
|
|
40
|
+
memory: AsyncSqliteSaver. The async database saver used to fetch resume trigger data.
|
|
41
|
+
resume_triggers_table: str, optional. The name of the database table containing resume triggers (default: "__uipath_resume_triggers").
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Any: For fresh executions, returns the input JSON data directly.
|
|
45
|
+
For resume scenarios, returns a Command object containing the resume data
|
|
46
|
+
processed through the appropriate trigger handler.
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
LangGraphRuntimeError: If there's an error fetching trigger data from the database
|
|
50
|
+
during resume processing.
|
|
24
51
|
"""
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if not self.context.resume:
|
|
60
|
-
if self.context.input_message:
|
|
61
|
-
return {
|
|
62
|
-
"messages": uipath_to_human_messages(self.context.input_message)
|
|
63
|
-
}
|
|
64
|
-
return self.context.input_json
|
|
65
|
-
|
|
66
|
-
if self.context.input_json:
|
|
67
|
-
return Command(resume=self.context.input_json)
|
|
68
|
-
|
|
69
|
-
trigger = await self._get_latest_trigger()
|
|
70
|
-
if not trigger:
|
|
71
|
-
return Command(resume=self.context.input_json)
|
|
72
|
-
|
|
73
|
-
trigger_type, key, folder_path, folder_key, payload = trigger
|
|
74
|
-
resume_trigger = UiPathResumeTrigger(
|
|
75
|
-
trigger_type=trigger_type,
|
|
76
|
-
item_key=key,
|
|
77
|
-
folder_path=folder_path,
|
|
78
|
-
folder_key=folder_key,
|
|
79
|
-
payload=payload,
|
|
52
|
+
logger.debug(f"Resumed: {context.resume} Input: {context.input_json}")
|
|
53
|
+
|
|
54
|
+
# Fresh execution - return input directly
|
|
55
|
+
if not context.resume:
|
|
56
|
+
if context.input_message:
|
|
57
|
+
return {"messages": uipath_to_human_messages(context.input_message)}
|
|
58
|
+
return context.input_json
|
|
59
|
+
|
|
60
|
+
# Resume with explicit input provided
|
|
61
|
+
if context.input_json:
|
|
62
|
+
return Command(resume=context.input_json)
|
|
63
|
+
|
|
64
|
+
# Resume from database trigger
|
|
65
|
+
trigger = await _get_latest_trigger(
|
|
66
|
+
memory, resume_triggers_table=resume_triggers_table
|
|
67
|
+
)
|
|
68
|
+
if not trigger:
|
|
69
|
+
return Command(resume=context.input_json)
|
|
70
|
+
|
|
71
|
+
trigger_type, key, folder_path, folder_key, payload = trigger
|
|
72
|
+
resume_trigger = UiPathResumeTrigger(
|
|
73
|
+
trigger_type=trigger_type,
|
|
74
|
+
item_key=key,
|
|
75
|
+
folder_path=folder_path,
|
|
76
|
+
folder_key=folder_key,
|
|
77
|
+
payload=payload,
|
|
78
|
+
)
|
|
79
|
+
logger.debug(f"ResumeTrigger: {trigger_type} {key}")
|
|
80
|
+
|
|
81
|
+
# Populate back expected fields for api_triggers
|
|
82
|
+
if resume_trigger.trigger_type == UiPathResumeTriggerType.API:
|
|
83
|
+
resume_trigger.api_resume = UiPathApiTrigger(
|
|
84
|
+
inbox_id=resume_trigger.item_key, request=resume_trigger.payload
|
|
80
85
|
)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
86
|
+
|
|
87
|
+
return Command(resume=await HitlReader.read(resume_trigger))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
async def _get_latest_trigger(
|
|
91
|
+
memory: AsyncSqliteSaver,
|
|
92
|
+
resume_triggers_table: str = "__uipath_resume_triggers",
|
|
93
|
+
) -> Optional[tuple[str, str, str, str, str]]:
|
|
94
|
+
"""
|
|
95
|
+
Fetch the most recent resume trigger from the database.
|
|
96
|
+
|
|
97
|
+
This private method queries the resume triggers table to retrieve the latest trigger
|
|
98
|
+
information based on timestamp. It handles database connection setup and executes
|
|
99
|
+
a SQL query to fetch trigger data needed for resume operations.
|
|
100
|
+
|
|
101
|
+
The method returns trigger information as a tuple containing:
|
|
102
|
+
- type: The type of trigger (e.g., 'API', 'MANUAL', etc.)
|
|
103
|
+
- key: The unique identifier for the trigger/item
|
|
104
|
+
- folder_path: The path to the folder containing the trigger
|
|
105
|
+
- folder_key: The unique identifier for the folder
|
|
106
|
+
- payload: The serialized payload data associated with the trigger
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
memory: The AsyncSqliteSaver instance used to access the database.
|
|
110
|
+
resume_triggers_table: The name of the table containing resume triggers (default: "__uipath_resume_triggers").
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Optional[tuple[str, str, str, str, str]]: A tuple containing (type, key, folder_path,
|
|
114
|
+
folder_key, payload) for the most recent trigger, or None if no triggers are found
|
|
115
|
+
or if the memory context is not available.
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
LangGraphRuntimeError: If there's an error during database connection setup, query
|
|
119
|
+
execution, or result fetching. The original exception is wrapped with context
|
|
120
|
+
about the database operation failure.
|
|
121
|
+
"""
|
|
122
|
+
if memory is None:
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
await memory.setup()
|
|
127
|
+
async with (
|
|
128
|
+
memory.lock,
|
|
129
|
+
memory.conn.cursor() as cur,
|
|
130
|
+
):
|
|
131
|
+
await cur.execute(f"""
|
|
132
|
+
SELECT type, key, folder_path, folder_key, payload
|
|
133
|
+
FROM {resume_triggers_table}
|
|
134
|
+
ORDER BY timestamp DESC
|
|
135
|
+
LIMIT 1
|
|
136
|
+
""")
|
|
137
|
+
result = await cur.fetchone()
|
|
138
|
+
if result is None:
|
|
139
|
+
return None
|
|
140
|
+
return cast(tuple[str, str, str, str, str], tuple(result))
|
|
141
|
+
except Exception as e:
|
|
142
|
+
raise LangGraphRuntimeError(
|
|
143
|
+
"DB_QUERY_FAILED",
|
|
144
|
+
"Database query failed",
|
|
145
|
+
f"Error querying resume trigger information: {str(e)}",
|
|
146
|
+
UiPathErrorCategory.SYSTEM,
|
|
147
|
+
) from e
|
|
@@ -1,234 +1,148 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
|
-
from
|
|
4
|
-
from typing import Any, Dict, Optional, cast
|
|
3
|
+
from typing import Any
|
|
5
4
|
|
|
6
|
-
from langgraph.
|
|
5
|
+
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
|
|
7
6
|
from uipath._cli._runtime._contracts import (
|
|
8
7
|
UiPathErrorCategory,
|
|
9
8
|
UiPathResumeTrigger,
|
|
10
|
-
UiPathRuntimeResult,
|
|
11
|
-
UiPathRuntimeStatus,
|
|
12
9
|
)
|
|
13
10
|
from uipath._cli._runtime._hitl import HitlProcessor
|
|
14
11
|
|
|
15
|
-
from ._context import LangGraphRuntimeContext
|
|
16
12
|
from ._exception import LangGraphRuntimeError
|
|
17
13
|
|
|
18
14
|
logger = logging.getLogger(__name__)
|
|
19
15
|
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
"""
|
|
23
|
-
Contains and manages the complete output information from graph execution.
|
|
24
|
-
Handles serialization, interrupt data, and file output.
|
|
17
|
+
def serialize_output(output: Any) -> Any:
|
|
25
18
|
"""
|
|
19
|
+
Recursively serialize an output object.
|
|
26
20
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Initialize the LangGraphOutputProcessor.
|
|
30
|
-
|
|
31
|
-
Args:
|
|
32
|
-
context: The runtime context for the graph execution.
|
|
33
|
-
"""
|
|
34
|
-
self.context = context
|
|
35
|
-
self._hitl_processor: Optional[HitlProcessor] = None
|
|
36
|
-
self._resume_trigger: Optional[UiPathResumeTrigger] = None
|
|
37
|
-
|
|
38
|
-
@classmethod
|
|
39
|
-
async def create(
|
|
40
|
-
cls, context: LangGraphRuntimeContext
|
|
41
|
-
) -> "LangGraphOutputProcessor":
|
|
42
|
-
"""
|
|
43
|
-
Create and initialize a new LangGraphOutputProcessor instance asynchronously.
|
|
44
|
-
|
|
45
|
-
Args:
|
|
46
|
-
context: The runtime context for the graph execution.
|
|
47
|
-
|
|
48
|
-
Returns:
|
|
49
|
-
LangGraphOutputProcessor: A new initialized instance.
|
|
50
|
-
"""
|
|
51
|
-
instance = cls(context)
|
|
52
|
-
|
|
53
|
-
# Process interrupt information during initialization
|
|
54
|
-
state = cast(StateSnapshot, context.state)
|
|
55
|
-
if not state or not hasattr(state, "next") or not state.next:
|
|
56
|
-
return instance
|
|
57
|
-
|
|
58
|
-
for task in state.tasks:
|
|
59
|
-
if hasattr(task, "interrupts") and task.interrupts:
|
|
60
|
-
for interrupt in task.interrupts:
|
|
61
|
-
if isinstance(interrupt, Interrupt):
|
|
62
|
-
instance._hitl_processor = HitlProcessor(interrupt.value)
|
|
63
|
-
return instance
|
|
64
|
-
|
|
65
|
-
return instance
|
|
66
|
-
|
|
67
|
-
@property
|
|
68
|
-
def status(self) -> UiPathRuntimeStatus:
|
|
69
|
-
"""Determines the execution status based on state."""
|
|
70
|
-
return (
|
|
71
|
-
UiPathRuntimeStatus.SUSPENDED
|
|
72
|
-
if self._hitl_processor
|
|
73
|
-
else UiPathRuntimeStatus.SUCCESSFUL
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
@cached_property
|
|
77
|
-
def serialized_output(self) -> Dict[str, Any]:
|
|
78
|
-
"""Serializes the graph execution result."""
|
|
79
|
-
try:
|
|
80
|
-
if self.context.output is None:
|
|
81
|
-
return {}
|
|
21
|
+
Args:
|
|
22
|
+
output: The object to serialize
|
|
82
23
|
|
|
83
|
-
|
|
24
|
+
Returns:
|
|
25
|
+
Dict[str, Any]: Serialized output as dictionary
|
|
26
|
+
"""
|
|
27
|
+
if output is None:
|
|
28
|
+
return {}
|
|
29
|
+
|
|
30
|
+
# Handle Pydantic models
|
|
31
|
+
if hasattr(output, "model_dump"):
|
|
32
|
+
return serialize_output(output.model_dump(by_alias=True))
|
|
33
|
+
elif hasattr(output, "dict"):
|
|
34
|
+
return serialize_output(output.dict())
|
|
35
|
+
elif hasattr(output, "to_dict"):
|
|
36
|
+
return serialize_output(output.to_dict())
|
|
37
|
+
|
|
38
|
+
# Handle dictionaries
|
|
39
|
+
elif isinstance(output, dict):
|
|
40
|
+
return {k: serialize_output(v) for k, v in output.items()}
|
|
41
|
+
|
|
42
|
+
# Handle lists
|
|
43
|
+
elif isinstance(output, list):
|
|
44
|
+
return [serialize_output(item) for item in output]
|
|
45
|
+
|
|
46
|
+
# Handle other iterables (convert to dict first)
|
|
47
|
+
elif hasattr(output, "__iter__") and not isinstance(output, (str, bytes)):
|
|
48
|
+
try:
|
|
49
|
+
return serialize_output(dict(output))
|
|
50
|
+
except (TypeError, ValueError):
|
|
51
|
+
return output
|
|
84
52
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"OUTPUT_SERIALIZATION_FAILED",
|
|
88
|
-
"Failed to serialize graph output",
|
|
89
|
-
f"Error serializing output data: {str(e)}",
|
|
90
|
-
UiPathErrorCategory.SYSTEM,
|
|
91
|
-
) from e
|
|
53
|
+
# Return primitive types as is
|
|
54
|
+
return output
|
|
92
55
|
|
|
93
|
-
def _serialize_object(self, obj):
|
|
94
|
-
"""Recursively serializes an object and all its nested components."""
|
|
95
|
-
# Handle Pydantic models
|
|
96
|
-
if hasattr(obj, "dict"):
|
|
97
|
-
return self._serialize_object(obj.dict())
|
|
98
|
-
elif hasattr(obj, "model_dump"):
|
|
99
|
-
return self._serialize_object(obj.model_dump(by_alias=True))
|
|
100
|
-
elif hasattr(obj, "to_dict"):
|
|
101
|
-
return self._serialize_object(obj.to_dict())
|
|
102
|
-
# Handle dictionaries
|
|
103
|
-
elif isinstance(obj, dict):
|
|
104
|
-
return {k: self._serialize_object(v) for k, v in obj.items()}
|
|
105
|
-
# Handle lists
|
|
106
|
-
elif isinstance(obj, list):
|
|
107
|
-
return [self._serialize_object(item) for item in obj]
|
|
108
|
-
# Handle other iterable objects (convert to dict first)
|
|
109
|
-
elif hasattr(obj, "__iter__") and not isinstance(obj, (str, bytes)):
|
|
110
|
-
try:
|
|
111
|
-
return self._serialize_object(dict(obj))
|
|
112
|
-
except (TypeError, ValueError):
|
|
113
|
-
return obj
|
|
114
|
-
# Return primitive types as is
|
|
115
|
-
else:
|
|
116
|
-
return obj
|
|
117
56
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
57
|
+
async def create_and_save_resume_trigger(
|
|
58
|
+
interrupt_value: Any,
|
|
59
|
+
memory: AsyncSqliteSaver,
|
|
60
|
+
resume_triggers_table: str = "__uipath_resume_triggers",
|
|
61
|
+
) -> UiPathResumeTrigger:
|
|
62
|
+
"""
|
|
63
|
+
Create a resume trigger from interrupt value and save it to the database.
|
|
121
64
|
|
|
122
|
-
|
|
123
|
-
|
|
65
|
+
Args:
|
|
66
|
+
interrupt_value: The interrupt value from dynamic interrupt
|
|
67
|
+
memory: The SQLite checkpointer/memory instance
|
|
68
|
+
resume_triggers_table: Name of the resume triggers table
|
|
124
69
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
"""
|
|
128
|
-
try:
|
|
129
|
-
await self._save_resume_trigger()
|
|
70
|
+
Returns:
|
|
71
|
+
UiPathResumeTrigger: The created resume trigger
|
|
130
72
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
73
|
+
Raises:
|
|
74
|
+
LangGraphRuntimeError: If database operations fail
|
|
75
|
+
"""
|
|
76
|
+
# Create HITL processor
|
|
77
|
+
hitl_processor = HitlProcessor(interrupt_value)
|
|
136
78
|
|
|
137
|
-
|
|
138
|
-
|
|
79
|
+
# Setup database and create table if needed
|
|
80
|
+
await memory.setup()
|
|
81
|
+
async with memory.lock, memory.conn.cursor() as cur:
|
|
82
|
+
try:
|
|
83
|
+
await cur.execute(f"""
|
|
84
|
+
CREATE TABLE IF NOT EXISTS {resume_triggers_table} (
|
|
85
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
86
|
+
type TEXT NOT NULL,
|
|
87
|
+
key TEXT,
|
|
88
|
+
folder_key TEXT,
|
|
89
|
+
folder_path TEXT,
|
|
90
|
+
payload TEXT,
|
|
91
|
+
timestamp DATETIME DEFAULT (strftime('%Y-%m-%d %H:%M:%S', 'now', 'utc'))
|
|
92
|
+
)
|
|
93
|
+
""")
|
|
139
94
|
except Exception as e:
|
|
140
95
|
raise LangGraphRuntimeError(
|
|
141
|
-
"
|
|
142
|
-
"Failed to
|
|
143
|
-
f"
|
|
96
|
+
"DB_TABLE_CREATION_FAILED",
|
|
97
|
+
"Failed to create resume triggers table",
|
|
98
|
+
f"Database error while creating table: {str(e)}",
|
|
144
99
|
UiPathErrorCategory.SYSTEM,
|
|
145
100
|
) from e
|
|
146
101
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
102
|
+
# Create resume trigger
|
|
103
|
+
try:
|
|
104
|
+
resume_trigger = await hitl_processor.create_resume_trigger()
|
|
105
|
+
except Exception as e:
|
|
106
|
+
raise LangGraphRuntimeError(
|
|
107
|
+
"HITL_EVENT_CREATION_FAILED",
|
|
108
|
+
"Failed to process HITL request",
|
|
109
|
+
f"Error while trying to process HITL request: {str(e)}",
|
|
110
|
+
UiPathErrorCategory.SYSTEM,
|
|
111
|
+
) from e
|
|
150
112
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
113
|
+
# Save to database
|
|
114
|
+
if resume_trigger.api_resume:
|
|
115
|
+
trigger_key = resume_trigger.api_resume.inbox_id
|
|
116
|
+
else:
|
|
117
|
+
trigger_key = resume_trigger.item_key
|
|
156
118
|
|
|
157
119
|
try:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
):
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
"Failed to create resume triggers table",
|
|
179
|
-
f"Database error while creating table: {str(e)}",
|
|
180
|
-
UiPathErrorCategory.SYSTEM,
|
|
181
|
-
) from e
|
|
182
|
-
|
|
183
|
-
try:
|
|
184
|
-
self._resume_trigger = (
|
|
185
|
-
await self._hitl_processor.create_resume_trigger()
|
|
186
|
-
)
|
|
187
|
-
except Exception as e:
|
|
188
|
-
raise LangGraphRuntimeError(
|
|
189
|
-
"HITL_EVENT_CREATION_FAILED",
|
|
190
|
-
"Failed to process HITL request",
|
|
191
|
-
f"Error while trying to process HITL request: {str(e)}",
|
|
192
|
-
UiPathErrorCategory.SYSTEM,
|
|
193
|
-
) from e
|
|
194
|
-
# if API trigger, override item_key and payload
|
|
195
|
-
if self._resume_trigger:
|
|
196
|
-
if self._resume_trigger.api_resume:
|
|
197
|
-
trigger_key = self._resume_trigger.api_resume.inbox_id
|
|
198
|
-
else:
|
|
199
|
-
trigger_key = self._resume_trigger.item_key
|
|
200
|
-
try:
|
|
201
|
-
logger.debug(
|
|
202
|
-
f"ResumeTrigger: {self._resume_trigger.trigger_type} {self._resume_trigger.item_key}"
|
|
203
|
-
)
|
|
204
|
-
if isinstance(self._resume_trigger.payload, dict):
|
|
205
|
-
payload = json.dumps(self._resume_trigger.payload)
|
|
206
|
-
else:
|
|
207
|
-
payload = str(self._resume_trigger.payload)
|
|
208
|
-
await cur.execute(
|
|
209
|
-
f"INSERT INTO {self.context.resume_triggers_table} (type, key, payload, folder_path, folder_key) VALUES (?, ?, ?, ?, ?)",
|
|
210
|
-
(
|
|
211
|
-
self._resume_trigger.trigger_type.value,
|
|
212
|
-
trigger_key,
|
|
213
|
-
payload,
|
|
214
|
-
self._resume_trigger.folder_path,
|
|
215
|
-
self._resume_trigger.folder_key,
|
|
216
|
-
),
|
|
217
|
-
)
|
|
218
|
-
await self.context.memory.conn.commit()
|
|
219
|
-
except Exception as e:
|
|
220
|
-
raise LangGraphRuntimeError(
|
|
221
|
-
"DB_INSERT_FAILED",
|
|
222
|
-
"Failed to save resume trigger",
|
|
223
|
-
f"Database error while saving resume trigger: {str(e)}",
|
|
224
|
-
UiPathErrorCategory.SYSTEM,
|
|
225
|
-
) from e
|
|
226
|
-
except LangGraphRuntimeError:
|
|
227
|
-
raise
|
|
120
|
+
logger.debug(
|
|
121
|
+
f"ResumeTrigger: {resume_trigger.trigger_type} {resume_trigger.item_key}"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
if isinstance(resume_trigger.payload, dict):
|
|
125
|
+
payload = json.dumps(resume_trigger.payload)
|
|
126
|
+
else:
|
|
127
|
+
payload = str(resume_trigger.payload)
|
|
128
|
+
|
|
129
|
+
await cur.execute(
|
|
130
|
+
f"INSERT INTO {resume_triggers_table} (type, key, payload, folder_path, folder_key) VALUES (?, ?, ?, ?, ?)",
|
|
131
|
+
(
|
|
132
|
+
resume_trigger.trigger_type.value,
|
|
133
|
+
trigger_key,
|
|
134
|
+
payload,
|
|
135
|
+
resume_trigger.folder_path,
|
|
136
|
+
resume_trigger.folder_key,
|
|
137
|
+
),
|
|
138
|
+
)
|
|
139
|
+
await memory.conn.commit()
|
|
228
140
|
except Exception as e:
|
|
229
141
|
raise LangGraphRuntimeError(
|
|
230
|
-
"
|
|
142
|
+
"DB_INSERT_FAILED",
|
|
231
143
|
"Failed to save resume trigger",
|
|
232
|
-
f"
|
|
144
|
+
f"Database error while saving resume trigger: {str(e)}",
|
|
233
145
|
UiPathErrorCategory.SYSTEM,
|
|
234
146
|
) from e
|
|
147
|
+
|
|
148
|
+
return resume_trigger
|