fino-solid 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +158 -0
- data/lib/fino/solid/adapter.rb +1 -1
- data/lib/fino/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d001f6bb38d131992464c9cdeab1e5927fd5a16623759459c7020b1c35d6664a
|
|
4
|
+
data.tar.gz: 339ab6d35c8dc40ecd553f835f536eb74a6978375ed29943cf5c8905c9c812e2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6f81641ee84473e0fb6262bfa41d6beefd0e2051eb8caf6fc15364198385324f162f395a959d6658791dd70b9dbc28de46259a561a7c08b7df90f7d04f7c0f54
|
|
7
|
+
data.tar.gz: 53eef4644ef38aaa358cf40d9f2cbf5ed587800a5acb0e47b94cf09b8f51371d5cb5a34678385f691a524d2beda08ed5814e029ebbf0180cf63a6055299585c8
|
data/README.md
CHANGED
|
@@ -83,6 +83,126 @@ Fino.disable(:maintenance_mode, for: "qa")
|
|
|
83
83
|
Fino.enabled?(:maintenance_mode, for: "qa") #=> false
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
### Select setting
|
|
87
|
+
|
|
88
|
+
The simplest way to define select setting in fino is the following
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
Fino.configure do
|
|
92
|
+
# ...
|
|
93
|
+
section :storefront, label: "Storefront" do
|
|
94
|
+
setting :purchase_button_color,
|
|
95
|
+
:select,
|
|
96
|
+
options: [
|
|
97
|
+
Fino::Settings::Select::Option.new(label: "Red", value: "red"),
|
|
98
|
+
Fino::Settings::Select::Option.new(label: "Blue", value: "blue")
|
|
99
|
+
],
|
|
100
|
+
default: "red",
|
|
101
|
+
description: "Color of the purchase button"
|
|
102
|
+
end
|
|
103
|
+
# ...
|
|
104
|
+
end
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Options must be an array of `Fino::Settings::Select::Option` instances
|
|
108
|
+
|
|
109
|
+
Then you can interact with the setting like that
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
# Read selected option
|
|
113
|
+
selected_option = Fino.value(:purchase_button_color, at: :storefront)
|
|
114
|
+
# => #<Fino::Settings::Select::Option:0x0000000124fecd90 @label="Red", @metadata={}, @value="red">
|
|
115
|
+
|
|
116
|
+
# Read setting value
|
|
117
|
+
selected_option.value
|
|
118
|
+
# => "red"
|
|
119
|
+
|
|
120
|
+
# Read options
|
|
121
|
+
Fino.setting(:purchase_button_color, at: :storefront).options
|
|
122
|
+
# => [#<Fino::Settings::Select::Option:0x0000000124fecd90 @label="Red", @metadata={}, @value="red">,
|
|
123
|
+
#<Fino::Settings::Select::Option:0x0000000124fecca0 @label="Blue", @metadata={}, @value="blue">]
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### Dynamic option
|
|
127
|
+
|
|
128
|
+
Options can also be defined dynamically using any callable object. Let's take a look at dynamic options in an example
|
|
129
|
+
of LLM model setting using [RubyLLM](https://github.com/crmne/ruby_llm) by @crmne
|
|
130
|
+
|
|
131
|
+
To define dynamic select options, use `:select` as setting type and provide a callable to `options`. Your object's
|
|
132
|
+
`call` method might be called with `refresh` option which is true only when user initiates options refresh manually.
|
|
133
|
+
This is useful for updating models list in RubyLLM for example
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
section :llm, label: "LLM" do
|
|
137
|
+
setting :model,
|
|
138
|
+
:select,
|
|
139
|
+
options: proc { |refresh:|
|
|
140
|
+
RubyLLM.models.refresh! if refresh
|
|
141
|
+
models = RubyLLM.models.chat_models
|
|
142
|
+
|
|
143
|
+
openai_models = models.by_provider(:openai)
|
|
144
|
+
anthropic_models = models.by_provider(:anthropic)
|
|
145
|
+
|
|
146
|
+
build_pricing_label = proc do |model|
|
|
147
|
+
text_pricing = model.pricing&.text_tokens
|
|
148
|
+
next unless text_pricing && text_pricing.input && text_pricing.output
|
|
149
|
+
|
|
150
|
+
"$#{text_pricing.input} / $#{text_pricing.output} per 1M tokens"
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
[*openai_models, *anthropic_models].map do |model|
|
|
154
|
+
Fino::Settings::Select::Option.new(
|
|
155
|
+
label: model.name,
|
|
156
|
+
value: model.id,
|
|
157
|
+
metadata: {
|
|
158
|
+
provider: model.provider_class.name,
|
|
159
|
+
pricing: build_pricing_label.call(model)
|
|
160
|
+
}.compact
|
|
161
|
+
)
|
|
162
|
+
end
|
|
163
|
+
},
|
|
164
|
+
default: "gpt-5",
|
|
165
|
+
description: "Chat model for AI-powered features"
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
# Read selected option
|
|
172
|
+
selected_option = Fino.setting(:model, at: :llm).value
|
|
173
|
+
# => #<Fino::Settings::Select::Option:0x0000000126257728
|
|
174
|
+
# @label="GPT-4",
|
|
175
|
+
# @metadata={provider: "OpenAI", pricing: "$30 / $60 per 1M tokens"},
|
|
176
|
+
# @value="gpt-4">
|
|
177
|
+
|
|
178
|
+
# Read setting value
|
|
179
|
+
selected_option.value
|
|
180
|
+
# => "gpt-4"
|
|
181
|
+
|
|
182
|
+
# Read options
|
|
183
|
+
Fino.setting(:model, at: :llm).options
|
|
184
|
+
# => [#<Fino::Settings::Select::Option:0x000000012629e448
|
|
185
|
+
# @label="GPT-5.3 Codex Spark",
|
|
186
|
+
# @metadata={provider: "OpenAI", pricing: "$1.75 / $14 per 1M tokens"},
|
|
187
|
+
# @value="gpt-5.3-codex-spark">,
|
|
188
|
+
# #<Fino::Settings::Select::Option:0x000000012629e1c8
|
|
189
|
+
# @label="GPT-5.4",
|
|
190
|
+
# @metadata={provider: "OpenAI", pricing: "$2.5 / $15 per 1M tokens"},
|
|
191
|
+
# @value="gpt-5.4">, ...]
|
|
192
|
+
|
|
193
|
+
# Refresh options
|
|
194
|
+
Fino.setting(:model, at: :llm).refresh!
|
|
195
|
+
# I, [2026-03-22T18:23:13.615270 #67293] INFO -- RubyLLM: Fetching models from providers:
|
|
196
|
+
# I, [2026-03-22T18:23:13.615934 #67293] INFO -- RubyLLM: Fetching models from models.dev API...
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
#### Use with RubyLLM
|
|
200
|
+
|
|
201
|
+
```ruby
|
|
202
|
+
chat = RubyLLM.chat(model: Fino.setting(:model, at: :llm).value)
|
|
203
|
+
chat.ask "Why Ruby?"
|
|
204
|
+
```
|
|
205
|
+
|
|
86
206
|
### Overrides
|
|
87
207
|
|
|
88
208
|
```ruby
|
|
@@ -116,6 +236,44 @@ Fino.value(:model, at: :openai, for: "user_1") #=> "gpt-6"
|
|
|
116
236
|
Fino.value(:model, at: :openai, for: "user_2") #=> "gpt-5"
|
|
117
237
|
```
|
|
118
238
|
|
|
239
|
+
#### Experiment analysis
|
|
240
|
+
|
|
241
|
+
Some Fino adapters support A/B testing analysis, e.g built-in Redis adapter
|
|
242
|
+
|
|
243
|
+
When you run an A/B test for a setting, fino automatically calculates variant based on a stable identifier you pass as
|
|
244
|
+
a `for` option
|
|
245
|
+
|
|
246
|
+
```ruby
|
|
247
|
+
Fino.set(model: "gpt-5", at: :openai, variants: { 20.0 => "gpt-6" })
|
|
248
|
+
|
|
249
|
+
Fino.value(:model, at: :openai, for: "user_1") #=> "gpt-6"
|
|
250
|
+
Fino.value(:model, at: :openai, for: "user_1") #=> "gpt-6"
|
|
251
|
+
|
|
252
|
+
Fino.value(:model, at: :openai, for: "user_2") #=> "gpt-5"
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Later in your code, when user performs a "desired" action, simply call
|
|
256
|
+
|
|
257
|
+
```ruby
|
|
258
|
+
Fino.convert!(:model, at: :openai, for: "user_2")
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
to record a "convertion" for `user_2`. As Fino knows the right variant for `user_2`, conversion will be counted
|
|
262
|
+
todards it. Thanks to that later you'll be able to call
|
|
263
|
+
|
|
264
|
+
```ruby
|
|
265
|
+
Fino.analyse(:model, at: :openai)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
to receive a detailed report over variants performance. Also bar charts comparing all variants and a chart displaying
|
|
269
|
+
amount of conversions over time per variant will be accessible on UI with `fino-rails`
|
|
270
|
+
|
|
271
|
+
To reset analysis data for an experiment:
|
|
272
|
+
|
|
273
|
+
```ruby
|
|
274
|
+
Fino.reset_analysis!(:model, at: :openai)
|
|
275
|
+
```
|
|
276
|
+
|
|
119
277
|
### Unit conversion
|
|
120
278
|
|
|
121
279
|
Fino is able to convert numeric settings into various units
|
data/lib/fino/solid/adapter.rb
CHANGED
|
@@ -21,7 +21,7 @@ module Fino
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def write(setting_definition, value, overrides, variants)
|
|
24
|
-
serialize_value = ->(raw_value) { setting_definition.
|
|
24
|
+
serialize_value = ->(raw_value) { setting_definition.serialize(raw_value) }
|
|
25
25
|
|
|
26
26
|
data = { VALUE_KEY => serialize_value.call(value) }
|
|
27
27
|
|
data/lib/fino/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fino-solid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Egor Iskrenkov
|
|
@@ -29,14 +29,14 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 1.
|
|
32
|
+
version: 1.10.0
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 1.
|
|
39
|
+
version: 1.10.0
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: mysql2
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|