Phase 1: investor↔contacts (member_of), system status, thesis seed v1
- entity_resolution: emit member_of relationship edges (contact -> investor), so one investor entity owns many contacts (institution) and a HNWI is the N=1 case; crm_tools.get_investor_contacts + get_entity contacts/member_of; MCP tool. - seed_synthetic: multi-contact institutions to exercise it (Harbor & Vine = 5). - server.py: GET /api/system/status (index/entity/thesis/activity health) for an in-app status view (no shell needed to verify the index). - docs/thesis-seed-v1.md: grounded v1 thesis (throughline, 6 pillars, objections, per-segment angles, voice) drawn from Ten31's newsletter/site/essays. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -63,10 +63,34 @@ def get_entity(lp_id, db=None):
|
||||
out["interaction_count"] = (c.execute(
|
||||
"SELECT COUNT(*) FROM communications WHERE contact_id IN (%s)" % ",".join("?" * len(cids)),
|
||||
list(cids)).fetchone()[0] if cids else 0)
|
||||
# An investor's contacts (member_of edges) — and, for a person, the investor(s)
|
||||
# they belong to. This is how one investor owns many contacts.
|
||||
out["contacts"] = [dict(r) for r in c.execute(
|
||||
"SELECT ce.id, ce.display_name, ce.primary_email FROM relationship_edges re "
|
||||
"JOIN canonical_entities ce ON ce.id=re.src_id "
|
||||
"WHERE re.dst_id=? AND re.edge_type='member_of' AND ce.deleted_at IS NULL ORDER BY ce.display_name", (lp_id,))]
|
||||
out["member_of"] = [dict(r) for r in c.execute(
|
||||
"SELECT ce.id, ce.display_name, ce.entity_kind FROM relationship_edges re "
|
||||
"JOIN canonical_entities ce ON ce.id=re.dst_id "
|
||||
"WHERE re.src_id=? AND re.edge_type='member_of' AND ce.deleted_at IS NULL", (lp_id,))]
|
||||
out["contact_count"] = len(out["contacts"])
|
||||
c.close()
|
||||
return out
|
||||
|
||||
|
||||
def get_investor_contacts(lp_id, db=None):
|
||||
"""List all contacts (person entities) that belong to an investor entity —
|
||||
the explicit one-investor-to-many-contacts relationship."""
|
||||
c = _conn(db)
|
||||
inv = c.execute("SELECT id, entity_kind, display_name FROM canonical_entities WHERE id=?", (lp_id,)).fetchone()
|
||||
contacts = [dict(r) for r in c.execute(
|
||||
"SELECT ce.id, ce.display_name, ce.primary_email FROM relationship_edges re "
|
||||
"JOIN canonical_entities ce ON ce.id=re.src_id "
|
||||
"WHERE re.dst_id=? AND re.edge_type='member_of' AND ce.deleted_at IS NULL ORDER BY ce.display_name", (lp_id,))]
|
||||
c.close()
|
||||
return {"investor": dict(inv) if inv else None, "contacts": contacts, "contact_count": len(contacts)}
|
||||
|
||||
|
||||
def search_records(query=None, entity_kind=None, limit=20, db=None):
|
||||
"""Structured search over canonical entities (name substring + kind)."""
|
||||
c = _conn(db)
|
||||
|
||||
@@ -47,6 +47,13 @@ def get_interaction_history(lp_id: str, limit: int = 20) -> dict:
|
||||
return t.get_interaction_history(lp_id, limit=limit)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def get_investor_contacts(lp_id: str) -> dict:
|
||||
"""List all contacts (people) belonging to an investor entity — the
|
||||
one-investor-to-many-contacts relationship (e.g. a family office's several people)."""
|
||||
return t.get_investor_contacts(lp_id)
|
||||
|
||||
|
||||
# ── retrieval modes ──
|
||||
@mcp.tool()
|
||||
def hybrid_search(query: str, top_k: int = 8, lp_id: str = "", doc_type: str = "",
|
||||
|
||||
Reference in New Issue
Block a user