Skip to content

Commit d85a752

Browse files
feat: add extra metadata for tools (#608)
1 parent a8da676 commit d85a752

File tree

7 files changed

+52
-16
lines changed

7 files changed

+52
-16
lines changed

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
[project]
22
name = "uipath-langchain"
3-
version = "0.7.6"
3+
version = "0.7.7"
44
description = "Python SDK that enables developers to build and deploy LangGraph agents to the UiPath Cloud Platform"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"
77
dependencies = [
8-
"uipath>=2.9.10, <2.10.0",
8+
"uipath>=2.9.12, <2.10.0",
99
"uipath-core>=0.5.2, <0.6.0",
10-
"uipath-platform>=0.0.1, < 0.1.0",
10+
"uipath-platform>=0.0.4, < 0.1.0",
1111
"uipath-runtime>=0.9.1, <0.10.0",
1212
"langgraph>=1.0.0, <2.0.0",
1313
"langchain-core>=1.2.11, <2.0.0",

src/uipath_langchain/agent/tools/context_tool.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ async def context_tool_fn(query: Optional[str] = None) -> dict[str, Any]:
123123
metadata={
124124
"tool_type": "context",
125125
"display_name": resource.name,
126+
"index_name": resource.index_name,
127+
"context_retrieval_mode": resource.settings.retrieval_mode,
126128
},
127129
)
128130

@@ -202,6 +204,8 @@ async def create_deep_rag():
202204
metadata={
203205
"tool_type": "context",
204206
"display_name": resource.name,
207+
"index_name": resource.index_name,
208+
"context_retrieval_mode": resource.settings.retrieval_mode,
205209
},
206210
)
207211

@@ -335,6 +339,8 @@ async def context_batch_transform_wrapper(
335339
metadata={
336340
"tool_type": "context",
337341
"display_name": resource.name,
342+
"index_name": resource.index_name,
343+
"context_retrieval_mode": resource.settings.retrieval_mode,
338344
},
339345
)
340346
tool.set_tool_wrappers(awrapper=context_batch_transform_wrapper)

src/uipath_langchain/agent/tools/escalation_tool.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ class EscalationToolOutput(BaseModel):
154154
action: Literal["approve", "reject"]
155155
data: output_model
156156

157+
_bts_context: dict[str, Any] = {}
158+
157159
async def escalation_tool_fn(**kwargs: Any) -> dict[str, Any]:
158160
recipient: TaskRecipient | None = (
159161
await resolve_recipient_value(channel.recipients[0])
@@ -191,6 +193,10 @@ async def create_escalation_task():
191193
is_actionable_message_enabled=channel.properties.is_actionable_message_enabled,
192194
actionable_message_metadata=channel.properties.actionable_message_meta_data,
193195
)
196+
197+
if created_task.id is not None:
198+
_bts_context["task_key"] = str(created_task.id)
199+
194200
return WaitEscalation(
195201
action=created_task,
196202
app_folder_path=channel.properties.folder_name,
@@ -283,6 +289,7 @@ async def escalation_wrapper(
283289
"recipient": None,
284290
"args_schema": input_model,
285291
"output_schema": output_model,
292+
"_bts_context": _bts_context,
286293
},
287294
)
288295
tool.set_tool_wrappers(awrapper=escalation_wrapper)

src/uipath_langchain/agent/tools/integration_tool.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@ async def integration_tool_wrapper(
201201
metadata={
202202
"tool_type": "integration",
203203
"display_name": resource.name,
204+
"connector_key": resource.properties.connection.id,
205+
"connector_name": resource.properties.connection.name,
204206
},
205207
)
206208
tool.set_tool_wrappers(awrapper=integration_tool_wrapper)

src/uipath_langchain/agent/tools/process_tool.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from langchain.tools import BaseTool
66
from langchain_core.messages import ToolCall
77
from langchain_core.tools import StructuredTool
8-
from uipath.agent.models.agent import AgentProcessToolResourceConfig
8+
from uipath.agent.models.agent import AgentProcessToolResourceConfig, AgentToolType
99
from uipath.eval.mocks import mockable
1010
from uipath.platform import UiPath
1111
from uipath.platform.common import WaitJob
@@ -38,6 +38,7 @@ def create_process_tool(resource: AgentProcessToolResourceConfig) -> StructuredT
3838
output_model: Any = create_model(resource.output_schema)
3939

