Entity Graph
Plain text search is great for "find me everything about Q2 fundraising". But sometimes you need to ask who depends on X? or which decisions reference the new pricing? — questions that are about relationships between entities, not about matching words.
GrooveOS keeps an entity graph alongside your team's memory. Every time something gets written to memory — a chat message, a meeting summary, a Drive document — the system extracts the entities mentioned (people, projects, decisions, dates) and the relationships between them. You query the graph from chat or from the API.
What's in the graph
The graph is built from two kinds of things:
-
Entities — people (
Alice), organisations (Acme), projects (Q2-Fundraising), decisions, concepts, dates. Each entity is uniquely identified within your team's scope. -
Relationships — edges like
LEADS,WORKS_ON,DECIDED,CONTRIBUTED_TO,DEPENDS_ON,FUNDED. Every edge carries the time window when it was valid (see Time-aware facts).
The graph is team-scoped. Team A's graph is completely separate from Team B's — same isolation guarantees as the rest of GrooveOS.
Why a graph and not just search?
Some examples of questions a graph answers cleanly that plain search can't:
- "Who currently leads the engineering team?"
- "Which projects depend on the database migration?"
- "What decisions did Alice make before she changed roles?"
- "Show me every conversation that mentions both the Series A and the pivot."
- "Who, on this team, has worked with anyone at Acme?"
Search can hint at these answers; the graph gives you a clean, structured result. Behind the scenes, GrooveOS uses graphiti, an open-source temporal-graph engine on top of Neo4j.
How it gets built
You don't curate the graph. It builds itself, fail-soft:
- You (or the system) write something to memory — a chat message, a clip, a meeting summary.
- GrooveOS sends the content to the graph engine in the background.
- The engine extracts entities and relationships, resolves contradictions against existing facts, and saves time-bounded edges to your team's graph partition.
Graph ingest is never blocking
If the graph engine is slow or temporarily unavailable, your memory write still succeeds — the graph just catches up later. Plain-text search in memory works regardless of the graph state. The graph is an enrichment layer.
Querying from chat
The fastest way to use the graph is to ask a question naturally in LibreChat or Open WebUI. The chat surface routes graph-style questions to the right backend automatically:
You: who currently leads engineering at Acme?
xbrain: Bob has been Tech Lead of Acme Engineering since June 1, 2025.
(Alice was Tech Lead from January to May 2025.)
You: which projects depend on the database migration?
xbrain: Three projects depend on it: payment-flow, audit-log, and crm-sync.
The migration is owned by Alice and scheduled for July.
You don't need to mention "graph" — xbrain figures out when to use the graph and when to fall back to plain memory search.
Querying from the API
For programmatic use, the API exposes
GET /v1/graph/neighbors. Given an entity name, it returns all
directly-connected entities with the type and validity window of each
relationship:
curl "https://api.grooveos.app/v1/graph/neighbors?entity=Alice" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team"
# Returns:
{
"entity": "Alice",
"team_scope": "your-team",
"neighbors": [
{
"name": "Acme.Engineering",
"relationship": "WAS_TECH_LEAD",
"valid_from": "2025-01-15",
"valid_until": "2025-05-31"
},
{
"name": "Q2-Fundraising",
"relationship": "CONTRIBUTED_TO",
"valid_from": "2025-03-01",
"valid_until": null
}
]
}
Pass ?depth=2 to also include neighbors-of-neighbors. The default
is depth 1 (max 3).
Time-aware facts
A common knowledge-graph problem is that "Alice is the Tech Lead"
eventually becomes wrong, but the old answer is still useful for past questions.
GrooveOS handles this by giving every relationship a
validity window (valid_from and
valid_until):
// Two facts that look contradictory:
"Alice is Tech Lead." (recorded 2025-01-15)
"Bob is now Tech Lead. Alice moved to product." (recorded 2025-06-01)
// What the graph stores:
Alice -[WAS_TECH_LEAD: 2025-01-15 → 2025-05-31]→ Acme.Engineering
Bob -[IS_TECH_LEAD: 2025-06-01 → now]→ Acme.Engineering
// Querying:
"who was Tech Lead in March 2025?" → Alice
"who is Tech Lead now?" → Bob
Both facts coexist forever. The graph picks the one that matches the time you're asking about.
FAQ
Do I need to do anything to enable the graph?
No — it's on by default for every team. The first few memory writes after sign-up populate the graph automatically. You'll see the graph answer chat questions within minutes of getting started.
Can I see a visualisation of my team's graph?
A graph-explorer UI ships in v1.5 (under
/account/teams/.../graph/). For now, the API surface and the chat
questions are the supported entry points.
Can I edit graph entities directly?
Not yet. The graph is rebuilt from your memory items, so the way to "edit" it
is to write a corrective memory item — e.g. promote a memory item to
CANONICAL stating "Alice is the Tech Lead, not Bob". The next graph
pass will create a corrective time-bounded edge.
Are entities shared across teams?
No. Each team has its own graph partition. Even if two teams both mention "Acme Corp", those are separate nodes — Team A's "Acme Corp" cannot reveal anything about Team B's.
What's next
- Memory & Knowledge — How plain-text memory and the graph relate.
- Team Chat — How chat picks between memory search and graph queries.
- API Reference — Programmatic graph queries (
GET /v1/graph/neighbors).