Class: Woods::ResolvedConfig

Inherits:
Object
  • Object
show all
Defined in:
lib/woods/resolved_config.rb

Overview

Immutable Whole Value representing the resolved embedding configuration captured at embed time and read back by the MCP server at boot.

This is NOT a declared-config bag of fields — it records what was *actually used* during embedding (provider class, model, host, dimension, store types). The MCP server compares a stored ResolvedConfig against the current host config to detect incompatible re-deployments.

Build via ResolvedConfig.from_hash (parses woods.json) or ResolvedConfig.from_configuration (captures the current Configuration at embed time).

Examples:

Parsing woods.json

config = Woods::ResolvedConfig.from_hash(JSON.parse(File.read("woods.json")))
config.dimension          # => 768
config.provider_signature # => "Ollama/nomic-embed-text@http://host.docker.internal:11434"

Asserting compatibility before hydrating stores

stored = Woods::ResolvedConfig.from_hash(snapshot)
live   = Woods::ResolvedConfig.from_configuration(Woods.configuration)
live.assert_compatible!(stored)

Constant Summary collapse

SCHEMA_VERSION_SUPPORTED =

The only schema version this gem release can read or write.

1

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(schema_version:, gem_version:, created_at:, embedding_provider:, stores:) ⇒ ResolvedConfig

Returns a new instance of ResolvedConfig.

Parameters:

  • schema_version (Integer)
  • gem_version (String)
  • created_at (Time)
  • embedding_provider (Hash)
  • stores (Hash)


116
117
118
119
120
121
122
123
# File 'lib/woods/resolved_config.rb', line 116

def initialize(schema_version:, gem_version:, created_at:, embedding_provider:, stores:)
  @schema_version = schema_version
  @gem_version = gem_version.to_s.freeze
  @created_at = created_at
  @embedding_provider = deep_freeze(embedding_provider)
  @stores = deep_freeze(stores)
  freeze
end

Instance Attribute Details

#created_atTime (readonly)

Returns:

  • (Time)


39
40
41
# File 'lib/woods/resolved_config.rb', line 39

def created_at
  @created_at
end

#embedding_providerHash (readonly)

Returns Provider details — :class, :model, :host, :num_ctx, :read_timeout, :dimension.

Returns:

  • (Hash)

    Provider details — :class, :model, :host, :num_ctx, :read_timeout, :dimension



42
43
44
# File 'lib/woods/resolved_config.rb', line 42

def embedding_provider
  @embedding_provider
end

#gem_versionString (readonly)

Returns Gem version at embed time (e.g. “1.2.0”).

Returns:

  • (String)

    Gem version at embed time (e.g. “1.2.0”)



36
37
38
# File 'lib/woods/resolved_config.rb', line 36

def gem_version
  @gem_version
end

#schema_versionInteger (readonly)

Returns:

  • (Integer)


33
34
35
# File 'lib/woods/resolved_config.rb', line 33

def schema_version
  @schema_version
end

#storesHash (readonly)

Returns Store types — :vector_store, :metadata_store, :graph_store (Symbols).

Returns:

  • (Hash)

    Store types — :vector_store, :metadata_store, :graph_store (Symbols)



45
46
47
# File 'lib/woods/resolved_config.rb', line 45

def stores
  @stores
end

Class Method Details

.from_configuration(config, gem_version: nil, provider: nil) ⇒ ResolvedConfig

Capture the current Configuration as a Woods::ResolvedConfig.

The provider: kwarg lets callers pass a live embedding provider so the dimension is discovered at runtime instead of being read from a declared-only field. This matters for Ollama — dimensions come from the model, not the config — and doesn’t hurt OpenAI, whose provider exposes the same #dimensions interface.

When provider: is omitted, dimension falls back to config.embedding_options[:dimension] (useful for specs and for offline ResolvedConfig construction where no provider exists).

