Skip to content

Commit a6320f5

Browse files
fix: eph index key
1 parent 2864869 commit a6320f5

2 files changed

Lines changed: 65 additions & 78 deletions

File tree

packages/uipath-platform/src/uipath/platform/context_grounding/_context_grounding_service.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -696,8 +696,12 @@ def create_ephemeral_index(
696696
Returns:
697697
ContextGroundingIndex: The created index information.
698698
"""
699-
if folder_key is not None or folder_path is not None:
700-
folder_key = self._resolve_folder_key(folder_key, folder_path)
699+
# Resolve the folder key the same way retrieve_by_id does (falling back
700+
# to the ambient folder context) so the index is created in the same
701+
# folder scope it will later be retrieved from. Otherwise the index is
702+
# created tenant-scoped but the GET sends x-uipath-folderkey, scoping the
703+
# lookup to a folder the index doesn't live in -> 404 "Schema not found".
704+
folder_key = self._resolve_folder_key(folder_key, folder_path)
701705
spec = self._create_ephemeral_spec(
702706
usage,
703707
attachments,
@@ -733,8 +737,12 @@ async def create_ephemeral_index_async(
733737
Returns:
734738
ContextGroundingIndex: The created index information.
735739
"""
736-
if folder_key is not None or folder_path is not None:
737-
folder_key = self._resolve_folder_key(folder_key, folder_path)
740+
# Resolve the folder key the same way retrieve_by_id does (falling back
741+
# to the ambient folder context) so the index is created in the same
742+
# folder scope it will later be retrieved from. Otherwise the index is
743+
# created tenant-scoped but the GET sends x-uipath-folderkey, scoping the
744+
# lookup to a folder the index doesn't live in -> 404 "Schema not found".
745+
folder_key = self._resolve_folder_key(folder_key, folder_path)
738746
spec = self._create_ephemeral_spec(
739747
usage,
740748
attachments,
@@ -2262,11 +2270,13 @@ def _retrieve_by_id_spec(
22622270
folder_key: Optional[str] = None,
22632271
folder_path: Optional[str] = None,
22642272
) -> RequestSpec:
2265-
# Folder key not needed by retrieve by id. Adding it breaks ephemeral indexes
2273+
folder_key = self._resolve_folder_key(folder_key, folder_path)
2274+
22662275
return RequestSpec(
22672276
method="GET",
22682277
endpoint=Endpoint(f"/ecs_/v2/indexes/{id}"),
22692278
headers={
2279+
**header_folder(folder_key, None),
22702280
**header_job_key(),
22712281
},
22722282
)

packages/uipath-platform/tests/services/test_context_grounding_service.py

Lines changed: 50 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from uipath.platform import UiPathApiConfig, UiPathExecutionContext
99
from uipath.platform.common.constants import (
1010
ENV_JOB_KEY,
11-
HEADER_FOLDER_KEY,
1211
HEADER_JOB_KEY,
1312
HEADER_USER_AGENT,
1413
)
@@ -3178,6 +3177,19 @@ def test_create_ephemeral_index(
31783177
) -> None:
31793178
import uuid
31803179

3180+
httpx_mock.add_response(
3181+
url=f"{base_url}{org}{tenant}/orchestrator_/api/FoldersNavigation/GetFoldersForCurrentUser?searchText=test-folder-path&skip=0&take=20",
3182+
status_code=200,
3183+
json={
3184+
"PageItems": [
3185+
{
3186+
"Key": "test-folder-key",
3187+
"FullyQualifiedName": "test-folder-path",
3188+
}
3189+
]
3190+
},
3191+
)
3192+
31813193
httpx_mock.add_response(
31823194
url=f"{base_url}{org}{tenant}/ecs_/v2/indexes/createephemeral",
31833195
status_code=200,
@@ -3203,22 +3215,27 @@ def test_create_ephemeral_index(
32033215
if sent_requests is None:
32043216
raise Exception("No request was sent")
32053217

3206-
assert sent_requests[0].method == "POST"
3207-
assert (
3208-
sent_requests[0].url
3209-
== f"{base_url}{org}{tenant}/ecs_/v2/indexes/createephemeral"
3218+
create_request = next(
3219+
r
3220+
for r in sent_requests
3221+
if str(r.url).endswith("/ecs_/v2/indexes/createephemeral")
32103222
)
3223+
assert create_request.method == "POST"
32113224

3212-
request_data = json.loads(sent_requests[0].content)
3225+
request_data = json.loads(create_request.content)
32133226
assert request_data["usage"] == "DeepRAG"
32143227
assert "dataSource" in request_data
32153228
assert request_data["dataSource"]["attachments"] == [
32163229
str(att) for att in attachment_ids
32173230
]
32183231

3219-
assert HEADER_USER_AGENT in sent_requests[0].headers
3232+
# Ambient folder context is resolved and scopes the create, matching how
3233+
# retrieve_by_id resolves the folder for the subsequent GET.
3234+
assert create_request.headers["x-uipath-folderkey"] == "test-folder-key"
3235+
3236+
assert HEADER_USER_AGENT in create_request.headers
32203237
assert (
3221-
sent_requests[0].headers[HEADER_USER_AGENT]
3238+
create_request.headers[HEADER_USER_AGENT]
32223239
== f"UiPath.Python.Sdk/UiPath.Python.Sdk.Activities.ContextGroundingService.create_ephemeral_index/{version}"
32233240
)
32243241

@@ -3234,6 +3251,19 @@ async def test_create_ephemeral_index_async(
32343251
) -> None:
32353252
import uuid
32363253

3254+
httpx_mock.add_response(
3255+
url=f"{base_url}{org}{tenant}/orchestrator_/api/FoldersNavigation/GetFoldersForCurrentUser?searchText=test-folder-path&skip=0&take=20",
3256+
status_code=200,
3257+
json={
3258+
"PageItems": [
3259+
{
3260+
"Key": "test-folder-key",
3261+
"FullyQualifiedName": "test-folder-path",
3262+
}
3263+
]
3264+
},
3265+
)
3266+
32373267
httpx_mock.add_response(
32383268
url=f"{base_url}{org}{tenant}/ecs_/v2/indexes/createephemeral",
32393269
status_code=200,
@@ -3259,22 +3289,27 @@ async def test_create_ephemeral_index_async(
32593289
if sent_requests is None:
32603290
raise Exception("No request was sent")
32613291

3262-
assert sent_requests[0].method == "POST"
3263-
assert (
3264-
sent_requests[0].url
3265-
== f"{base_url}{org}{tenant}/ecs_/v2/indexes/createephemeral"
3292+
create_request = next(
3293+
r
3294+
for r in sent_requests
3295+
if str(r.url).endswith("/ecs_/v2/indexes/createephemeral")
32663296
)
3297+
assert create_request.method == "POST"
32673298

3268-
request_data = json.loads(sent_requests[0].content)
3299+
request_data = json.loads(create_request.content)
32693300
assert request_data["usage"] == "DeepRAG"
32703301
assert "dataSource" in request_data
32713302
assert request_data["dataSource"]["attachments"] == [
32723303
str(att) for att in attachment_ids
32733304
]
32743305

3275-
assert HEADER_USER_AGENT in sent_requests[0].headers
3306+
# Ambient folder context is resolved and scopes the create, matching how
3307+
# retrieve_by_id resolves the folder for the subsequent GET.
3308+
assert create_request.headers["x-uipath-folderkey"] == "test-folder-key"
3309+
3310+
assert HEADER_USER_AGENT in create_request.headers
32763311
assert (
3277-
sent_requests[0].headers[HEADER_USER_AGENT]
3312+
create_request.headers[HEADER_USER_AGENT]
32783313
== f"UiPath.Python.Sdk/UiPath.Python.Sdk.Activities.ContextGroundingService.create_ephemeral_index_async/{version}"
32793314
)
32803315

@@ -3919,61 +3954,3 @@ def test_ingest_data_omits_job_key_header_when_env_unset(
39193954

39203955
headers = mock_request.call_args[1]["headers"]
39213956
assert HEADER_JOB_KEY not in headers
3922-
3923-
3924-
class TestRetrieveByIdFolderScoping:
3925-
"""retrieve_by_id must not send a folder header.
3926-
3927-
Indexes are looked up by id tenant-wide. Ephemeral indexes are created
3928-
tenant-scoped, so sending x-uipath-folderkey on the GET scopes the lookup to
3929-
a folder the index does not live in, producing a 404 "Schema not found".
3930-
Regression: ECS-1819.
3931-
"""
3932-
3933-
def test_omits_folder_header_even_with_ambient_folder(
3934-
self,
3935-
service: ContextGroundingService,
3936-
monkeypatch: pytest.MonkeyPatch,
3937-
) -> None:
3938-
monkeypatch.delenv(ENV_JOB_KEY, raising=False)
3939-
# Simulate running inside a folder context (e.g. UIPATH_FOLDER_KEY set).
3940-
service._folder_key = "ambient-folder-key"
3941-
3942-
with patch.object(service, "request") as mock_request:
3943-
mock_request.return_value = MagicMock()
3944-
service.retrieve_by_id("ephemeral-index-id")
3945-
3946-
headers = mock_request.call_args[1]["headers"]
3947-
assert HEADER_FOLDER_KEY not in headers
3948-
3949-
@pytest.mark.anyio
3950-
async def test_async_omits_folder_header_even_with_ambient_folder(
3951-
self,
3952-
service: ContextGroundingService,
3953-
monkeypatch: pytest.MonkeyPatch,
3954-
) -> None:
3955-
monkeypatch.delenv(ENV_JOB_KEY, raising=False)
3956-
service._folder_key = "ambient-folder-key"
3957-
3958-
with patch.object(service, "request_async") as mock_request:
3959-
mock_request.return_value = MagicMock()
3960-
await service.retrieve_by_id_async("ephemeral-index-id")
3961-
3962-
headers = mock_request.call_args[1]["headers"]
3963-
assert HEADER_FOLDER_KEY not in headers
3964-
3965-
def test_still_carries_job_key(
3966-
self,
3967-
service: ContextGroundingService,
3968-
monkeypatch: pytest.MonkeyPatch,
3969-
) -> None:
3970-
monkeypatch.setenv(ENV_JOB_KEY, "job-key-retrieve")
3971-
service._folder_key = "ambient-folder-key"
3972-
3973-
with patch.object(service, "request") as mock_request:
3974-
mock_request.return_value = MagicMock()
3975-
service.retrieve_by_id("ephemeral-index-id")
3976-
3977-
headers = mock_request.call_args[1]["headers"]
3978-
assert headers[HEADER_JOB_KEY] == "job-key-retrieve"
3979-
assert HEADER_FOLDER_KEY not in headers

0 commit comments

Comments
 (0)