feat: scaffold MemoryService for Agent Episodic Memory (URT migration) by mjnovice · Pull Request #1467 · UiPath/uipath-python
mjnovice
marked this pull request as ready for review
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>
…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>
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters