Module: Labimotion::ProfileAiSettings

Extended by:
ActiveSupport::Concern
Defined in:
lib/labimotion/models/concerns/profile_ai_settings.rb

Overview

Per-user LabIMotion AI settings, stored on the host's profile record.

The model, an optional personal API key and an optional provider endpoint of one's own live under one key in the host's data jsonb. They sit on the profile rather than in a table of ours because the credential belongs to the person, not to LabIMotion — but the shape of the value is ours, so reading and writing it belongs here rather than in each host.

The API key is stored ENCRYPTED and never returned to a client: only whether one is set. Encryption stays the HOST's — key management is a deployment concern and a gem has no business inventing one. Chemotion supplies it as an Encryptor concern, which is pulled in below when present, so a host there includes this one alone:

class Profile < ApplicationRecord
include Labimotion::ProfileAiSettings
end

A host that names its encryption differently just defines encrypt_value and decrypt_value on the model itself.

Constant Summary collapse

DATA_KEY =
'labimotion_ai'

Instance Method Summary collapse

Instance Method Details

#labimotion_ai_api_keyObject

Decrypted personal API key, or nil when none is stored.



72
73
74
75
76
77
# File 'lib/labimotion/models/concerns/profile_ai_settings.rb', line 72

def labimotion_ai_api_key
  enc = labimotion_ai_settings['api_key']
  return nil if enc.blank?

  decrypt_value(enc).presence
end

#labimotion_ai_api_key?Boolean

Returns:

  • (Boolean)


67
68
69
# File 'lib/labimotion/models/concerns/profile_ai_settings.rb', line 67

def labimotion_ai_api_key?
  labimotion_ai_settings['api_key'].present?
end

#labimotion_ai_api_pathObject



63
64
65
# File 'lib/labimotion/models/concerns/profile_ai_settings.rb', line 63

def labimotion_ai_api_path
  labimotion_ai_settings['api_path'].presence
end

#labimotion_ai_base_urlObject

Personal provider endpoint (BYO-Provider). Honored only alongside a personal key, and SSRF-validated by Labimotion::AiEgressGuard before any request is made.



59
60
61
# File 'lib/labimotion/models/concerns/profile_ai_settings.rb', line 59

def labimotion_ai_base_url
  labimotion_ai_settings['base_url'].presence
end

#labimotion_ai_modelObject



52
53
54
# File 'lib/labimotion/models/concerns/profile_ai_settings.rb', line 52

def labimotion_ai_model
  labimotion_ai_settings['model'].presence
end

#labimotion_ai_settingsObject



48
49
50
# File 'lib/labimotion/models/concerns/profile_ai_settings.rb', line 48

def labimotion_ai_settings
  (data || {})[DATA_KEY] || {}
end

#update_labimotion_ai(model: nil, api_key: nil, base_url: nil, api_path: nil) ⇒ Object

Persist the settings. For every field: nil leaves it unchanged, a blank string clears it, anything else is stored (the api_key encrypted). Returns the safe summary — never the key itself.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/labimotion/models/concerns/profile_ai_settings.rb', line 82

def update_labimotion_ai(model: nil, api_key: nil, base_url: nil, api_path: nil)
  ai = labimotion_ai_settings.dup
  ai['model'] = model.to_s if model
  unless api_key.nil?
    if api_key.to_s.strip.blank?
      ai.delete('api_key')
    else
      ai['api_key'] = encrypt_value(api_key.to_s.strip)
    end
  end
  set_or_clear_labimotion_ai(ai, 'base_url', base_url)
  set_or_clear_labimotion_ai(ai, 'api_path', api_path)
  # Reassign the whole `data` hash rather than mutating in place, so the
  # jsonb column is reliably marked dirty and actually persisted.
  self.data = (data || {}).merge(DATA_KEY => ai)
  save!
  { model: ai['model'], api_key_set: ai['api_key'].present?,
    base_url: ai['base_url'], api_path: ai['api_path'] }
end