Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,4 @@ samples/**/entry-points.json
samples/**/uv.lock

testcases/**/uv.lock
poc_out/
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ dependencies = [
"pillow>=12.1.1",
"a2a-sdk>=0.2.0,<1.0.0",
"uipath-langchain-client[openai]>=1.14.0,<1.15.0",
"rdflib>=7.0.0,<8.0.0",
]

classifiers = [
Expand Down
96 changes: 96 additions & 0 deletions scripts/POC_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Data Fabric Ontology Write POC

Demonstrates an OWL ontology driving native Data Fabric **writes** through the
agent tooling: the ontology compiles into a structured intermediate
representation, activates in the write handler, governs which entities/operations
are allowed, and the resulting writes persist to Data Fabric.

Hero case: a contact-center **refund** agent over 5 entities — `Customer`
(read-only), `Contact`, `PurchaseOrder`, `CustomerRisk`, `RefundRequest`.

## Components

| File | Role |
|------|------|
| `src/.../datafabric_tool/ontology_compiler.py` | OWL Turtle → `CompiledOntology` (entity_access, measure/state/reference fields, HITL, relationships) |
| `src/.../datafabric_tool/compiled_ontology.py` | the IR model |
| `src/.../datafabric_tool/datafabric_tool.py` | write tool + handler; fetches+compiles ontology, maps entity name→id for CRUD |
| `src/.../datafabric_tool/write_validation.py` | writability + mutation-intent validation (ontology-constrained) |
| `src/.../datafabric_tool/datafabric_prompt_builder.py` | read schema; retains the primary key for writable entities |
| `scripts/poc_refund_setup.sh` | create + seed staging entities, emit ontology + entity-set + ids |
| `scripts/poc_refund_drive.py` | drive the real write handler with the ontology active, verify by read-back |
| `scripts/poc_refund_teardown.sh` | delete the POC entities |
| `scripts/run_agent_with_ontology.py` | full LLM-in-the-loop variant (see "Known gap") |

## Prerequisites

```bash
# 1. CLI auth to the target tenant (entity create/seed/verify)
uip login

# 2. SDK env vars — the Python SDK reads these (separate from the CLI's auth).
# Source the access token from a logged-in session; do NOT hardcode it.
export UIPATH_ACCESS_TOKEN="$(python3 -c "import json,os;print(json.load(open(os.path.expanduser('~/.uipath/.auth.json')))['access_token'])")"
export UIPATH_URL="https://<host>/<org>/<tenant>"
export UIPATH_ORGANIZATION_ID="<org-guid>"
export UIPATH_TENANT_ID="<tenant-guid>"
```

The access token is short-lived (~1h); re-export after re-login.

## Run

### A. Ontology compiles + activates (offline, no staging)

```bash
uv run python scripts/run_agent_with_ontology.py \
--ontology ../../../df-agent-os/roadmap/p1-owl-write-extension.ttl \
--entity-set scripts/sample_refund_entity_set.json \
--prompt x --dry-run
```

Prints the extracted ontology facts without any network call.

### B. Ontology governs writes that persist (live staging) — the working POC

```bash
bash scripts/poc_refund_setup.sh ./poc_out # create + seed
uv run python scripts/poc_refund_drive.py ./poc_out # drive writes, verify
bash scripts/poc_refund_teardown.sh ./poc_out # clean up
```

`poc_refund_drive.py` prints `ontology ACTIVE`, runs insert RefundRequest +
update Order/CustomerRisk/Contact through the real handler, and verifies all
four mutations by read-back.

### C. Full LLM agent picks the tools (live)

```bash
set -a; source ./poc_out/refund_ids.env; set +a
uv run python scripts/run_agent_with_ontology.py \
--ontology ./poc_out/refund_ontology.ttl \
--entity-set ./poc_out/refund_entity_set.json \
--system-prompt scripts/sample_refund_sop.txt \
--model gpt-4.1-2025-04-14 --agenthub-config agentsplayground \
--prompt "Process the refund for contact ${CONTACT_ID}. Order id ${ORDER_ID}, CustomerRisk id ${RISK_ID}, Customer id ${CUSTOMER_ID}. ..."
```

The LLM reads, decides, and emits ontology-correct write calls (insert on
RefundRequest, update on the writable entities, never on read-only Customer).

## Known gap

In path **C**, the standalone `create_agent` harness terminates on control-flow
tools and the gateway returns tool calls in the OpenAI Responses format; the
terminal write batch is *planned* but not auto-executed by this harness. That is
agent-runtime plumbing, not the ontology or the write tool — path **B** confirms
the writes themselves land. The production `uipath_agents` runtime drives the
tool-execution loop and is the place to validate path C end-to-end.

## Notes

- Status fields are plain STRING (the `choice-set-values` endpoint was
unreliable on staging); the ontology still models `OrderStatus` as a
`StateField`. Swap to ChoiceSet fields when the endpoint is stable.
- The seeded entities are not FK-linked (simplified scenario); pass the record
ids explicitly in path C rather than relying on relationship discovery.
110 changes: 110 additions & 0 deletions scripts/README_run_agent_with_ontology.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# run_agent_with_ontology.py

A standalone CLI helper that loads an OWL ontology, injects it into the Data
Fabric **write** tool's fetch path, and runs a coded ReAct agent against a Data
Fabric entity set.

This is a developer helper, not part of the shipped package. No tests cover it.

## What it does

1. **Compiles + prints the ontology up front.** It runs
`compile_ontology()` on your `.ttl` and prints the extracted facts —
`entity_access`, `hitl_operations`, `state_fields`, `reference_fields`,
`measure_fields`, `entity_relationships`. This validates the `.ttl` and
shows exactly what the ontology contributes before any agent work. This step
needs **no network**.
2. **Bridges a not-yet-shipped platform method** (see below).
3. Builds the Data Fabric read + write tools, force-initializes the write
handler, prints the compiled ontology actually in use and the generated
write tool description, then (unless `--dry-run`) runs the agent.

## The monkeypatch bridge — and why it exists

The runtime's `DataFabricWriteHandler._maybe_compile_ontology()` fetches the
ontology by calling:

```python
entities_service.get_ontology_file_async("owl")
```

**The platform does not yet expose that method.** Verified: the attribute is
absent on
`uipath.platform.entities._entities_service.EntitiesService`. When it is
missing, the handler degrades to the metadata-only write path
(`_compiled_ontology` stays `None`).

This CLI closes that gap by monkeypatching the method onto the class **before**
the handler runs:

```
uipath.platform.entities._entities_service.EntitiesService.get_ontology_file_async
```

The injected async method returns the text of your `--ontology` file for the
`"owl"` file type. The handler's own `_maybe_compile_ontology` then discovers
it via `getattr`, compiles it, and uses it in write validation and the write
tool description — exactly as it will behave once the platform ships the real
method.

A class-level patch is used (not an instance patch) because the handler
constructs `UiPath()` internally and resolves the `EntitiesService` lazily, so
the instance is never reachable from the CLI. If the class patch ever fails,
the script falls back to setting `handler._compiled_ontology` directly and
rebuilding the description.

After initialization the script prints either:

- `ontology ACTIVE` — the handler compiled and is using the ontology, or
- `ontology INACTIVE (fell back to metadata-only)` — it did not.

## Run it offline (dry-run)

The ontology compilation + fact printing needs no network. Building tools /
resolving entities **does** need UiPath auth + network; in `--dry-run` that
failure is caught and the script still prints the standalone ontology facts and
exits `0`.

```bash
uv run python scripts/run_agent_with_ontology.py \
--ontology /Users/harshit/DF-Agents-2/df-agent-os/roadmap/p1-owl-write-extension.ttl \
--entity-set scripts/sample_refund_entity_set.json \
--prompt "test" --dry-run
```

## Run it for real (against staging)

1. `uip login` (sets `UIPATH_ACCESS_TOKEN`, `UIPATH_URL`,
`UIPATH_TENANT_ID`, `UIPATH_ORGANIZATION_ID`).
2. Edit `scripts/sample_refund_entity_set.json` and replace the **placeholder
fake UUIDs** (`id`, `folderId`, `referenceKey`) with the real ids for your
tenant's entities. The shipped values are clearly fake and will not resolve.
3. Run without `--dry-run`:

```bash
uv run python scripts/run_agent_with_ontology.py \
--ontology /path/to/ontology.ttl \
--entity-set scripts/sample_refund_entity_set.json \
--prompt "Process the refund for contact Jane Doe on order PO-1042" \
--model anthropic.claude-sonnet-4-5-20250929-v1:0 \
--system-prompt scripts/sample_refund_sop.txt
```

## Options

| Flag | Required | Description |
|------|----------|-------------|
| `--ontology` | yes | Path to the OWL 2 QL Turtle `.ttl` file. |
| `--entity-set` | yes | JSON list of `DataFabricEntityItem` dicts (`id`, `name`, `folderId`, `referenceKey`, `description`). |
| `--prompt` | yes | The user prompt for the agent. |
| `--model` | no | UiPath-gateway model name. Default: `anthropic.claude-sonnet-4-5-20250929-v1:0`. |
| `--system-prompt` | no | Path to a system-prompt/SOP `.txt`. Generic default when omitted. |
| `--resource-name` | no | Name for the Data Fabric context resource. Default: `datafabric`. |
| `--dry-run` | no | Do not call the LLM; build tools, inject the ontology, print facts + write tool description, exit. Degrades gracefully offline. |

## Sample files

- `sample_refund_entity_set.json` — refund hero-case entities (Customer,
Contact, Order/PurchaseOrder, CustomerRisk, RefundRequest) with **placeholder
fake ids**. Fill in real tenant ids to run against staging.
- `sample_refund_sop.txt` — the refund SOP (RFC §4.3) as a system prompt.
Loading