Class: Ask::LLM::Catalog

Inherits:
Object
  • Object
show all
Defined in:
lib/ask/llm/catalog.rb

Overview

Orchestrates model catalog loading from multiple sources:

1. Bundled JSON files in lib/ask/llm/models/*.json (shipped with the gem)
2. ~/.ask-llm-providers/models.json (user-defined overrides)
3. Provider API list_models() calls (on explicit refresh!)

Loaded models are registered into Ask::ModelCatalog for use by ask-agent, ask-mcp, and llm-proxy.

Ask::LLM::Catalog.load!  # load bundled + user config
Ask::LLM::Catalog.refresh!  # also fetch from provider APIs

Defined Under Namespace

Classes: Error, LoadError

Constant Summary collapse

USER_CONFIG_PATH =
File.expand_path("~/.ask-llm-providers/models.json").freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCatalog

Returns a new instance of Catalog.



58
59
60
61
# File 'lib/ask/llm/catalog.rb', line 58

def initialize
  @entries = []
  @model_keys = Set.new
end

Class Method Details

.load!Object

Load bundled model definitions and user overrides into Ask::ModelCatalog. Idempotent — subsequent calls clear and reload.



30
31
32
33
34
35
36
# File 'lib/ask/llm/catalog.rb', line 30

def load!
  instance.clear
  instance.load_bundled
  instance.load_user_config
  instance.register_all
  true
end

.refresh!Object

Like load! but also fetches model lists from configured providers' list_models() APIs. Unknown models are added with minimal metadata.



40
41
42
43
44
45
# File 'lib/ask/llm/catalog.rb', line 40

def refresh!
  load!
  instance.fetch_from_providers
  instance.register_all
  true
end

Instance Method Details

#clearObject



63
64
65
66
# File 'lib/ask/llm/catalog.rb', line 63

def clear
  @entries.clear
  @model_keys.clear
end

#fetch_from_providersObject

Fetch model lists from all configured providers via their list_models() API. Adds unknown models with minimal metadata (no capability guessing).



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/ask/llm/catalog.rb', line 96

def fetch_from_providers
  Ask::Provider.providers.each do |slug, provider_class|
    next unless provider_class.configured?(nil)

    begin
      provider = provider_class.new
      models = provider.list_models
      models.each do |m|
        add_entry(m) unless @model_keys.include?([m[:id], slug.to_s])
      end
    rescue StandardError => e
      warn "Warning: Failed to fetch models from #{slug}: #{e.message}"
    end
  end
end

#load_bundledObject

Load bundled model JSONs from the gem's lib/ask/llm/models/ directory.



69
70
71
72
73
74
75
# File 'lib/ask/llm/catalog.rb', line 69

def load_bundled
  pattern = File.expand_path("models/*.json", __dir__)
  Dir[pattern].sort.each do |path|
    raw = JSON.parse(File.read(path))
    raw.each { |entry| add_entry(entry) }
  end
end

#load_user_configObject

Load user-defined model overrides from ~/.ask-llm-providers/models.json. Silently skipped if the file doesn't exist.



79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/ask/llm/catalog.rb', line 79

def load_user_config
  path = USER_CONFIG_PATH
  return unless File.exist?(path)

  raw = JSON.parse(File.read(path))
  unless raw.is_a?(Array)
    warn "Warning: #{path} should be a JSON array of model entries, got #{raw.class}"
    return
  end

  raw.each { |entry| merge_or_add(entry) }
rescue JSON::ParserError => e
  warn "Warning: Failed to parse #{path}: #{e.message}"
end

#register_allObject

Register all accumulated entries into Ask::ModelCatalog. Also registers alias entries so models can be found by alias name.



114
115
116
117
118
119
120
121
# File 'lib/ask/llm/catalog.rb', line 114

def register_all
  @entries.each do |entry|
    info = build_model_info(entry)
    Ask::ModelCatalog.instance.register(info)
  end

  register_alias_entries
end