Skip to content

Commit febe3a6

Browse files
committed
refactor(model): extract attention primitives into model/attn
Move the shared fused-QKV split helpers (SplitContiguousQKV, SplitGroupedQKV, SplitInterleavedQKV, SplitContiguousGateUp) and the RoPE geometry primitive (RopeParams + RotaryDim) out of the flat model root into a dedicated model/attn feature package, so any arch composes them off the shelf. model/attn is a leaf: it imports only safetensors + core, never model, so there is no import cycle. Consumers updated to attn.*: bloom, dbrx, falcon, glm4, mpt, phi, stablelm. No engine/metal or engine/hip consumer references these symbols. RopeParams error prefixes retargeted model.* -> attn.* to match the new home. Pure move, byte-identical: tests unmodified beyond the package/import rename. The QK-norm / attention-scaling helpers named in the lane brief are NOT standalone functions - they live inside the Arch type in model/arch.go and resist a clean extraction, so they are left in place (flagged, not forced). Co-Authored-By: Virgil <virgil@lethean.io>
1 parent ba85f3c commit febe3a6

13 files changed

Lines changed: 30 additions & 23 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-Licence-Identifier: EUPL-1.2
22

3-
package model
3+
package attn
44

55
import "dappco.re/go/inference/model/safetensors"
66

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-Licence-Identifier: EUPL-1.2
22

3-
package model
3+
package attn
44

55
import (
66
"slices"
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-Licence-Identifier: EUPL-1.2
22

3-
package model
3+
package attn
44

55
import (
66
"math"
@@ -19,22 +19,22 @@ type RopeParams struct {
1919
// RotaryDim resolves the even prefix width rotated by the RoPE op.
2020
func (p RopeParams) RotaryDim() (int, error) {
2121
if p.HeadDim <= 0 {
22-
return 0, core.NewError("model.RopeParams.RotaryDim: head dimension must be > 0")
22+
return 0, core.NewError("attn.RopeParams.RotaryDim: head dimension must be > 0")
2323
}
2424
factor := p.PartialRotaryFactor
2525
if factor == 0 {
2626
factor = 1
2727
}
2828
if factor <= 0 || factor > 1 {
29-
return 0, core.NewError("model.RopeParams.RotaryDim: partial rotary factor must be in (0,1]")
29+
return 0, core.NewError("attn.RopeParams.RotaryDim: partial rotary factor must be in (0,1]")
3030
}
3131
resolved := float64(p.HeadDim) * float64(factor)
3232
dim := int(math.Round(resolved))
3333
if math.Abs(resolved-float64(dim)) > 1e-5 {
34-
return 0, core.NewError("model.RopeParams.RotaryDim: factor must resolve to a whole dimension")
34+
return 0, core.NewError("attn.RopeParams.RotaryDim: factor must resolve to a whole dimension")
3535
}
3636
if dim <= 0 || dim > p.HeadDim || dim%2 != 0 {
37-
return 0, core.NewError("model.RopeParams.RotaryDim: resolved rotary dimension must be positive and even")
37+
return 0, core.NewError("attn.RopeParams.RotaryDim: resolved rotary dimension must be positive and even")
3838
}
3939
return dim, nil
4040
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-Licence-Identifier: EUPL-1.2
22

3-
package model
3+
package attn
44

55
import "testing"
66

Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
// SPDX-Licence-Identifier: EUPL-1.2
22

3-
package model_test
3+
package attn_test
44

55
import (
66
core "dappco.re/go"
7-
"dappco.re/go/inference/model"
7+
"dappco.re/go/inference/model/attn"
88
)
99

1010
func ExampleRopeParams() {
11-
dim, _ := (model.RopeParams{HeadDim: 80, PartialRotaryFactor: 0.4}).RotaryDim()
11+
dim, _ := (attn.RopeParams{HeadDim: 80, PartialRotaryFactor: 0.4}).RotaryDim()
1212
core.Println(dim)
1313
// Output: 32
1414
}
1515

1616
func ExampleRopeParams_RotaryDim() {
17-
dim, _ := (model.RopeParams{HeadDim: 64}).RotaryDim()
17+
dim, _ := (attn.RopeParams{HeadDim: 64}).RotaryDim()
1818
core.Println(dim)
1919
// Output: 64
2020
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-Licence-Identifier: EUPL-1.2
22

3-
package model
3+
package attn
44

55
import "testing"
66

go/model/bloom/register.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package bloom
55
import (
66
core "dappco.re/go"
77
"dappco.re/go/inference/model"
8+
"dappco.re/go/inference/model/attn"
89
"dappco.re/go/inference/model/safetensors"
910
)
1011

@@ -25,7 +26,7 @@ func init() {
2526
break
2627
}
2728
headDim := cfg.HiddenSize / cfg.NumAttentionHeads
28-
tensors = model.SplitInterleavedQKV(tensors, p+"query_key_value", p+"query", p+"key", p+"value", cfg.NumAttentionHeads, headDim)
29+
tensors = attn.SplitInterleavedQKV(tensors, p+"query_key_value", p+"query", p+"key", p+"value", cfg.NumAttentionHeads, headDim)
2930
}
3031
return tensors
3132
}})

go/model/dbrx/register.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package dbrx
55
import (
66
core "dappco.re/go"
77
"dappco.re/go/inference/model"
8+
"dappco.re/go/inference/model/attn"
89
"dappco.re/go/inference/model/composed"
910
"dappco.re/go/inference/model/safetensors"
1011
)
@@ -33,7 +34,7 @@ func NormalizeWeights(in map[string]safetensors.Tensor, cfg Config) map[string]s
3334
alias(dst+"input_layernorm.weight", src+"norm_attn_norm.norm_1.weight")
3435
alias(dst+"post_attention_layernorm.weight", src+"norm_attn_norm.norm_2.weight")
3536
alias(dst+"self_attn.o_proj.weight", src+"norm_attn_norm.attn.out_proj.weight")
36-
out = model.SplitContiguousQKV(out, src+"norm_attn_norm.attn.Wqkv", dst+"self_attn.q_proj", dst+"self_attn.k_proj", dst+"self_attn.v_proj", cfg.Heads*headDim, cfg.Attention.KVHeads*headDim)
37+
out = attn.SplitContiguousQKV(out, src+"norm_attn_norm.attn.Wqkv", dst+"self_attn.q_proj", dst+"self_attn.k_proj", dst+"self_attn.v_proj", cfg.Heads*headDim, cfg.Attention.KVHeads*headDim)
3738
alias(dst+"mlp.gate.weight", src+"ffn.router.layer.weight")
3839
for _, part := range []struct{ source, target string }{{"w1", "gate_proj.weight"}, {"v1", "up_proj.weight"}, {"w2", "down_proj.weight"}} {
3940
tensor, ok := in[src+"ffn.experts.mlp."+part.source]

go/model/falcon/register.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package falcon
55
import (
66
core "dappco.re/go"
77
"dappco.re/go/inference/model"
8+
"dappco.re/go/inference/model/attn"
89
"dappco.re/go/inference/model/safetensors"
910
)
1011

@@ -27,11 +28,11 @@ func init() {
2728
}
2829
headDim := cfg.HiddenSize / cfg.NumAttentionHeads
2930
if cfg.NewDecoderArchitecture && cfg.NumKVHeads > 0 {
30-
tensors = model.SplitGroupedQKV(tensors, p+"query_key_value", p+"query", p+"key", p+"value", cfg.NumAttentionHeads, cfg.NumKVHeads, headDim)
31+
tensors = attn.SplitGroupedQKV(tensors, p+"query_key_value", p+"query", p+"key", p+"value", cfg.NumAttentionHeads, cfg.NumKVHeads, headDim)
3132
} else if cfg.MultiQuery {
32-
tensors = model.SplitContiguousQKV(tensors, p+"query_key_value", p+"query", p+"key", p+"value", cfg.NumAttentionHeads*headDim, headDim)
33+
tensors = attn.SplitContiguousQKV(tensors, p+"query_key_value", p+"query", p+"key", p+"value", cfg.NumAttentionHeads*headDim, headDim)
3334
} else {
34-
tensors = model.SplitInterleavedQKV(tensors, p+"query_key_value", p+"query", p+"key", p+"value", cfg.NumAttentionHeads, headDim)
35+
tensors = attn.SplitInterleavedQKV(tensors, p+"query_key_value", p+"query", p+"key", p+"value", cfg.NumAttentionHeads, headDim)
3536
}
3637
}
3738
return tensors

go/model/glm4/glm4.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
core "dappco.re/go"
1010
"dappco.re/go/inference/model"
11+
"dappco.re/go/inference/model/attn"
1112
"dappco.re/go/inference/model/safetensors"
1213
)
1314

