Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 57 additions & 2 deletions lib/classifier/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,9 @@ def command_models
end

def list_remote_models
registry_arg = @args.shift
registry = parse_registry(registry_arg) || DEFAULT_REGISTRY
registry_or_model_arg = @args.shift
registry, model = detect_registry_and_model(registry_or_model_arg)
registry = parse_registry(registry) || DEFAULT_REGISTRY
index = fetch_registry_index(registry)

return if @exit_code != 0
Expand All @@ -381,6 +382,28 @@ def list_remote_models
return
end

if model
name, info = index['models'].find { |name, _| name == model }
if name.nil?
@output << "No model #{model.inspect} found in registry"
return
end

type = info['type'] || 'unknown'
size = info['size'] || 'unknown'
desc = info['description'] || ''
categories = (info['categories'] || []).map(&:downcase).join(', ')
version = info['version'] || ''
author = info['author'] || ''
@output << format(
"Name: %<name>s\nDescription: %<desc>s\nType: %<type>s\n" \
"Categories: %<categories>s\nVersion: %<version>s\nAuthor: %<author>s\nSize: %<size>s",
name: name, desc: desc.slice(0, 40), type: type, categories: categories,
version: version, author: author, size: size
)
return
end

index['models'].each do |name, info|
type = info['type'] || 'unknown'
size = info['size'] || 'unknown'
Expand All @@ -390,6 +413,8 @@ def list_remote_models
end

def list_local_models
model_arg = @args.shift

models_dir = File.join(CACHE_DIR, 'models')

unless Dir.exist?(models_dir)
Expand Down Expand Up @@ -418,6 +443,27 @@ def list_local_models
return
end

if model_arg
model = models.find { |model| model[:name] == model_arg }
if model.nil?
@output << "No local model #{model_arg.inspect} found"
return
end
display_name = model[:registry] ? "@#{model[:registry]}:#{model[:name]}" : model[:name]
info = load_model_info(model[:path])
type = info['type'] || 'unknown'
version = info['version']
categories = (info['categories'] || {}).keys.map(&:downcase).join(', ')
size = File.size(model[:path])
@output << format(
"Name: %<name>s\nType: %<type>s\n" \
"Categories: %<categories>s\nVersion: %<version>s\nSize: %<size>s",
name: display_name, type: type, categories: categories,
version: version, size: human_size(size)
)
return
end

models.each do |model|
info = load_model_info(model[:path])
type = info['type'] || 'unknown'
Expand Down Expand Up @@ -800,6 +846,15 @@ def show_getting_started
@output << 'Run "classifier --help" for full usage.'
end

# @rbs (String?) -> [String?, String?]
def detect_registry_and_model(arg)
return nil, nil if arg.nil?
return *arg.split(':') if arg.include?(':')
return nil, arg unless arg.start_with?('@')

[arg, nil]
end

# Parse @user/repo format to extract registry
# @rbs (String?) -> String?
def parse_registry(arg)
Expand Down
73 changes: 68 additions & 5 deletions test/cli/registry_commands_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ def run_cli(*args, stdin: nil)
cli.run
end

def create_local_model_fixtures
# Create some cached models
models_dir = File.join(@cache_dir, 'models')
FileUtils.mkdir_p(models_dir)
File.write(File.join(models_dir, 'spam-filter.json'), @model_json)
File.write(File.join(models_dir, 'sentiment.json'), @model_json)
end

#
# Models Command
#
Expand Down Expand Up @@ -106,12 +114,45 @@ def test_models_handles_network_error
assert_match(/failed to fetch/i, result[:error])
end

def test_models_model_detail_view
Comment thread
Yegorov marked this conversation as resolved.
stub_request(:get, 'https://raw.githubusercontent.com/cardmagic/classifier-models/main/models.json')
.to_return(status: 200, body: @models_json)

result = run_cli('models', 'sentiment')

assert_equal 0, result[:exit_code]
assert_match('Name: sentiment', result[:output])
assert_match('Description: Sentiment analysis', result[:output])
assert_match('Type: bayes', result[:output])
assert_empty result[:error]
end

def test_models_model_detail_view_if_not_found
stub_request(:get, 'https://raw.githubusercontent.com/cardmagic/classifier-models/main/models.json')
.to_return(status: 200, body: @models_json)

result = run_cli('models', 'no-model')

assert_equal 0, result[:exit_code]
assert_match('No model "no-model" found in registry', result[:output])
assert_empty result[:error]
end

def test_models_model_detail_view_with_custom_registry
Comment thread
Yegorov marked this conversation as resolved.
stub_request(:get, 'https://raw.githubusercontent.com/someone/models/main/models.json')
.to_return(status: 200, body: @models_json)

result = run_cli('models', '@someone/models:spam-filter')

assert_equal 0, result[:exit_code]
assert_match('Name: spam-filter', result[:output])
assert_match('Description: Email spam detection', result[:output])
assert_match('Type: bayes', result[:output])
assert_empty result[:error]
end

def test_models_local_lists_cached_models
# Create some cached models
models_dir = File.join(@cache_dir, 'models')
FileUtils.mkdir_p(models_dir)
File.write(File.join(models_dir, 'spam-filter.json'), @model_json)
File.write(File.join(models_dir, 'sentiment.json'), @model_json)
create_local_model_fixtures

result = run_cli('models', '--local')

Expand Down Expand Up @@ -151,6 +192,28 @@ def test_models_local_shows_no_models_when_cache_dir_missing
assert_match(/no local models found/i, result[:output])
end

def test_models_local_model_detail_view
Comment thread
Yegorov marked this conversation as resolved.
create_local_model_fixtures

result = run_cli('models', '--local', 'spam-filter')

assert_equal 0, result[:exit_code]
assert_match('Name: spam-filter', result[:output])
assert_match('Type: bayes', result[:output])
assert_match('Categories: spam, ham', result[:output])
assert_empty result[:error]
end

def test_models_local_model_detail_view_if_not_found
create_local_model_fixtures

result = run_cli('models', '--local', 'no-model')

assert_equal 0, result[:exit_code]
assert_match('No local model "no-model" found', result[:output])
assert_empty result[:error]
end

#
# Pull Command
#
Expand Down
Loading