|
1
|
# Onde — Ruby Gem |
|
2
|
|
|
3
|
Ruby bindings for the [Onde](https://ondeinference.com) on-device inference engine, powered by Rust via [magnus](https://github.com/matsadler/magnus). |
|
4
|
|
|
5
|
Manage HuggingFace model caches, inspect supported models, and access inference configuration — all from Ruby, with zero C dependencies. |
|
6
|
|
|
7
|
## Requirements |
|
8
|
|
|
9
|
- Ruby >= 3.0 |
|
10
|
- Rust >= 1.87 (installed via [rustup](https://rustup.rs)) |
|
11
|
- macOS, Linux, or Windows |
|
12
|
|
|
13
|
## Installation |
|
14
|
|
|
15
|
Add to your Gemfile: |
|
16
|
|
|
17
|
```ruby |
|
18
|
gem 'onde' |
|
19
|
``` |
|
20
|
|
|
21
|
Then: |
|
22
|
|
|
23
|
```bash |
|
24
|
bundle install |
|
25
|
``` |
|
26
|
|
|
27
|
Or install directly: |
|
28
|
|
|
29
|
```bash |
|
30
|
gem install onde |
|
31
|
``` |
|
32
|
|
|
33
|
The gem compiles a native Rust extension on install. Rust must be available on your `$PATH`. |
|
34
|
|
|
35
|
## Development Setup |
|
36
|
|
|
37
|
```bash |
|
38
|
cd lib/crates/onde/gem |
|
39
|
|
|
40
|
# Install dependencies and compile the native extension |
|
41
|
bin/setup |
|
42
|
|
|
43
|
# Or step by step: |
|
44
|
bundle install |
|
45
|
bundle exec rake compile |
|
46
|
|
|
47
|
# Open an interactive console with the gem loaded |
|
48
|
bin/console |
|
49
|
``` |
|
50
|
|
|
51
|
## Building & Testing |
|
52
|
|
|
53
|
```bash |
|
54
|
# Compile the Rust extension |
|
55
|
bundle exec rake compile |
|
56
|
|
|
57
|
# Build the gem package |
|
58
|
bundle exec rake build |
|
59
|
|
|
60
|
# Run the default task (compile) |
|
61
|
bundle exec rake |
|
62
|
``` |
|
63
|
|
|
64
|
## API Reference |
|
65
|
|
|
66
|
### `Onde.list_local_models` → Hash |
|
67
|
|
|
68
|
Scans the local HuggingFace hub cache (`~/.cache/huggingface/hub/`) and returns all downloaded models that the inference engine supports. |
|
69
|
|
|
70
|
```ruby |
|
71
|
response = Onde.list_local_models |
|
72
|
|
|
73
|
response["cache_path"] # => "/Users/you/.cache/huggingface/hub" |
|
74
|
response["total_size_bytes"] # => 24768454656 |
|
75
|
response["total_size_display"] # => "23.07 GB" |
|
76
|
response["models"].each do |model| |
|
77
|
puts "#{model["model_id"]} — #{model["size_display"]} (#{model["revisions"].length} revisions)" |
|
78
|
puts " Path: #{model["path"]}" |
|
79
|
end |
|
80
|
``` |
|
81
|
|
|
82
|
### `Onde.list_supported_models` → Hash |
|
83
|
|
|
84
|
Returns all models the engine supports with download status flags. |
|
85
|
|
|
86
|
```ruby |
|
87
|
Onde.list_supported_models["models"].each do |m| |
|
88
|
status = if m["is_downloaded"] |
|
89
|
"✓ downloaded" |
|
90
|
elsif m["is_incomplete"] |
|
91
|
"⏳ incomplete (#{m["local_size_display"]} / #{m["expected_size_display"]})" |
|
92
|
else |
|
93
|
"✗ not downloaded" |
|
94
|
end |
|
95
|
puts "[#{status}] #{m["name"]} (#{m["org"]})" |
|
96
|
puts " #{m["description"]}" |
|
97
|
end |
|
98
|
``` |
|
99
|
|
|
100
|
Each model Hash contains: |
|
101
|
|
|
102
|
| Key | Type | Description | |
|
103
|
|-------------------------|---------|----------------------------------------------------| |
|
104
|
| `model_id` | String | Full HuggingFace ID, e.g. `"bartowski/Qwen2.5-1.5B-Instruct-GGUF"` | |
|
105
|
| `name` | String | Human-friendly display name | |
|
106
|
| `org` | String | Organisation or publisher | |
|
107
|
| `description` | String | Short description of the model | |
|
108
|
| `is_downloaded` | Boolean | Fully downloaded locally | |
|
109
|
| `is_incomplete` | Boolean | Partial download exists on disk | |
|
110
|
| `local_size_bytes` | Integer | Bytes currently on disk | |
|
111
|
| `local_size_display` | String | Human-readable local size | |
|
112
|
| `expected_size_bytes` | Integer | Approximate total size when fully downloaded | |
|
113
|
| `expected_size_display` | String | Human-readable expected size | |
|
114
|
|
|
115
|
### `Onde.delete_model(model_id)` → nil |
|
116
|
|
|
117
|
Delete a locally cached model. Raises `RuntimeError` if the model is not found or deletion fails. |
|
118
|
|
|
119
|
```ruby |
|
120
|
Onde.delete_model("bartowski/Qwen2.5-1.5B-Instruct-GGUF") |
|
121
|
``` |
|
122
|
|
|
123
|
### `Onde.model_info(model_id)` → Hash or nil |
|
124
|
|
|
125
|
Look up rich metadata for a single supported model. Returns `nil` if the model ID is not in the supported list. |
|
126
|
|
|
127
|
```ruby |
|
128
|
info = Onde.model_info("bartowski/Qwen2.5-1.5B-Instruct-GGUF") |
|
129
|
# => { |
|
130
|
# "id" => "bartowski/Qwen2.5-1.5B-Instruct-GGUF", |
|
131
|
# "name" => "Qwen 2.5 1.5B (GGUF)", |
|
132
|
# "org" => "Qwen / Alibaba", |
|
133
|
# "description" => "Lightest pre-quantized chat model — ideal for iOS & Android (~941 MB)...", |
|
134
|
# "expected_size_bytes" => 986048768 |
|
135
|
# } |
|
136
|
``` |
|
137
|
|
|
138
|
### `Onde.supported_model_ids` → Array |
|
139
|
|
|
140
|
Returns the list of all supported model IDs as strings. |
|
141
|
|
|
142
|
```ruby |
|
143
|
Onde.supported_model_ids |
|
144
|
# => ["black-forest-labs/FLUX.1-schnell", "google/gemma-3n-E2B-it", ...] |
|
145
|
``` |
|
146
|
|
|
147
|
### `Onde.cache_path` → String or nil |
|
148
|
|
|
149
|
Returns the resolved HuggingFace cache directory path, or `nil` if it cannot be determined. |
|
150
|
|
|
151
|
```ruby |
|
152
|
Onde.cache_path |
|
153
|
# => "/Users/you/.cache/huggingface/hub" |
|
154
|
``` |
|
155
|
|
|
156
|
### Sampling Config Helpers |
|
157
|
|
|
158
|
Return sampling parameter Hashes suitable for passing to inference engines: |
|
159
|
|
|
160
|
```ruby |
|
161
|
Onde.default_sampling_config |
|
162
|
# => { "temperature" => 0.7, "top_p" => 0.95, "max_tokens" => 512, ... } |
|
163
|
|
|
164
|
Onde.deterministic_sampling_config |
|
165
|
# => { "temperature" => 0.0, "max_tokens" => 512, ... } |
|
166
|
|
|
167
|
Onde.mobile_sampling_config |
|
168
|
# => { "temperature" => 0.7, "top_p" => 0.95, "max_tokens" => 128, ... } |
|
169
|
``` |
|
170
|
|
|
171
|
### Constants |
|
172
|
|
|
173
|
```ruby |
|
174
|
Onde::VERSION # => "0.1.0" (gem version) |
|
175
|
Onde::NATIVE_VERSION # => "0.1.0" (Rust crate version) |
|
176
|
Onde::SUPPORTED_MODELS # => ["black-forest-labs/FLUX.1-schnell", ...] (frozen Array) |
|
177
|
Onde::SUPPORTED_MODEL_INFO # => [{ "id" => ..., "name" => ..., ... }, ...] (frozen Array of frozen Hashes) |
|
178
|
``` |
|
179
|
|
|
180
|
## Example: Rails Admin Dashboard |
|
181
|
|
|
182
|
```ruby |
|
183
|
# app/controllers/admin/models_controller.rb |
|
184
|
class Admin::ModelsController < ApplicationController |
|
185
|
def index |
|
186
|
@supported = Onde.list_supported_models["models"] |
|
187
|
@local = Onde.list_local_models |
|
188
|
end |
|
189
|
|
|
190
|
def destroy |
|
191
|
Onde.delete_model(params[:model_id]) |
|
192
|
redirect_to admin_models_path, notice: "Model deleted." |
|
193
|
rescue RuntimeError => e |
|
194
|
redirect_to admin_models_path, alert: e.message |
|
195
|
end |
|
196
|
end |
|
197
|
``` |
|
198
|
|
|
199
|
## Architecture |
|
200
|
|
|
201
|
``` |
|
202
|
gem/ |
|
203
|
├── onde.gemspec # Gem specification |
|
204
|
├── Gemfile # Dev dependencies (rake-compiler, rb_sys) |
|
205
|
├── Rakefile # Build tasks via RbSys::ExtensionTask |
|
206
|
├── rust-toolchain.toml # Pinned Rust toolchain version |
|
207
|
├── lib/ |
|
208
|
│ ├── onde.rb # Entry point — loads native ext + defines module |
|
209
|
│ └── onde/ |
|
210
|
│ └── version.rb # Onde::VERSION |
|
211
|
├── ext/ |
|
212
|
│ └── onde/ |
|
213
|
│ ├── Cargo.toml # Rust crate — depends on `onde` + `magnus` |
|
214
|
│ ├── extconf.rb # rb_sys build hook |
|
215
|
│ └── src/ |
|
216
|
│ └── lib.rs # Magnus bindings — the bridge between Rust and Ruby |
|
217
|
└── bin/ |
|
218
|
├── console # IRB with gem loaded |
|
219
|
└── setup # One-step dev setup |
|
220
|
``` |
|
221
|
|
|
222
|
The native extension compiles the `onde` Rust crate (which wraps [mistral.rs](https://github.com/EricLBuehler/mistral.rs) for LLM inference and HuggingFace Hub for model management) into a shared library (`.bundle` on macOS, `.so` on Linux) that Ruby loads at require time. |
|
223
|
|
|
224
|
All Rust structs are converted to Ruby Hashes via `serde_json` serialization, keeping the binding layer thin and the Ruby API simple — no custom Ruby classes needed. |
|
225
|
|
|
226
|
## License |
|
227
|
|
|
228
|
MIT OR Apache-2.0 — same as the Onde crate. |