Skip to content

feat: raw-text observe, identity provenance, knowledge-graph service - #5

Merged
rrader26 merged 1 commit into
mainfrom
feat/graph-raw-observe-and-identity
Aug 19, 2026
Merged

feat: raw-text observe, identity provenance, knowledge-graph service#5
rrader26 merged 1 commit into
mainfrom
feat/graph-raw-observe-and-identity

Conversation

@rrader26

Copy link
Copy Markdown
Contributor

Go parity with @memmesh/sdk v0.9.0 — see
thinkfleet-memory-sdk#21.

Go was the furthest behind of the five SDKs: it had no raw-text Observe at
all
, only the legacy verbatim path posting to /admin/memory.

Observe reaches the engine

res, _ := c.Memory.Observe(ctx, memmesh.Observe{
    Text:      "I just moved to Denver.",
    UserID:    "user-123",   // provenance on whatever the engine keeps
    SessionID: "thread-456", // keeps a conversation's turns linkable
})
// res.Saved, res.CandidateCount

Set Text and the raw turn goes to /memory/observe — extract → dedupe → graph
→ embed — and comes back with what survived. CandidateCount is what extraction
proposed; Saved is what was kept. Filler returns an empty Saved, which is
the engine working, not an error.

UserID / AgentID / SessionID are forwarded, omitted when unset. Provenance,
not a tenancy boundary — search filters chatIdentityId IS NULL OR = $1,
permissive by design.

The legacy Content path still posts verbatim to /admin/memory, wrapped in the
same response shape so callers don't branch on which path ran.

New: c.Graph

Method Endpoint
Stats(ctx) GET /admin/memory/graph/stats
ListEntities(ctx, params) GET /admin/memory/entities
GetEntity(ctx, id, asOf) GET /admin/memory/entities/:id
ListEdges(ctx, asOf, limit) GET /admin/memory/graph/edges
Traverse(ctx, id, params) POST /admin/memory/graph/traverse

Use Stats, not len(ListEntities(..)) for any "how big is it" question.
The list routes page, so their length is the page size — against a real project,
1,000 vs the actual 12,142.

Edges come back hydrated

ListEdges, Traverse, and GetEntity().Edges return GraphTraversalEdge:
Subject and Object are full entities, not ids, plus a Hop counter. The
raw memory_edge row is deliberately not modelled — no read route returns it,
and assuming otherwise broke the Rust port outright with
missing field 'subjectId'.

Hop is 0 from ListEdges (no seed) and 1-indexed from Traverse.

Breaking

Observe returns *ObserveResponse, was *MemoryItem. On the legacy Content
path the old value is now res.Saved[0]. This matches the same change already
made in the TypeScript, Python, Rust, and .NET SDKs.

Verification

  • go build, go vet, and go test ./... clean; 12 new tests covering
    route + body shaping, filter omission, query escaping, the hydrated and
    literal-object edge shapes, and legacy-path compatibility.
  • Verified live against app.memmesh.ai: 12,142 entities / 287,698 edges,
    decoding NVIDIA CORP -[reported_metric]-> Cost of Revenue and
    NVIDIA CORP -[ticker_symbol]-> NVDA.

Note

This branch is cut from origin/main and does not include the separate
uncommitted work sitting in the local checkout (brains.go, consent.go,
financial.go, pagination.go, and their tests). main builds fine without
it, so I left it alone rather than folding someone else's staged work into this
PR — but it is worth landing on its own.

Parity with @memmesh/sdk v0.9.0 (thinkfleet-memory-sdk#21). Go was the
furthest behind of the five SDKs — it had no raw-text observe at all.

Observe now takes Text and routes it to /memory/observe, the engine's
extraction pipeline, returning what it kept:

    res, _ := c.Memory.Observe(ctx, memmesh.Observe{
        Text: "I just moved to Denver.", UserID: "u1", SessionID: "t1",
    })

CandidateCount is what extraction proposed; Saved is what survived dedupe
and the token budget. Filler comes back with an empty Saved — success, not
an error. UserID / AgentID / SessionID are provenance, omitted when unset,
and are NOT a tenancy boundary.

The legacy Content path still posts verbatim to /admin/memory, wrapped in
the same response so callers don't branch on which path ran.

New c.Graph — Stats, ListEntities, GetEntity, ListEdges, Traverse. Edges
decode as GraphTraversalEdge: Subject and Object are hydrated entities,
not ids, plus a Hop counter. The raw memory_edge row is not modelled
because no read route returns it — assuming otherwise broke the Rust port
outright.

Prefer Graph.Stats over len(ListEntities(..)) for size questions; the list
routes page, so their length is the page size (1000 vs an actual 12142).

BREAKING: Observe returns *ObserveResponse, was *MemoryItem. The old
return is now res.Saved[0] on the Content path.

Verified live against app.memmesh.ai — 12142 entities / 287698 edges,
decoding NVIDIA CORP -[reported_metric]-> Cost of Revenue. 12 new tests.
@rrader26
rrader26 merged commit 40ab563 into main Aug 19, 2026
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