Skip to content

[BUG] Project-scoped writes (ingest with project_id, remember_decision) are stored without embeddings #216

Closed

Description

@RockinPaul

What happened?

Memories written through the project path never get a semantic vector, even when an embedding provider is configured and working. They are stored with vectors.semantic: null, so recall scores them vector: 0 and they can only ever match lexically.

This affects every longmemory_remember_decision call (it requires project_id) and every longmemory_ingest call that passes project_id. The same text ingested without project_id is embedded correctly.

Expected: project-scoped writes are embedded with the configured provider, like the non-project path.

Cause. The non-project path embeds before ingesting (src/core/create_memory.ts:389, inside ingest()). The project path does not:

  • src/mcp/tools/ingest.ts:33 and src/mcp/tools/remember_decision.ts:31 call project_memory.ingestProjectEvent()
  • which calls ingest_project_event() → memory.applyImportPlan(built.plan) (src/core/project/project_ingest.ts:133)
  • applyImportPlan (src/core/create_memory.ts:544) calls engine.ingest({...}) at line 610 with no vector, inside a synchronous import_transaction().run(...)
  • so ingest_engine stores vectors: { semantic: parsed.event.vector ?? null, ... } (src/core/engine/ingest_engine.ts:387) as null

src/core/connectors/connector_ingest.ts:89 also goes through applyImportPlan, so connector imports are probably affected too (not tested).

A possible fix: compute the embeddings for plan.nodes_to_create before entering the transaction, using the same embed(cfg.embedding_provider, multilingual_embeddings, embedding_cache, …, 'document') call as ingest(), and pass each as vector to engine.ingest.

Related: #214 fixes the same pattern (a write path that bypasses the configured embedder) for legacy migration.

Steps to Reproduce

  1. Start the MCP HTTP server with a working embedding provider, e.g.:
    LONGMEMORY_EMBEDDING_PROVIDER=openai
    LONGMEMORY_EMBEDDING_TIER=deep
    LONGMEMORY_EMBEDDING_DIMENSION=1024
    LONGMEMORY_OPENAI_BASE_URL=<any OpenAI-compatible /embeddings endpoint>
    LONGMEMORY_OPENAI_EMBEDDING_MODEL=<model>
    OPENAI_API_KEY=<key>
    
    Startup logs embedding provider: openai.
  2. Call longmemory_ingest with project_id: "demo" and the text:
    the staging database credentials rotate every forty-five days, and Dana owns that schedule.
  3. Call longmemory_recall with mode: "associative", project_id: "demo", query how often do test DB passwords change.
    The record ranks first, but items[0].node.vectors.semantic is null and items[0].breakdown.vector is 0.
  4. Repeat steps 2–3 without project_id.
    Now vectors.semantic is a 1024-dimension array and breakdown.vector is 0.390, with lexical: 0: a real semantic match.

Component

Backend (API/Server)

Environment

  • LongMemory 4da4986 in Docker (node:22-bookworm-slim), MCP over streamable HTTP
  • Embedding provider openai against an OpenAI-compatible endpoint, 1024 dimensions, tier deep
  • The same code path is unchanged on main at 9ee2c8e (2026-09-20)

Relevant log output

No error is logged: the provider works, it is just never called for project writes. Recall breakdown for the same text:

with project_id:    vectors.semantic = null            breakdown.vector = 0      lexical = 0
without project_id: vectors.semantic = array(1024)     breakdown.vector = 0.390  lexical = 0

Code of Conduct

  • I agree to follow this project's Code of Conduct

Activity

  1. self-assigned this
    on Oct 17, 2026
  2. added 2 commits that reference this issue on Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions