-
Notifications
You must be signed in to change notification settings - Fork 28
feat(agent): add ontology binding to Data Fabric context config #1728
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8badd70
906d618
cd4ff88
8175316
2048c56
28d3c0c
33f76f0
d4ccd79
f22074c
74d47bb
bcbf2dd
75aaab5
336bfb3
c231702
2fe9114
6877f6a
69d42df
1ce9049
9e22841
b6f0c9a
56ac4a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ | |
| """ | ||
|
|
||
| import logging | ||
| import re | ||
| from typing import Any, Dict, List, Optional, Type | ||
|
|
||
| from httpx import Response | ||
|
|
@@ -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 ( | ||
|
|
@@ -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"}) | ||
|
|
||
|
|
||
| class EntitiesService(BaseService): | ||
| """Service for managing UiPath Data Service entities. | ||
|
|
@@ -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]: | ||
|
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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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() | ||
|
sankalp-uipath marked this conversation as resolved.
|
||
|
|
||
| @staticmethod | ||
| def _validate_ontology_name(ontology_name: str) -> None: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -843,6 +843,27 @@ class DataFabricEntityItem(BaseModel): | |
| description: Optional[str] = None | ||
|
|
||
|
|
||
| class DataFabricOntologyItem(BaseModel): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.""" | ||
|
|
||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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, | ||
| ) | ||
|
|
@@ -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): | ||
|
|
@@ -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( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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" | ||
| ) | ||
|
|
@@ -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.""" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whats the rationale here?