Skip to content

Commit e23f93f

Browse files
committed
� fix: correct the orchestrator api used for file upload
1 parent a3c7f53 commit e23f93f

3 files changed

Lines changed: 22 additions & 12 deletions

File tree

src/uipath_langchain/agent/tools/internal_tools/analyze_files_tool.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
from uipath_langchain.agent.react.jsonschema_pydantic_converter import create_model
4141
from uipath_langchain.agent.tools.internal_tools.pii_masker import (
4242
PiiMasker,
43-
_masked_name_for,
43+
masked_name_for,
4444
)
4545
from uipath_langchain.agent.tools.structured_tool_with_argument_properties import (
4646
StructuredToolWithArgumentProperties,
@@ -120,7 +120,7 @@ def _llm_call_attachments_payload(files: list[FileInfo]) -> str | None:
120120
att_id = file.masked_attachment_id or _masked_attachment_id(
121121
file.masked_attachment_url
122122
)
123-
name = _masked_name_for(file.name)
123+
name = masked_name_for(file.name)
124124
else:
125125
att_id = _original_attachment_id(file)
126126
name = file.name
@@ -192,7 +192,7 @@ def _emit_pii_masking_attachments(span: otel_trace.Span, files: list[FileInfo])
192192
masked_id = file.masked_attachment_id or _masked_attachment_id(
193193
file.masked_attachment_url
194194
)
195-
masked_name = _masked_name_for(file.name)
195+
masked_name = masked_name_for(file.name)
196196
attachments.append(
197197
SpanAttachment(
198198
id=masked_id,
@@ -415,7 +415,7 @@ async def add_files_to_message(
415415
llm_file = (
416416
FileInfo(
417417
url=file.masked_attachment_url,
418-
name=_masked_name_for(file.name),
418+
name=masked_name_for(file.name),
419419
mime_type=file.mime_type,
420420
)
421421
if file.masked_attachment_url

src/uipath_langchain/agent/tools/internal_tools/pii_masker.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
_FEATURE_FLAG = "FilePiiMaskingEnabled"
2727

2828

29-
def _masked_name_for(name: str) -> str:
29+
def masked_name_for(name: str) -> str:
3030
"""Apply the ``pii_masked_`` filename prefix for re-uploaded masked files."""
3131
if "." in name:
3232
base, ext = name.rsplit(".", 1)
@@ -193,8 +193,10 @@ async def _upload_masked_to_orchestrator(self, file: FileInfo) -> str | None:
193193
194194
The PII service returns a blob URL that LLMOps has no way to resolve, so
195195
clicking the masked attachment in the trace viewer fails. Fetching the
196-
bytes and uploading them via ``client.attachments.upload_async`` gives
197-
us a real orchestrator UUID that the UI knows how to download.
196+
bytes and uploading them via ``client.jobs.create_attachment_async``
197+
gives us a real orchestrator UUID that the UI knows how to download,
198+
and links the attachment to the current job so it shows up under the
199+
job's attachments (job_key falls back to the running job's instance_key).
198200
199201
Returns the uploaded attachment id, or ``None`` on failure (callers fall
200202
back to a synthesized uuid5 — the trace still shows the file, just not
@@ -206,9 +208,16 @@ async def _upload_masked_to_orchestrator(self, file: FileInfo) -> str | None:
206208
content = base64.b64decode(
207209
await download_file_base64(file.masked_attachment_url)
208210
)
209-
attachment_key = await self._client.attachments.upload_async(
210-
name=_masked_name_for(file.name),
211+
masked_name = masked_name_for(file.name)
212+
attachment_key = await self._client.jobs.create_attachment_async(
213+
name=masked_name,
211214
content=content,
215+
category="pii masked",
216+
)
217+
logger.info(
218+
"Uploaded masked attachment '%s' as id=%s",
219+
masked_name,
220+
attachment_key,
212221
)
213222
return str(attachment_key)
214223
except Exception:

tests/agent/tools/internal_tools/test_pii_masker.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ def _make_client(
5555
client = Mock()
5656
client.semantic_proxy = Mock()
5757
client.semantic_proxy.detect_pii_async = AsyncMock(return_value=response)
58-
client.attachments = Mock()
59-
client.attachments.upload_async = AsyncMock(
58+
client.jobs = Mock()
59+
client.jobs.create_attachment_async = AsyncMock(
6060
return_value=upload_result or uuid.uuid4()
6161
)
6262
return client
@@ -245,9 +245,10 @@ async def test_masks_prompt_and_annotates_files(self, httpx_mock):
245245
assert request.files[0].file_name == "doc.pdf"
246246
assert request.files[0].file_type == "pdf"
247247

248-
upload_call = client.attachments.upload_async.call_args
248+
upload_call = client.jobs.create_attachment_async.call_args
249249
assert upload_call.kwargs["name"] == "pii_masked_doc.pdf"
250250
assert upload_call.kwargs["content"] == b"redacted-bytes"
251+
assert upload_call.kwargs["category"] == "pii masked"
251252

252253
async def test_returns_original_files_when_no_redactions(self):
253254
response = _make_pii_response(masked_prompt="clean prompt")

0 commit comments

Comments
 (0)