Skip to content

Commit 931ae5c

Browse files
committed
Merge remote-tracking branch 'origin/pr/90' into HEAD
2 parents 2e8afc7 + fe7a0a4 commit 931ae5c

1 file changed

Lines changed: 52 additions & 0 deletions

File tree

cmd/aima/tooldeps_deploy.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"log/slog"
88
"math"
99
"os"
10+
"path/filepath"
1011
"strconv"
1112
"strings"
1213
"time"
@@ -83,6 +84,22 @@ func buildDeployDeps(ac *appContext, deps *mcp.ToolDeps,
8384
}
8485
}
8586

87+
// Auto-wire the multimodal projector for llama.cpp VL models. A GGUF vision
88+
// model ships a co-located mmproj-*.gguf, and llama-server needs --mmproj to
89+
// accept images. If the caller didn't set it, inject the projector path so
90+
// vision works zero-config (it flows through configToFlags as --mmproj).
91+
if resolved.ModelFormat == "gguf" && strings.HasPrefix(strings.ToLower(resolved.Engine), "llamacpp") {
92+
if _, set := resolved.Config["mmproj"]; !set {
93+
if mm := findColocatedMMProj(modelPath); mm != "" {
94+
if resolved.Config == nil {
95+
resolved.Config = map[string]any{}
96+
}
97+
resolved.Config["mmproj"] = mm
98+
slog.Info("auto-wired multimodal projector for vision", "model", modelName, "mmproj", mm)
99+
}
100+
}
101+
}
102+
86103
req := &runtime.DeployRequest{
87104
Name: modelName,
88105
Engine: resolved.Engine,
@@ -856,3 +873,38 @@ func normalizeServedModelName(modelName, raw string) string {
856873
}
857874
return served
858875
}
876+
877+
// findColocatedMMProj returns the path of a multimodal projector (mmproj-*.gguf)
878+
// next to a GGUF model, preferring an f16 projector for quality. Returns "" when
879+
// none is present (i.e. the model is not multimodal). modelPath may be the model
880+
// file or its directory; the projector is expected in the same directory.
881+
func findColocatedMMProj(modelPath string) string {
882+
dir := modelPath
883+
if fi, err := os.Stat(modelPath); err == nil && !fi.IsDir() {
884+
dir = filepath.Dir(modelPath)
885+
}
886+
entries, err := os.ReadDir(dir)
887+
if err != nil {
888+
return ""
889+
}
890+
var f16, other string
891+
for _, e := range entries {
892+
if e.IsDir() {
893+
continue
894+
}
895+
lower := strings.ToLower(e.Name())
896+
if !strings.HasSuffix(lower, ".gguf") || !strings.Contains(lower, "mmproj") {
897+
continue
898+
}
899+
full := filepath.Join(dir, e.Name())
900+
if strings.Contains(lower, "f16") || strings.Contains(lower, "fp16") {
901+
f16 = full
902+
} else if other == "" {
903+
other = full
904+
}
905+
}
906+
if f16 != "" {
907+
return f16
908+
}
909+
return other
910+
}

0 commit comments

Comments
 (0)