Skip to content

feat: scaffold MemoryService for Agent Episodic Memory (URT migration)#1467

Open
mjnovice wants to merge 17 commits intomainfrom
feat/memory-service-scaffold
Open

feat: scaffold MemoryService for Agent Episodic Memory (URT migration)#1467
mjnovice wants to merge 17 commits intomainfrom
feat/memory-service-scaffold

Conversation

@mjnovice
Copy link
Copy Markdown
Contributor

@mjnovice mjnovice commented Mar 20, 2026

Summary

Adds MemoryService to uipath-platform — the SDK client for Agent Memory Spaces. This enables programmatic access to memory space CRUD, dynamic few-shot search, and escalation memory operations used by the UiPath agentic loop.

What's included

  • MemoryService exposed as sdk.memory on the UiPath class
  • 5 methods (sync + async): create, list, search, escalation_search, escalation_ingest
  • Pydantic models matching ECS v2 and LLMOps swagger contracts: MemorySpace, MemorySpaceCreateRequest, MemorySearchRequest/Response, EscalationMemoryIngestRequest, etc.
  • 13 unit tests with pytest-httpx HTTP mocking
  • E2E test suite for alpha environment validation
  • Two gateways: ECS v2 (/ecs_/v2/episodicmemories) for CRUD, LLMOps (/llmopstenant_/api/Agent/memory) for search and escalation
  • Folder-scoped operations with serverless folder_path resolution

SDK Methods → Agents Repo References

create() — POST /ecs_/v2/episodicmemories/create

Layer File Component
Backend External.Clients/Ecs/EcsClient.cs:30 CreateMemorySpaceAsync()
Backend External.Clients/Ecs/AgentMemoryService.cs:20 CreateNamespaceAsync() (wrapper)
Frontend frontend/src/stores/memoryStore.ts:108 useCreateMemorySpace()
Frontend frontend/src/stores/memoryStore.ts:462 useCreateEpisodicMemory()
Frontend-SW frontend-sw/src/api/hooks/memory.ts:93 useCreateMemorySpace()

list() — GET /ecs_/v2/episodicmemories

Layer File Component
Backend External.Clients/Ecs/EcsClient.cs:186 GetEpisodicMemorySpaceAsync() (with $filter)
Frontend frontend/src/stores/memoryStore.ts:129 useFetchMemorySpaces()
Frontend-SW frontend-sw/src/api/hooks/memory.ts:37 useFetchMemorySpaces()

search() — POST /llmopstenant_/api/Agent/memory/{id}/search

Layer File Component
Backend External.Clients/LlmOps/LlmOpsClient.cs:238 GetDynamicFewShotExamplesFromTracesAsync()
Backend Execution.Shared/MemorySpaces/MemorySpacesExecutor.cs:44 QueryDynamicFewShotPromptsWithDetailsAsync()agentic loop
Interface Common/Clients/ILlmOpsClient.cs:33 ILlmOpsClient contract

escalation_search() — POST /llmopstenant_/api/Agent/memory/{id}/escalation/search

Layer File Component
Backend External.Clients/LlmOps/LlmOpsClient.cs:259 SearchEscalationMemoryAsync()
Backend Execution.Shared/Tools/EscalationTool/EscalationToolExecutor.cs:642 FetchFromMemory()escalation tool
Interface Common/Clients/ILlmOpsClient.cs:40 ILlmOpsClient contract

escalation_ingest() — POST /llmopstenant_/api/Agent/memory/{id}/escalation/ingest

Layer File Component
Backend External.Clients/LlmOps/LlmOpsClient.cs:296 IngestEscalationMemoryAsync()
Backend Execution.Shared/Tools/EscalationTool/EscalationToolExecutor.cs:518 GetEscalationOutcomeAsync()escalation tool
Interface Common/Clients/ILlmOpsClient.cs:47 ILlmOpsClient contract

API Contract References

Test plan

  • Unit tests with pytest-httpx mocking (13 tests)
  • Lint (ruff check), format (ruff format), type check (mypy) all pass
  • Verify endpoint URLs match ECS v2 and LLMOps swagger contracts
  • Verify models serialize/deserialize correctly with camelCase aliases
  • E2E integration test on alpha environment

🤖 Generated with Claude Code

@github-actions github-actions bot added the test:uipath-langchain Triggers tests in the uipath-langchain-python repository label Mar 20, 2026
@mjnovice mjnovice marked this pull request as draft March 20, 2026 18:03
@mjnovice mjnovice force-pushed the feat/memory-service-scaffold branch 2 times, most recently from 44bd0a8 to 239b787 Compare March 23, 2026 22:06
@mjnovice mjnovice marked this pull request as ready for review March 23, 2026 22:11
mjnovice and others added 15 commits March 30, 2026 16:23
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>
@mjnovice mjnovice force-pushed the feat/memory-service-scaffold branch from 1dcb374 to a0ea814 Compare March 30, 2026 23:24
mjnovice and others added 2 commits March 30, 2026 16:29
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:uipath-langchain Triggers tests in the uipath-langchain-python repository

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants