uipath-langchain 0.0.143__py3-none-any.whl → 0.0.144__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/_exception.py +31 -5
- uipath_langchain/_cli/_runtime/_graph_resolver.py +10 -12
- uipath_langchain/_cli/_runtime/_input.py +2 -2
- uipath_langchain/_cli/_runtime/_output.py +5 -8
- uipath_langchain/_cli/_runtime/_runtime.py +12 -6
- uipath_langchain/_utils/_request_mixin.py +5 -2
- uipath_langchain/agent/react/__init__.py +12 -0
- uipath_langchain/agent/react/agent.py +76 -0
- uipath_langchain/agent/react/constants.py +2 -0
- uipath_langchain/agent/react/exceptions.py +11 -0
- uipath_langchain/agent/react/init_node.py +16 -0
- uipath_langchain/agent/react/llm_node.py +44 -0
- uipath_langchain/agent/react/router.py +97 -0
- uipath_langchain/agent/react/state.py +18 -0
- uipath_langchain/agent/react/terminate_node.py +53 -0
- uipath_langchain/agent/react/tools/__init__.py +7 -0
- uipath_langchain/agent/react/tools/tools.py +50 -0
- uipath_langchain/agent/react/utils.py +39 -0
- uipath_langchain/agent/tools/__init__.py +8 -0
- uipath_langchain/agent/tools/context_tool.py +42 -0
- uipath_langchain/agent/tools/process_tool.py +51 -0
- uipath_langchain/agent/tools/tool_factory.py +39 -0
- uipath_langchain/agent/tools/tool_node.py +22 -0
- uipath_langchain/agent/tools/utils.py +11 -0
- {uipath_langchain-0.0.143.dist-info → uipath_langchain-0.0.144.dist-info}/METADATA +1 -1
- {uipath_langchain-0.0.143.dist-info → uipath_langchain-0.0.144.dist-info}/RECORD +29 -11
- {uipath_langchain-0.0.143.dist-info → uipath_langchain-0.0.144.dist-info}/WHEEL +0 -0
- {uipath_langchain-0.0.143.dist-info → uipath_langchain-0.0.144.dist-info}/entry_points.txt +0 -0
- {uipath_langchain-0.0.143.dist-info → uipath_langchain-0.0.144.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,17 +1,43 @@
|
|
|
1
|
-
from
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Optional, Union
|
|
2
3
|
|
|
3
|
-
from uipath._cli._runtime._contracts import
|
|
4
|
+
from uipath._cli._runtime._contracts import (
|
|
5
|
+
UiPathBaseRuntimeError,
|
|
6
|
+
UiPathErrorCategory,
|
|
7
|
+
UiPathErrorCode,
|
|
8
|
+
)
|
|
4
9
|
|
|
5
10
|
|
|
6
|
-
class
|
|
11
|
+
class LangGraphErrorCode(Enum):
|
|
12
|
+
CONFIG_MISSING = "CONFIG_MISSING"
|
|
13
|
+
CONFIG_INVALID = "CONFIG_INVALID"
|
|
14
|
+
|
|
15
|
+
GRAPH_NOT_FOUND = "GRAPH_NOT_FOUND"
|
|
16
|
+
GRAPH_IMPORT_ERROR = "GRAPH_IMPORT_ERROR"
|
|
17
|
+
GRAPH_TYPE_ERROR = "GRAPH_TYPE_ERROR"
|
|
18
|
+
GRAPH_VALUE_ERROR = "GRAPH_VALUE_ERROR"
|
|
19
|
+
GRAPH_LOAD_ERROR = "GRAPH_LOAD_ERROR"
|
|
20
|
+
GRAPH_INVALID_UPDATE = "GRAPH_INVALID_UPDATE"
|
|
21
|
+
GRAPH_EMPTY_INPUT = "GRAPH_EMPTY_INPUT"
|
|
22
|
+
|
|
23
|
+
DB_QUERY_FAILED = "DB_QUERY_FAILED"
|
|
24
|
+
DB_TABLE_CREATION_FAILED = "DB_TABLE_CREATION_FAILED"
|
|
25
|
+
HITL_EVENT_CREATION_FAILED = "HITL_EVENT_CREATION_FAILED"
|
|
26
|
+
DB_INSERT_FAILED = "DB_INSERT_FAILED"
|
|
27
|
+
LICENSE_NOT_AVAILABLE = "LICENSE_NOT_AVAILABLE"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class LangGraphRuntimeError(UiPathBaseRuntimeError):
|
|
7
31
|
"""Custom exception for LangGraph runtime errors with structured error information."""
|
|
8
32
|
|
|
9
33
|
def __init__(
|
|
10
34
|
self,
|
|
11
|
-
code:
|
|
35
|
+
code: Union[LangGraphErrorCode, UiPathErrorCode],
|
|
12
36
|
title: str,
|
|
13
37
|
detail: str,
|
|
14
38
|
category: UiPathErrorCategory = UiPathErrorCategory.UNKNOWN,
|
|
15
39
|
status: Optional[int] = None,
|
|
16
40
|
):
|
|
17
|
-
super().__init__(
|
|
41
|
+
super().__init__(
|
|
42
|
+
code.value, title, detail, category, status, prefix="LANGGRAPH"
|
|
43
|
+
)
|
|
@@ -2,12 +2,10 @@ import asyncio
|
|
|
2
2
|
from typing import Any, Awaitable, Callable, Optional
|
|
3
3
|
|
|
4
4
|
from langgraph.graph.state import CompiledStateGraph, StateGraph
|
|
5
|
-
from uipath._cli._runtime._contracts import
|
|
6
|
-
UiPathErrorCategory,
|
|
7
|
-
)
|
|
5
|
+
from uipath._cli._runtime._contracts import UiPathErrorCategory, UiPathErrorCode
|
|
8
6
|
|
|
9
7
|
from .._utils._graph import GraphConfig, LangGraphConfig
|
|
10
|
-
from ._exception import LangGraphRuntimeError
|
|
8
|
+
from ._exception import LangGraphErrorCode, LangGraphRuntimeError
|
|
11
9
|
|
|
12
10
|
|
|
13
11
|
class LangGraphJsonResolver:
|
|
@@ -36,7 +34,7 @@ class LangGraphJsonResolver:
|
|
|
36
34
|
config = LangGraphConfig()
|
|
37
35
|
if not config.exists:
|
|
38
36
|
raise LangGraphRuntimeError(
|
|
39
|
-
|
|
37
|
+
LangGraphErrorCode.CONFIG_MISSING,
|
|
40
38
|
"Invalid configuration",
|
|
41
39
|
"Failed to load configuration",
|
|
42
40
|
UiPathErrorCategory.DEPLOYMENT,
|
|
@@ -46,7 +44,7 @@ class LangGraphJsonResolver:
|
|
|
46
44
|
config.load_config()
|
|
47
45
|
except Exception as e:
|
|
48
46
|
raise LangGraphRuntimeError(
|
|
49
|
-
|
|
47
|
+
LangGraphErrorCode.CONFIG_INVALID,
|
|
50
48
|
"Invalid configuration",
|
|
51
49
|
f"Failed to load configuration: {str(e)}",
|
|
52
50
|
UiPathErrorCategory.DEPLOYMENT,
|
|
@@ -59,7 +57,7 @@ class LangGraphJsonResolver:
|
|
|
59
57
|
elif not entrypoint:
|
|
60
58
|
graph_names = ", ".join(g.name for g in graphs)
|
|
61
59
|
raise LangGraphRuntimeError(
|
|
62
|
-
|
|
60
|
+
UiPathErrorCode.ENTRYPOINT_MISSING,
|
|
63
61
|
"Entrypoint required",
|
|
64
62
|
f"Multiple graphs available. Please specify one of: {graph_names}.",
|
|
65
63
|
UiPathErrorCategory.DEPLOYMENT,
|
|
@@ -69,7 +67,7 @@ class LangGraphJsonResolver:
|
|
|
69
67
|
self.graph_config = config.get_graph(entrypoint)
|
|
70
68
|
if not self.graph_config:
|
|
71
69
|
raise LangGraphRuntimeError(
|
|
72
|
-
|
|
70
|
+
LangGraphErrorCode.GRAPH_NOT_FOUND,
|
|
73
71
|
"Graph not found",
|
|
74
72
|
f"Graph '{entrypoint}' not found.",
|
|
75
73
|
UiPathErrorCategory.DEPLOYMENT,
|
|
@@ -83,28 +81,28 @@ class LangGraphJsonResolver:
|
|
|
83
81
|
)
|
|
84
82
|
except ImportError as e:
|
|
85
83
|
raise LangGraphRuntimeError(
|
|
86
|
-
|
|
84
|
+
LangGraphErrorCode.GRAPH_IMPORT_ERROR,
|
|
87
85
|
"Graph import failed",
|
|
88
86
|
f"Failed to import graph '{entrypoint}': {str(e)}",
|
|
89
87
|
UiPathErrorCategory.USER,
|
|
90
88
|
) from e
|
|
91
89
|
except TypeError as e:
|
|
92
90
|
raise LangGraphRuntimeError(
|
|
93
|
-
|
|
91
|
+
LangGraphErrorCode.GRAPH_TYPE_ERROR,
|
|
94
92
|
"Invalid graph type",
|
|
95
93
|
f"Graph '{entrypoint}' is not a valid StateGraph or CompiledStateGraph: {str(e)}",
|
|
96
94
|
UiPathErrorCategory.USER,
|
|
97
95
|
) from e
|
|
98
96
|
except ValueError as e:
|
|
99
97
|
raise LangGraphRuntimeError(
|
|
100
|
-
|
|
98
|
+
LangGraphErrorCode.GRAPH_VALUE_ERROR,
|
|
101
99
|
"Invalid graph value",
|
|
102
100
|
f"Invalid value in graph '{entrypoint}': {str(e)}",
|
|
103
101
|
UiPathErrorCategory.USER,
|
|
104
102
|
) from e
|
|
105
103
|
except Exception as e:
|
|
106
104
|
raise LangGraphRuntimeError(
|
|
107
|
-
|
|
105
|
+
LangGraphErrorCode.GRAPH_LOAD_ERROR,
|
|
108
106
|
"Failed to load graph",
|
|
109
107
|
f"Unexpected error loading graph '{entrypoint}': {str(e)}",
|
|
110
108
|
UiPathErrorCategory.USER,
|
|
@@ -13,7 +13,7 @@ from uipath._cli._runtime._hitl import HitlReader
|
|
|
13
13
|
|
|
14
14
|
from ._context import LangGraphRuntimeContext
|
|
15
15
|
from ._conversation import uipath_to_human_messages
|
|
16
|
-
from ._exception import LangGraphRuntimeError
|
|
16
|
+
from ._exception import LangGraphErrorCode, LangGraphRuntimeError
|
|
17
17
|
|
|
18
18
|
logger = logging.getLogger(__name__)
|
|
19
19
|
|
|
@@ -140,7 +140,7 @@ async def _get_latest_trigger(
|
|
|
140
140
|
return cast(tuple[str, str, str, str, str], tuple(result))
|
|
141
141
|
except Exception as e:
|
|
142
142
|
raise LangGraphRuntimeError(
|
|
143
|
-
|
|
143
|
+
LangGraphErrorCode.DB_QUERY_FAILED,
|
|
144
144
|
"Database query failed",
|
|
145
145
|
f"Error querying resume trigger information: {str(e)}",
|
|
146
146
|
UiPathErrorCategory.SYSTEM,
|
|
@@ -3,13 +3,10 @@ import logging
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
5
|
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
|
|
6
|
-
from uipath._cli._runtime._contracts import
|
|
7
|
-
UiPathErrorCategory,
|
|
8
|
-
UiPathResumeTrigger,
|
|
9
|
-
)
|
|
6
|
+
from uipath._cli._runtime._contracts import UiPathErrorCategory, UiPathResumeTrigger
|
|
10
7
|
from uipath._cli._runtime._hitl import HitlProcessor
|
|
11
8
|
|
|
12
|
-
from ._exception import LangGraphRuntimeError
|
|
9
|
+
from ._exception import LangGraphErrorCode, LangGraphRuntimeError
|
|
13
10
|
|
|
14
11
|
logger = logging.getLogger(__name__)
|
|
15
12
|
|
|
@@ -93,7 +90,7 @@ async def create_and_save_resume_trigger(
|
|
|
93
90
|
""")
|
|
94
91
|
except Exception as e:
|
|
95
92
|
raise LangGraphRuntimeError(
|
|
96
|
-
|
|
93
|
+
LangGraphErrorCode.DB_TABLE_CREATION_FAILED,
|
|
97
94
|
"Failed to create resume triggers table",
|
|
98
95
|
f"Database error while creating table: {str(e)}",
|
|
99
96
|
UiPathErrorCategory.SYSTEM,
|
|
@@ -104,7 +101,7 @@ async def create_and_save_resume_trigger(
|
|
|
104
101
|
resume_trigger = await hitl_processor.create_resume_trigger()
|
|
105
102
|
except Exception as e:
|
|
106
103
|
raise LangGraphRuntimeError(
|
|
107
|
-
|
|
104
|
+
LangGraphErrorCode.HITL_EVENT_CREATION_FAILED,
|
|
108
105
|
"Failed to process HITL request",
|
|
109
106
|
f"Error while trying to process HITL request: {str(e)}",
|
|
110
107
|
UiPathErrorCategory.SYSTEM,
|
|
@@ -139,7 +136,7 @@ async def create_and_save_resume_trigger(
|
|
|
139
136
|
await memory.conn.commit()
|
|
140
137
|
except Exception as e:
|
|
141
138
|
raise LangGraphRuntimeError(
|
|
142
|
-
|
|
139
|
+
LangGraphErrorCode.DB_INSERT_FAILED,
|
|
143
140
|
"Failed to save resume trigger",
|
|
144
141
|
f"Database error while saving resume trigger: {str(e)}",
|
|
145
142
|
UiPathErrorCategory.SYSTEM,
|
|
@@ -12,6 +12,7 @@ from uipath._cli._runtime._contracts import (
|
|
|
12
12
|
UiPathBaseRuntime,
|
|
13
13
|
UiPathBreakpointResult,
|
|
14
14
|
UiPathErrorCategory,
|
|
15
|
+
UiPathErrorCode,
|
|
15
16
|
UiPathResumeTrigger,
|
|
16
17
|
UiPathRuntimeResult,
|
|
17
18
|
UiPathRuntimeStatus,
|
|
@@ -23,7 +24,7 @@ from uipath._events._events import (
|
|
|
23
24
|
)
|
|
24
25
|
|
|
25
26
|
from ._context import LangGraphRuntimeContext
|
|
26
|
-
from ._exception import LangGraphRuntimeError
|
|
27
|
+
from ._exception import LangGraphErrorCode, LangGraphRuntimeError
|
|
27
28
|
from ._graph_resolver import AsyncResolver, LangGraphJsonResolver
|
|
28
29
|
from ._input import get_graph_input
|
|
29
30
|
from ._output import create_and_save_resume_trigger, serialize_output
|
|
@@ -79,7 +80,11 @@ class LangGraphRuntime(UiPathBaseRuntime):
|
|
|
79
80
|
graph_config = self._get_graph_config()
|
|
80
81
|
|
|
81
82
|
# Execute without streaming
|
|
82
|
-
graph_output = await compiled_graph.ainvoke(
|
|
83
|
+
graph_output = await compiled_graph.ainvoke(
|
|
84
|
+
graph_input,
|
|
85
|
+
graph_config,
|
|
86
|
+
interrupt_before=self.context.breakpoints,
|
|
87
|
+
)
|
|
83
88
|
|
|
84
89
|
# Get final state and create result
|
|
85
90
|
self.context.result = await self._create_runtime_result(
|
|
@@ -140,6 +145,7 @@ class LangGraphRuntime(UiPathBaseRuntime):
|
|
|
140
145
|
async for stream_chunk in compiled_graph.astream(
|
|
141
146
|
graph_input,
|
|
142
147
|
graph_config,
|
|
148
|
+
interrupt_before=self.context.breakpoints,
|
|
143
149
|
stream_mode=["messages", "updates"],
|
|
144
150
|
subgraphs=True,
|
|
145
151
|
):
|
|
@@ -426,7 +432,7 @@ class LangGraphRuntime(UiPathBaseRuntime):
|
|
|
426
432
|
|
|
427
433
|
if isinstance(e, GraphRecursionError):
|
|
428
434
|
return LangGraphRuntimeError(
|
|
429
|
-
|
|
435
|
+
LangGraphErrorCode.GRAPH_LOAD_ERROR,
|
|
430
436
|
"Graph recursion limit exceeded",
|
|
431
437
|
detail,
|
|
432
438
|
UiPathErrorCategory.USER,
|
|
@@ -434,7 +440,7 @@ class LangGraphRuntime(UiPathBaseRuntime):
|
|
|
434
440
|
|
|
435
441
|
if isinstance(e, InvalidUpdateError):
|
|
436
442
|
return LangGraphRuntimeError(
|
|
437
|
-
|
|
443
|
+
LangGraphErrorCode.GRAPH_INVALID_UPDATE,
|
|
438
444
|
str(e),
|
|
439
445
|
detail,
|
|
440
446
|
UiPathErrorCategory.USER,
|
|
@@ -442,14 +448,14 @@ class LangGraphRuntime(UiPathBaseRuntime):
|
|
|
442
448
|
|
|
443
449
|
if isinstance(e, EmptyInputError):
|
|
444
450
|
return LangGraphRuntimeError(
|
|
445
|
-
|
|
451
|
+
LangGraphErrorCode.GRAPH_EMPTY_INPUT,
|
|
446
452
|
"The input data is empty",
|
|
447
453
|
detail,
|
|
448
454
|
UiPathErrorCategory.USER,
|
|
449
455
|
)
|
|
450
456
|
|
|
451
457
|
return LangGraphRuntimeError(
|
|
452
|
-
|
|
458
|
+
UiPathErrorCode.EXECUTION_ERROR,
|
|
453
459
|
"Graph execution failed",
|
|
454
460
|
detail,
|
|
455
461
|
UiPathErrorCategory.USER,
|
|
@@ -20,7 +20,10 @@ from tenacity import (
|
|
|
20
20
|
from uipath._cli._runtime._contracts import UiPathErrorCategory, UiPathRuntimeError
|
|
21
21
|
from uipath._utils._ssl_context import get_httpx_client_kwargs
|
|
22
22
|
|
|
23
|
-
from uipath_langchain._cli._runtime._exception import
|
|
23
|
+
from uipath_langchain._cli._runtime._exception import (
|
|
24
|
+
LangGraphErrorCode,
|
|
25
|
+
LangGraphRuntimeError,
|
|
26
|
+
)
|
|
24
27
|
from uipath_langchain._utils._settings import (
|
|
25
28
|
UiPathClientFactorySettings,
|
|
26
29
|
UiPathClientSettings,
|
|
@@ -371,7 +374,7 @@ class UiPathRequestMixin(BaseModel):
|
|
|
371
374
|
title = body.get("title", "").lower()
|
|
372
375
|
if title == "license not available":
|
|
373
376
|
raise LangGraphRuntimeError(
|
|
374
|
-
code=
|
|
377
|
+
code=LangGraphErrorCode.LICENSE_NOT_AVAILABLE,
|
|
375
378
|
title=body.get("title", "License Not Available"),
|
|
376
379
|
detail=body.get(
|
|
377
380
|
"detail", "License not available for this service"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""UiPath ReAct Agent implementation"""
|
|
2
|
+
|
|
3
|
+
from .agent import create_agent
|
|
4
|
+
from .state import AgentGraphNode, AgentGraphState
|
|
5
|
+
from .utils import resolve_output_model
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"create_agent",
|
|
9
|
+
"AgentGraphState",
|
|
10
|
+
"AgentGraphNode",
|
|
11
|
+
"resolve_output_model",
|
|
12
|
+
]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Sequence, Type
|
|
3
|
+
|
|
4
|
+
from langchain_core.language_models import BaseChatModel
|
|
5
|
+
from langchain_core.messages import HumanMessage, SystemMessage
|
|
6
|
+
from langchain_core.tools import BaseTool
|
|
7
|
+
from langgraph.constants import END, START
|
|
8
|
+
from langgraph.graph import StateGraph
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
|
|
11
|
+
from ..tools import create_tool_node
|
|
12
|
+
from .init_node import (
|
|
13
|
+
create_init_node,
|
|
14
|
+
)
|
|
15
|
+
from .llm_node import (
|
|
16
|
+
create_llm_node,
|
|
17
|
+
)
|
|
18
|
+
from .router import (
|
|
19
|
+
route_agent,
|
|
20
|
+
)
|
|
21
|
+
from .state import AgentGraphNode, AgentGraphState
|
|
22
|
+
from .terminate_node import (
|
|
23
|
+
create_terminate_node,
|
|
24
|
+
)
|
|
25
|
+
from .tools import create_flow_control_tools
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def create_agent(
|
|
29
|
+
model: BaseChatModel,
|
|
30
|
+
tools: Sequence[BaseTool],
|
|
31
|
+
messages: Sequence[SystemMessage | HumanMessage],
|
|
32
|
+
*,
|
|
33
|
+
state_schema: Type[AgentGraphState] = AgentGraphState,
|
|
34
|
+
response_format: type[BaseModel] | None = None,
|
|
35
|
+
recursion_limit: int = 50,
|
|
36
|
+
) -> StateGraph[AgentGraphState]:
|
|
37
|
+
"""Build agent graph with INIT -> AGENT <-> TOOLS loop, terminated by control flow tools.
|
|
38
|
+
|
|
39
|
+
Control flow tools (end_execution, raise_error) are auto-injected alongside regular tools.
|
|
40
|
+
"""
|
|
41
|
+
os.environ["LANGCHAIN_RECURSION_LIMIT"] = str(recursion_limit)
|
|
42
|
+
|
|
43
|
+
agent_tools = list(tools)
|
|
44
|
+
flow_control_tools: list[BaseTool] = create_flow_control_tools(response_format)
|
|
45
|
+
llm_tools: list[BaseTool] = [*agent_tools, *flow_control_tools]
|
|
46
|
+
|
|
47
|
+
init_node = create_init_node(messages)
|
|
48
|
+
agent_node = create_llm_node(model, llm_tools)
|
|
49
|
+
tool_nodes = create_tool_node(agent_tools)
|
|
50
|
+
terminate_node = create_terminate_node(response_format)
|
|
51
|
+
|
|
52
|
+
builder: StateGraph[AgentGraphState] = StateGraph(state_schema)
|
|
53
|
+
builder.add_node(AgentGraphNode.INIT, init_node)
|
|
54
|
+
builder.add_node(AgentGraphNode.AGENT, agent_node)
|
|
55
|
+
|
|
56
|
+
for tool_name, tool_node in tool_nodes.items():
|
|
57
|
+
builder.add_node(tool_name, tool_node)
|
|
58
|
+
|
|
59
|
+
builder.add_node(AgentGraphNode.TERMINATE, terminate_node)
|
|
60
|
+
|
|
61
|
+
builder.add_edge(START, AgentGraphNode.INIT)
|
|
62
|
+
builder.add_edge(AgentGraphNode.INIT, AgentGraphNode.AGENT)
|
|
63
|
+
|
|
64
|
+
tool_node_names = list(tool_nodes.keys())
|
|
65
|
+
builder.add_conditional_edges(
|
|
66
|
+
AgentGraphNode.AGENT,
|
|
67
|
+
route_agent,
|
|
68
|
+
[AgentGraphNode.AGENT, *tool_node_names, AgentGraphNode.TERMINATE],
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
for tool_name in tool_node_names:
|
|
72
|
+
builder.add_edge(tool_name, AgentGraphNode.AGENT)
|
|
73
|
+
|
|
74
|
+
builder.add_edge(AgentGraphNode.TERMINATE, END)
|
|
75
|
+
|
|
76
|
+
return builder
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""State initialization node for the ReAct Agent graph."""
|
|
2
|
+
|
|
3
|
+
from typing import Sequence
|
|
4
|
+
|
|
5
|
+
from langchain_core.messages import HumanMessage, SystemMessage
|
|
6
|
+
|
|
7
|
+
from .state import AgentGraphState
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def create_init_node(
|
|
11
|
+
messages: Sequence[SystemMessage | HumanMessage],
|
|
12
|
+
):
|
|
13
|
+
def graph_state_init(_: AgentGraphState):
|
|
14
|
+
return {"messages": list(messages)}
|
|
15
|
+
|
|
16
|
+
return graph_state_init
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""LLM node implementation for LangGraph."""
|
|
2
|
+
|
|
3
|
+
from typing import Sequence
|
|
4
|
+
|
|
5
|
+
from langchain_core.language_models import BaseChatModel
|
|
6
|
+
from langchain_core.messages import AIMessage, AnyMessage
|
|
7
|
+
from langchain_core.tools import BaseTool
|
|
8
|
+
|
|
9
|
+
from .constants import MAX_SUCCESSIVE_COMPLETIONS
|
|
10
|
+
from .state import AgentGraphState
|
|
11
|
+
from .utils import count_successive_completions
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_llm_node(
|
|
15
|
+
model: BaseChatModel,
|
|
16
|
+
tools: Sequence[BaseTool] | None = None,
|
|
17
|
+
):
|
|
18
|
+
"""Invoke LLM with tools and dynamically control tool_choice based on successive completions.
|
|
19
|
+
|
|
20
|
+
When successive completions reach the limit, tool_choice is set to "required" to force
|
|
21
|
+
the LLM to use a tool and prevent infinite reasoning loops.
|
|
22
|
+
"""
|
|
23
|
+
bindable_tools = list(tools) if tools else []
|
|
24
|
+
base_llm = model.bind_tools(bindable_tools) if bindable_tools else model
|
|
25
|
+
|
|
26
|
+
async def llm_node(state: AgentGraphState):
|
|
27
|
+
messages: list[AnyMessage] = state["messages"]
|
|
28
|
+
|
|
29
|
+
successive_completions = count_successive_completions(messages)
|
|
30
|
+
|
|
31
|
+
if successive_completions >= MAX_SUCCESSIVE_COMPLETIONS and bindable_tools:
|
|
32
|
+
llm = base_llm.bind(tool_choice="required")
|
|
33
|
+
else:
|
|
34
|
+
llm = base_llm
|
|
35
|
+
|
|
36
|
+
response = await llm.ainvoke(messages)
|
|
37
|
+
if not isinstance(response, AIMessage):
|
|
38
|
+
raise TypeError(
|
|
39
|
+
f"LLM returned {type(response).__name__} instead of AIMessage"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
return {"messages": [response]}
|
|
43
|
+
|
|
44
|
+
return llm_node
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Routing functions for conditional edges in the agent graph."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from langchain_core.messages import AIMessage, AnyMessage, ToolCall
|
|
6
|
+
from uipath.agent.react import END_EXECUTION_TOOL, RAISE_ERROR_TOOL
|
|
7
|
+
|
|
8
|
+
from .constants import MAX_SUCCESSIVE_COMPLETIONS
|
|
9
|
+
from .exceptions import AgentNodeRoutingException
|
|
10
|
+
from .state import AgentGraphNode, AgentGraphState
|
|
11
|
+
from .utils import count_successive_completions
|
|
12
|
+
|
|
13
|
+
FLOW_CONTROL_TOOLS = [END_EXECUTION_TOOL.name, RAISE_ERROR_TOOL.name]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def __filter_control_flow_tool_calls(
|
|
17
|
+
tool_calls: list[ToolCall],
|
|
18
|
+
) -> list[ToolCall]:
|
|
19
|
+
"""Remove control flow tools when multiple tool calls exist."""
|
|
20
|
+
if len(tool_calls) <= 1:
|
|
21
|
+
return tool_calls
|
|
22
|
+
|
|
23
|
+
return [tc for tc in tool_calls if tc.get("name") not in FLOW_CONTROL_TOOLS]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def __has_control_flow_tool(tool_calls: list[ToolCall]) -> bool:
|
|
27
|
+
"""Check if any tool call is of a control flow tool."""
|
|
28
|
+
return any(tc.get("name") in FLOW_CONTROL_TOOLS for tc in tool_calls)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def __validate_last_message_is_AI(messages: list[AnyMessage]) -> AIMessage:
|
|
32
|
+
"""Validate and return last message from state.
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
AgentNodeRoutingException: If messages are empty or last message is not AIMessage
|
|
36
|
+
"""
|
|
37
|
+
if not messages:
|
|
38
|
+
raise AgentNodeRoutingException(
|
|
39
|
+
"No messages in state - cannot route after agent"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
last_message = messages[-1]
|
|
43
|
+
if not isinstance(last_message, AIMessage):
|
|
44
|
+
raise AgentNodeRoutingException(
|
|
45
|
+
f"Last message is not AIMessage (type: {type(last_message).__name__}) - cannot route after agent"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
return last_message
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def route_agent(
|
|
52
|
+
state: AgentGraphState,
|
|
53
|
+
) -> list[str] | Literal[AgentGraphNode.AGENT, AgentGraphNode.TERMINATE]:
|
|
54
|
+
"""Route after agent: handles all routing logic including control flow detection.
|
|
55
|
+
|
|
56
|
+
Routing logic:
|
|
57
|
+
1. If multiple tool calls exist, filter out control flow tools (EndExecution, RaiseError)
|
|
58
|
+
2. If control flow tool(s) remain, route to TERMINATE
|
|
59
|
+
3. If regular tool calls remain, route to specific tool nodes (return list of tool names)
|
|
60
|
+
4. If no tool calls, handle successive completions
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
- list[str]: Tool node names for parallel execution
|
|
64
|
+
- AgentGraphNode.AGENT: For successive completions
|
|
65
|
+
- AgentGraphNode.TERMINATE: For control flow termination
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
AgentNodeRoutingException: When encountering unexpected state (empty messages, non-AIMessage, or excessive completions)
|
|
69
|
+
"""
|
|
70
|
+
messages = state.get("messages", [])
|
|
71
|
+
last_message = __validate_last_message_is_AI(messages)
|
|
72
|
+
|
|
73
|
+
tool_calls = list(last_message.tool_calls) if last_message.tool_calls else []
|
|
74
|
+
tool_calls = __filter_control_flow_tool_calls(tool_calls)
|
|
75
|
+
|
|
76
|
+
if tool_calls and __has_control_flow_tool(tool_calls):
|
|
77
|
+
return AgentGraphNode.TERMINATE
|
|
78
|
+
|
|
79
|
+
if tool_calls:
|
|
80
|
+
return [tc["name"] for tc in tool_calls]
|
|
81
|
+
|
|
82
|
+
successive_completions = count_successive_completions(messages)
|
|
83
|
+
|
|
84
|
+
if successive_completions > MAX_SUCCESSIVE_COMPLETIONS:
|
|
85
|
+
raise AgentNodeRoutingException(
|
|
86
|
+
f"Agent exceeded successive completions limit without producing tool calls "
|
|
87
|
+
f"(completions: {successive_completions}, max: {MAX_SUCCESSIVE_COMPLETIONS}). "
|
|
88
|
+
f"This should not happen as tool_choice='required' is enforced at the limit."
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if last_message.content:
|
|
92
|
+
return AgentGraphNode.AGENT
|
|
93
|
+
|
|
94
|
+
raise AgentNodeRoutingException(
|
|
95
|
+
f"Agent produced empty response without tool calls "
|
|
96
|
+
f"(completions: {successive_completions}, has_content: False)"
|
|
97
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
|
|
5
|
+
from langgraph.graph import MessagesState
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AgentGraphState(MessagesState):
|
|
9
|
+
"""Agent Graph state for standard loop execution."""
|
|
10
|
+
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AgentGraphNode(StrEnum):
|
|
15
|
+
INIT = "init"
|
|
16
|
+
AGENT = "agent"
|
|
17
|
+
TOOLS = "tools"
|
|
18
|
+
TERMINATE = "terminate"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Termination node for the Agent graph."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from langchain_core.messages import AIMessage
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
from uipath._cli._runtime._contracts import UiPathErrorCode
|
|
8
|
+
from uipath.agent.react import END_EXECUTION_TOOL, RAISE_ERROR_TOOL
|
|
9
|
+
|
|
10
|
+
from .exceptions import (
|
|
11
|
+
AgentNodeRoutingException,
|
|
12
|
+
AgentTerminationException,
|
|
13
|
+
)
|
|
14
|
+
from .state import AgentGraphState
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def create_terminate_node(
|
|
18
|
+
response_schema: type[BaseModel] | None = None,
|
|
19
|
+
):
|
|
20
|
+
"""Validates and returns end_execution args, or raises AgentTerminationException for raise_error."""
|
|
21
|
+
|
|
22
|
+
def terminate_node(state: AgentGraphState):
|
|
23
|
+
last_message = state["messages"][-1]
|
|
24
|
+
if not isinstance(last_message, AIMessage):
|
|
25
|
+
raise AgentNodeRoutingException(
|
|
26
|
+
f"Expected last message to be AIMessage, got {type(last_message).__name__}"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
for tool_call in last_message.tool_calls:
|
|
30
|
+
tool_name = tool_call["name"]
|
|
31
|
+
|
|
32
|
+
if tool_name == END_EXECUTION_TOOL.name:
|
|
33
|
+
args = tool_call["args"]
|
|
34
|
+
output_schema = response_schema or END_EXECUTION_TOOL.args_schema
|
|
35
|
+
validated = output_schema.model_validate(args)
|
|
36
|
+
return validated.model_dump()
|
|
37
|
+
|
|
38
|
+
if tool_name == RAISE_ERROR_TOOL.name:
|
|
39
|
+
error_message = tool_call["args"].get(
|
|
40
|
+
"message", "The LLM did not set the error message"
|
|
41
|
+
)
|
|
42
|
+
detail = tool_call["args"].get("details", "")
|
|
43
|
+
raise AgentTerminationException(
|
|
44
|
+
code=UiPathErrorCode.EXECUTION_ERROR,
|
|
45
|
+
title=error_message,
|
|
46
|
+
detail=detail,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
raise AgentNodeRoutingException(
|
|
50
|
+
"No control flow tool call found in terminate node. Unexpected state."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return terminate_node
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Control flow tools for agent execution."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from langchain_core.tools import BaseTool, StructuredTool
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
from uipath.agent.react import (
|
|
8
|
+
END_EXECUTION_TOOL,
|
|
9
|
+
RAISE_ERROR_TOOL,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def create_end_execution_tool(
|
|
14
|
+
agent_output_schema: type[BaseModel] | None = None,
|
|
15
|
+
) -> StructuredTool:
|
|
16
|
+
"""Never executed - routing intercepts and extracts args for successful termination."""
|
|
17
|
+
input_schema = agent_output_schema or END_EXECUTION_TOOL.args_schema
|
|
18
|
+
|
|
19
|
+
async def end_execution_fn(**kwargs: Any) -> dict[str, Any]:
|
|
20
|
+
return kwargs
|
|
21
|
+
|
|
22
|
+
return StructuredTool(
|
|
23
|
+
name=END_EXECUTION_TOOL.name,
|
|
24
|
+
description=END_EXECUTION_TOOL.description,
|
|
25
|
+
args_schema=input_schema,
|
|
26
|
+
coroutine=end_execution_fn,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def create_raise_error_tool() -> StructuredTool:
|
|
31
|
+
"""Never executed - routing intercepts and raises AgentTerminationException."""
|
|
32
|
+
|
|
33
|
+
async def raise_error_fn(**kwargs: Any) -> dict[str, Any]:
|
|
34
|
+
return kwargs
|
|
35
|
+
|
|
36
|
+
return StructuredTool(
|
|
37
|
+
name=RAISE_ERROR_TOOL.name,
|
|
38
|
+
description=RAISE_ERROR_TOOL.description,
|
|
39
|
+
args_schema=RAISE_ERROR_TOOL.args_schema,
|
|
40
|
+
coroutine=raise_error_fn,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def create_flow_control_tools(
|
|
45
|
+
agent_output_schema: type[BaseModel] | None = None,
|
|
46
|
+
) -> list[BaseTool]:
|
|
47
|
+
return [
|
|
48
|
+
create_end_execution_tool(agent_output_schema),
|
|
49
|
+
create_raise_error_tool(),
|
|
50
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""ReAct Agent loop utilities."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Sequence
|
|
4
|
+
|
|
5
|
+
from jsonschema_pydantic import jsonschema_to_pydantic # type: ignore[import-untyped]
|
|
6
|
+
from langchain_core.messages import AIMessage, BaseMessage
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
from uipath.agent.react import END_EXECUTION_TOOL
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def resolve_output_model(
|
|
12
|
+
output_schema: dict[str, Any] | None,
|
|
13
|
+
) -> type[BaseModel]:
|
|
14
|
+
"""Fallback to default end_execution tool schema when no agent output schema is provided."""
|
|
15
|
+
if output_schema:
|
|
16
|
+
return jsonschema_to_pydantic(output_schema)
|
|
17
|
+
|
|
18
|
+
return END_EXECUTION_TOOL.args_schema
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def count_successive_completions(messages: Sequence[BaseMessage]) -> int:
|
|
22
|
+
"""Count consecutive AIMessages without tool calls at end of message history."""
|
|
23
|
+
if not messages:
|
|
24
|
+
return 0
|
|
25
|
+
|
|
26
|
+
count = 0
|
|
27
|
+
for message in reversed(messages):
|
|
28
|
+
if not isinstance(message, AIMessage):
|
|
29
|
+
break
|
|
30
|
+
|
|
31
|
+
if message.tool_calls:
|
|
32
|
+
break
|
|
33
|
+
|
|
34
|
+
if not message.content:
|
|
35
|
+
break
|
|
36
|
+
|
|
37
|
+
count += 1
|
|
38
|
+
|
|
39
|
+
return count
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Context tool creation for semantic index retrieval."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
from langchain_core.tools import StructuredTool
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
from uipath.agent.models.agent import AgentContextResourceConfig
|
|
10
|
+
|
|
11
|
+
from uipath_langchain.retrievers import ContextGroundingRetriever
|
|
12
|
+
|
|
13
|
+
from .utils import sanitize_tool_name
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def create_context_tool(resource: AgentContextResourceConfig) -> StructuredTool:
|
|
17
|
+
tool_name = sanitize_tool_name(resource.name)
|
|
18
|
+
retriever = ContextGroundingRetriever(
|
|
19
|
+
index_name=resource.index_name,
|
|
20
|
+
folder_path=resource.folder_path,
|
|
21
|
+
number_of_results=resource.settings.result_count,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
async def context_tool_fn(query: str) -> str:
|
|
25
|
+
docs = await retriever.ainvoke(query)
|
|
26
|
+
|
|
27
|
+
if not docs:
|
|
28
|
+
return ""
|
|
29
|
+
|
|
30
|
+
return json.dumps([doc.model_dump() for doc in docs], indent=2)
|
|
31
|
+
|
|
32
|
+
class ContextInputSchemaModel(BaseModel):
|
|
33
|
+
query: str = Field(
|
|
34
|
+
..., description="The query to search for in the knowledge base"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
return StructuredTool(
|
|
38
|
+
name=tool_name,
|
|
39
|
+
description=resource.description,
|
|
40
|
+
args_schema=ContextInputSchemaModel,
|
|
41
|
+
coroutine=context_tool_fn,
|
|
42
|
+
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Process tool creation for UiPath process execution."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Type
|
|
6
|
+
|
|
7
|
+
from jsonschema_pydantic import jsonschema_to_pydantic # type: ignore[import-untyped]
|
|
8
|
+
from langchain_core.tools import StructuredTool
|
|
9
|
+
from langgraph.types import interrupt
|
|
10
|
+
from pydantic import BaseModel
|
|
11
|
+
from uipath.agent.models.agent import AgentProcessToolResourceConfig
|
|
12
|
+
from uipath.models import InvokeProcess
|
|
13
|
+
|
|
14
|
+
from .utils import sanitize_tool_name
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def create_process_tool(resource: AgentProcessToolResourceConfig) -> StructuredTool:
|
|
18
|
+
"""Uses interrupt() to suspend graph execution until process completes (handled by runtime)."""
|
|
19
|
+
tool_name: str = sanitize_tool_name(resource.name)
|
|
20
|
+
process_name = resource.properties.process_name
|
|
21
|
+
folder_path = resource.properties.folder_path
|
|
22
|
+
|
|
23
|
+
input_model: Type[BaseModel] = jsonschema_to_pydantic(resource.input_schema)
|
|
24
|
+
output_model: Type[BaseModel] = jsonschema_to_pydantic(resource.output_schema)
|
|
25
|
+
|
|
26
|
+
async def process_tool_fn(**kwargs: Any):
|
|
27
|
+
try:
|
|
28
|
+
result = interrupt(
|
|
29
|
+
InvokeProcess(
|
|
30
|
+
name=process_name,
|
|
31
|
+
input_arguments=kwargs,
|
|
32
|
+
process_folder_path=folder_path,
|
|
33
|
+
process_folder_key=None,
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
except Exception:
|
|
37
|
+
raise
|
|
38
|
+
|
|
39
|
+
return result
|
|
40
|
+
|
|
41
|
+
class ProcessTool(StructuredTool):
|
|
42
|
+
"""Process tool with OutputType for schema compatibility."""
|
|
43
|
+
|
|
44
|
+
OutputType: Type[BaseModel] = output_model
|
|
45
|
+
|
|
46
|
+
return ProcessTool(
|
|
47
|
+
name=tool_name,
|
|
48
|
+
description=resource.description,
|
|
49
|
+
args_schema=input_model,
|
|
50
|
+
coroutine=process_tool_fn,
|
|
51
|
+
)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Factory functions for creating tools from agent resources."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from langchain_core.tools import BaseTool, StructuredTool
|
|
6
|
+
from uipath.agent.models.agent import (
|
|
7
|
+
AgentContextResourceConfig,
|
|
8
|
+
AgentProcessToolResourceConfig,
|
|
9
|
+
BaseAgentResourceConfig,
|
|
10
|
+
LowCodeAgentDefinition,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from .context_tool import create_context_tool
|
|
14
|
+
from .process_tool import create_process_tool
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def create_tools_from_resources(
|
|
18
|
+
agent: LowCodeAgentDefinition,
|
|
19
|
+
) -> list[BaseTool]:
|
|
20
|
+
tools: list[BaseTool] = []
|
|
21
|
+
|
|
22
|
+
for resource in agent.resources:
|
|
23
|
+
tool = await _build_tool_for_resource(resource)
|
|
24
|
+
if tool is not None:
|
|
25
|
+
tools.append(tool)
|
|
26
|
+
|
|
27
|
+
return tools
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async def _build_tool_for_resource(
|
|
31
|
+
resource: BaseAgentResourceConfig,
|
|
32
|
+
) -> StructuredTool | None:
|
|
33
|
+
if isinstance(resource, AgentProcessToolResourceConfig):
|
|
34
|
+
return create_process_tool(resource)
|
|
35
|
+
|
|
36
|
+
elif isinstance(resource, AgentContextResourceConfig):
|
|
37
|
+
return create_context_tool(resource)
|
|
38
|
+
|
|
39
|
+
return None
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Tool node factory wiring directly to LangGraph's ToolNode."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
|
|
5
|
+
from langchain_core.tools import BaseTool
|
|
6
|
+
from langgraph.prebuilt import ToolNode
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def create_tool_node(tools: Sequence[BaseTool]) -> dict[str, ToolNode]:
|
|
10
|
+
"""Create individual ToolNode for each tool.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
tools: Sequence of tools to create nodes for.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
Dict mapping tool.name -> ToolNode([tool]).
|
|
17
|
+
Each tool gets its own dedicated node for middleware composition.
|
|
18
|
+
|
|
19
|
+
Note:
|
|
20
|
+
handle_tool_errors=False delegates error handling to LangGraph's error boundary.
|
|
21
|
+
"""
|
|
22
|
+
return {tool.name: ToolNode([tool], handle_tool_errors=False) for tool in tools}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Tool-related utility functions."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def sanitize_tool_name(name: str) -> str:
|
|
7
|
+
"""Sanitize tool name for LLM compatibility (alphanumeric, underscore, hyphen only, max 64 chars)."""
|
|
8
|
+
trim_whitespaces = "_".join(name.split())
|
|
9
|
+
sanitized_tool_name = re.sub(r"[^a-zA-Z0-9_-]", "", trim_whitespaces)
|
|
10
|
+
sanitized_tool_name = sanitized_tool_name[:64]
|
|
11
|
+
return sanitized_tool_name
|
|
@@ -10,11 +10,11 @@ uipath_langchain/_cli/cli_new.py,sha256=KKLxCzz7cDQ__rRr_a496IHWlSQXhmrBNgmKHnXA
|
|
|
10
10
|
uipath_langchain/_cli/cli_run.py,sha256=DIsAKsbQ8gTRz44q9ZV3jBjrbM8bhS6lEQ3dd4joDFU,3712
|
|
11
11
|
uipath_langchain/_cli/_runtime/_context.py,sha256=mjmGEogKiO8tUV878BgV9rFIeA9MCmEH6hgs5W_dm4g,328
|
|
12
12
|
uipath_langchain/_cli/_runtime/_conversation.py,sha256=ayghRqhyLeVUZg1WHnpeOYtPNhRwDOl4z8OSYiJkWSU,11529
|
|
13
|
-
uipath_langchain/_cli/_runtime/_exception.py,sha256=
|
|
14
|
-
uipath_langchain/_cli/_runtime/_graph_resolver.py,sha256=
|
|
15
|
-
uipath_langchain/_cli/_runtime/_input.py,sha256=
|
|
16
|
-
uipath_langchain/_cli/_runtime/_output.py,sha256=
|
|
17
|
-
uipath_langchain/_cli/_runtime/_runtime.py,sha256=
|
|
13
|
+
uipath_langchain/_cli/_runtime/_exception.py,sha256=xHKeu8njByiMcObbggyZk0cXYXX5BjLLF9PtSJtB4_Q,1358
|
|
14
|
+
uipath_langchain/_cli/_runtime/_graph_resolver.py,sha256=c-JrsX7rx_CflDPfKhz9q-PgBrgI2IOBcYwiffwddh8,5457
|
|
15
|
+
uipath_langchain/_cli/_runtime/_input.py,sha256=HAJUxjNmOg9q7l_ebF1AzIKL5_ysXyjk1bWXHsjhEPI,5761
|
|
16
|
+
uipath_langchain/_cli/_runtime/_output.py,sha256=2VvdW4olv7Vd0c4grtTQazXxfBbcuocgSSP6V2P8uHE,4887
|
|
17
|
+
uipath_langchain/_cli/_runtime/_runtime.py,sha256=HFWU2h864Ifq0gK9-_Jy6sbg0CMxmFExSdSEiUArChE,18548
|
|
18
18
|
uipath_langchain/_cli/_templates/langgraph.json.template,sha256=eeh391Gta_hoRgaNaZ58nW1LNvCVXA7hlAH6l7Veous,107
|
|
19
19
|
uipath_langchain/_cli/_templates/main.py.template,sha256=GpSblGH2hwS9ibqQmX2iB2nsmOA5zDfEEF4ChLiMxbQ,875
|
|
20
20
|
uipath_langchain/_cli/_utils/_graph.py,sha256=nMJWy8FmaD9rqPUY2lHc5uVpUzbXD1RO12uJnhe0kdo,6803
|
|
@@ -24,9 +24,27 @@ uipath_langchain/_tracing/__init__.py,sha256=C2dRvQ2ynxCmyICgE-rJHimWKEcFRME_o9g
|
|
|
24
24
|
uipath_langchain/_tracing/_instrument_traceable.py,sha256=8f9FyAKWE6kH1N8ErbpwqZHAzNjGwbLjQn7jdX5yAgA,4343
|
|
25
25
|
uipath_langchain/_tracing/_utils.py,sha256=r_fiSk3HDDAcePY_UbbEYiSbNqzn5gFeMPYBDvGrFx0,902
|
|
26
26
|
uipath_langchain/_utils/__init__.py,sha256=-w-4TD9ZnJDCpj4VIPXhJciukrmDJJbmnOFnhAkAaEU,81
|
|
27
|
-
uipath_langchain/_utils/_request_mixin.py,sha256=
|
|
27
|
+
uipath_langchain/_utils/_request_mixin.py,sha256=_drxHTRpfyVn3g3ppKgn466EBaUWH83qyeGKLY41CGY,20142
|
|
28
28
|
uipath_langchain/_utils/_settings.py,sha256=2fExMQJ88YptfldmzMfZIpsx-m1gfMkeYGf5t6KIe0A,3084
|
|
29
29
|
uipath_langchain/_utils/_sleep_policy.py,sha256=e9pHdjmcCj4CVoFM1jMyZFelH11YatsgWfpyrfXzKBQ,1251
|
|
30
|
+
uipath_langchain/agent/react/__init__.py,sha256=rfVB6PQWUhPHff3J1BnPMBKBMaHfgEkII1gXwjiqUMY,272
|
|
31
|
+
uipath_langchain/agent/react/agent.py,sha256=cU9ZiXi7EfV1pMf9nZBO8LhjYUmRsncyBk9k1pEBRh8,2455
|
|
32
|
+
uipath_langchain/agent/react/constants.py,sha256=B2yqryh242DETslaRYacUPbVdpjvvApjsBira_qhQwk,61
|
|
33
|
+
uipath_langchain/agent/react/exceptions.py,sha256=b3lDhrIIHFljlLK3zXPznT7fYzfMRjSd8JfF4247tbI,226
|
|
34
|
+
uipath_langchain/agent/react/init_node.py,sha256=zfPKgxi_mWsX7nBcK6wpqcDjHx8Q61TSnXFcTPLUd28,389
|
|
35
|
+
uipath_langchain/agent/react/llm_node.py,sha256=jkbfzPNn6rNubgncPlPDQRNuk-sJbj08r95JfWxxWL8,1491
|
|
36
|
+
uipath_langchain/agent/react/router.py,sha256=Ttq5O1_8t-z7pQ9tGhiaMmd_Da7_TWULLcgOED7gw_A,3626
|
|
37
|
+
uipath_langchain/agent/react/state.py,sha256=EnkGXFlmMtJUy7BTZrbYlGBvvAZ70_HwKPW8n6uwjz0,330
|
|
38
|
+
uipath_langchain/agent/react/terminate_node.py,sha256=Uuc-0z4qcPjHB_qZlaEaM2mK1ymCuJJludS7LopyCZg,1898
|
|
39
|
+
uipath_langchain/agent/react/utils.py,sha256=0kZoEkGzddtTZSlGQcqbaPHH5MVtZegq0kBI5_vreGA,1060
|
|
40
|
+
uipath_langchain/agent/react/tools/__init__.py,sha256=LGfG8Dc32ffKdXQyMI2oYzhNnTs1wbzsddXz6eU-0MY,102
|
|
41
|
+
uipath_langchain/agent/react/tools/tools.py,sha256=vFBGnFrGocX__sotKisMJr2lxRRVqA0-uThzzhPADIw,1443
|
|
42
|
+
uipath_langchain/agent/tools/__init__.py,sha256=GqQZLoMxkujjsButdLruxgzmQLX9k0YPtBaBAULSV2o,222
|
|
43
|
+
uipath_langchain/agent/tools/context_tool.py,sha256=Xd2qvc52Ks2eFXF2n-i0d8QDnbHXls_V8jyyNXwdySI,1243
|
|
44
|
+
uipath_langchain/agent/tools/process_tool.py,sha256=3OPxkIAJw_haohDeyDwSo8CIz9300XSvqIz5yjItspk,1715
|
|
45
|
+
uipath_langchain/agent/tools/tool_factory.py,sha256=NfhUU7EWf-zkt4xglkzp8ReOYDvKmoOTiz-OBh3ACWs,1053
|
|
46
|
+
uipath_langchain/agent/tools/tool_node.py,sha256=TnXsjoShvhsoBuV5RoUVoJCc2zYPKSnJYSC9MGJoeOk,707
|
|
47
|
+
uipath_langchain/agent/tools/utils.py,sha256=DsFeZ7kDzFaZ0bGHQN6TlGMJ90wYr7P1Vo1rpHPHWws,401
|
|
30
48
|
uipath_langchain/chat/__init__.py,sha256=WDcvy91ixvZ3Mq7Ae94g5CjyQwXovDBnEv1NlD5SXBE,116
|
|
31
49
|
uipath_langchain/chat/models.py,sha256=PifcbDURqfttqVYKSnzdbOdbSiLiwHfQ6lWgVAtoLj8,16407
|
|
32
50
|
uipath_langchain/embeddings/__init__.py,sha256=QICtYB58ZyqFfDQrEaO8lTEgAU5NuEKlR7iIrS0OBtc,156
|
|
@@ -37,8 +55,8 @@ uipath_langchain/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
|
37
55
|
uipath_langchain/tools/preconfigured.py,sha256=SyvrLrM1kezZxVVytgScVO8nBfVYfFGobWjY7erzsYU,7490
|
|
38
56
|
uipath_langchain/vectorstores/__init__.py,sha256=w8qs1P548ud1aIcVA_QhBgf_jZDrRMK5Lono78yA8cs,114
|
|
39
57
|
uipath_langchain/vectorstores/context_grounding_vectorstore.py,sha256=TncIXG-YsUlO0R5ZYzWsM-Dj1SVCZbzmo2LraVxXelc,9559
|
|
40
|
-
uipath_langchain-0.0.
|
|
41
|
-
uipath_langchain-0.0.
|
|
42
|
-
uipath_langchain-0.0.
|
|
43
|
-
uipath_langchain-0.0.
|
|
44
|
-
uipath_langchain-0.0.
|
|
58
|
+
uipath_langchain-0.0.144.dist-info/METADATA,sha256=fnn1iHs-_Q0lyubeoDFTb6c5LGZaWZbpxDnMytjjbRg,4276
|
|
59
|
+
uipath_langchain-0.0.144.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
60
|
+
uipath_langchain-0.0.144.dist-info/entry_points.txt,sha256=FUtzqGOEntlJKMJIXhQUfT7ZTbQmGhke1iCmDWZaQZI,81
|
|
61
|
+
uipath_langchain-0.0.144.dist-info/licenses/LICENSE,sha256=JDpt-uotAkHFmxpwxi6gwx6HQ25e-lG4U_Gzcvgp7JY,1063
|
|
62
|
+
uipath_langchain-0.0.144.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|