Skip to content

Demand-based model wants: unified gossiped demand map - #6

Merged
michaelneale merged 4 commits into
mainfrom
demand-based-wants
Mar 1, 2026
Merged

Demand-based model wants: unified gossiped demand map#6
michaelneale merged 4 commits into
mainfrom
demand-based-wants

Conversation

@michaelneale

Copy link
Copy Markdown
Collaborator

Problem

When a node declares --model GLM and later leaves the mesh, the "want" for GLM disappears entirely. The mesh forgets it ever wanted GLM. New nodes joining see no demand and go standby, even if they have GLM on disk and spare VRAM.

This happened because model wants were tracked in three disconnected ways:

  • requested_models — per-node, gossiped, dies when the node leaves
  • mesh_wanted — local in-memory accumulator, never gossiped, dies on restart
  • request_rates — per-node request counts, not used for want discovery

Solution

Replace all three with a single model_demand map that gets gossiped infectiously across the mesh:

pub struct ModelDemand {
    pub last_active: u64,      // unix timestamp of last request or declaration
    pub request_count: u64,    // total requests seen (merged via max)
}

How it works

  • --model flags seed demand entries at startup
  • API requests (including misses/503s for unserved models) refresh last_active and increment request_count
  • Every gossip exchange carries the full mesh-wide demand map — it propagates infectiously
  • Merge via max: last_active = max(ours, theirs), request_count = max(ours, theirs)
  • Natural decay: entries older than 2 hours (DEMAND_TTL_SECS) are ignored unless pinned by a live --model node
  • pick_model_assignment() prioritizes by demand (hottest unserved model first)

What this fixes

  1. Node leaves, want survives — demand was gossiped to all nodes, they keep spreading it
  2. API request for unserved model creates demand — someone asks for Qwen3-8B, it enters the demand map, standby nodes can pick it up
  3. One concept instead of threemodel_demand replaces requested_models + mesh_wanted + request_rates
  4. Nostr wanted list derived from active demand instead of per-node requested_models

Backward compatible

  • Old nodes ignore the new model_demand field (serde skips unknown fields)
  • New nodes receiving from old nodes synthesize demand entries from requested_models + request_rates
  • requested_models and request_rates still gossiped for old node compat

Tests

7 new unit tests covering merge logic, TTL filtering, backward compat synthesis, and serialization.

Replace three separate concepts (requested_models, mesh_wanted, request_rates)
with a single model_demand map that gets gossiped across the mesh.

Key changes:
- ModelDemand { last_active, request_count } struct gossiped in PeerAnnouncement
- Demand entries created by: --model flags, API requests (including misses/503s)
- Merged across peers via max(last_active, request_count) — propagates infectiously
- Entries decay after DEMAND_TTL_SECS (2 hours) unless pinned by a live --model node
- pick_model_assignment() now prioritizes by demand (hottest unserved first)
- Nostr 'wanted' derived from active demand instead of requested_models
- Backward compatible: old nodes still send requested_models/request_rates,
  new nodes synthesize demand entries from those fields

This fixes the core issue where model wants disappeared when the declaring
node left the mesh. Now demand signals reverberate across all nodes via
gossip and persist as long as they're recent or actively requested.
…emand in API

- gc_demand(): prune expired demand entries in heartbeat loop to prevent
  unbounded map growth over long-running meshes
- check_unserved_model(): gate rebalancing on last_active recency (60 min)
  before using request_count/servers ratio — fixes spurious promotions from
  stale cumulative counts
- /api/status: expose request_count and last_active_secs_ago per model
  so external tools can see mesh demand signals
- Remove dead mesh_wanted_models() wrapper
- Fix DEMAND_DESIGN.md: remove pinned field that was never in the struct
- Remove DEMAND_DESIGN.md (internals now in code comments)
- README: update demand-aware rebalancing description
- docs site: update feature blurb
- TODO: tick off demand in /api/status and demand-based Nostr listings

No parameter changes — fully transparent upgrade, backward compatible.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant