Module: Smith::Models

Extended by:
Dry::Container::Mixin
Defined in:
lib/smith/models.rb,
lib/smith/models/profile.rb,
lib/smith/models/inference.rb,
lib/smith/models/normalizer.rb

Overview

Capability registry for model ids. Decoupled from Smith.config.pricing (per-installation billing) — this catalog describes payload-shape capabilities (thinking encoding, temperature acceptance, endpoint preferences for tools+thinking).

The library ships NO specific model_id declarations. Smith::Models::Inference provides PATTERN-BASED PROVIDER RULES that match model_ids at runtime (e.g., “Anthropic Opus 4.7+ uses adaptive thinking”). Applications register explicit Profile overrides via Smith::Models.register ONLY when they have a custom model that diverges from its provider’s default behavior.

Resolution order in find_or_infer(model_id):

1. Application-registered explicit Profile (override wins)
2. Library Inference rule match
3. Safe default (no thinking, accepts temp, no routing)

Defined Under Namespace

Modules: Inference Classes: CollisionError, Normalizer, Profile

Class Method Summary collapse

Class Method Details

.allObject



92
93
94
95
96
# File 'lib/smith/models.rb', line 92

def self.all
  registry_monitor.synchronize do
    keys.sort.map { |k| resolve(k) }
  end
end

.clear!Object



98
99
100
# File 'lib/smith/models.rb', line 98

def self.clear!
  registry_monitor.synchronize { @_container&.clear }
end

.find(model_id) ⇒ Object



31
32
33
34
35
36
# File 'lib/smith/models.rb', line 31

def self.find(model_id)
  registry_monitor.synchronize do
    key = normalize_key(model_id)
    key?(key) ? resolve(key) : nil
  end
end

.find_or_infer(model_id, provider: nil) ⇒ Object

Application overrides first, then Inference rules, then safe default.



39
40
41
# File 'lib/smith/models.rb', line 39

def self.find_or_infer(model_id, provider: nil)
  find(model_id) || infer(model_id, provider: provider)
end

.guess_provider(model_id) ⇒ Object



118
119
120
121
122
# File 'lib/smith/models.rb', line 118

def self.guess_provider(model_id)
  key = normalize_key(model_id)
  PROVIDER_PATTERNS.each { |provider, pattern| return provider if key.match?(pattern) }
  :unknown
end

.infer(model_id, provider: nil) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/smith/models.rb', line 43

def self.infer(model_id, provider: nil)
  inferred = Inference.profile_for(model_id) if defined?(Inference)
  return inferred if inferred

  Profile.new(
    model_id:                   normalize_key(model_id),
    provider:                   provider || guess_provider(model_id),
    thinking_shape:             nil,
    accepts_temperature:        true,
    tools_with_thinking_native: false,
    tools_with_thinking_route:  nil
  )
end

.normalize_key(model_id) ⇒ Object



27
28
29
# File 'lib/smith/models.rb', line 27

def self.normalize_key(model_id)
  model_id.to_s
end

.register(profile) ⇒ Object

Register a Profile. Idempotent when re-registering an identical profile; replaces silently on Rails-reload (same model_id, possibly different Profile object after autoload swap); raises CollisionError on a genuinely conflicting registration.

The stale-reload-binding pattern mirrors Smith::Agent::Registry (agent/registry.rb:118-124) which solves the same problem for agent classes during Rails autoreload.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/smith/models.rb', line 65

def self.register(profile)
  registry_monitor.synchronize do
    key = normalize_key(profile.model_id)
    existing = key?(key) ? resolve(key) : nil

    return profile if existing == profile

    if existing && stale_reload_binding?(existing, profile)
      # Same model_id, value-unequal Profile — Rails reload swap.
      # Document trade-off: a host that intentionally re-registers with
      # different capabilities also gets silent replacement (same
      # behavior Smith::Agent::Registry chose).
      _container.delete(key)
      super(key, profile)
      return profile
    end

    if existing
      raise CollisionError,
            "model #{key.inspect} already registered with a different profile"
    end

    super(key, profile)
    profile
  end
end

.registry_monitorObject



107
108
109
# File 'lib/smith/models.rb', line 107

def self.registry_monitor
  @_registry_monitor
end