• Stefy Lanza (nextime / spora )'s avatar
    longcat: cache prompt embeddings, so the encoder need not load (v0.2.81) · 175f2b2a
    Stefy Lanza (nextime / spora ) authored
    The UMT5-XXL text encoder is ~11 GB and turns a prompt into a few MB of
    embeddings that are a pure function of the prompt and the encoder. v0.2.76 made
    that run once per request instead of once per segment; this makes it once per
    prompt, ever.
    
    The saved GPU pass is not the point — it is seconds. The point is that a cached
    prompt needs no encoder AT ALL, so with the cache on the encoder is built
    lazily: ~11 GB of host RAM and about a minute of load that a repeated prompt
    never spends. That is the answer to "can we keep everything in VRAM": not by
    quantising the encoder (v0.2.79 measured int8 resident at 24.0 GB on a 24 GB
    card, and NF4 at 22.1 GB with less headroom than a 29-frame clip already uses),
    but by not having an encoder resident at all.
    
    Off by default — embeddings are derived data, but they are also several MB each
    under the user's cache directory, so growing that is opted into:
    
        "prompt_cache": "off",          # off | disk
        "prompt_cache_dir": "",         # blank = <coderai home>/prompt_cache/longcat
        "prompt_cache_max_gb": 0,       # 0 = 2 GB, least-recently-used evicted
    
    per model or server-wide, validated at startup rather than after a seven-minute
    load, settable from the models page, and in api_model_configure's whitelist so a
    save from that page does not silently drop it.
    
    An entry served to the wrong pipeline would be a silent quality change, which is
    worse than a miss, so the key covers the checkpoint, the dtype, the encoder's
    quantisation and the call's own arguments — an NF4 encoder cannot be served a
    bf16 entry. `device` is deliberately excluded: it moves the embeddings without
    changing them, and including it would split one entry into one per device.
    
    Every failure is a miss, never an error. Entries are written to a .tmp and
    renamed, read with weights_only=True (a cache directory is not a trusted source
    for a pickle), deleted when unreadable, and a cache that cannot be written warns
    once and generation carries on without it.
    
    _LazyEncoder is deliberately not a transparent proxy: upstream guards every use
    with `if self.text_encoder is not None`, so a proxy that looked like a module
    would be moved to the card by pipe.to() and offloaded by _place_pipeline,
    building the very weights it exists to avoid. pipe.text_encoder stays None and
    the holder sits beside it in _state, which is why both paths already do the
    right thing.
    
    Found while wiring it: a lazily realised encoder can be quantised, and the
    offload swap would have shuttled bitsandbytes weights to the host. The swap
    guard now checks the model, not just the offload mode.
    
    /health reports the cache and whether the encoder was ever built — laziness is
    invisible otherwise — and GET /prompt-cache returns its stats.
    
    No new dependencies. Unverified on the card: the round trip, eviction, corrupt
    entries and the key are covered by 54 tests against real tensors, but no
    generation has run through it yet.
    Co-Authored-By: 's avatarClaude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_0185zEBiy3KWB37xNGSrxL7w
    175f2b2a
Name
Last commit
Last update
..
admin Loading commit data...
api Loading commit data...
backends Loading commit data...
broker Loading commit data...
cluster Loading commit data...
compose Loading commit data...
frontproxy Loading commit data...
models Loading commit data...
ocr Loading commit data...
openai Loading commit data...
pydantic Loading commit data...
queue Loading commit data...
tasks Loading commit data...
__init__.py Loading commit data...
cli.py Loading commit data...
config.py Loading commit data...
main.py Loading commit data...
platform_paths.py Loading commit data...
system_app.py Loading commit data...