Class: Labimotion::UserAiSettings

Inherits:
Object
  • Object
show all
Defined in:
lib/labimotion/libs/user_ai_settings.rb

Overview

Per-user LabIMotion AI settings: the model, an optional personal API key and an optional provider endpoint of one's own.

These used to sit in the host's profile jsonb. They now live in our own user_settings table under the 'ai' key, because the settings are LabIMotion's and the host's profile is not a good place to keep growing them — see Labimotion::UserSetting.

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, pulled in below when present. A host that names its encryption differently defines encrypt_value / decrypt_value instead.

settings = Labimotion::UserAiSettings.for(current_user)
settings.model            # => 'azure.gpt-4.1'
settings.api_key?         # => true   (never the key itself, to a client)
settings.update(model: 'azure.gpt-5')

store is injectable so the shape can be exercised without a database.

Constant Summary collapse

KEY =
Labimotion::Constants::UserSettingKey::AI

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(user_id, store: Labimotion::UserSetting) ⇒ UserAiSettings

Returns a new instance of UserAiSettings.



40
41
42
43
# File 'lib/labimotion/libs/user_ai_settings.rb', line 40

def initialize(user_id, store: Labimotion::UserSetting)
  @user_id = user_id
  @store = store
end

Instance Attribute Details

#user_idObject (readonly)

rubocop:enable Layout/EmptyLinesAfterModuleInclusion



38
39
40
# File 'lib/labimotion/libs/user_ai_settings.rb', line 38

def user_id
  @user_id
end

Class Method Details

.for(user, store: Labimotion::UserSetting) ⇒ Object

From whatever the host calls a user. Accepts the record or a bare id.



46
47
48
# File 'lib/labimotion/libs/user_ai_settings.rb', line 46

def self.for(user, store: Labimotion::UserSetting)
  new(user.respond_to?(:id) ? user.id : user, store: store)
end

Instance Method Details

#api_keyObject

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



74
75
76
77
78
79
# File 'lib/labimotion/libs/user_ai_settings.rb', line 74

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

  decrypt_value(enc).presence
end

#api_key?Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/labimotion/libs/user_ai_settings.rb', line 69

def api_key?
  settings['api_key'].present?
end

#api_pathObject



65
66
67
# File 'lib/labimotion/libs/user_ai_settings.rb', line 65

def api_path
  settings['api_path'].presence
end

#base_urlObject

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



61
62
63
# File 'lib/labimotion/libs/user_ai_settings.rb', line 61

def base_url
  settings['base_url'].presence
end

#modelObject



54
55
56
# File 'lib/labimotion/libs/user_ai_settings.rb', line 54

def model
  settings['model'].presence
end

#settingsObject



50
51
52
# File 'lib/labimotion/libs/user_ai_settings.rb', line 50

def settings
  @store.settings_for(@user_id, KEY)
end

#summaryObject

What is safe to hand a client: everything but the key, which is reported only as set-or-not.



83
84
85
86
87
# File 'lib/labimotion/libs/user_ai_settings.rb', line 83

def summary
  stored = settings
  { model: stored['model'], api_key_set: stored['api_key'].present?,
    base_url: stored['base_url'], api_path: stored['api_path'] }
end

#update(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.



92
93
94
95
96
97
98
99
100
101
# File 'lib/labimotion/libs/user_ai_settings.rb', line 92

def update(model: nil, api_key: nil, base_url: nil, api_path: nil)
  @store.update_settings(@user_id, KEY) do |ai|
    set_or_clear(ai, 'model', model)
    set_or_clear_api_key(ai, api_key)
    set_or_clear(ai, 'base_url', base_url)
    set_or_clear(ai, 'api_path', api_path)
    ai
  end
  summary
end