Class: Legion::Extensions::Llm::Vertex::Provider
- Inherits:
-
Provider
- Object
- Provider
- Legion::Extensions::Llm::Vertex::Provider
- Defined in:
- lib/legion/extensions/llm/vertex/provider.rb
Overview
Google Cloud Vertex AI provider implementation for the Legion::Extensions::Llm contract.
Defined Under Namespace
Modules: Capabilities
Constant Summary collapse
- DEFAULT_LOCATION =
rubocop:disable Metrics/ClassLength
'us-central1'- DEFAULT_PROJECT =
'env://GOOGLE_CLOUD_PROJECT'- DEFAULT_PUBLISHER =
'google'- STATIC_MODELS =
[ { model: 'gemini-2.5-flash', alias: 'gemini-flash', publisher: 'google', model_family: :gemini }, { model: 'gemini-2.5-pro', alias: 'gemini-pro', publisher: 'google', model_family: :gemini }, { model: 'gemini-embedding-001', alias: 'gemini-embedding', publisher: 'google', model_family: :gemini, usage_type: :embedding }, { model: 'text-embedding-005', alias: 'text-embedding', publisher: 'google', model_family: :gemini, usage_type: :embedding }, { model: 'claude-sonnet-4-5', alias: 'claude-sonnet', publisher: 'anthropic', model_family: :anthropic, api: :raw_predict }, { model: 'mistral-medium-3', alias: 'mistral-medium', publisher: 'mistralai', model_family: :mistral, api: :raw_predict }, { model: 'llama-4-maverick', alias: 'llama-4-maverick', publisher: 'meta', model_family: :meta, api: :raw_predict } ].freeze
- ALIASES =
STATIC_MODELS.to_h { |entry| [entry.fetch(:alias), entry.fetch(:model)] }.freeze
- PUBLISHERS =
STATIC_MODELS.to_h { |entry| [entry.fetch(:model), entry.fetch(:publisher)] }.freeze
- API_MODES =
STATIC_MODELS.to_h { |entry| [entry.fetch(:model), entry.fetch(:api, :generate_content)] }.freeze
- MODEL_FAMILIES =
STATIC_MODELS.to_h { |entry| [entry.fetch(:model), entry.fetch(:model_family)] }.freeze
Class Method Summary collapse
- .capabilities ⇒ Object
- .configuration_options ⇒ Object
- .configuration_requirements ⇒ Object
- .resolve_model_id(model_id, config: nil) ⇒ Object
- .slug ⇒ Object
Instance Method Summary collapse
- #api_base ⇒ Object
- #chat(messages, model:, temperature: nil, max_tokens: nil, tools: {}, tool_prefs: nil, params: {}) ⇒ Object
- #complete(messages, tools:, temperature:, model:, params: {}, schema: nil, thinking: nil, tool_prefs: nil) ⇒ Object
- #completion_url ⇒ Object
- #count_tokens(messages, model:, params: {}) ⇒ Object
- #count_tokens_url(model:) ⇒ Object
- #discover_offerings(live: false, **filters) ⇒ Object
- #embed(text, model:, dimensions: nil, task_type: nil, title: nil, params: {}) ⇒ Object
- #embedding_url(model:) ⇒ Object
- #generate_content_url(model:) ⇒ Object
- #headers ⇒ Object
- #health(live: false) ⇒ Object
- #location ⇒ Object
- #models_url ⇒ Object
- #offering_for(model:, model_family: nil, instance_id: :default, **metadata) ⇒ Object
- #project ⇒ Object
- #raw_predict_url(model:, stream: false) ⇒ Object
- #readiness(live: false) ⇒ Object
- #stream(messages, model:, temperature: nil, max_tokens: nil, tools: {}, tool_prefs: nil, params: {}) {|chunk| ... } ⇒ Object
- #stream_generate_content_url(model:) ⇒ Object
- #stream_url ⇒ Object
Class Method Details
.capabilities ⇒ Object
55 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 55 def capabilities = Capabilities |
.configuration_options ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 42 def %i[ vertex_project vertex_location vertex_api_base vertex_access_token vertex_credentials vertex_model_aliases vertex_discovery_live ] end |
.configuration_requirements ⇒ Object
54 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 54 def configuration_requirements = [] |
.resolve_model_id(model_id, config: nil) ⇒ Object
57 58 59 60 61 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 57 def resolve_model_id(model_id, config: nil) configured_aliases = config.respond_to?(:vertex_model_aliases) ? config.vertex_model_aliases : nil aliases = ALIASES.merge((configured_aliases || {}).transform_keys(&:to_s)) aliases.fetch(model_id.to_s, model_id.to_s) end |
.slug ⇒ Object
40 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 40 def slug = 'vertex' |
Instance Method Details
#api_base ⇒ Object
81 82 83 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 81 def api_base config.vertex_api_base || "https://#{location}-aiplatform.googleapis.com/v1" end |
#chat(messages, model:, temperature: nil, max_tokens: nil, tools: {}, tool_prefs: nil, params: {}) ⇒ Object
157 158 159 160 161 162 163 164 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 157 def chat(, model:, temperature: nil, max_tokens: nil, tools: {}, tool_prefs: nil, params: {}) model_id = model_id(model) @model = model_id payload = Utils.deep_merge(chat_payload(, model: model_id, temperature:, max_tokens:, tools:, tool_prefs:, stream: false), params) response = connection.post(chat_url(model_id, stream: false), payload) parse_chat_response(response, model: model_id) end |
#complete(messages, tools:, temperature:, model:, params: {}, schema: nil, thinking: nil, tool_prefs: nil) ⇒ Object
206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 206 def complete(, tools:, temperature:, model:, params: {}, schema: nil, thinking: nil, tool_prefs: nil, &) payload = params.dup payload[:generationConfig] = Utils.deep_merge(payload[:generationConfig] || {}, generation_config(temperature, schema, thinking)) if block_given? stream(, model:, temperature:, tools:, tool_prefs:, params: payload, &) else chat(, model:, temperature:, tools:, tool_prefs:, params: payload) end end |
#completion_url ⇒ Object
92 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 92 def completion_url = generate_content_url(model: @model || STATIC_MODELS.first.fetch(:model)) |
#count_tokens(messages, model:, params: {}) ⇒ Object
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 177 def count_tokens(, model:, params: {}) model_id = model_id(model) unless generate_content_model?(model_id) return { supported: false, provider: :vertex, model: resource_name(model_id), reason: 'Vertex countTokens is standardized for generateContent publisher models' } end payload = Utils.deep_merge({ contents: () }, params) response = connection.post(count_tokens_url(model: model_id), payload) { input_tokens: response.body['totalTokens'], raw: response.body } end |
#count_tokens_url(model:) ⇒ Object
94 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 94 def count_tokens_url(model:) = "#{publisher_model_path(model)}:countTokens" |
#discover_offerings(live: false, **filters) ⇒ Object
110 111 112 113 114 115 116 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 110 def discover_offerings(live: false, **filters) return static_offerings(**filters) unless live response = connection.get(models_url) models = response.body['publisherModels'] || response.body['models'] || [] models.map { |model| offering_from_live_model(model) } end |
#embed(text, model:, dimensions: nil, task_type: nil, title: nil, params: {}) ⇒ Object
193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 193 def (text, model:, dimensions: nil, task_type: nil, title: nil, params: {}) model_id = model_id(model) unless Capabilities.(model_id) raise NotImplementedError, "Vertex embedding payload for #{model_id} is not standardized" end instances = Array(text).map { |item| (item, task_type:, title:) } parameters = { outputDimensionality: dimensions }.compact payload = Utils.deep_merge({ instances: instances, parameters: parameters }, params) response = connection.post((model: model_id), payload) (response, model: model_id) end |
#embedding_url(model:) ⇒ Object
95 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 95 def (model:) = "#{publisher_model_path(model)}:predict" |
#generate_content_url(model:) ⇒ Object
97 98 99 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 97 def generate_content_url(model:) "#{publisher_model_path(model)}:generateContent" end |
#headers ⇒ Object
85 86 87 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 85 def headers { 'Authorization' => bearer_token, 'Content-Type' => 'application/json; charset=utf-8' }.compact end |
#health(live: false) ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 135 def health(live: false) baseline = { provider: :vertex, project: project, location: location, configured: configured?, ready: configured?, live: live, credentials: credential_source } return baseline.merge(checked: false) unless live connection.get(models_url) baseline.merge(checked: true) rescue StandardError => e baseline.merge(checked: true, ready: false, error: e.class.name, message: e.) end |
#location ⇒ Object
90 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 90 def location = config.vertex_location || DEFAULT_LOCATION |
#models_url ⇒ Object
91 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 91 def models_url = publisher_parent |
#offering_for(model:, model_family: nil, instance_id: :default, **metadata) ⇒ Object
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 118 def offering_for(model:, model_family: nil, instance_id: :default, **) model_id = model_id(model) publisher = .delete(:publisher) || publisher_for(model_id) family = model_family || .delete(:model_family) || model_family_for(model_id, publisher) build_offering( model: resource_name(model_id, publisher:), alias_name: alias_for(model_id), model_family: family, instance_id: instance_id, publisher: publisher, usage_type: .delete(:usage_type) || usage_type_for(model_id), api: .delete(:api) || api_for(model_id), metadata: ) end |
#project ⇒ Object
89 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 89 def project = config.vertex_project || ENV.fetch('GOOGLE_CLOUD_PROJECT', DEFAULT_PROJECT) |
#raw_predict_url(model:, stream: false) ⇒ Object
105 106 107 108 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 105 def raw_predict_url(model:, stream: false) suffix = stream ? 'streamRawPredict' : 'rawPredict' "#{publisher_model_path(model)}:#{suffix}" end |
#readiness(live: false) ⇒ Object
153 154 155 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 153 def readiness(live: false) health(live:).merge(local: false, remote: true, api_base: api_base, endpoints: endpoint_manifest) end |
#stream(messages, model:, temperature: nil, max_tokens: nil, tools: {}, tool_prefs: nil, params: {}) {|chunk| ... } ⇒ Object
166 167 168 169 170 171 172 173 174 175 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 166 def stream(, model:, temperature: nil, max_tokens: nil, tools: {}, tool_prefs: nil, params: {}) model_id = model_id(model) @model = model_id payload = Utils.deep_merge(chat_payload(, model: model_id, temperature:, max_tokens:, tools:, tool_prefs:, stream: true), params) response = connection.post(chat_url(model_id, stream: true), payload) chunk = build_chunk(response.body, model: model_id) yield chunk if block_given? && chunk.content parse_chat_response(response, model: model_id) end |
#stream_generate_content_url(model:) ⇒ Object
101 102 103 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 101 def stream_generate_content_url(model:) "#{publisher_model_path(model)}:streamGenerateContent?alt=sse" end |
#stream_url ⇒ Object
93 |
# File 'lib/legion/extensions/llm/vertex/provider.rb', line 93 def stream_url = stream_generate_content_url(model: @model || STATIC_MODELS.first.fetch(:model)) |