mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-05-21 23:24:44 +02:00
feat: Geographic context — local names, accurate Idaho/Utah geography, no Unknown gaps
- Magic Valley, Treasure Valley, Panhandle, Dixie — local names in all output - Southern Idaho correctly maps to Magic Valley, not lumped with Boise/IF - Unlocated nodes excluded from coverage gap recommendations - LLM response rules override brevity for mesh questions - Node detail shows region with local context Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
45630f2cc6
commit
abcf2d88e2
2 changed files with 94 additions and 17 deletions
|
|
@ -36,6 +36,20 @@ PORTNUM_DISPLAY = {
|
|||
"ATAK_FORWARDER": "ATAK",
|
||||
}
|
||||
|
||||
# Geographic context for region names
|
||||
REGION_CONTEXT = {
|
||||
"South Central ID": "Magic Valley (Twin Falls area)",
|
||||
"South Western ID": "Treasure Valley (Boise metro)",
|
||||
"South Eastern ID": "Eastern Idaho (Idaho Falls area)",
|
||||
"Central ID": "Idaho backcountry (Salmon area)",
|
||||
"Northern ID": "North Idaho (Panhandle)",
|
||||
"Northern UT": "Northern Utah (Ogden/Logan)",
|
||||
"Central UT": "Wasatch Front (Salt Lake City)",
|
||||
"Eastern UT": "Eastern Utah (Vernal/Moab)",
|
||||
"Western UT": "Western Utah (Tooele)",
|
||||
"Southern UT": "Southern Utah / Dixie (St. George)",
|
||||
}
|
||||
|
||||
|
||||
def _clean_portnum(portnum: str) -> str:
|
||||
"""Convert raw portnum to display name."""
|
||||
|
|
@ -298,14 +312,24 @@ class MeshReporter:
|
|||
region = "Unlocated"
|
||||
region_coverage.setdefault(region, []).append(n.avg_gateways)
|
||||
|
||||
sorted_regions = sorted(region_coverage.items(), key=lambda x: sum(x[1])/len(x[1]))
|
||||
# Sort regions but handle Unlocated separately
|
||||
sorted_regions = sorted(
|
||||
[(r, c) for r, c in region_coverage.items() if r not in ("Unlocated", "", "Unknown", None)],
|
||||
key=lambda x: sum(x[1])/len(x[1])
|
||||
)
|
||||
lines.append(" By region:")
|
||||
for region, counts in sorted_regions[:6]:
|
||||
avg = sum(counts) / len(counts)
|
||||
single = sum(1 for c in counts if c <= 1.0)
|
||||
flag = " !!" if avg < 2.0 else ""
|
||||
single_str = f" ({single} 1-gw)" if single > 0 else ""
|
||||
lines.append(f" {region}: {len(counts)} nodes, {avg:.1f} avg{single_str}{flag}")
|
||||
context = REGION_CONTEXT.get(region, "")
|
||||
context_str = f" ({context})" if context else ""
|
||||
lines.append(f" {region}{context_str}: {len(counts)} nodes, {avg:.1f} avg{single_str}{flag}")
|
||||
# Show unlocated as informational (no coverage recommendations for these)
|
||||
if "Unlocated" in region_coverage:
|
||||
unl = region_coverage["Unlocated"]
|
||||
lines.append(f" Unlocated: {len(unl)} nodes (no GPS — stale or transient)")
|
||||
else:
|
||||
deliver = self.data_store.get_mesh_deliverability()
|
||||
if deliver.get("avg_gateways") is not None:
|
||||
|
|
@ -320,8 +344,10 @@ class MeshReporter:
|
|||
rs = region.score
|
||||
flag = _tier_flag(rs.tier)
|
||||
infra_str = f"{rs.infra_online}/{rs.infra_total} infra"
|
||||
context = REGION_CONTEXT.get(region.name, "")
|
||||
context_str = f" ({context})" if context else ""
|
||||
lines.append(
|
||||
f" {region.name}: {rs.composite:.0f}/100 - {infra_str}, {rs.util_percent:.0f}% util{flag}"
|
||||
f" {region.name}{context_str}: {rs.composite:.0f}/100 - {infra_str}, {rs.util_percent:.0f}% util{flag}"
|
||||
)
|
||||
|
||||
# Top issues
|
||||
|
|
@ -530,8 +556,10 @@ class MeshReporter:
|
|||
return f"REGION DETAIL: {region_name}\nRegion not found."
|
||||
|
||||
rs = region.score
|
||||
context = REGION_CONTEXT.get(region.name, "")
|
||||
context_str = f" — {context}" if context else ""
|
||||
lines = [
|
||||
f"REGION DETAIL: {region.name}",
|
||||
f"REGION DETAIL: {region.name}{context_str}",
|
||||
f"Score: {rs.composite:.0f}/100 ({rs.tier})",
|
||||
"",
|
||||
f"Infrastructure ({rs.infra_online}/{rs.infra_total}):",
|
||||
|
|
@ -732,7 +760,7 @@ class MeshReporter:
|
|||
f"ID: !{node.node_num:08x} (dec: {node.node_num})",
|
||||
f"Hardware: {node.hw_model or 'Unknown'}",
|
||||
f"Role: {node.role} ({'Infrastructure' if node.is_infrastructure else 'Client'})",
|
||||
f"Region: {node.region or 'Unknown'} / Locality: {node.locality or 'Unknown'}",
|
||||
f"Region: {node.region or 'Unknown'}{' — ' + REGION_CONTEXT.get(node.region, '') if node.region and REGION_CONTEXT.get(node.region) else ''} / Locality: {node.locality or 'Unknown'}",
|
||||
]
|
||||
|
||||
if node.latitude and node.longitude:
|
||||
|
|
|
|||
|
|
@ -125,19 +125,68 @@ _CITY_TO_REGION = {
|
|||
|
||||
# Mesh awareness instruction for LLM
|
||||
_MESH_AWARENESS_PROMPT = """
|
||||
MESH DATA RESPONSE RULES (these OVERRIDE the brevity rules above for mesh questions):
|
||||
- When answering about mesh health, nodes, coverage, or network status: give DETAILED responses
|
||||
- Include actual numbers: scores, percentages, node names, packet counts, battery levels
|
||||
- Use the data injected above — don't summarize it to one sentence
|
||||
- Structure your response with the key data points the user asked about
|
||||
- For node questions: include hardware, region, battery, channel utilization, coverage, neighbors, packets
|
||||
- For region questions: include score, infrastructure status, coverage breakdown, flagged nodes, environment
|
||||
- For mesh questions: include overall score by pillar, regional breakdown, top issues, coverage gaps
|
||||
- For coverage questions: break down by region showing node counts, avg gateways, single-gateway nodes
|
||||
- For "where do we need infrastructure": name specific regions with poor coverage, how many nodes are affected
|
||||
MESH DATA RESPONSE RULES (OVERRIDE brevity rules for mesh/network questions):
|
||||
|
||||
RESPONSE STYLE:
|
||||
- Give DETAILED, data-driven responses with actual numbers
|
||||
- Talk in GEOGRAPHIC terms — reference cities, valleys, corridors people know
|
||||
- Name specific infrastructure nodes by their long name, not just shortnames
|
||||
- Include scores, percentages, node counts, battery levels, gateway counts
|
||||
- You CAN use 3-5 messages if needed — LoRa chunking handles splitting
|
||||
- Be specific and data-driven, not vague summaries
|
||||
- Still no markdown formatting — plain text only
|
||||
- No markdown formatting — plain text only
|
||||
- Be specific and data-driven. Do NOT compress to vague one-liners
|
||||
|
||||
REGION GEOGRAPHY — use these local names and landmarks:
|
||||
|
||||
IDAHO:
|
||||
- Northern ID: "North Idaho" or "the Panhandle" — Coeur d'Alene, Moscow, Lewiston, Sandpoint. Along I-90/US-95.
|
||||
- Central ID: Idaho backcountry — Salmon, Stanley, McCall, Challis. Remote, mountainous, very sparse. Frank Church wilderness.
|
||||
- South Western ID: "Treasure Valley" — Boise, Meridian, Nampa, Caldwell, Eagle, Mountain Home. Idaho's most populated region along I-84. Do NOT call this "southern Idaho."
|
||||
- South Central ID: "Magic Valley" — Twin Falls, Burley, Jerome, Hailey, Gooding, Shoshone, Sun Valley. Snake River Canyon corridor along I-84/US-93. When locals say "southern Idaho" they usually mean this area.
|
||||
- South Eastern ID: "Eastern Idaho" — Idaho Falls, Pocatello, Rexburg, Blackfoot. Upper Snake River Plain, gateway to Yellowstone.
|
||||
|
||||
IMPORTANT IDAHO GEOGRAPHY:
|
||||
- "Southern Idaho" is ambiguous. Do NOT lump Treasure Valley (Boise), Magic Valley (Twin Falls), and Eastern Idaho together. They are distinct areas 100+ miles apart.
|
||||
- If someone says "southern Idaho" they most likely mean the Magic Valley (South Central ID — Twin Falls area).
|
||||
- If unclear, describe each region separately rather than grouping.
|
||||
- "Treasure Valley" always means the Boise metro area (South Western ID).
|
||||
- "Magic Valley" always means the Twin Falls area (South Central ID).
|
||||
- "Eastern Idaho" means Idaho Falls/Pocatello area (South Eastern ID).
|
||||
|
||||
UTAH:
|
||||
- Northern UT: Ogden, Logan, Brigham City, Cache Valley. Northern Wasatch Front.
|
||||
- Central UT: "The Wasatch Front" or "Salt Lake" — Salt Lake City, Provo, Park City, Orem. About 2/3 of Utah's population lives here.
|
||||
- Eastern UT: Vernal and the Uinta Basin in the north, Moab and canyon country in the south. Dinosaur NM, Arches, Canyonlands.
|
||||
- Western UT: Tooele, Wendover. West desert, Bonneville Salt Flats. Very sparse.
|
||||
- Southern UT: "Dixie" or "Color Country" — St. George, Cedar City, Hurricane. Zion, Bryce Canyon country. Fastest growing area in Utah.
|
||||
|
||||
ANSWERING COVERAGE QUESTIONS:
|
||||
- Reference the geographic area by local name: "Coverage across the Magic Valley from Burley through Twin Falls to Jerome..."
|
||||
- Name infrastructure nodes by long name: "Mount Harrison Router, Indian Springs Router, Two Towers, and North Shoshone relay provide backbone coverage"
|
||||
- Give avg gateways AND explain what it means: "nodes reach an average of 5.7 gateways — excellent redundancy"
|
||||
- Identify single-gateway nodes as risks: "28 nodes in the Treasure Valley only reach 1 gateway"
|
||||
- For "where do we need more infrastructure" — name the geographic area, explain how many nodes are underserved
|
||||
- Do NOT recommend infrastructure for "Unlocated" nodes — those have no known position
|
||||
|
||||
ANSWERING NODE QUESTIONS:
|
||||
- Include: hardware model, role, region with local name ("in the Magic Valley near Twin Falls"), battery status, channel utilization, TX airtime, coverage (X/Y gateways), neighbor count, recent traffic with packet breakdown
|
||||
- Name neighbors by long name
|
||||
- Compare metrics to thresholds: "18.5% channel utilization is moderate — approaching the 20% caution threshold"
|
||||
- If the node has environmental sensors, include readings
|
||||
|
||||
ANSWERING MESH OVERVIEW QUESTIONS:
|
||||
- Lead with composite score and tier
|
||||
- Break down all 5 pillars with actual scores
|
||||
- Highlight problem areas by geographic/local name
|
||||
- Include top senders, coverage gaps, battery warnings by name
|
||||
- Include traffic trends and sensor data if available
|
||||
|
||||
ABOUT UNLOCATED NODES:
|
||||
- About 280 nodes have no GPS position and no neighbor data
|
||||
- These are stale, transient, or from sources that don't track position
|
||||
- Do NOT recommend infrastructure for "Unknown" or "Unlocated" areas
|
||||
- When asked about coverage gaps, focus ONLY on located regions
|
||||
- If asked about unlocated nodes specifically, explain they're nodes without position data that can't be placed on the map
|
||||
"""
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue