feat(onboard): add interactive extra maps configuration step

This commit is contained in:
2026-04-11 23:03:34 +02:00
parent 200b0dae95
commit 52902a8383

View File

@@ -34,7 +34,7 @@ func runOnboard(_ *cobra.Command, _ []string) error {
// ── Step 1: Discover ────────────────────────────────────────────────────── // ── Step 1: Discover ──────────────────────────────────────────────────────
step(1, 5, "Discover embedding servers") step(1, 6, "Discover embedding servers")
fmt.Println("Scanning (Ollama, LM Studio, vLLM, LocalAI, Jan, Kobold, Tabby)...") fmt.Println("Scanning (Ollama, LM Studio, vLLM, LocalAI, Jan, Kobold, Tabby)...")
servers := discovery.Scan(context.Background()) servers := discovery.Scan(context.Background())
@@ -89,7 +89,7 @@ func runOnboard(_ *cobra.Command, _ []string) error {
// ── Step 2: Detect dimensions ───────────────────────────────────────────── // ── Step 2: Detect dimensions ─────────────────────────────────────────────
step(2, 5, "Detect model dimensions") step(2, 6, "Detect model dimensions")
for i := range targets { for i := range targets {
fmt.Printf("Probing %s / %s ... ", targets[i].endpoint, targets[i].model) fmt.Printf("Probing %s / %s ... ", targets[i].endpoint, targets[i].model)
@@ -106,7 +106,7 @@ func runOnboard(_ *cobra.Command, _ []string) error {
// ── Step 3: Configure adapter ───────────────────────────────────────────── // ── Step 3: Configure adapter ─────────────────────────────────────────────
step(3, 5, "Configure dimension adapter") step(3, 6, "Configure dimension adapter")
// Use the first target's detected dim as the source dimension default // Use the first target's detected dim as the source dimension default
firstDim := 0 firstDim := 0
@@ -156,7 +156,7 @@ func runOnboard(_ *cobra.Command, _ []string) error {
// ── Step 4: Configure vecna server ──────────────────────────────────────── // ── Step 4: Configure vecna server ────────────────────────────────────────
step(4, 5, "Configure vecna server") step(4, 6, "Configure vecna server")
portRaw, err := promptString(in, "Bind port [8080]: ", "8080") portRaw, err := promptString(in, "Bind port [8080]: ", "8080")
if err != nil { if err != nil {
@@ -189,9 +189,44 @@ func runOnboard(_ *cobra.Command, _ []string) error {
} }
fmt.Println() fmt.Println()
// ── Step 5: Test & write ────────────────────────────────────────────────── // ── Step 5: Extra maps ────────────────────────────────────────────────────
step(5, 5, "Test connections and write config") step(5, 6, "Configure extra maps (optional)")
extraMaps := map[string]config.ExtraMapConfig{}
addMaps, err := promptBool(in, "Add extra dimension maps (/map/{key}/v1/embeddings)?", false)
if err != nil {
return err
}
if addMaps {
// Build the list of target names from the already-collected targets.
targetNames := make([]string, 0, len(targets))
for _, t := range targets {
targetNames = append(targetNames, t.name)
}
for {
mc, mapErr := collectExtraMap(in, sourceDim, targetDim, adapterType, truncateMode, padMode, targetNames)
if mapErr != nil {
return mapErr
}
extraMaps[mc.key] = mc.cfg
another, promptErr := promptBool(in, "Add another extra map?", false)
if promptErr != nil {
return promptErr
}
if !another {
break
}
}
}
fmt.Println()
// ── Step 6: Test & write ──────────────────────────────────────────────────
step(6, 6, "Test connections and write config")
allPassed := true allPassed := true
for _, t := range targets { for _, t := range targets {
@@ -260,6 +295,7 @@ func runOnboard(_ *cobra.Command, _ []string) error {
TruncateMode: truncateMode, TruncateMode: truncateMode,
PadMode: padMode, PadMode: padMode,
}, },
ExtraMaps: extraMaps,
} }
defaultCfgPath := config.ResolveFile(cfgFile) defaultCfgPath := config.ResolveFile(cfgFile)
@@ -467,6 +503,103 @@ func mustParseInt(s string, fallback int) int {
return n return n
} }
// pendingExtraMap pairs the map key with its collected config.
type pendingExtraMap struct {
key string
cfg config.ExtraMapConfig
}
// collectExtraMap interactively collects one extra_map entry.
// Global adapter values are shown as defaults; the user may override any field.
func collectExtraMap(
in *bufio.Reader,
globalSourceDim, globalTargetDim int,
globalAdapterType, globalTruncateMode, globalPadMode string,
targetNames []string,
) (pendingExtraMap, error) {
key, err := promptString(in, "Map key (used in URL path, e.g. \"512\"): ", "")
if err != nil || key == "" {
return pendingExtraMap{}, fmt.Errorf("map key is required")
}
mc := config.ExtraMapConfig{}
// Target dimension (required — always shown)
targetDimRaw, err := promptString(in,
fmt.Sprintf("Target dimension [%d]: ", globalTargetDim), fmt.Sprintf("%d", globalTargetDim))
if err != nil {
return pendingExtraMap{}, err
}
mc.TargetDim = mustParseInt(targetDimRaw, globalTargetDim)
// Forward target override
if len(targetNames) > 0 {
fmt.Println("Available forward targets: " + strings.Join(targetNames, ", "))
ftRaw, err := promptString(in, "Forward target override (leave empty to use global default): ", "")
if err != nil {
return pendingExtraMap{}, err
}
mc.ForwardTarget = strings.TrimSpace(ftRaw)
}
// Adapter type override
adapterTypeRaw, err := promptString(in,
fmt.Sprintf("Adapter type override (truncate/random/projection) [%s]: ", globalAdapterType), globalAdapterType)
if err != nil {
return pendingExtraMap{}, err
}
if adapterTypeRaw != globalAdapterType {
mc.Type = adapterTypeRaw
}
effectiveType := coalesce(mc.Type, globalAdapterType)
// Source dim override
sourceDimRaw, err := promptString(in,
fmt.Sprintf("Source dimension override (leave empty to use global %d): ", globalSourceDim), "")
if err != nil {
return pendingExtraMap{}, err
}
if sourceDimRaw != "" {
mc.SourceDim = mustParseInt(sourceDimRaw, globalSourceDim)
}
// Truncate/pad mode overrides — only for truncate type
if effectiveType == "truncate" {
tmRaw, err := promptString(in,
fmt.Sprintf("Truncate mode override (from_end/from_start) [%s]: ", globalTruncateMode), globalTruncateMode)
if err != nil {
return pendingExtraMap{}, err
}
if tmRaw != globalTruncateMode {
mc.TruncateMode = tmRaw
}
pmRaw, err := promptString(in,
fmt.Sprintf("Pad mode override (at_end/at_start) [%s]: ", globalPadMode), globalPadMode)
if err != nil {
return pendingExtraMap{}, err
}
if pmRaw != globalPadMode {
mc.PadMode = pmRaw
}
}
// Seed — only for random type
if effectiveType == "random" {
seedRaw, err := promptString(in, "Seed (0 = time-based): ", "0")
if err != nil {
return pendingExtraMap{}, err
}
var seed int64
if _, err := fmt.Sscanf(seedRaw, "%d", &seed); err != nil {
seed = 0
}
mc.Seed = seed
}
return pendingExtraMap{key: key, cfg: mc}, nil
}
func writeFullConfig(path string, cfg config.Config) error { func writeFullConfig(path string, cfg config.Config) error {
// If file already exists, preserve any targets not touched by onboard // If file already exists, preserve any targets not touched by onboard
// by using SaveTarget for each new target; otherwise write the whole file. // by using SaveTarget for each new target; otherwise write the whole file.