Sync from GitHub: e2c43430bd73d3ad00b29583505268c8439f1fbb
Browse files- app.py +68 -11
- nuwave/organism.py +57 -2
app.py
CHANGED
|
@@ -708,6 +708,63 @@ organism.set_concept_extractor(_bitnet_concept_extractor)
|
|
| 708 |
logger.info("NuWave concept helper wired: dual-pass extraction live")
|
| 709 |
|
| 710 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 711 |
# ── Chat Handler ──────────────────────────────────────────────────
|
| 712 |
|
| 713 |
def on_send(message, history):
|
|
@@ -731,18 +788,16 @@ def on_send(message, history):
|
|
| 731 |
sys_ctx = kiss_string_result.get("system_context", system_prompt)
|
| 732 |
|
| 733 |
# ── 4. Pith bucket — extract relevant context from the River ──
|
| 734 |
-
pith_context = organism.
|
| 735 |
|
| 736 |
# Pith context REPLACES old message history, not adds to it.
|
| 737 |
# The substrate carries what the older messages contained — the model
|
| 738 |
# doesn't need both. Recent messages pass verbatim (the model needs
|
| 739 |
# immediate context). Older messages are replaced by substrate context.
|
| 740 |
if pith_context:
|
| 741 |
-
substrate_ctx =
|
| 742 |
-
if
|
| 743 |
-
sys_ctx = substrate_ctx + "\n\n" + sys_ctx
|
| 744 |
-
else:
|
| 745 |
-
sys_ctx = substrate_ctx
|
| 746 |
|
| 747 |
# Build prompt — Pith context replaces old history
|
| 748 |
# Only send recent messages. The substrate carries the rest.
|
|
@@ -979,10 +1034,11 @@ def on_benchmark(num_turns):
|
|
| 979 |
sys_ctx = kiss_r.get("system_context", system_prompt)
|
| 980 |
|
| 981 |
# Pith Born rule extraction from substrate
|
| 982 |
-
pith_context = nw_organism.
|
| 983 |
if pith_context:
|
| 984 |
-
substrate_ctx =
|
| 985 |
-
|
|
|
|
| 986 |
|
| 987 |
# Trim old messages — always, not gated on Pith.
|
| 988 |
# The substrate + KISS carry what the older messages contained.
|
|
@@ -1230,8 +1286,9 @@ def on_interleaved_benchmark(
|
|
| 1230 |
kiss_r = nw_kiss_inst.filter_context(nw_msgs, system_prompt)
|
| 1231 |
sys_ctx = kiss_r.get("system_context", system_prompt)
|
| 1232 |
if pith_context:
|
| 1233 |
-
substrate_ctx =
|
| 1234 |
-
|
|
|
|
| 1235 |
|
| 1236 |
recent_window = 6
|
| 1237 |
recent = nw_msgs[-recent_window:] if len(nw_msgs) > recent_window else nw_msgs
|
|
|
|
| 708 |
logger.info("NuWave concept helper wired: dual-pass extraction live")
|
| 709 |
|
| 710 |
|
| 711 |
+
# ── Substrate context formatter ───────────────────────────────────
|
| 712 |
+
# Replaces the prior "\n".join(pith_context) lump with explicit type
|
| 713 |
+
# sections so BitNet's attention has structural cues to work with.
|
| 714 |
+
# Group surfaced content by node-kind via ID prefix:
|
| 715 |
+
# tree_* → "Related concepts" (concept words from dual-pass)
|
| 716 |
+
# exp_* → "Prior questions on this topic" (deposit nodes)
|
| 717 |
+
# resp_* → "Prior responses"
|
| 718 |
+
# concept_narr_* → operational telemetry, omitted from prompt
|
| 719 |
+
# (it's substrate self-monitoring, not knowledge)
|
| 720 |
+
# other → "Other context"
|
| 721 |
+
# The "Dave Plummer Tempest analog" for LLM presentation: curated
|
| 722 |
+
# typed input vs. raw mush. See feedback_substrate_representation_first.md.
|
| 723 |
+
|
| 724 |
+
def _format_substrate_context(pith_context, pith_ids=None) -> str:
|
| 725 |
+
"""Return a sectioned substrate-context string for prompt injection."""
|
| 726 |
+
if not pith_context:
|
| 727 |
+
return ""
|
| 728 |
+
if not pith_ids or len(pith_ids) != len(pith_context):
|
| 729 |
+
# No IDs available — can't section. Fallback to plain join so
|
| 730 |
+
# callers without _with_ids still produce something usable.
|
| 731 |
+
return "\n".join(pith_context)
|
| 732 |
+
|
| 733 |
+
concepts, questions, responses, other = [], [], [], []
|
| 734 |
+
for text, pid in zip(pith_context, pith_ids):
|
| 735 |
+
if not text:
|
| 736 |
+
continue
|
| 737 |
+
if pid.startswith("tree_"):
|
| 738 |
+
concepts.append(text)
|
| 739 |
+
elif pid.startswith("exp_"):
|
| 740 |
+
questions.append(text)
|
| 741 |
+
elif pid.startswith("resp_"):
|
| 742 |
+
responses.append(text)
|
| 743 |
+
elif pid.startswith("concept_narr_"):
|
| 744 |
+
# Operational telemetry — omit from prompt context (Bunyan-shaped
|
| 745 |
+
# data; legitimate substrate experience but not user knowledge).
|
| 746 |
+
continue
|
| 747 |
+
else:
|
| 748 |
+
other.append(text)
|
| 749 |
+
|
| 750 |
+
parts = []
|
| 751 |
+
if concepts:
|
| 752 |
+
parts.append(
|
| 753 |
+
"[Related concepts from substrate:]\n"
|
| 754 |
+
+ "\n".join(f"- {c}" for c in concepts)
|
| 755 |
+
)
|
| 756 |
+
if questions:
|
| 757 |
+
parts.append(
|
| 758 |
+
"[Prior questions on this topic:]\n"
|
| 759 |
+
+ "\n".join(f"- {q}" for q in questions)
|
| 760 |
+
)
|
| 761 |
+
if responses:
|
| 762 |
+
parts.append("[Prior context:]\n" + "\n".join(responses))
|
| 763 |
+
if other:
|
| 764 |
+
parts.append("[Other context:]\n" + "\n".join(f"- {o}" for o in other))
|
| 765 |
+
return "\n\n".join(parts)
|
| 766 |
+
|
| 767 |
+
|
| 768 |
# ── Chat Handler ──────────────────────────────────────────────────
|
| 769 |
|
| 770 |
def on_send(message, history):
|
|
|
|
| 788 |
sys_ctx = kiss_string_result.get("system_context", system_prompt)
|
| 789 |
|
| 790 |
# ── 4. Pith bucket — extract relevant context from the River ──
|
| 791 |
+
pith_context, pith_ids = organism.pith_extract_with_ids(message, max_context=5)
|
| 792 |
|
| 793 |
# Pith context REPLACES old message history, not adds to it.
|
| 794 |
# The substrate carries what the older messages contained — the model
|
| 795 |
# doesn't need both. Recent messages pass verbatim (the model needs
|
| 796 |
# immediate context). Older messages are replaced by substrate context.
|
| 797 |
if pith_context:
|
| 798 |
+
substrate_ctx = _format_substrate_context(pith_context, pith_ids)
|
| 799 |
+
if substrate_ctx:
|
| 800 |
+
sys_ctx = substrate_ctx + "\n\n" + sys_ctx if sys_ctx else substrate_ctx
|
|
|
|
|
|
|
| 801 |
|
| 802 |
# Build prompt — Pith context replaces old history
|
| 803 |
# Only send recent messages. The substrate carries the rest.
|
|
|
|
| 1034 |
sys_ctx = kiss_r.get("system_context", system_prompt)
|
| 1035 |
|
| 1036 |
# Pith Born rule extraction from substrate
|
| 1037 |
+
pith_context, pith_ids = nw_organism.pith_extract_with_ids(prompt_text, max_context=5)
|
| 1038 |
if pith_context:
|
| 1039 |
+
substrate_ctx = _format_substrate_context(pith_context, pith_ids)
|
| 1040 |
+
if substrate_ctx:
|
| 1041 |
+
sys_ctx = substrate_ctx + "\n\n" + sys_ctx if sys_ctx else substrate_ctx
|
| 1042 |
|
| 1043 |
# Trim old messages — always, not gated on Pith.
|
| 1044 |
# The substrate + KISS carry what the older messages contained.
|
|
|
|
| 1286 |
kiss_r = nw_kiss_inst.filter_context(nw_msgs, system_prompt)
|
| 1287 |
sys_ctx = kiss_r.get("system_context", system_prompt)
|
| 1288 |
if pith_context:
|
| 1289 |
+
substrate_ctx = _format_substrate_context(pith_context, pith_ids)
|
| 1290 |
+
if substrate_ctx:
|
| 1291 |
+
sys_ctx = substrate_ctx + "\n\n" + sys_ctx if sys_ctx else substrate_ctx
|
| 1292 |
|
| 1293 |
recent_window = 6
|
| 1294 |
recent = nw_msgs[-recent_window:] if len(nw_msgs) > recent_window else nw_msgs
|
nuwave/organism.py
CHANGED
|
@@ -1198,6 +1198,50 @@ class NuWaveOrganism:
|
|
| 1198 |
summaries.append(' '.join(ordered))
|
| 1199 |
return summaries
|
| 1200 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1201 |
def pith_extract(
|
| 1202 |
self,
|
| 1203 |
query: str,
|
|
@@ -1266,7 +1310,14 @@ class NuWaveOrganism:
|
|
| 1266 |
effective_amp = max(0.0, min(1.0, amp + self._interference_rate * interference))
|
| 1267 |
|
| 1268 |
# Born rule: probability ∝ amplitude²
|
| 1269 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1270 |
|
| 1271 |
content = self._node_content.get(nid, '')
|
| 1272 |
if content and born_score > 0.001:
|
|
@@ -1480,8 +1531,12 @@ class NuWaveOrganism:
|
|
| 1480 |
|
| 1481 |
node = nodes_by_id.get(nid)
|
| 1482 |
excitability = float(getattr(node, "intrinsic_excitability", 1.0)) if node else 1.0
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1483 |
|
| 1484 |
-
surface_score = sim * latency_boost * excitability
|
| 1485 |
|
| 1486 |
content = self._node_content.get(nid, "")
|
| 1487 |
if content and surface_score > 0.001:
|
|
|
|
| 1198 |
summaries.append(' '.join(ordered))
|
| 1199 |
return summaries
|
| 1200 |
|
| 1201 |
+
# ── Type-aware retrieval bias (Phase 2 — representation-first redesign) ──
|
| 1202 |
+
# Inductive bias on what kinds of nodes deserve weight in retrieval
|
| 1203 |
+
# scoring. Bootstrapped to expert weights, decaying toward substrate
|
| 1204 |
+
# authority (1.0 = no bias) as substrate accumulates step_count.
|
| 1205 |
+
# Substrate Authority Pattern applied to retrieval: hand-coded expert
|
| 1206 |
+
# carries the substrate through Apprentice region; STDP + three-factor
|
| 1207 |
+
# learning gradually take over as competence accumulates.
|
| 1208 |
+
#
|
| 1209 |
+
# Per-kind raw biases (Apprentice values; reduce toward 1.0 with decay):
|
| 1210 |
+
# tree_* → 1.30 concept words from dual-pass; usually high-value
|
| 1211 |
+
# exp_* → 1.00 question deposits; neutral baseline
|
| 1212 |
+
# resp_* → 1.10 past responses; slight boost (often informative)
|
| 1213 |
+
# concept_narr_* → 0.30 operational telemetry; strong suppression
|
| 1214 |
+
# (Bunyan-shaped narrative; substrate-self-monitoring)
|
| 1215 |
+
# other → 1.00 unknown prefix; neutral
|
| 1216 |
+
#
|
| 1217 |
+
# Expert-decay schedule: linear ramp from full-bias at step_count=0 to
|
| 1218 |
+
# zero-bias (1.0 multiplier for everything) at step_count=COMPETENCE_CEILING.
|
| 1219 |
+
# Default ceiling 50000 is several days of normal Tonic activity — gives
|
| 1220 |
+
# the substrate ample Apprentice time to accumulate outcome experience
|
| 1221 |
+
# before the inductive bias hands off.
|
| 1222 |
+
TYPE_BIAS_RAW = {
|
| 1223 |
+
"tree_": 1.30,
|
| 1224 |
+
"exp_": 1.00,
|
| 1225 |
+
"resp_": 1.10,
|
| 1226 |
+
"concept_narr_": 0.30,
|
| 1227 |
+
}
|
| 1228 |
+
COMPETENCE_CEILING = 50000 # step_count at which expert influence → 0
|
| 1229 |
+
|
| 1230 |
+
def _type_bias_for_node(self, node_id: str) -> float:
|
| 1231 |
+
"""Type-bias multiplier with expert decay, per Substrate Authority Pattern."""
|
| 1232 |
+
# Find matching prefix (longest match first)
|
| 1233 |
+
raw = 1.0
|
| 1234 |
+
for prefix in ("concept_narr_", "tree_", "resp_", "exp_"):
|
| 1235 |
+
if node_id.startswith(prefix):
|
| 1236 |
+
raw = self.TYPE_BIAS_RAW[prefix]
|
| 1237 |
+
break
|
| 1238 |
+
# Linear decay: at step 0, expert_weight=1.0; at ceiling, expert_weight=0.0
|
| 1239 |
+
expert_weight = max(0.0, min(1.0,
|
| 1240 |
+
1.0 - self._step_count / max(1, self.COMPETENCE_CEILING),
|
| 1241 |
+
))
|
| 1242 |
+
# Blend: full expert bias → substrate authority (1.0) over time
|
| 1243 |
+
return 1.0 + (raw - 1.0) * expert_weight
|
| 1244 |
+
|
| 1245 |
def pith_extract(
|
| 1246 |
self,
|
| 1247 |
query: str,
|
|
|
|
| 1310 |
effective_amp = max(0.0, min(1.0, amp + self._interference_rate * interference))
|
| 1311 |
|
| 1312 |
# Born rule: probability ∝ amplitude²
|
| 1313 |
+
# Plus type-aware bias (Substrate Authority Pattern):
|
| 1314 |
+
# apprentice-region inductive bias on node-kind, decaying
|
| 1315 |
+
# toward substrate authority over step_count. Same formula
|
| 1316 |
+
# as surface_extract — applied as a multiplicative factor
|
| 1317 |
+
# so high-amplitude narrative nodes can still be picked
|
| 1318 |
+
# if they're the only thing matching, but get scaled down.
|
| 1319 |
+
type_bias = self._type_bias_for_node(nid)
|
| 1320 |
+
born_score = effective_amp * effective_amp * type_bias
|
| 1321 |
|
| 1322 |
content = self._node_content.get(nid, '')
|
| 1323 |
if content and born_score > 0.001:
|
|
|
|
| 1531 |
|
| 1532 |
node = nodes_by_id.get(nid)
|
| 1533 |
excitability = float(getattr(node, "intrinsic_excitability", 1.0)) if node else 1.0
|
| 1534 |
+
# Type-aware bias (Substrate Authority Pattern): hand-coded
|
| 1535 |
+
# inductive bias on node-kind, decaying toward substrate
|
| 1536 |
+
# authority as step_count accumulates. See _type_bias_for_node.
|
| 1537 |
+
type_bias = self._type_bias_for_node(nid)
|
| 1538 |
|
| 1539 |
+
surface_score = sim * latency_boost * excitability * type_bias
|
| 1540 |
|
| 1541 |
content = self._node_content.get(nid, "")
|
| 1542 |
if content and surface_score > 0.001:
|