Parameters:

  • config (Woods::Configuration)
  • gem_version (String) (defaults to: nil)

    Defaults to VERSION

  • provider (#dimensions, nil) (defaults to: nil)

    Optional live provider to probe for dimension when config.embedding_options[:dimension] is absent.

Returns:



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/woods/resolved_config.rb', line 82

def self.from_configuration(config, gem_version: nil, provider: nil) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
  require_relative 'version'

  opts = config.embedding_options || {}
  declared_dim = opts[:dimension] || opts['dimension']
  dim = declared_dim || (provider.respond_to?(:dimensions) ? provider.dimensions : nil)

  provider_hash = {
    class: resolve_provider_class(config.embedding_provider),
    model: (opts[:model] || opts['model'] || config.embedding_model).to_s,
    dimension: dim.to_i,
    host: opts[:host] || opts['host'],
    num_ctx: opts[:num_ctx] || opts['num_ctx'],
    read_timeout: opts[:read_timeout] || opts['read_timeout']
  }.compact

  new(
    schema_version: SCHEMA_VERSION_SUPPORTED,
    gem_version: (gem_version || Woods::VERSION).to_s,
    created_at: Time.now.utc,
    embedding_provider: provider_hash,
    stores: {
      vector_store: config.vector_store,
      metadata_store: config.,
      graph_store: config.graph_store
    }
  )
end

.from_hash(raw) ⇒ ResolvedConfig

Parse a woods.json hash into a Woods::ResolvedConfig.

Parameters:

  • raw (Hash)

    Parsed JSON hash (string or symbol keys)

Returns:

Raises:



52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/woods/resolved_config.rb', line 52

def self.from_hash(raw)
  data = normalize_keys(raw)
  validate_schema_version!(data[:schema_version].to_i)

  new(
    schema_version: data[:schema_version].to_i,
    gem_version: data[:gem_version].to_s,
    created_at: parse_time(data[:created_at]),
    embedding_provider: parse_provider(data[:embedding_provider] || {}),
    stores: parse_stores(data[:stores] || {})
  )
end

Instance Method Details

#assert_compatible!(stored_config) ⇒ void

This method returns an undefined value.

Assert that stored_config (the config captured at embed time) is compatible with self (the live host config). Raises typed errors so the operator can diagnose the mismatch without reading source.

Parameters:

Raises:



163
164
165
166
# File 'lib/woods/resolved_config.rb', line 163

def assert_compatible!(stored_config)
  assert_dimensions_match!(stored_config)
  assert_provider_matches!(stored_config)
end

#dimensionInteger

Returns Embedding dimension declared by the provider.

Returns:

  • (Integer)

    Embedding dimension declared by the provider



126
127
128
# File 'lib/woods/resolved_config.rb', line 126

def dimension
  embedding_provider[:dimension].to_i
end

#matches?(other) ⇒ Boolean

Returns true if other uses the same provider class, model, dimension, and store types. Ignores gem version, read_timeout, num_ctx, and created_at.

Parameters:

Returns:

  • (Boolean)


146
147
148
149
150
151
152
153
# File 'lib/woods/resolved_config.rb', line 146

def matches?(other)
  embedding_provider[:class] == other.embedding_provider[:class] &&
    embedding_provider[:model] == other.embedding_provider[:model] &&
    dimension == other.dimension &&
    stores[:vector_store] == other.stores[:vector_store] &&
    stores[:metadata_store] == other.stores[:metadata_store] &&
    stores[:graph_store] == other.stores[:graph_store]
end

#provider_signatureString

Short string identifying this provider configuration, useful for log messages and ConfigMismatch error text.

Returns:



134
135
136
137
138
139
# File 'lib/woods/resolved_config.rb', line 134

def provider_signature
  klass = embedding_provider[:class].to_s.split('::').last
  model = embedding_provider[:model]
  host  = embedding_provider[:host]
  host ? "#{klass}/#{model}@#{host}" : "#{klass}/#{model}"
end

#to_hHash

Returns:

  • (Hash)


183
184
185
# File 'lib/woods/resolved_config.rb', line 183

def to_h
  to_snapshot_json.freeze
end

#to_snapshot_jsonHash

Serialize to a Hash suitable for JSON.generate and round-trippable through from_hash.

Returns:

  • (Hash)


172
173
174
175
176
177
178
179
180
# File 'lib/woods/resolved_config.rb', line 172

def to_snapshot_json
  {
    'schema_version' => schema_version,
    'gem_version' => gem_version,
    'created_at' => created_at.iso8601,
    'embedding_provider' => embedding_provider.transform_keys(&:to_s),
    'stores' => stores.transform_keys(&:to_s).transform_values(&:to_s)
  }
end