Module: Legion::Extensions::Llm::Gemini::Runners::Discovery

Extended by:
Discovery
Includes:
Discovery::Pipeline
Included in:
Discovery
Defined in:
lib/legion/extensions/llm/gemini/runners/discovery.rb

Overview

Gemini discovery runner: ONLY the Gemini-specific work. The generic reconcile / claim / activate / probe (cadence + reactive) / replace / weight-publication / health-display pipeline is mixed in from the shared Discovery::Pipeline. Weight is NOT computed here — the shared WeightReconciler recomputes it from live settings at publish.

The catalog is Gemini's own list-models API (GET models -> body, entries named models/<id>), not OpenAI-shaped, so fetch_raw_models / model_id_from / check_health are overridden. The overrides are the x-goog-api-key auth header (not bearer), the /v1beta base URL, the 8-char-credential physical id, and the offering-draft evidence.

Instance Method Summary collapse

Instance Method Details

#apply_auth_headers(faraday:, instance_cfg:) ⇒ Object

Gemini authenticates with the x-goog-api-key header, not a bearer Authorization header.



48
49
50
51
52
53
54
# File 'lib/legion/extensions/llm/gemini/runners/discovery.rb', line 48

def apply_auth_headers(faraday:, instance_cfg:)
  api_key = instance_cfg[:gemini_api_key] || instance_cfg[:api_key] ||
            instance_cfg.dig(:credentials, :api_key)
  return unless api_key.is_a?(String) && !api_key.strip.empty?

  faraday.headers['x-goog-api-key'] = api_key
end

#auth_token(instance_cfg:) ⇒ Object



40
41
42
43
44
# File 'lib/legion/extensions/llm/gemini/runners/discovery.rb', line 40

def auth_token(instance_cfg:)
  token = instance_cfg[:gemini_api_key] || instance_cfg[:api_key] ||
          instance_cfg.dig(:credentials, :api_key)
  token if token.is_a?(String) && !token.strip.empty?
end

#build_callable(instance_cfg:) ⇒ Object



100
101
102
# File 'lib/legion/extensions/llm/gemini/runners/discovery.rb', line 100

def build_callable(instance_cfg:)
  Legion::Extensions::Llm::Gemini::Helpers::Callable.new(instance_cfg: instance_cfg, logger: log)
end

#build_offering_draft(instance_cfg:, instance_key:, model_id:, model_data:) ⇒ Object

── Offering draft (evidence + metadata; NO weight) ───────────────



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/legion/extensions/llm/gemini/runners/discovery.rb', line 126

def build_offering_draft(instance_cfg:, instance_key:, model_id:, model_data:)
  tier = instance_cfg[:tier] || :frontier
  generation_methods = extract_generation_methods(model_data: model_data)

  Legion::Extensions::Llm::Inventory::OfferingDraft.new(
    provider_native_key: model_id, model: model_id, tier: tier,
    operation_evidence: build_operation_evidence(generation_methods: generation_methods),
    capability_evidence: build_capability_evidence(generation_methods: generation_methods),
    context_evidence: build_context_evidence(model_data: model_data),
    max_output_evidence: build_max_output_evidence(model_data: model_data),
    embedding_dimensions_evidence: build_embedding_dimensions_evidence(
      model_data: model_data, generation_methods: generation_methods
    ),
    model_revision_evidence: build_model_revision_evidence(model_data: model_data),
    tokenizer_evidence: build_tokenizer_evidence,
    quota_domains: {},
    metadata: (model_data: model_data, instance_key: instance_key),
    publication_source: :provider_catalog
  )
end

#catalog_base_url(instance_cfg:) ⇒ Object

── Gemini instance-config keys / connection ─────────────────────



35
36
37
38
# File 'lib/legion/extensions/llm/gemini/runners/discovery.rb', line 35

def catalog_base_url(instance_cfg:)
  (instance_cfg[:gemini_api_base] || instance_cfg[:endpoint] ||
    'https://generativelanguage.googleapis.com/v1beta').to_s
end

#check_health(instance_cfg:) ⇒ Object

Readiness is a safe non-inference models listing (GET models?pageSize=1). The probe is overridden rather than health_path: an absolute path would resolve against the host root and drop the /v1beta prefix of the base URL.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/legion/extensions/llm/gemini/runners/discovery.rb', line 60

def check_health(instance_cfg:)
  base_url = catalog_base_url(instance_cfg: instance_cfg)
  conn = build_connection(base_url: base_url, instance_cfg: instance_cfg, timeout: 15, open_timeout: 5)
  response = conn.get('models', { pageSize: 1 })
  models_api_readiness(response: response, base_url: base_url)
rescue Faraday::ConnectionFailed => e
  handle_exception(e, level: :warn, handled: true,
                      operation: 'gemini.runner.discovery.health.connection', base_url: base_url)
  readiness_error_result(error: e, reason: "Gemini models API connection failed: #{e.message}")
rescue StandardError => e
  raise e if discovery_programming_error?(e)

  handle_exception(e, level: :warn, handled: true,
                      operation: 'gemini.runner.discovery.health', base_url: base_url)
  readiness_error_result(error: e, reason: "Gemini models API error: #{e.message}")
end

#derive_physical_id(instance_cfg:) ⇒ Object

── Secondary physical id (dedup/diagnostics only) ──────────────── host:port, or host:port/ak:<8-char credential digest> when a key is present. Never identity — the instance identity is the operator's config name.



108
109
110
111
112
113
114
# File 'lib/legion/extensions/llm/gemini/runners/discovery.rb', line 108

def derive_physical_id(instance_cfg:)
  host_port = extract_host_port(url: catalog_base_url(instance_cfg: instance_cfg))
  api_key = auth_token(instance_cfg: instance_cfg)
  return "#{host_port}/ak:#{::Digest::SHA256.hexdigest(api_key)[0, 8]}" if api_key

  host_port
end

#extract_host_port(url:) ⇒ Object



116
117
118
119
120
121
122
123
# File 'lib/legion/extensions/llm/gemini/runners/discovery.rb', line 116

def extract_host_port(url:)
  uri = URI.parse(url.to_s)
  "#{uri.host || 'generativelanguage.googleapis.com'}:#{uri.port}"
rescue URI::InvalidURIError => e
  handle_exception(e, level: :warn, handled: true,
                      operation: 'gemini.runner.discovery.extract_host_port', url: url.to_s)
  'unknown:0'
end

#fetch_raw_models(instance_cfg:) ⇒ Object

The catalog is GET models -> body (not the OpenAI-compatible /v1/models -> body).



79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/legion/extensions/llm/gemini/runners/discovery.rb', line 79

def fetch_raw_models(instance_cfg:)
  base_url = catalog_base_url(instance_cfg: instance_cfg)
  conn = build_connection(base_url: base_url, instance_cfg: instance_cfg,
                          timeout: 15, open_timeout: 5)
  response = conn.get('models')
  unless response.status.between?(
    200, 299
  )
    raise CatalogFetchFailure,
          "catalog fetch returned HTTP #{response.status}"
  end

  Array(Legion::JSON.load(response.body)[:models])
end

#model_id_from(model_data) ⇒ Object

The models API lists each entry under :name as models/<id>.



95
96
97
98
# File 'lib/legion/extensions/llm/gemini/runners/discovery.rb', line 95

def model_id_from(model_data)
  name = model_data[:name] || model_data['name'] || ''
  name.to_s.delete_prefix('models/')
end