Skip to content

Commit 1eed1e2

Browse files
committed
clean up
1 parent 528de50 commit 1eed1e2

File tree

5 files changed

+8
-54
lines changed

5 files changed

+8
-54
lines changed

src/uipath_langchain/agent/tools/tool_node.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -274,11 +274,7 @@ async def _afunc(state: AgentGraphState) -> OutputType:
274274
raise
275275
return result
276276

277-
wrapped = RunnableCallable(func=_func, afunc=_afunc, name=tool_name)
278-
# Preserve .tool so _get_tool_confirmation_info can find metadata
279-
if hasattr(tool_node, "tool"):
280-
wrapped.tool = tool_node.tool # type: ignore[attr-defined]
281-
return wrapped
277+
return RunnableCallable(func=_func, afunc=_afunc, name=tool_name)
282278

283279

284280
class ToolWrapperMixin:

src/uipath_langchain/chat/hitl.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,6 @@ def request_approval(
108108
"""Interrupt the graph to request user approval for a tool call.
109109
110110
Returns the (possibly edited) tool arguments if approved, or None if rejected.
111-
112-
Confirmation data (inputSchema, inputValue) is included in the startToolCall
113-
event emitted by the mapper. The @durable_interrupt pauses the graph; the
114-
resume payload is the confirmToolCall event body from CAS:
115-
{"approved": bool, "input": <edited args | None>}
116111
"""
117112
tool_call_id: str = tool_args.pop("tool_call_id")
118113

src/uipath_langchain/runtime/messages.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ def __init__(self, runtime_id: str, storage: UiPathRuntimeStorageProtocol | None
5959
self.runtime_id = runtime_id
6060
self.storage = storage
6161
self.current_message: AIMessageChunk | AIMessage
62-
self.tool_names_requiring_confirmation: set[str] = set()
6362
self.tool_confirmation_schemas: dict[str, Any] = {}
6463
self.seen_message_ids: set[str] = set()
6564
self._storage_lock = asyncio.Lock()
@@ -321,7 +320,6 @@ async def map_ai_message_chunk_to_events(
321320

322321
events: list[UiPathConversationMessageEvent] = []
323322

324-
# For every new message_id, start a new message
325323
if message.id not in self.seen_message_ids:
326324
self.current_message = AIMessageChunk(content="", id=message.id)
327325
self.seen_message_ids.add(message.id)
@@ -428,7 +426,7 @@ async def map_current_message_to_start_tool_call_events(self):
428426

429427
tool_name = tool_call["name"]
430428
require_confirmation = (
431-
tool_name in self.tool_names_requiring_confirmation
429+
tool_name in self.tool_confirmation_schemas
432430
)
433431
input_schema = self.tool_confirmation_schemas.get(tool_name)
434432
events.append(

src/uipath_langchain/runtime/runtime.py

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
)
3030
from uipath.runtime.schema import UiPathRuntimeSchema
3131

32-
from uipath_langchain.chat.hitl import REQUIRE_CONVERSATIONAL_CONFIRMATION
3332
from uipath_langchain.runtime.errors import LangGraphErrorCode, LangGraphRuntimeError
3433
from uipath_langchain.runtime.messages import UiPathChatMessagesMapper
3534
from uipath_langchain.runtime.schema import get_entrypoints_schema, get_graph_schema
@@ -51,6 +50,7 @@ def __init__(
5150
entrypoint: str | None = None,
5251
callbacks: list[BaseCallbackHandler] | None = None,
5352
storage: UiPathRuntimeStorageProtocol | None = None,
53+
tool_confirmation_schemas: dict[str, Any] | None = None,
5454
):
5555
"""
5656
Initialize the runtime.
@@ -59,15 +59,14 @@ def __init__(
5959
graph: The CompiledStateGraph to execute
6060
runtime_id: Unique identifier for this runtime instance
6161
entrypoint: Optional entrypoint name (for schema generation)
62+
tool_confirmation_schemas: Map of tool name to JSON schema for tool confirmation
6263
"""
6364
self.graph: CompiledStateGraph[Any, Any, Any, Any] = graph
6465
self.runtime_id: str = runtime_id or "default"
6566
self.entrypoint: str | None = entrypoint
6667
self.callbacks: list[BaseCallbackHandler] = callbacks or []
6768
self.chat = UiPathChatMessagesMapper(self.runtime_id, storage)
68-
confirmation_names, confirmation_schemas = self._get_tool_confirmation_info()
69-
self.chat.tool_names_requiring_confirmation = confirmation_names
70-
self.chat.tool_confirmation_schemas = confirmation_schemas
69+
self.chat.tool_confirmation_schemas = tool_confirmation_schemas or {}
7170
self._middleware_node_names: set[str] = self._detect_middleware_nodes()
7271

7372
async def execute(
@@ -490,40 +489,6 @@ def _detect_middleware_nodes(self) -> set[str]:
490489

491490
return middleware_nodes
492491

493-
def _get_tool_confirmation_info(self) -> tuple[set[str], dict[str, Any]]:
494-
"""Single pass over graph nodes to collect confirmation tool names and schemas."""
495-
names: set[str] = set()
496-
schemas: dict[str, Any] = {}
497-
498-
def _record(tool: Any, fallback_name: str) -> None:
499-
metadata = getattr(tool, "metadata", None) or {}
500-
if not metadata.get(REQUIRE_CONVERSATIONAL_CONFIRMATION):
501-
return
502-
tool_name = getattr(tool, "name", fallback_name)
503-
names.add(tool_name)
504-
tool_call_schema = getattr(tool, "tool_call_schema", None)
505-
if tool_call_schema is not None:
506-
schemas[tool_name] = tool_call_schema.model_json_schema()
507-
else:
508-
schemas[tool_name] = {}
509-
510-
for node_name, node_spec in self.graph.nodes.items():
511-
bound = getattr(node_spec, "bound", None)
512-
if bound is None:
513-
continue
514-
# Low-code: single-tool UiPathToolNode
515-
tool = getattr(bound, "tool", None)
516-
if tool is not None:
517-
_record(tool, node_name)
518-
continue
519-
# Coded: multi-tool ToolNode (create_agent)
520-
tools_by_name = getattr(bound, "tools_by_name", None)
521-
if isinstance(tools_by_name, dict):
522-
for fallback_name, tool in tools_by_name.items():
523-
_record(tool, fallback_name)
524-
525-
return names, schemas
526-
527492
def _is_middleware_node(self, node_name: str) -> bool:
528493
"""Check if a node name represents a middleware node."""
529494
return node_name in self._middleware_node_names

tests/runtime/test_chat_message_mapper.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2019,7 +2019,7 @@ async def test_confirmation_tool_has_requires_confirmation_metadata(self):
20192019
storage = create_mock_storage()
20202020
storage.get_value.return_value = {}
20212021
mapper = UiPathChatMessagesMapper("test-runtime", storage)
2022-
mapper.tool_names_requiring_confirmation = {"confirm_tool"}
2022+
mapper.tool_confirmation_schemas = {"confirm_tool": {}}
20232023

20242024
first_chunk = AIMessageChunk(
20252025
content="",
@@ -2053,7 +2053,7 @@ async def test_normal_tool_has_no_confirmation_metadata(self):
20532053
storage = create_mock_storage()
20542054
storage.get_value.return_value = {}
20552055
mapper = UiPathChatMessagesMapper("test-runtime", storage)
2056-
mapper.tool_names_requiring_confirmation = {"other_tool"}
2056+
mapper.tool_confirmation_schemas = {"other_tool": {}}
20572057

20582058
first_chunk = AIMessageChunk(
20592059
content="",
@@ -2086,7 +2086,7 @@ async def test_mixed_tools_only_confirmation_has_metadata(self):
20862086
storage = create_mock_storage()
20872087
storage.get_value.return_value = {}
20882088
mapper = UiPathChatMessagesMapper("test-runtime", storage)
2089-
mapper.tool_names_requiring_confirmation = {"confirm_tool"}
2089+
mapper.tool_confirmation_schemas = {"confirm_tool": {}}
20902090

20912091
first_chunk = AIMessageChunk(
20922092
content="",

0 commit comments

Comments
 (0)