From 6fb5e20e1865877b68bf1bde54332cdb64e2d50a Mon Sep 17 00:00:00 2001 From: Jer Miller Date: Tue, 14 Apr 2026 22:02:58 -0600 Subject: [PATCH] entity intelligence: add --brief flag to truncate activity and network MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add brief=False parameter to get_entity_intelligence() and --brief/-b CLI flag to `sol call entities intelligence`. When active, truncates activity to 20 most recent signals and network to 20 top co-occurring entities. Adds _meta key with truncation counts so consumers know data was omitted. Default behavior (brief=False) is unchanged — no _meta key. --- apps/entities/call.py | 5 +++- tests/test_entity_intelligence.py | 47 +++++++++++++++++++++++++++++++ think/indexer/journal.py | 19 ++++++++++++- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/apps/entities/call.py b/apps/entities/call.py index 178aefa99..50445486e 100644 --- a/apps/entities/call.py +++ b/apps/entities/call.py @@ -522,11 +522,14 @@ def entity_search( def entity_intel( entity: str = typer.Argument(help="Entity name or identifier."), facet: str | None = typer.Option(None, "--facet", "-f", help="Filter by facet."), + brief: bool = typer.Option( + False, "--brief", "-b", help="Truncate activity and network to 20 items." + ), ) -> None: """Get a full intelligence briefing for an entity.""" import json as _json - result = get_entity_intelligence(entity, facet=facet) + result = get_entity_intelligence(entity, facet=facet, brief=brief) if result is None: typer.echo(f"Error: Entity '{entity}' not found.", err=True) raise typer.Exit(1) diff --git a/tests/test_entity_intelligence.py b/tests/test_entity_intelligence.py index 05d36e380..93eaf53dc 100644 --- a/tests/test_entity_intelligence.py +++ b/tests/test_entity_intelligence.py @@ -149,3 +149,50 @@ class TestEntityIntelligence: result = get_entity_intelligence("Bob Smith") assert result is not None assert result["identity"]["entity_id"] == "bob_smith" + + +class TestEntityIntelligenceBrief: + def test_brief_has_meta(self): + result = get_entity_intelligence("Alice Johnson", brief=True) + assert result is not None + assert "_meta" in result + assert result["_meta"]["brief"] is True + + def test_brief_has_all_sections(self): + result = get_entity_intelligence("Alice Johnson", brief=True) + for section in ( + "identity", + "relationships", + "observations", + "activity", + "strength", + "network", + "facets", + ): + assert section in result + + def test_brief_truncates_activity(self): + result = get_entity_intelligence("Alice Johnson", brief=True) + assert len(result["activity"]) <= 20 + assert result["_meta"]["activity_included"] == len(result["activity"]) + + def test_brief_truncates_network(self): + result = get_entity_intelligence("Alice Johnson", brief=True) + assert len(result["network"]) <= 20 + assert result["_meta"]["network_included"] == len(result["network"]) + + def test_brief_meta_counts_consistent(self): + result = get_entity_intelligence("Alice Johnson", brief=True) + meta = result["_meta"] + assert meta["activity_included"] <= meta["activity_total"] + assert meta["network_included"] <= meta["network_total"] + + def test_brief_preserves_other_sections(self): + full = get_entity_intelligence("Alice Johnson") + brief = get_entity_intelligence("Alice Johnson", brief=True) + for key in ("identity", "relationships", "observations", "strength", "facets"): + assert full[key] == brief[key] + + def test_default_no_meta(self): + result = get_entity_intelligence("Alice Johnson") + assert "_meta" not in result diff --git a/think/indexer/journal.py b/think/indexer/journal.py index e713b4aee..ade52d17f 100644 --- a/think/indexer/journal.py +++ b/think/indexer/journal.py @@ -2705,6 +2705,7 @@ def search_entities( def get_entity_intelligence( entity: str, facet: str | None = None, + brief: bool = False, ) -> dict[str, Any] | None: """Get a full intelligence briefing for an entity.""" conn, _ = get_journal_index() @@ -2938,7 +2939,20 @@ def get_entity_intelligence( "photo_count": photo_count, } - return { + if brief: + activity_total = len(activity) + network_total = len(network) + activity = activity[:20] + network = dict(list(network.items())[:20]) + meta = { + "brief": True, + "activity_total": activity_total, + "activity_included": len(activity), + "network_total": network_total, + "network_included": len(network), + } + + result = { "identity": identity, "relationships": relationships, "observations": observations, @@ -2947,5 +2961,8 @@ def get_entity_intelligence( "network": network, "facets": all_facets, } + if brief: + result["_meta"] = meta + return result finally: conn.close() -- 2.51.2