4040
_span_context: dict[str, Any] = {}
41+
_bts_context: dict[str, Any] = {}
4142

4243
async def process_tool_fn(**kwargs: Any):
4344
attachments = get_job_attachments(input_model, kwargs)
@@ -52,6 +53,7 @@ async def process_tool_fn(**kwargs: Any):
5253
)
5354
async def invoke_process(**_tool_kwargs: Any):
5455
parent_span_id = _span_context.pop("parent_span_id", None)
56+
parent_operation_id = _bts_context.pop("parent_operation_id", None)
5557

5658
@durable_interrupt
5759
async def start_job():
@@ -62,7 +64,17 @@ async def start_job():
6264
folder_path=folder_path,
6365
attachments=attachments,
6466
parent_span_id=parent_span_id,
67+
parent_operation_id=parent_operation_id,
6568
)
69+
70+
if job.key:
71+
bts_key = (
72+
"wait_for_agent_job_key"
73+
if resource.type == AgentToolType.AGENT
74+
else "wait_for_job_key"
75+
)
76+
_bts_context[bts_key] = str(job.key)
77+
6678
return WaitJob(job=job, process_folder_key=job.folder_key)
6779

6880
return await start_job()
@@ -92,6 +104,7 @@ async def process_tool_wrapper(
92104
"args_schema": input_model,
93105
"output_schema": output_model,
94106
"_span_context": _span_context,
107+
"_bts_context": _bts_context,
95108
},
96109
argument_properties=resource.argument_properties,
97110
)

tests/agent/tools/test_process_tool.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ async def test_invoke_calls_processes_invoke_async(
121121
):
122122
"""Test that invoking the tool calls client.processes.invoke_async."""
123123
mock_job = MagicMock(spec=Job)
124+
mock_job.key = "job-key-123"
124125
mock_job.folder_key = "folder-key-123"
125126

126127
mock_client = MagicMock()
@@ -138,6 +139,7 @@ async def test_invoke_calls_processes_invoke_async(
138139
folder_path="/Shared/MyFolder",
139140
attachments=[],
140141
parent_span_id=None,
142+
parent_operation_id=None,
141143
)
142144

143145
@pytest.mark.asyncio
@@ -148,6 +150,7 @@ async def test_invoke_interrupts_with_wait_job(
148150
):
149151
"""Test that after invoking, the tool interrupts with WaitJob."""
150152
mock_job = MagicMock(spec=Job)
153+
mock_job.key = "job-key-456"
151154
mock_job.folder_key = "folder-key-456"
152155

153156
mock_client = MagicMock()
@@ -173,6 +176,7 @@ async def test_invoke_passes_input_arguments(
173176
):
174177
"""Test that input arguments are correctly passed to invoke_async."""
175178
mock_job = MagicMock(spec=Job)
179+
mock_job.key = "job-key"
176180
mock_job.folder_key = "folder-key"
177181

178182
mock_client = MagicMock()
@@ -197,6 +201,7 @@ async def test_invoke_returns_interrupt_value(
197201
):
198202
"""Test that the tool returns the value from interrupt()."""
199203
mock_job = MagicMock(spec=Job)
204+
mock_job.key = "job-key"
200205
mock_job.folder_key = "folder-key"
201206

202207
mock_client = MagicMock()
@@ -222,6 +227,7 @@ async def test_span_context_parent_span_id_passed_to_invoke(
222227
):
223228
"""Test that parent_span_id from _span_context is forwarded to invoke_async."""
224229
mock_job = MagicMock(spec=Job)
230+
mock_job.key = "job-key"
225231
mock_job.folder_key = "folder-key"
226232

227233
mock_client = MagicMock()
@@ -249,6 +255,7 @@ async def test_span_context_consumed_after_invoke(
249255
):
250256
"""Test that parent_span_id is popped (consumed) from _span_context after use."""
251257
mock_job = MagicMock(spec=Job)
258+
mock_job.key = "job-key"
252259
mock_job.folder_key = "folder-key"
253260

254261
mock_client = MagicMock()
@@ -274,6 +281,7 @@ async def test_span_context_defaults_to_none_when_empty(
274281
):
275282
"""Test that parent_span_id defaults to None when _span_context is empty."""
276283
mock_job = MagicMock(spec=Job)
284+
mock_job.key = "job-key"
277285
mock_job.folder_key = "folder-key"
278286

279287
mock_client = MagicMock()

uv.lock

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)