diff --git a/cmd/vecna/convert.go b/cmd/vecna/convert.go index 3715856..f3ea4f2 100644 --- a/cmd/vecna/convert.go +++ b/cmd/vecna/convert.go @@ -95,6 +95,9 @@ func openWriter(path string) (io.Writer, error) { // buildAdapter constructs the Adapter from the loaded config. func buildAdapter(cfg *config.Config) (adapter.Adapter, error) { ac := cfg.Adapter + if ac.SourceDim == 0 && ac.TargetDim == 0 { + return adapter.NewPassthrough(), nil + } switch ac.Type { case "truncate": tm, pm, err := parseTruncateModes(ac.TruncateMode, ac.PadMode) diff --git a/go.mod b/go.mod index f0056d0..0807514 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/Warky-Devs/vecna.git go 1.26.1 require ( + github.com/go-viper/mapstructure/v2 v2.4.0 github.com/prometheus/client_golang v1.23.2 github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 @@ -16,7 +17,6 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect diff --git a/pkg/adapter/adapter.go b/pkg/adapter/adapter.go index abdd48c..1d29933 100644 --- a/pkg/adapter/adapter.go +++ b/pkg/adapter/adapter.go @@ -41,6 +41,17 @@ type Adapter interface { TargetDim() int } +// passthroughAdapter returns the input vector unchanged. +type passthroughAdapter struct{} + +func (passthroughAdapter) Adapt(vec []float32) ([]float32, error) { return vec, nil } +func (passthroughAdapter) SourceDim() int { return 0 } +func (passthroughAdapter) TargetDim() int { return 0 } + +// NewPassthrough returns an Adapter that returns vectors unchanged. +// Use when no dimension adaptation is needed. +func NewPassthrough() Adapter { return passthroughAdapter{} } + // NewTruncate returns a TruncateAdapter for Matryoshka-style or simple truncation/padding. // t controls which end is dropped when downscaling; p controls which end is padded when upscaling. func NewTruncate(sourceDim, targetDim int, t TruncateMode, p PadMode) (Adapter, error) { diff --git a/pkg/config/config.go b/pkg/config/config.go index 9048dc3..0351898 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -6,65 +6,66 @@ import ( "os" "strings" + "github.com/go-viper/mapstructure/v2" "github.com/spf13/viper" ) // Config is the root configuration for vecna. type Config struct { - Server ServerConfig `mapstructure:"server"` - Metrics MetricsConfig `mapstructure:"metrics"` - Forward ForwardConfig `mapstructure:"forward"` - Adapter AdapterConfig `mapstructure:"adapter"` + Server ServerConfig `mapstructure:"server" json:"server" yaml:"server" xml:"server"` + Metrics MetricsConfig `mapstructure:"metrics" json:"metrics" yaml:"metrics" xml:"metrics"` + Forward ForwardConfig `mapstructure:"forward" json:"forward" yaml:"forward" xml:"forward"` + Adapter AdapterConfig `mapstructure:"adapter" json:"adapter" yaml:"adapter" xml:"adapter"` } // ServerConfig controls the HTTP listener and inbound auth. type ServerConfig struct { - Port int `mapstructure:"port"` - Host string `mapstructure:"host"` - APIKeys []string `mapstructure:"api_keys"` + Port int `mapstructure:"port" json:"port" yaml:"port" xml:"port"` + Host string `mapstructure:"host" json:"host" yaml:"host" xml:"host"` + APIKeys []string `mapstructure:"api_keys" json:"api_keys" yaml:"api_keys" xml:"api_keys"` } // MetricsConfig controls Prometheus metrics exposure. type MetricsConfig struct { - Enabled bool `mapstructure:"enabled"` - Path string `mapstructure:"path"` - APIKey string `mapstructure:"api_key"` + Enabled bool `mapstructure:"enabled" json:"enabled" yaml:"enabled" xml:"enabled"` + Path string `mapstructure:"path" json:"path" yaml:"path" xml:"path"` + APIKey string `mapstructure:"api_key" json:"api_key" yaml:"api_key" xml:"api_key"` } // ForwardConfig holds all named forwarding targets. type ForwardConfig struct { - Default string `mapstructure:"default"` - Targets map[string]ForwardTarget `mapstructure:"targets"` + Default string `mapstructure:"default" json:"default" yaml:"default" xml:"default"` + Targets map[string]ForwardTarget `mapstructure:"targets" json:"targets" yaml:"targets" xml:"targets"` } // ForwardTarget is a named backing embedding model with one or more endpoints. type ForwardTarget struct { - Endpoints []EndpointConfig `mapstructure:"endpoints"` - Model string `mapstructure:"model"` - APIKey string `mapstructure:"api_key"` - APIType string `mapstructure:"api_type"` - TimeoutSecs int `mapstructure:"timeout_secs"` - CooldownSecs int `mapstructure:"cooldown_secs"` - PriorityDecay int `mapstructure:"priority_decay"` - PriorityRecovery int `mapstructure:"priority_recovery"` + Endpoints []EndpointConfig `mapstructure:"endpoints" json:"endpoints" yaml:"endpoints" xml:"endpoints"` + Model string `mapstructure:"model" json:"model" yaml:"model" xml:"model"` + APIKey string `mapstructure:"api_key" json:"api_key" yaml:"api_key" xml:"api_key"` + APIType string `mapstructure:"api_type" json:"api_type" yaml:"api_type" xml:"api_type"` + TimeoutSecs int `mapstructure:"timeout_secs" json:"timeout_secs" yaml:"timeout_secs" xml:"timeout_secs"` + CooldownSecs int `mapstructure:"cooldown_secs" json:"cooldown_secs" yaml:"cooldown_secs" xml:"cooldown_secs"` + PriorityDecay int `mapstructure:"priority_decay" json:"priority_decay" yaml:"priority_decay" xml:"priority_decay"` + PriorityRecovery int `mapstructure:"priority_recovery" json:"priority_recovery" yaml:"priority_recovery" xml:"priority_recovery"` } // EndpointConfig is a single URL within a ForwardTarget. type EndpointConfig struct { - URL string `mapstructure:"url"` - Priority int `mapstructure:"priority"` - APIKey string `mapstructure:"api_key"` + URL string `mapstructure:"url" json:"url" yaml:"url" xml:"url"` + Priority int `mapstructure:"priority" json:"priority" yaml:"priority" xml:"priority"` + APIKey string `mapstructure:"api_key" json:"api_key" yaml:"api_key" xml:"api_key"` } // AdapterConfig selects and tunes the dimension adapter. type AdapterConfig struct { - Type string `mapstructure:"type"` - SourceDim int `mapstructure:"source_dim"` - TargetDim int `mapstructure:"target_dim"` - TruncateMode string `mapstructure:"truncate_mode"` - PadMode string `mapstructure:"pad_mode"` - Seed int64 `mapstructure:"seed"` - MatrixFile string `mapstructure:"matrix_file"` + Type string `mapstructure:"type" json:"type" yaml:"type" xml:"type"` + SourceDim int `mapstructure:"source_dim" json:"source_dim" yaml:"source_dim" xml:"source_dim"` + TargetDim int `mapstructure:"target_dim" json:"target_dim" yaml:"target_dim" xml:"target_dim"` + TruncateMode string `mapstructure:"truncate_mode" json:"truncate_mode" yaml:"truncate_mode" xml:"truncate_mode"` + PadMode string `mapstructure:"pad_mode" json:"pad_mode" yaml:"pad_mode" xml:"pad_mode"` + Seed int64 `mapstructure:"seed" json:"seed" yaml:"seed" xml:"seed"` + MatrixFile string `mapstructure:"matrix_file" json:"matrix_file" yaml:"matrix_file" xml:"matrix_file"` } // extensions viper will detect automatically. @@ -132,7 +133,17 @@ func Load(cfgFile string) (*Config, error) { } var cfg Config - if err := v.Unmarshal(&cfg); err != nil { + if err := v.Unmarshal(&cfg, func(dc *mapstructure.DecoderConfig) { + // Viper lowercases all keys (e.g. "SourceDim" → "sourcedim"), but + // mapstructure tags use snake_case ("source_dim"). Strip underscores + // before comparing so both forms resolve correctly. + dc.MatchName = func(mapKey, fieldName string) bool { + norm := func(s string) string { + return strings.ToLower(strings.ReplaceAll(s, "_", "")) + } + return norm(mapKey) == norm(fieldName) + } + }); err != nil { return nil, fmt.Errorf("unmarshal config: %w", err) }