@@ -32,7 +33,7 @@ func (c *Config) Arch() (model.Arch, error) {
3233
if c.ModelType != "glm4" || c.HiddenSize <= 0 || c.IntermediateSize <= 0 || c.NumHiddenLayers <= 0 || c.NumAttentionHeads <= 0 || c.NumKeyValueHeads <= 0 || c.HeadDim <= 0 || c.VocabSize <= 0 || c.RMSNormEps <= 0 || c.RopeTheta <= 0 || c.NumAttentionHeads%c.NumKeyValueHeads != 0 {
3334
return model.Arch{}, core.NewError("glm4.Config.Arch: invalid dense GLM-4 declaration")
3435
}
35-
rd, err := (model.RopeParams{HeadDim: c.HeadDim, PartialRotaryFactor: c.PartialRotaryFactor}).RotaryDim()
36+
rd, err := (attn.RopeParams{HeadDim: c.HeadDim, PartialRotaryFactor: c.PartialRotaryFactor}).RotaryDim()
3637
if err != nil {
3738
return model.Arch{}, core.E("glm4.Config.Arch", "partial rotary", err)
3839
}
@@ -56,7 +57,7 @@ func init() {
5657
if _, ok := out[p+"gate_up_proj.weight"]; !ok {
5758
break
5859
}
59-
out = model.SplitContiguousGateUp(out, p+"gate_up_proj", p+"gate_proj", p+"up_proj")
60+
out = attn.SplitContiguousGateUp(out, p+"gate_up_proj", p+"gate_proj", p+"up_proj")
6061
}
6162
return out
6263
}})

0 commit comments

Comments
 (0)