Hoshe: - Mark --dump-prompts / name_feature() as vestigial with TODO note - Fix --refresh help string: 200 → 1000 (matches actual default) - Fix _RIVER_POOLS comment numbering: Pool 6 before Pool 5 → correct order - Remove dead first-pass code in fix_fewshot_bleed.py - _CAPTURE_FILE leak noted in vestigial TODO Tyre: - Fix stale "Gemma 2" strings in banner, argparse description, model help - Note dead code for cleanup pass (name_feature ~700 lines) Hoshe (prune): - prune_atlas_features.py: named features sort before unnamed, preventing silent discard of hand-authored names during pruning naming_core: - v0.2: few-shot blocklist, stricter is_valid_name (min 3 chars, no digits, no brackets), prompt fragment rejection expanded Miri clarification: the 261 "empty-string" files contain only roads (37) and railroads (37) — infrastructure features never in naming scope. All cities/rivers/oceans/mountains/POIs are clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -152,47 +152,7 @@ def main():
|
|||||||
print(f"WARNING: only {len(available)} replacements for {len(replacements_needed)} features")
|
print(f"WARNING: only {len(available)} replacements for {len(replacements_needed)} features")
|
||||||
print(" some features will keep their few-shot names")
|
print(" some features will keep their few-shot names")
|
||||||
|
|
||||||
# Assign replacements deterministically — hash body_id + feature_id
|
# Group by file, reload, replace, write
|
||||||
# to pick from the pool, ensuring each body gets different names
|
|
||||||
used_per_body = defaultdict(set)
|
|
||||||
replacement_idx = 0
|
|
||||||
changed_files = set()
|
|
||||||
total_replaced = 0
|
|
||||||
|
|
||||||
for markers_path, body_id, section, feat in replacements_needed:
|
|
||||||
old_name = feat["name"]
|
|
||||||
|
|
||||||
# Find next available name not yet used on this body
|
|
||||||
assigned = None
|
|
||||||
for attempt in range(len(available)):
|
|
||||||
candidate = available[(replacement_idx + attempt) % len(available)]
|
|
||||||
if candidate.lower() not in used_per_body[body_id]:
|
|
||||||
assigned = candidate
|
|
||||||
replacement_idx = (replacement_idx + attempt + 1) % len(available)
|
|
||||||
break
|
|
||||||
|
|
||||||
if assigned is None:
|
|
||||||
print(f" SKIP {body_id}/{section}: no unique replacement for \"{old_name}\"")
|
|
||||||
continue
|
|
||||||
|
|
||||||
feat["name"] = assigned
|
|
||||||
used_per_body[body_id].add(assigned.lower())
|
|
||||||
global_names.add(assigned.lower())
|
|
||||||
changed_files.add(markers_path)
|
|
||||||
total_replaced += 1
|
|
||||||
|
|
||||||
# Write changed files
|
|
||||||
for markers_path in changed_files:
|
|
||||||
body_id = markers_path.parent.name
|
|
||||||
m = json.loads(markers_path.read_text())
|
|
||||||
|
|
||||||
# Re-apply changes (re-read since we modified feat objects in memory)
|
|
||||||
# Actually the feat dicts are still referenced — just rewrite
|
|
||||||
# But we need to reload and re-match since we didn't track which file
|
|
||||||
# has which changes...
|
|
||||||
|
|
||||||
# Simpler approach: reload, replace, write
|
|
||||||
# Reset and do it properly
|
|
||||||
replacement_idx = 0
|
replacement_idx = 0
|
||||||
used_per_body = defaultdict(set)
|
used_per_body = defaultdict(set)
|
||||||
changed_bodies = []
|
changed_bodies = []
|
||||||
|
|||||||
@@ -71,14 +71,11 @@ DB_PATH = REPO_ROOT / "server" / "data" / "systems.db"
|
|||||||
WIKI_SYSTEMS = REPO_ROOT / "wiki" / "star-systems"
|
WIKI_SYSTEMS = REPO_ROOT / "wiki" / "star-systems"
|
||||||
BLOCKLIST_PATH = TOOLING_DIR / "earth_blocklist.txt"
|
BLOCKLIST_PATH = TOOLING_DIR / "earth_blocklist.txt"
|
||||||
|
|
||||||
# When --dump-prompts is set, name_feature short-circuits: it builds the
|
# NOTE: --dump-prompts and name_feature() are vestigial from the Gemma 2
|
||||||
# attempt-0 prompt for each feature, writes a JSONL line to this file,
|
# single-name pipeline. The live path uses _batch_fill() →
|
||||||
# and returns a unique deterministic placeholder so the pipeline runs to
|
# name_features_batch() from naming_core.py. The old code is retained
|
||||||
# completion without touching a real LLM. Lets us capture the exact prompt
|
# for reference but not called. TODO(#833): remove in a cleanup pass.
|
||||||
# set any backend (Gemma CPU, Gemma GPU, Haiku) would receive on first
|
_CAPTURE_FILE = None # vestigial — see note above
|
||||||
# attempt — so an offline backend can replay them and the resulting names
|
|
||||||
# can be A/B compared.
|
|
||||||
_CAPTURE_FILE = None # set in main() when --dump-prompts is used
|
|
||||||
|
|
||||||
# Default binary + model paths. The sr-voice binary is platform-specific
|
# Default binary + model paths. The sr-voice binary is platform-specific
|
||||||
# (GPU backend baked in per-build) and lives OUTSIDE any git worktree so
|
# (GPU backend baked in per-build) and lives OUTSIDE any git worktree so
|
||||||
@@ -562,7 +559,7 @@ _RIVER_POOLS: list[list[tuple[str, str]]] = [
|
|||||||
("Italian", "Fiume Marconi"),
|
("Italian", "Fiume Marconi"),
|
||||||
("Greek", "Petrakis Rema"),
|
("Greek", "Petrakis Rema"),
|
||||||
],
|
],
|
||||||
# Pool 6 — founder FIRST name possessive (Clifford's Bay shape)
|
# Pool 5 — founder FIRST name possessive (Clifford's Bay shape)
|
||||||
# Added so first-name-possessive naming joins the rotation alongside
|
# Added so first-name-possessive naming joins the rotation alongside
|
||||||
# the surname pools without replacing any of them.
|
# the surname pools without replacing any of them.
|
||||||
[
|
[
|
||||||
@@ -573,7 +570,7 @@ _RIVER_POOLS: list[list[tuple[str, str]]] = [
|
|||||||
("Portuguese", "Rio de Ana"),
|
("Portuguese", "Rio de Ana"),
|
||||||
("French", "Rivière d'Elena"),
|
("French", "Rivière d'Elena"),
|
||||||
],
|
],
|
||||||
# Pool 5 — classical / institutional / Latinate (occasional ~17%)
|
# Pool 6 — classical / institutional / Latinate (occasional ~17%)
|
||||||
[
|
[
|
||||||
("British/Australian", "Aqueduct Run"),
|
("British/Australian", "Aqueduct Run"),
|
||||||
("Italian", "Acqua Vetusta"),
|
("Italian", "Acqua Vetusta"),
|
||||||
@@ -1973,7 +1970,7 @@ def discover_bodies(
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Batch-name atlas features via Gemma 2 voice pipeline (#833)"
|
description="Batch-name atlas features via Gemma 4 E2B tooling pipeline (#833)"
|
||||||
)
|
)
|
||||||
parser.add_argument("--db", default=str(DB_PATH), help="Path to systems.db")
|
parser.add_argument("--db", default=str(DB_PATH), help="Path to systems.db")
|
||||||
parser.add_argument("--body", help="Process only this body_id")
|
parser.add_argument("--body", help="Process only this body_id")
|
||||||
@@ -1995,7 +1992,7 @@ def main():
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--model",
|
"--model",
|
||||||
default=str(DEFAULT_MODEL),
|
default=str(DEFAULT_MODEL),
|
||||||
help="Path to the Gemma 2 GGUF model (ignored in --mock mode)",
|
help="Path to the GGUF model file (ignored in --mock mode)",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--distrobox",
|
"--distrobox",
|
||||||
@@ -2010,7 +2007,7 @@ def main():
|
|||||||
"--refresh",
|
"--refresh",
|
||||||
type=int,
|
type=int,
|
||||||
default=1000,
|
default=1000,
|
||||||
help="Restart the voice subprocess every N requests (default: 200) "
|
help="Restart the voice subprocess every N requests (default: 1000) "
|
||||||
"to prevent KV-cache context bleed",
|
"to prevent KV-cache context bleed",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -2120,7 +2117,7 @@ def main():
|
|||||||
last_hop = hop_order.get(_body_id_from_path(markers_paths[-1])[0], (99, ""))[0]
|
last_hop = hop_order.get(_body_id_from_path(markers_paths[-1])[0], (99, ""))[0]
|
||||||
|
|
||||||
log.raw("")
|
log.raw("")
|
||||||
log.raw(f" Gemma 2 Batch Naming Pipeline (#833)")
|
log.raw(f" Gemma 4 Batch Naming Pipeline (#833)")
|
||||||
log.raw(f" DB: {db_path}")
|
log.raw(f" DB: {db_path}")
|
||||||
log.raw(f" Mode: {'MOCK' if args.mock else 'REAL'}")
|
log.raw(f" Mode: {'MOCK' if args.mock else 'REAL'}")
|
||||||
log.raw(f" sr-voice: {MOCK_STDIO if args.mock else sr_voice_bin}")
|
log.raw(f" sr-voice: {MOCK_STDIO if args.mock else sr_voice_bin}")
|
||||||
|
|||||||
@@ -62,10 +62,11 @@ def prune_mountains(markers: dict, cap: int) -> int:
|
|||||||
mtns = markers.get("mountain_ranges") or []
|
mtns = markers.get("mountain_ranges") or []
|
||||||
if len(mtns) <= cap:
|
if len(mtns) <= cap:
|
||||||
return 0
|
return 0
|
||||||
|
# Named features sort first (preserve hand-authored names),
|
||||||
|
# then by area_cells descending.
|
||||||
ranked = sorted(
|
ranked = sorted(
|
||||||
mtns,
|
mtns,
|
||||||
key=lambda m: int(m.get("area_cells") or 0),
|
key=lambda m: (0 if m.get("name") else 1, -(int(m.get("area_cells") or 0))),
|
||||||
reverse=True,
|
|
||||||
)
|
)
|
||||||
markers["mountain_ranges"] = ranked[:cap]
|
markers["mountain_ranges"] = ranked[:cap]
|
||||||
return len(mtns) - cap
|
return len(mtns) - cap
|
||||||
@@ -75,10 +76,11 @@ def prune_rivers(markers: dict, cap: int) -> int:
|
|||||||
rivers = markers.get("rivers") or []
|
rivers = markers.get("rivers") or []
|
||||||
if len(rivers) <= cap:
|
if len(rivers) <= cap:
|
||||||
return 0
|
return 0
|
||||||
|
# Named features sort first (preserve hand-authored names),
|
||||||
|
# then by path length descending.
|
||||||
ranked = sorted(
|
ranked = sorted(
|
||||||
rivers,
|
rivers,
|
||||||
key=lambda r: len(r.get("path") or []),
|
key=lambda r: (0 if r.get("name") else 1, -len(r.get("path") or [])),
|
||||||
reverse=True,
|
|
||||||
)
|
)
|
||||||
markers["rivers"] = ranked[:cap]
|
markers["rivers"] = ranked[:cap]
|
||||||
return len(rivers) - cap
|
return len(rivers) - cap
|
||||||
|
|||||||
Reference in New Issue
Block a user