Skip to content

fix(openclaw): migrate example plugin memory search to the v1 POST + Filters DSL API#245

Open
Fearvox wants to merge 1 commit into
EverMind-AI:mainfrom
Fearvox:fix/openclaw-plugin-v1-search-migration
Open

fix(openclaw): migrate example plugin memory search to the v1 POST + Filters DSL API#245
Fearvox wants to merge 1 commit into
EverMind-AI:mainfrom
Fearvox:fix/openclaw-plugin-v1-search-migration

Conversation

@Fearvox
Copy link
Copy Markdown
Collaborator

@Fearvox Fearvox commented Jun 3, 2026

What

The OpenClaw example plugin's memory recall never worked against the v1
backend. searchMemories (examples/openclaw-plugin/src/api.js) still spoke the
v0 dialect on both the request and the response side:

Request — it sent GET /api/v1/memories/search with a flat top-level
user_id and a retrieve_method field. Against the v1 API that fails twice:

  • the route is POST-only, so the GET returns 405 Method Not Allowed;
  • even as a POST, SearchMemoriesRequest requires a Filters DSL object
    (filters must carry user_id/group_id at the first level), so a flat
    top-level user_id returns 422 "Field required: filters";
  • the retrieval-method field is method, not retrieve_method.

Response — it read r.result.memories / r.result.pending_messages, but the
v1 response is { data: { episodes, profiles, raw_messages, ... } }. So even a
successful call surfaced zero memories.

This PR migrates searchMemories to the v1 contract end-to-end while preserving
the function's return shape (so parseSearchResponse and the assembler are
untouched):

  • Build the v1 request envelope: POST + filters: { user_id, group_id? } +
    method (mapped from retrieve_method) + query / top_k / memory_types.
  • Map the v1 response: data.episodes → memories (tagging memory_type: "episodic_memory" so the downstream episodic filter matches; carrying
    summary/episode/subject/score/timestamp), and data.raw_messages → pending_messages (flattening content_items to text).

Verification

Live against a running EverCore (:1995):

  • old form GET …/search405; flat top-level user_id (POST) → 422
    "Field required: filters"
    .
  • the fixed body (POST + filters:{user_id} + method) → HTTP 200, with
    the response echoing "filters_applied": {"user_id": "…"}.
  • the real branch searchMemories() driven end-to-end against :1995
    (method=keyword) returns status: "ok" and maps the response with no error.

Note: method=hybrid/vector additionally require a reachable embedding
backend; on a host without one they 500 at the embedding step (an environment
dependency, not this code). keyword (ES-only) needs no embeddings and was
used for the live 200.

Offline regression — adds test/search-memories.test.js (node --test, no
live stack):

  • asserts the request is a POST whose body carries filters: { user_id } (no
    top-level user_id) and method (no retrieve_method), with only
    backend-searchable memory_types;
  • feeds a canned v1 { data: { episodes, raw_messages } } response and asserts
    it maps into { memories, pending_messages } and that the existing
    parseSearchResponse consumes the mapped shape
    into the expected episodic /
    pending text.
cd methods/EverCore/examples/openclaw-plugin
node --test test/search-memories.test.js

Result: 2 passed.

Scope

Surgical: src/api.js (searchMemories only) + its new regression test. The v1
demo migration (#191, PR #196) covered methods/evermemos/demo/* and docs but
not this example plugin, so the plugin's search path was left on v0. This PR
finishes that migration for the plugin. Does not touch saveMemories (shipped
separately as the #237 fix).

Credit

Builds on prior art:

Co-authored-by: CZH-THU CZH-THU@users.noreply.github.com
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

🤖 Generated with Claude Code

…Filters DSL API

The example plugin's memory recall never worked against the v1 backend.
searchMemories spoke the v0 dialect on both sides:

- Request: GET /api/v1/memories/search with a flat top-level user_id and a
  retrieve_method field. The v1 route is POST-only (GET -> 405); even as POST,
  SearchMemoriesRequest requires a Filters DSL object (user_id/group_id must sit
  inside `filters`), so a flat top-level user_id -> 422 "Field required: filters";
  and the method field is `method`, not `retrieve_method`.
- Response: it read r.result.memories / r.result.pending_messages, but the v1
  response is { data: { episodes, profiles, raw_messages, ... } }, so even a
  successful call surfaced zero memories.

Migrate searchMemories to the v1 contract end-to-end while preserving its return
shape (parseSearchResponse and the assembler are untouched): build the v1 POST
envelope with filters:{user_id,group_id?} + method, and map data.episodes ->
memories (tagging memory_type so the episodic filter matches) and
data.raw_messages -> pending_messages (flattening content_items to text).

Verified live against EverCore on :1995: old GET -> 405; flat top-level user_id
(POST) -> 422 "Field required: filters"; fixed body (POST + filters + method) ->
200 with "filters_applied":{"user_id":...}; the real searchMemories() driven
end-to-end (method=keyword) returns status:"ok" and maps the response with no
error. Adds offline test/search-memories.test.js (node --test) asserting the v1
request envelope and that the v1 response maps into the caller contract and is
consumable by the existing parseSearchResponse (2 passed).

The v1 demo migration (EverMind-AI#191, PR EverMind-AI#196) covered methods/evermemos/demo/* and docs
but not this example plugin; this finishes that migration for the plugin's search
path. Scope: src/api.js (searchMemories only) + its regression test. saveMemories
is migrated separately as the EverMind-AI#237 fix.

Co-authored-by: CZH-THU <CZH-THU@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 3, 2026 06:55
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants