feat: scaffold MemoryService for Agent Episodic Memory (URT migration) by mjnovice · Pull Request #1467 · UiPath/uipath-python

@mjnovice mjnovice marked this pull request as ready for review

March 23, 2026 22:11

pateljay43

pateljay43

pateljay43

pateljay43

CalebMartinUiPath

mjnovice

mjnovice

pateljay43

Add MemoryService client backed by ECS (/ecs_/memory/...) endpoints for
Agent Episodic Memory. This enables dynamic few-shot retrieval where
agents query past episodes at execution start and inject them as
examples into the system prompt.

New files:
- memory.py: Pydantic models (MemoryField, MemoryItem, MemoryQueryRequest, etc.)
- _memory_service.py: MemoryService with create, ingest, query, retrieve, delete, list
- __init__.py: Module exports

Also registers sdk.memory on the UiPath class.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tract

The original scaffold was based on a different/older API spec. This rewrites
models and endpoints to match the actual ECS v2 episodicmemories contract:
- Paths: /ecs_/v2/episodicmemories (not /ecs_/memory)
- Resources identified by GUID key (not name)
- Field model uses keyPath[] + value (not fieldName/fieldValue)
- Search replaces query, with structured SearchSettings and per-field settings
- Ingest now returns EpisodicMemoryIngestResponse with memory ID
- Added missing operations: delete_index, patch_memory (status active/inactive)
- EpisodicMemoryIndex includes all server fields (memoriesCount, folderKey, etc.)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per PR review feedback: ingestion should go to LLMOps, which extracts
fields from traces/feedback before forwarding to ECS.

Architecture:
- Index CRUD (create/list/get/delete) → ECS /ecs_/v2/episodicmemories
- Ingest → LLMOps /llmops_/api/Agent/memory/{id}/ingest (feedbackId-based)
- Search → LLMOps /llmops_/api/Agent/memory/{id}/search (returns systemPromptInjection)
- Patch/delete items → LLMOps /llmops_/api/Memory/{id}/items/{itemId}

LLMOps search returns systemPromptInjection — the formatted string
ready to inject into the agent's system prompt for few-shot retrieval.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Full lifecycle test: create index → create feedback → ingest via
LLMOps → search → verify response shape → delete index.

Tests are marked @pytest.mark.e2e and excluded from default runs.
Run with: uv run pytest -m e2e -v (requires UIPATH_URL,
UIPATH_ACCESS_TOKEN, UIPATH_FOLDER_KEY env vars).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- LLMOps endpoints use llmopstenant_ prefix (not llmops_) through
  the platform gateway — llmops_ returns 302, llmopstenant_ routes
  correctly
- SearchField.settings is now required (default FieldSettings()) per
  LLMOps API contract which requires the settings field on each field
- Fixed E2E test feedback endpoint to use llmopstenant_ prefix

E2E results: 5 passed, 1 skipped (ingest skipped because synthetic
trace/span IDs don't exist in LLMOps trace store — needs real agent
trace for full lifecycle test)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Adds e2e-uipath-platform job to test-packages.yml
- Runs on uipath-platform changes, Python 3.11, ubuntu-latest
- Uses ALPHA_TEST_CLIENT_ID/CLIENT_SECRET for auth (same as integration tests)
- Reads memory folder from UIPATH_MEMORY_FOLDER secret
- E2E results are non-blocking in the test gate (informational)
- Test supports both token-based (local) and client credentials (CI) auth

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per PR review: _resolve_folder now matches ContextGroundingService
pattern — resolves folder_key from folder_path via FolderService
when UIPATH_FOLDER_KEY is not set (serverless/robot environments
only have UIPATH_FOLDER_PATH).

MemoryService now takes a FolderService dependency, wired through
the UiPath class.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove methods not used by any frontend or backend flow:
- get(), delete_index() (neither frontend uses; backend uses v1.1)
- ingest() (IM-internal, requires feedback_id from trace pipeline)
- patch_memory(), delete_memory() (wrong endpoints, IM admin ops)

Remove unused models: FeedbackMemoryStatus, EpisodicMemoryPatchRequest,
MemoryIngestRequest/Response, MemoryItemUpdateRequest/Response.

Add escalation memory methods used by the backend agentic loop:
- escalation_search() -> POST /api/Agent/memory/{id}/escalation/search
- escalation_ingest() -> POST /api/Agent/memory/{id}/escalation/ingest

Add models: EscalationMemoryIngestRequest, EscalationMemorySearchResponse,
EscalationMemoryMatch, CachedRecall (matching backend C# contracts).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rename _ECS_BASE to _MEMORY_SPACES_BASE for clarity
- Add full docstrings (Args/Returns) to all async methods to match
  their sync counterparts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cover all 5 public methods: create, list, search,
escalation_search, escalation_ingest. Tests verify correct
URL construction, request body serialization, folder header
propagation, and response deserialization.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EpisodicMemoryField was for ECS ingest (removed). EpisodicMemoryStatus
is not referenced by any service method or model.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace "index" terminology with "memory space" throughout the
memory module to match the product language used by frontends
and backend (MemorySpaceResponse, MemorySpace, etc.).

Renames:
- EpisodicMemoryIndex → MemorySpace
- EpisodicMemoryListResponse → MemorySpaceListResponse
- EpisodicMemoryCreateRequest → MemorySpaceCreateRequest

All docstrings updated to use "memory space" instead of
"memory index".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>