Skip to content

Commit a5d1370

Browse files
committed
fix: resolve merge conflicts and ruff format
1 parent df53d15 commit a5d1370

4 files changed

Lines changed: 79 additions & 92 deletions

File tree

mempalace/gossip.py

Lines changed: 28 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,9 @@ def _merge_with_default(raw: dict[str, Any]) -> dict[str, Any]:
276276
return merged
277277

278278

279-
def load_gossip_config(path: Optional[str] = None, config: MempalaceConfig = None) -> dict[str, Any]:
279+
def load_gossip_config(
280+
path: Optional[str] = None, config: MempalaceConfig = None
281+
) -> dict[str, Any]:
280282
"""Load gossip configuration from ``path`` or the default location.
281283
282284
If the file does not exist, the default configuration is returned and
@@ -383,7 +385,9 @@ def text(self) -> str:
383385
def is_expired(self) -> bool:
384386
return (datetime.now(timezone.utc) - self._started_at).total_seconds() > self.ttl_seconds
385387

386-
def child(self, wing: str, room: Optional[str] = None, node_id: Optional[str] = None) -> GossipMessage:
388+
def child(
389+
self, wing: str, room: Optional[str] = None, node_id: Optional[str] = None
390+
) -> GossipMessage:
387391
"""Return a copy with incremented hop count and updated path."""
388392
new_path = list(self.path)
389393
if node_id:
@@ -423,7 +427,9 @@ def __init__(
423427
config: Optional[dict[str, Any]] = None,
424428
):
425429
self.mempalace_config = mempalace_config
426-
self.config = config if config is not None else load_gossip_config(config_path, mempalace_config)
430+
self.config = (
431+
config if config is not None else load_gossip_config(config_path, mempalace_config)
432+
)
427433
self._chatter_nodes = [ChatterNode.from_dict(n) for n in self.config["chatter_nodes"]]
428434
self.kg = kg
429435
self._kg_path = kg_path
@@ -456,9 +462,7 @@ def detect_topic(self, text: str, priority: Optional[str] = None) -> tuple[str,
456462
return "general", priority
457463
return "general", "normal"
458464

459-
def _get_hallway_context(
460-
self, message: GossipMessage
461-
) -> tuple[dict[str, float], set[str]]:
465+
def _get_hallway_context(self, message: GossipMessage) -> tuple[dict[str, float], set[str]]:
462466
"""Return (room_scores, related_entities) derived from within-wing hallways.
463467
464468
Hallways are entity-pair co-occurrence records built at mine time. When a
@@ -470,9 +474,7 @@ def _get_hallway_context(
470474
if not message.source_wing:
471475
return {}, set()
472476
try:
473-
hallways = list_hallways(
474-
wing=message.source_wing, config=self.mempalace_config
475-
)
477+
hallways = list_hallways(wing=message.source_wing, config=self.mempalace_config)
476478
except Exception:
477479
logger.debug("gossip: could not load hallways", exc_info=True)
478480
return {}, set()
@@ -537,8 +539,7 @@ def _random_walk_swap(
537539
off_radius = [
538540
n
539541
for n in self._chatter_nodes
540-
if n not in selected
541-
and source not in {normalize_wing_name(w) for w in n.gossip_radius}
542+
if n not in selected and source not in {normalize_wing_name(w) for w in n.gossip_radius}
542543
]
543544
if not off_radius:
544545
return selected
@@ -568,9 +569,7 @@ def select_chatter_nodes(
568569
"""
569570
params = self._channel_params(message)
570571
fanout = (
571-
fanout
572-
if fanout is not None
573-
else params.get("fanout", self.config.get("fanout", 5))
572+
fanout if fanout is not None else params.get("fanout", self.config.get("fanout", 5))
574573
)
575574
noise_tolerance = float(params.get("noise_tolerance", 0.2))
576575
room_scores, related_entities = self._get_hallway_context(message)
@@ -587,17 +586,13 @@ def select_chatter_nodes(
587586
# co-occurrence rooms are in different namespaces, so we match
588587
# rooms to the node's ``rooms`` list rather than to ``hall``.
589588
if room_scores and node.rooms:
590-
overlaps = set(room_scores.keys()) & {
591-
r.lower() for r in node.rooms
592-
}
589+
overlaps = set(room_scores.keys()) & {r.lower() for r in node.rooms}
593590
for room in overlaps:
594591
score += room_scores[room]
595592

596593
# Also boost if a related entity matches a specialty.
597594
if related_entities and node.specialties:
598-
overlaps = related_entities & {
599-
s.lower() for s in node.specialties
600-
}
595+
overlaps = related_entities & {s.lower() for s in node.specialties}
601596
score += min(0.3, len(overlaps) * 0.1)
602597

603598
score = max(0.0, min(2.0, score))
@@ -668,12 +663,8 @@ def _attenuate_echo_chamber(
668663
Each path entry is either a node id or ``node_id:message_text``. The
669664
similarity threshold determines whether a prior visit counts as an echo.
670665
"""
671-
threshold = float(
672-
self.config.get("echo_chamber_similarity_threshold", 0.8)
673-
)
674-
reinforcement_count = int(
675-
self.config.get("echo_chamber_reinforcement_count", 3)
676-
)
666+
threshold = float(self.config.get("echo_chamber_similarity_threshold", 0.8))
667+
reinforcement_count = int(self.config.get("echo_chamber_reinforcement_count", 3))
677668
attenuation = float(self.config.get("echo_chamber_attenuation", 0.5))
678669

679670
current_text = message.text
@@ -807,9 +798,7 @@ def _propagate_message(
807798
)
808799
report["triples_written"] += 1
809800
node_report["successful_targets"] += 1
810-
node_report["targets"].append(
811-
{"wing": target_wing, "room": target_room}
812-
)
801+
node_report["targets"].append({"wing": target_wing, "room": target_room})
813802
any_success = True
814803
children.append(message.child(target_wing, target_room, node_id=node.id))
815804
except Exception as exc:
@@ -869,9 +858,7 @@ def propagate(
869858

870859
# Preserve explicit caller arguments over channel defaults.
871860
final_max_hops = (
872-
max_hops
873-
if max_hops is not None
874-
else params.get("max_hops", preliminary.max_hops)
861+
max_hops if max_hops is not None else params.get("max_hops", preliminary.max_hops)
875862
)
876863

877864
message = GossipMessage(
@@ -907,9 +894,7 @@ def chatter_status(self) -> dict[str, Any]:
907894
"echo_chamber_reinforcement_count": self.config.get(
908895
"echo_chamber_reinforcement_count", 3
909896
),
910-
"echo_chamber_attenuation": self.config.get(
911-
"echo_chamber_attenuation", 0.5
912-
),
897+
"echo_chamber_attenuation": self.config.get("echo_chamber_attenuation", 0.5),
913898
"echo_chamber_similarity_threshold": self.config.get(
914899
"echo_chamber_similarity_threshold", 0.8
915900
),
@@ -949,20 +934,12 @@ def _active_triples(self, as_of: str = None) -> list[dict]:
949934
"""Return gossip triples active at ``as_of`` (or now)."""
950935
reference = as_of or datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
951936
triples = self.kg.query_gossip_triples(as_of=reference)
952-
return [
953-
t
954-
for t in triples
955-
if t.get("valid_to") is None or t["valid_to"] > reference
956-
]
937+
return [t for t in triples if t.get("valid_to") is None or t["valid_to"] > reference]
957938

958-
def trending_topics(
959-
self, n: int = 5, as_of: str = None
960-
) -> list[dict[str, Any]]:
939+
def trending_topics(self, n: int = 5, as_of: str = None) -> list[dict[str, Any]]:
961940
"""Return the top N topics by active gossip triple count."""
962941
triples = self._active_triples(as_of=as_of)
963-
topic_counts = Counter(
964-
self._parse_topic(t.get("source_file")) for t in triples
965-
)
942+
topic_counts = Counter(self._parse_topic(t.get("source_file")) for t in triples)
966943
total = max(1, sum(topic_counts.values()))
967944
ranked = topic_counts.most_common(n)
968945
return [
@@ -974,9 +951,7 @@ def trending_topics(
974951
for topic, count in ranked
975952
]
976953

977-
def viral_facts(
978-
self, min_count: int = 2, as_of: str = None
979-
) -> list[dict[str, Any]]:
954+
def viral_facts(self, min_count: int = 2, as_of: str = None) -> list[dict[str, Any]]:
980955
"""Return facts that appear in multiple gossip triples (viral content).
981956
982957
Facts are identified by the original (subject, predicate, object) so that
@@ -1023,9 +998,7 @@ def network_health(
1023998
and t["valid_from"] <= reference
1024999
]
10251000

1026-
by_topic = Counter(
1027-
self._parse_topic(t.get("source_file")) for t in triples
1028-
)
1001+
by_topic = Counter(self._parse_topic(t.get("source_file")) for t in triples)
10291002

10301003
return {
10311004
"active_triples": total,
@@ -1047,9 +1020,7 @@ def snapshot(
10471020
return {
10481021
"trending_topics": self.trending_topics(as_of=as_of),
10491022
"viral_facts": self.viral_facts(as_of=as_of),
1050-
"network_health": self.network_health(
1051-
chatter_nodes, config, as_of=as_of
1052-
),
1023+
"network_health": self.network_health(chatter_nodes, config, as_of=as_of),
10531024
}
10541025

10551026

@@ -1102,9 +1073,7 @@ def schedule(
11021073
priority=priority,
11031074
topic=detected_topic,
11041075
ttl_seconds=self.protocol.config.get("ttl_seconds", 60),
1105-
max_hops=max_hops
1106-
if max_hops is not None
1107-
else self.protocol.config.get("max_hops", 3),
1076+
max_hops=max_hops if max_hops is not None else self.protocol.config.get("max_hops", 3),
11081077
)
11091078
with self._lock:
11101079
self._queue.append(message)
@@ -1169,11 +1138,7 @@ def run_once(
11691138
# that concurrent schedulers may have added.
11701139
with self._lock:
11711140
available = max(0, self.max_requeue - len(self._queue))
1172-
kept = [
1173-
c
1174-
for c in children
1175-
if not c.is_expired() and c.hops < c.max_hops
1176-
][:available]
1141+
kept = [c for c in children if not c.is_expired() and c.hops < c.max_hops][:available]
11771142
for c in kept:
11781143
self._queue.append(c)
11791144
report["children_queued"] = len(kept)

mempalace/mcp_server.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5012,12 +5012,27 @@ def tool_patch_submit(
50125012
"type": "object",
50135013
"properties": {
50145014
"subject": {"type": "string", "description": "Entity the fact is about"},
5015-
"predicate": {"type": "string", "description": "Relationship type (e.g. 'found', 'announced')"},
5015+
"predicate": {
5016+
"type": "string",
5017+
"description": "Relationship type (e.g. 'found', 'announced')",
5018+
},
50165019
"object": {"type": "string", "description": "The target/value of the relationship"},
5017-
"source_wing": {"type": "string", "description": "Wing the fact originated from (optional)"},
5018-
"source_room": {"type": "string", "description": "Room the fact originated from (optional)"},
5019-
"priority": {"type": "string", "description": "critical, high, medium, or low (optional; detected from keywords if omitted)"},
5020-
"fanout": {"type": "integer", "description": "Max chatter nodes to involve (optional)"},
5020+
"source_wing": {
5021+
"type": "string",
5022+
"description": "Wing the fact originated from (optional)",
5023+
},
5024+
"source_room": {
5025+
"type": "string",
5026+
"description": "Room the fact originated from (optional)",
5027+
},
5028+
"priority": {
5029+
"type": "string",
5030+
"description": "critical, high, medium, or low (optional; detected from keywords if omitted)",
5031+
},
5032+
"fanout": {
5033+
"type": "integer",
5034+
"description": "Max chatter nodes to involve (optional)",
5035+
},
50215036
},
50225037
"required": ["subject", "predicate", "object"],
50235038
},

tests/test_gossip.py

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -113,33 +113,39 @@ def test_default_gossip_path_uses_home():
113113

114114

115115
def test_chatter_node_from_dict():
116-
node = ChatterNode.from_dict({
117-
"id": "x",
118-
"name": "X",
119-
"wing": "orkid",
120-
"hall": "technical",
121-
"role": "tech",
122-
"specialties": ["defi"],
123-
"gossip_radius": ["orkid"],
124-
"chatter_level": "high",
125-
"propagation_speed": "instant",
126-
})
116+
node = ChatterNode.from_dict(
117+
{
118+
"id": "x",
119+
"name": "X",
120+
"wing": "orkid",
121+
"hall": "technical",
122+
"role": "tech",
123+
"specialties": ["defi"],
124+
"gossip_radius": ["orkid"],
125+
"chatter_level": "high",
126+
"propagation_speed": "instant",
127+
}
128+
)
127129
assert node.id == "x"
128130
assert node.chatter_level == "high"
129131
assert node.match_score("new defi strategy", source_wing="orkid") > 0.5
130132

131133

132134
def test_chatter_node_match_score_ignores_unknown_wing():
133-
node = ChatterNode.from_dict({
134-
"id": "x",
135-
"name": "X",
136-
"wing": "orkid",
137-
"hall": "technical",
138-
"role": "tech",
139-
"specialties": ["defi"],
140-
"gossip_radius": ["orkid"],
141-
})
142-
assert node.match_score("defi", source_wing="unknown") < node.match_score("defi", source_wing="orkid")
135+
node = ChatterNode.from_dict(
136+
{
137+
"id": "x",
138+
"name": "X",
139+
"wing": "orkid",
140+
"hall": "technical",
141+
"role": "tech",
142+
"specialties": ["defi"],
143+
"gossip_radius": ["orkid"],
144+
}
145+
)
146+
assert node.match_score("defi", source_wing="unknown") < node.match_score(
147+
"defi", source_wing="orkid"
148+
)
143149

144150

145151
def test_gossip_message_expiry():

tests/test_mcp_server.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,14 @@ def _patch_mcp_server(monkeypatch, config, kg):
125125

126126
invalidate_graph_cache()
127127

128+
# Use the example gossip topology for tests so the default install is not
129+
# forced to carry project-specific names.
130+
monkeypatch.setattr(gossip_mod, "DEFAULT_GOSSIP_CONFIG", gossip_mod.EXAMPLE_GOSSIP_CONFIG)
131+
128132

129133
def _unexpected_client_read(*_a, **_k):
130134
"""Tripwire for paths that must stay off the chroma client (and HNSW)."""
131135
raise AssertionError("chroma collection opened — this path must read sqlite")
132-
# Use the example gossip topology for tests so the default install is not
133-
# forced to carry project-specific names.
134-
monkeypatch.setattr(gossip_mod, "DEFAULT_GOSSIP_CONFIG", gossip_mod.EXAMPLE_GOSSIP_CONFIG)
135136

136137

137138
def _get_collection(palace_path, create=False):

0 commit comments

Comments
 (0)