Class: LittleGhost::Providers::LMStudio::CatalogSource

Inherits:
Models::Catalog::Source show all
Defined in:
lib/little_ghost/providers/lm_studio/catalog_source.rb

Overview

Refreshes downloaded model metadata from LM Studio's native read-only model listing endpoint.

Constant Summary collapse

MAX_CATALOG_BYTES =

:nodoc:

25 * 1024 * 1024

Constants inherited from Models::Catalog::Source

Models::Catalog::Source::DEFAULT_ATTRIBUTE_MERGE_STRATEGIES

Instance Attribute Summary

Attributes inherited from Models::Catalog::Source

#name

Instance Method Summary collapse

Methods inherited from Models::Catalog::Source

#attribute_merge_strategies

Constructor Details

#initialize(provider:, credential_resolver:, clock: -> { Time.now.utc }, http_client: nil) ⇒ CatalogSource

Creates a source for one named LM Studio connection. Credentials are resolved only when the application explicitly refreshes its catalog.



17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/little_ghost/providers/lm_studio/catalog_source.rb', line 17

def initialize(provider:, credential_resolver:, clock: -> { Time.now.utc }, http_client: nil)
  super(name: "lm_studio")
  @provider = provider.to_s
  @credential_resolver = credential_resolver
  @clock = clock
  @known_targets = []
  @known_targets_mutex = Mutex.new
  @http_client = http_client || Support::HTTPClient.new(
    open_timeout: 5,
    read_timeout: 30,
    max_response_bytes: MAX_CATALOG_BYTES
  )
end

Instance Method Details

#refresh(target: nil) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/little_ghost/providers/lm_studio/catalog_source.rb', line 31

def refresh(target: nil)
  return {} if target && target.provider != @provider

  configuration = @credential_resolver.call
  raise TypeError, "credentials must be a mapping" unless configuration.is_a?(Hash)

  configuration = configuration.to_h.transform_keys(&:to_s)
  models = JSON.parse(request(configuration)).fetch("models")
  raise TypeError, "models must be an array" unless models.is_a?(Array)

  observed_at = @clock.call.iso8601
  return refresh_target(models, target:, observed_at:) if target

  records = models.to_h do |model|
    attributes = normalize(model).merge(observed_at:)
    ["#{@provider}:#{model.fetch("key")}", attributes]
  end
  reconcile(records, observed_at:)
rescue JSON::ParserError, KeyError, TypeError, ArgumentError
  raise ProviderError, "LM Studio returned an invalid model catalog"
end