Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8badd70
feat(agent): add ontology binding to Data Fabric context config
sankalp-uipath Jun 17, 2026
906d618
feat(entities): add ontology binding + get_ontology_file to Data Fabr…
sankalp-uipath Jun 17, 2026
cd4ff88
feat(agent): allow multiple ontologies per context (ontologySet)
sankalp-uipath Jun 17, 2026
8175316
Merge remote-tracking branch 'origin/main' into feat/agent-datafabric…
sankalp-uipath Jun 17, 2026
2048c56
fix(entities): scope ontology fetch via header_folder, not missing _f…
sankalp-uipath Jun 22, 2026
28d3c0c
feat(entities): validate ontology file_type; require folderId; add co…
sankalp-uipath Jun 22, 2026
33f76f0
Merge branch 'main' into feat/agent-datafabric-ontology-binding
sankalp-uipath Jun 22, 2026
d4ccd79
chore: bump uipath to 2.11.8 and uipath-platform to 0.1.72
sankalp-uipath Jun 22, 2026
f22074c
docs(entities): mark get_ontology_file_async as Preview Feature
sankalp-uipath Jun 23, 2026
74d47bb
Merge branch 'main' into feat/agent-datafabric-ontology-binding
sankalp-uipath Jun 23, 2026
bcbf2dd
chore: bump uipath to 2.11.9 (main advanced to 2.11.8)
sankalp-uipath Jun 23, 2026
75aaab5
feat(agent): ontology as standalone resource (ontologyRefs)
sankalp-uipath Jun 23, 2026
336bfb3
Merge remote-tracking branch 'origin/main' into feat/agent-datafabric…
sankalp-uipath Jun 23, 2026
c231702
test: annotate parsed for mypy (var-annotated)
sankalp-uipath Jun 23, 2026
2fe9114
Merge branch 'main' into feat/agent-datafabric-ontology-binding
sankalp-uipath Jun 24, 2026
6877f6a
chore: bump uipath to 2.11.11 (above main)
sankalp-uipath Jun 24, 2026
69d42df
refactor(agent): nest ontology as ontologySet on context
sankalp-uipath Jun 25, 2026
1ce9049
chore: merge main; bump uipath 2.11.13 / platform 0.1.77
sankalp-uipath Jun 25, 2026
9e22841
Merge branch 'main' into feat/agent-datafabric-ontology-binding
sankalp-uipath Jun 25, 2026
b6f0c9a
refactor(agent): model ontology as a datafabricontology context (bump…
sankalp-uipath Jun 25, 2026
56ac4a4
chore: merge main; bump uipath 2.11.14 / platform 0.1.79 (above main)
sankalp-uipath Jun 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/uipath-platform/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uipath-platform"
version = "0.1.78"
version = "0.1.79"
description = "HTTP client library for programmatic access to UiPath Platform"
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.11"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
AggregateRow,
ChoiceSetValue,
DataFabricEntityItem,
DataFabricOntologyItem,
Entity,
EntityAggregate,
EntityAggregateFunction,
Expand Down Expand Up @@ -46,6 +47,7 @@
"AggregateRow",
"ChoiceSetValue",
"DataFabricEntityItem",
"DataFabricOntologyItem",
"EntitiesService",
"Entity",
"EntityAggregate",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""

import logging
import re
from typing import Any, Dict, List, Optional, Type

from httpx import Response
Expand All @@ -24,6 +25,8 @@
from ..common._bindings import _resource_overwrites
from ..common._config import UiPathApiConfig
from ..common._execution_context import UiPathExecutionContext
from ..common._folder_context import header_folder
from ..common._models import Endpoint, RequestSpec
from ..orchestrator._folder_service import FolderService
from ._entity_data_service import EntityDataService, FileContent
from ._entity_resolution import (
Expand Down Expand Up @@ -58,6 +61,12 @@

logger = logging.getLogger(__name__)

# Ontology name contract (QueryEngine OntologyController): lowercase, starts
# with a letter, max 64 chars. The name becomes a URL path segment.
_ONTOLOGY_NAME_RE = re.compile(r"^[a-z][a-z0-9-]{0,63}$")
# Allowed ontology component file types (also URL path segments).
_ONTOLOGY_FILE_TYPES = frozenset({"owl", "r2rml", "shacl", "summary", "context"})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats the rationale here?



class EntitiesService(BaseService):
"""Service for managing UiPath Data Service entities.
Expand Down Expand Up @@ -1100,6 +1109,68 @@ async def delete_record_async(self, entity_key: str, record_id: str) -> None:
"""
await self._data.delete_record_async(entity_key, record_id)

async def get_ontology_file_async(
self,
Comment on lines +1112 to +1113
ontology_name: str,
file_type: str = "owl",
folder_key: Optional[str] = None,
) -> Dict[str, Any]:
Comment thread
sankalp-uipath marked this conversation as resolved.
"""Fetch one file of an ontology from Data Fabric.

!!! warning "Preview Feature"
This method is currently experimental. Behavior and parameters are
subject to change in future versions.

Ontologies are served by the same QueryEngine service as entity SQL

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this implementation detail from the doc

queries, under ``datafabric_/api/ontologies``. The JSON wrapper is
requested so the result is notation-agnostic — the ``owl`` file content
may be Turtle or OWL Functional Notation.

Args:
ontology_name: Ontology name. Validated against the QE name contract.
file_type: One of owl, r2rml, shacl, summary, context.
folder_key: Folder the ontology lives in.

Returns:
Dict[str, Any]: The file record (e.g. ``content``, ``mediaType``).

Raises:
ValueError: If the ontology name or file type is invalid.
"""
self._validate_ontology_name(ontology_name)
self._validate_file_type(file_type)
spec = self._ontology_file_spec(ontology_name, file_type)
headers = {"Accept": "application/json", **header_folder(folder_key, None)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need this? Generally its handled by the spec method through base class.

response = await self.request_async(spec.method, spec.endpoint, headers=headers)
return response.json()
Comment thread
sankalp-uipath marked this conversation as resolved.

@staticmethod
def _validate_ontology_name(ontology_name: str) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these helper methods needs to move away from this file. I would suggest to create entities_ontology_service separately and consume just the ontology methods from this class.

"""Validate the ontology name before it becomes a URL path segment."""
if not _ONTOLOGY_NAME_RE.match(ontology_name or ""):
raise ValueError(
f"Invalid ontology name {ontology_name!r}. "
"Must match ^[a-z][a-z0-9-]{0,63}$."
)

@staticmethod
def _validate_file_type(file_type: str) -> None:
"""Validate the file type before it becomes a URL path segment."""
if file_type not in _ONTOLOGY_FILE_TYPES:
allowed = ", ".join(sorted(_ONTOLOGY_FILE_TYPES))
raise ValueError(
f"Invalid ontology file type {file_type!r}. One of: {allowed}."
)

@staticmethod
def _ontology_file_spec(ontology_name: str, file_type: str) -> RequestSpec:
return RequestSpec(
method="GET",
endpoint=Endpoint(
f"datafabric_/api/ontologies/{ontology_name}/files/{file_type}"
),
)

@traced(name="entity_record_insert_batch", run_type="uipath")
def insert_records(
self,
Expand Down
21 changes: 21 additions & 0 deletions packages/uipath-platform/src/uipath/platform/entities/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,27 @@ class DataFabricEntityItem(BaseModel):
description: Optional[str] = None


class DataFabricOntologyItem(BaseModel):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for this. Move it to entities_ontology_service

"""A single Data Fabric ontology in an ontology context's ontologySet.

Mirrors :class:`DataFabricEntityItem`: the ontology context (contextType
``datafabricontology``) holds an ``ontologySet`` array just as the entity
context holds an ``entitySet``. Each item carries its own ``folderId`` so it
is fetched from its own folder; ``name`` identifies it to the QueryEngine
ontology API.
"""

model_config = ConfigDict(
validate_by_name=True, validate_by_alias=True, extra="allow"
)

name: str
ontology_key: Optional[str] = Field(None, alias="referenceKey")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ontology is not solution aware at this point. Does it make sense for this to come from here?

folder_key: str = Field(alias="folderId")
description: Optional[str] = None
id: Optional[str] = None


class EntitySetResolution(BaseModel):
"""Result of resolving an agent entity set with overwrites applied."""

Expand Down
75 changes: 75 additions & 0 deletions packages/uipath-platform/tests/services/test_entities_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2647,3 +2647,78 @@ def test_5xx_with_batch_shape_still_propagates(
entity_key=str(entity_key),
records=[{"name": "x"}],
)


class TestGetOntologyFileAsync:
"""Tests for EntitiesService.get_ontology_file_async."""

@pytest.mark.anyio
async def test_builds_endpoint_and_folder_header(
self, service: EntitiesService
) -> None:
response = MagicMock()
response.json.return_value = {"content": "OWL", "mediaType": "text/plain"}
service.request_async = AsyncMock(return_value=response) # type: ignore[method-assign]

result = await service.get_ontology_file_async(
"library", "owl", folder_key="folder-1"
)

assert result == {"content": "OWL", "mediaType": "text/plain"}
service.request_async.assert_called_once()
call = service.request_async.call_args
method, endpoint = call.args[0], call.args[1]
headers = call.kwargs["headers"]
assert method == "GET"
assert str(endpoint) == "/datafabric_/api/ontologies/library/files/owl"
assert headers["Accept"] == "application/json"
assert headers["x-uipath-folderkey"] == "folder-1"

@pytest.mark.anyio
async def test_no_folder_header_when_folder_key_none(
self, service: EntitiesService
) -> None:
response = MagicMock()
response.json.return_value = {"content": "OWL", "mediaType": "text/plain"}
service.request_async = AsyncMock(return_value=response) # type: ignore[method-assign]

await service.get_ontology_file_async("library")

headers = service.request_async.call_args.kwargs["headers"]
assert "x-uipath-folderkey" not in headers

@pytest.mark.anyio
@pytest.mark.parametrize(
"file_type", ["owl", "r2rml", "shacl", "summary", "context"]
)
async def test_accepts_allowed_file_types(
self, service: EntitiesService, file_type: str
) -> None:
response = MagicMock()
response.json.return_value = {"content": "x"}
service.request_async = AsyncMock(return_value=response) # type: ignore[method-assign]

await service.get_ontology_file_async("library", file_type)

endpoint = service.request_async.call_args.args[1]
assert str(endpoint) == f"/datafabric_/api/ontologies/library/files/{file_type}"

@pytest.mark.anyio
async def test_rejects_invalid_ontology_name(
self, service: EntitiesService
) -> None:
service.request_async = AsyncMock() # type: ignore[method-assign]

with pytest.raises(ValueError, match="Invalid ontology name"):
await service.get_ontology_file_async("Bad_Name") # uppercase + underscore

service.request_async.assert_not_called()

@pytest.mark.anyio
async def test_rejects_invalid_file_type(self, service: EntitiesService) -> None:
service.request_async = AsyncMock() # type: ignore[method-assign]

with pytest.raises(ValueError, match="Invalid ontology file type"):
await service.get_ontology_file_async("library", "exe")

service.request_async.assert_not_called()
2 changes: 1 addition & 1 deletion packages/uipath-platform/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/uipath/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[project]
name = "uipath"
version = "2.11.13"
version = "2.11.14"
description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools."
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.11"
dependencies = [
"uipath-core>=0.5.21, <0.6.0",
"uipath-runtime>=0.11.4, <0.12.0",
"uipath-platform>=0.1.78, <0.2.0",
"uipath-platform>=0.1.79, <0.2.0",
"click>=8.3.1",
"httpx>=0.28.1",
"pyjwt>=2.10.1",
Expand Down
18 changes: 17 additions & 1 deletion packages/uipath/src/uipath/agent/models/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
)
from uipath.eval.mocks import ExampleCall
from uipath.platform.connections import Connection
from uipath.platform.entities import DataFabricEntityItem
from uipath.platform.entities import DataFabricEntityItem, DataFabricOntologyItem
from uipath.platform.guardrails import (
BuiltInValidatorGuardrail,
)
Expand Down Expand Up @@ -169,6 +169,7 @@ class AgentContextType(str, CaseInsensitiveEnum):
INDEX = "index"
ATTACHMENTS = "attachments"
DATA_FABRIC_ENTITY_SET = "datafabricentityset"
DATA_FABRIC_ONTOLOGY = "datafabricontology"


class AgentMessageRole(str, CaseInsensitiveEnum):
Expand Down Expand Up @@ -440,6 +441,16 @@ class AgentContextResourceConfig(BaseAgentResourceConfig):
None, description="Context settings"
)
entity_set: Optional[List[DataFabricEntityItem]] = Field(None, alias="entitySet")
ontology_set: Optional[List[DataFabricOntologyItem]] = Field(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the plan to add set of ontologies or single ontology ? Set doesnt make sense to me , but let me know if that decision has already been settled. cc: @milind-jain-uipath

None,
alias="ontologySet",
description=(
"Data Fabric ontologies, on the dedicated ontology context "
"(contextType 'datafabricontology'). Mirrors entitySet on the entity "
"context; each item carries its own folderId and is fetched from the "
"QueryEngine ontology API at runtime."
),
)
Comment on lines 443 to +453
argument_properties: Dict[str, AgentToolArgumentProperties] = Field(
{}, alias="argumentProperties"
)
Expand All @@ -449,6 +460,11 @@ def is_datafabric(self) -> bool:
"""Check if this context is a Data Fabric entity set resource."""
return self.context_type == AgentContextType.DATA_FABRIC_ENTITY_SET

@property
def is_datafabric_ontology(self) -> bool:
"""Check if this context is a Data Fabric ontology resource."""
return self.context_type == AgentContextType.DATA_FABRIC_ONTOLOGY

@property
def datafabric_entity_identifiers(self) -> list[str]:
"""Extract entity identifiers from entitySet."""
Expand Down
Loading
Loading