mirror of
https://github.com/ollama/ollama.git
synced 2026-10-21 11:01:33 +00:00
* mlxrunner: restore System One scoring
Carry the MLX implementation and its tests from the original System One
commit. The API/compiler and llama-server implementation already exist on
the release branch, so retain those versions. Adapt the optional model test
to the renamed decision package and its explicit rendering/context setup.
(cherry picked from commit 26e546ddb3)
* Enable MLX System One and add regression coverage
* mlxrunner: add Laya support for System One
Import Laya checkpoints and implement bidirectional decision scoring.
* mlxrunner: release cached buffers after System One scoring
* decision: match Tev1 and Nimble publisher prompt formats
Select Tev1 prompts using renderer metadata and fix JSON formatting, with regression coverage for GGUF and MLX.
* mlx: support Clef and Clef Flash decision models
Add model import, quantization safeguards, and text/image scoring with the Clef joint decision head.
* support vision capabilities for decision models
* Make Clef scoring cancellable between prefill chunks
Preserve joint-head inputs and test cancellation, cleanup, and recovery.
* mlx: separate decision preparation and readout from forward
Let serial and batched scoring share model-owned preparation and readout
without duplicating architecture-specific logic in the runner.
* mlx: add Strands Decider support
* implement caching
---------
Co-authored-by: ParthSareen <parth.sareen@ollama.com>
161 lines
4.2 KiB
Go
161 lines
4.2 KiB
Go
package cache
|
|
|
|
import "github.com/ollama/ollama/mlx"
|
|
|
|
// HiddenCache retains backbone outputs for heads that read earlier tokens.
|
|
// It follows the same prefix snapshots as the attention and recurrent caches.
|
|
// Arrays are immutable: appending or restoring builds a new view or concatenation,
|
|
// so a snapshot never observes a later request's writes.
|
|
type HiddenCache struct {
|
|
hidden *mlx.Array
|
|
scope *mlx.Scope
|
|
backingBytes int
|
|
snapshots pendingSnapshots
|
|
}
|
|
|
|
func NewHiddenCache() *HiddenCache { return &HiddenCache{scope: mlx.NewScope()} }
|
|
|
|
func (c *HiddenCache) Append(hidden *mlx.Array) {
|
|
start := c.Offset()
|
|
if c.hidden != nil {
|
|
hidden = mlx.Concatenate([]*mlx.Array{c.hidden, hidden}, 1)
|
|
}
|
|
c.replace(hidden, hidden.NumBytes())
|
|
for _, offset := range c.snapshots.scheduledIn(start, c.Offset()) {
|
|
c.snapshots.captureReached(offset, func(int) Snapshot {
|
|
return c.snapshot(c.snapshots.base, offset)
|
|
})
|
|
}
|
|
}
|
|
|
|
func (c *HiddenCache) replace(hidden *mlx.Array, backingBytes int) {
|
|
if c.hidden == nil {
|
|
c.hidden = hidden
|
|
c.scope.Attach(hidden)
|
|
} else {
|
|
c.hidden.Set(hidden)
|
|
}
|
|
c.backingBytes = backingBytes
|
|
}
|
|
|
|
func (c *HiddenCache) State() []*mlx.Array {
|
|
if c.hidden == nil {
|
|
return nil
|
|
}
|
|
return []*mlx.Array{c.hidden}
|
|
}
|
|
|
|
func (c *HiddenCache) Offset() int {
|
|
if c.hidden == nil {
|
|
return 0
|
|
}
|
|
return c.hidden.Dim(1)
|
|
}
|
|
|
|
func (c *HiddenCache) Free() {
|
|
for _, s := range c.TakeSnapshots() {
|
|
if s != nil {
|
|
s.Close()
|
|
}
|
|
}
|
|
c.scope.Close()
|
|
c.scope = mlx.NewScope()
|
|
c.hidden = nil
|
|
c.backingBytes = 0
|
|
}
|
|
|
|
func (c *HiddenCache) PrepareSnapshots(offsets []int) { c.snapshots.prepare(c.Offset(), offsets) }
|
|
func (c *HiddenCache) TakeSnapshots() []Snapshot { return c.snapshots.take() }
|
|
|
|
type hiddenSnapshot struct {
|
|
hidden *mlx.Array
|
|
scope *mlx.Scope
|
|
start int
|
|
backingBytes int
|
|
}
|
|
|
|
// Charge the whole backing array even for a view. This may overcount shared
|
|
// storage, but eviction must never undercount a small slice retaining a full row.
|
|
func (s *hiddenSnapshot) Size() int { return s.backingBytes }
|
|
func (s *hiddenSnapshot) Close() { s.scope.Close() }
|
|
func (s *hiddenSnapshot) SetMaterializeHook(func(int)) {}
|
|
|
|
func newHiddenSnapshot(hidden *mlx.Array, start, backingBytes int) *hiddenSnapshot {
|
|
s := &hiddenSnapshot{hidden: hidden, scope: mlx.NewScope(), start: start, backingBytes: backingBytes}
|
|
s.scope.Attach(hidden)
|
|
return s
|
|
}
|
|
|
|
func (c *HiddenCache) snapshot(start, end int) Snapshot {
|
|
if start == end {
|
|
return nil
|
|
}
|
|
return newHiddenSnapshot(c.hidden.Slice(mlx.Slice(), mlx.Slice(start, end), mlx.Slice()), start, c.backingBytes)
|
|
}
|
|
|
|
func (c *HiddenCache) Snapshot(start int) Snapshot { return c.snapshot(start, c.Offset()) }
|
|
|
|
func (c *HiddenCache) Restore(snapshot Snapshot, target int) bool {
|
|
if target < 0 {
|
|
return false
|
|
}
|
|
if target == 0 {
|
|
c.Free()
|
|
return true
|
|
}
|
|
if target <= c.Offset() {
|
|
c.replace(c.hidden.Slice(mlx.Slice(), mlx.Slice(0, target), mlx.Slice()), c.backingBytes)
|
|
return true
|
|
}
|
|
if snapshot == nil {
|
|
return false
|
|
}
|
|
s := snapshot.(*hiddenSnapshot)
|
|
if s.start > c.Offset() || target > s.start+s.hidden.Dim(1) {
|
|
return false
|
|
}
|
|
part := s.hidden.Slice(mlx.Slice(), mlx.Slice(c.Offset()-s.start, target-s.start), mlx.Slice())
|
|
if c.hidden == nil {
|
|
c.replace(part, s.backingBytes)
|
|
} else {
|
|
c.Append(part)
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (c *HiddenCache) Split(snapshot Snapshot, at int) (Snapshot, Snapshot) {
|
|
if snapshot == nil {
|
|
return nil, nil
|
|
}
|
|
s := snapshot.(*hiddenSnapshot)
|
|
n := at - s.start
|
|
if n <= 0 {
|
|
return nil, s
|
|
}
|
|
if n >= s.hidden.Dim(1) {
|
|
return s, nil
|
|
}
|
|
left := newHiddenSnapshot(s.hidden.Slice(mlx.Slice(), mlx.Slice(0, n), mlx.Slice()), s.start, s.backingBytes)
|
|
right := newHiddenSnapshot(s.hidden.Slice(mlx.Slice(), mlx.Slice(n, mlx.End), mlx.Slice()), at, s.backingBytes)
|
|
s.Close()
|
|
return left, right
|
|
}
|
|
|
|
func (c *HiddenCache) Merge(parent, child Snapshot) Snapshot {
|
|
if parent == nil || child == nil {
|
|
if parent != nil {
|
|
parent.Close()
|
|
}
|
|
if child != nil {
|
|
child.Close()
|
|
}
|
|
return nil
|
|
}
|
|
a, b := parent.(*hiddenSnapshot), child.(*hiddenSnapshot)
|
|
joined := mlx.Concatenate([]*mlx.Array{a.hidden, b.hidden}, 1)
|
|
s := newHiddenSnapshot(joined, a.start, joined.NumBytes())
|
|
mlx.AsyncEval(joined)
|
|
a.Close()
|
|
b.Close()
|
|
return s
|
|
}
|