rllama 1.0.3-aarch64-linux-gnu → 1.2.0-aarch64-linux-gnu

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +18 -0
  3. data/README.md +107 -5
  4. data/bin/rllama +0 -2
  5. data/lib/rllama/aarch64-linux/libggml-base.so +1 -0
  6. data/lib/rllama/aarch64-linux/libggml-base.so.0 +1 -0
  7. data/lib/rllama/aarch64-linux/libggml-base.so.0.17.0 +0 -0
  8. data/lib/rllama/aarch64-linux/libggml-cpu-armv8.0_1.so +0 -0
  9. data/lib/rllama/aarch64-linux/libggml-cpu-armv8.2_1.so +0 -0
  10. data/lib/rllama/aarch64-linux/libggml-cpu-armv8.2_2.so +0 -0
  11. data/lib/rllama/aarch64-linux/libggml-cpu-armv8.2_3.so +0 -0
  12. data/lib/rllama/aarch64-linux/libggml-cpu-armv8.6_1.so +0 -0
  13. data/lib/rllama/aarch64-linux/libggml-cpu-armv8.6_2.so +0 -0
  14. data/lib/rllama/aarch64-linux/libggml-cpu-armv9.2_1.so +0 -0
  15. data/lib/rllama/aarch64-linux/libggml-cpu-armv9.2_2.so +0 -0
  16. data/lib/rllama/aarch64-linux/libggml-rpc.so +0 -0
  17. data/lib/rllama/aarch64-linux/libggml.so +1 -0
  18. data/lib/rllama/aarch64-linux/libggml.so.0 +1 -0
  19. data/lib/rllama/aarch64-linux/libggml.so.0.17.0 +0 -0
  20. data/lib/rllama/aarch64-linux/libllama-common.so +1 -0
  21. data/lib/rllama/aarch64-linux/libllama-common.so.0 +1 -0
  22. data/lib/rllama/aarch64-linux/libllama-common.so.0.0.10067 +0 -0
  23. data/lib/rllama/aarch64-linux/libllama.so +1 -0
  24. data/lib/rllama/aarch64-linux/libllama.so.0 +1 -0
  25. data/lib/rllama/aarch64-linux/libllama.so.0.0.10067 +0 -0
  26. data/lib/rllama/aarch64-linux/libllama_common.so +0 -0
  27. data/lib/rllama/cli.rb +11 -5
  28. data/lib/rllama/common.rb +134 -0
  29. data/lib/rllama/context.rb +220 -51
  30. data/lib/rllama/cpp.rb +18 -16
  31. data/lib/rllama/model.rb +67 -21
  32. data/lib/rllama/version.rb +1 -1
  33. data/lib/rllama.rb +2 -1
  34. data/licenses/LICENSE-boringssl +238 -0
  35. data/licenses/LICENSE-cpp-httplib +22 -0
  36. data/licenses/LICENSE-json +21 -0
  37. data/licenses/LICENSE-llama.cpp +21 -0
  38. metadata +28 -13
  39. data/lib/rllama/aarch64-linux/libggml-base.so +0 -0
  40. data/lib/rllama/aarch64-linux/libggml-cpu-alderlake.so +0 -0
  41. data/lib/rllama/aarch64-linux/libggml-cpu-haswell.so +0 -0
  42. data/lib/rllama/aarch64-linux/libggml-cpu-icelake.so +0 -0
  43. data/lib/rllama/aarch64-linux/libggml-cpu-sandybridge.so +0 -0
  44. data/lib/rllama/aarch64-linux/libggml-cpu-sapphirerapids.so +0 -0
  45. data/lib/rllama/aarch64-linux/libggml-cpu-skylakex.so +0 -0
  46. data/lib/rllama/aarch64-linux/libggml-cpu-sse42.so +0 -0
  47. data/lib/rllama/aarch64-linux/libggml-cpu-x64.so +0 -0
  48. data/lib/rllama/aarch64-linux/libggml-cpu.so +0 -0
  49. data/lib/rllama/aarch64-linux/libggml.so +0 -0
  50. data/lib/rllama/aarch64-linux/libllama.so +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f19cb41b6197028ad125265d82dc01ca449cc6c48b5179433aefa938f139662b
4
- data.tar.gz: 6ae54e060e85ce6abbfc8a22b8e5f6f66180685403516485757147eb3978e542
3
+ metadata.gz: 99e3ffe956c68e991661b218d27d0ed735efb0a36b6498d288d7dc4dd53fadef
4
+ data.tar.gz: 29cb1e766527527c2d53ad6f7d0dbbdb612c342b541cbb2a9b90c6becde859ad
5
5
  SHA512:
6
- metadata.gz: dd391eaaf38dbf33bb72eb561200c9893fa7645d09948c971763ef8b8a89a1d82328d527c5fa256ae7af42f70b45edff4b2121ba45b65c3b90107aaab91523d5
7
- data.tar.gz: 84f9fee026a509fe6e6c149d6d20e72e00bfbdee94e50a999b7dee8ce15d7b56f1dc4015860ce5c2e3aa8e9a643dab89e2826bef1dd1a2c98d24bcf819a27a09
6
+ metadata.gz: a76c30d46bd3cd39aff003fb5fcd097d13f03f0c507d23de4da0376fca057190258bf32f353e33610a3aaa2068145e97319bede5800788a197468714e2bfd44b
7
+ data.tar.gz: 469fc2dca9c5dac81ae290286af5ab4a07b0edf4e3488394cb3b8122218996a69528f44561226b871313ec1c430c72c4ab1a06135b27f472c65d6669f2e3ed72
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DocuSeal, LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6
+ associated documentation files (the "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
9
+ following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all copies or substantial
12
+ portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15
+ LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
16
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Rllama
4
4
 
5
- Ruby bindings for [llama.cpp](https://github.com/ggerganov/llama.cpp) to run open-source language models locally. Run models like GPT-OSS, Qwen 3, Gemma 3, Llama 3, and many others directly in your Ruby application code.
5
+ Ruby bindings for [llama.cpp](https://github.com/ggml-org/llama.cpp) to run open-source language models locally. Run models like Gemma 4, Qwen 3.5, GLM 4.7, Nemotron, LFM2, Llama 3, and many others directly in your Ruby application code.
6
6
 
7
7
  ## Installation
8
8
 
@@ -36,11 +36,14 @@ When you run `rllama` without arguments, it will display:
36
36
 
37
37
  - **Downloaded models**: Any models you've already downloaded to `~/.rllama/models/`
38
38
  - **Popular models**: A curated list of popular models available for download, including:
39
- - Gemma 3 1B
39
+ - Gemma 4 E4B / Gemma 4 26B-A4B
40
+ - Nemotron 3 Nano 4B
41
+ - Qwen 3.5 35B-A3B
42
+ - LFM2 24B-A2B
43
+ - GLM 4.7 Flash
44
+ - GPT-OSS 20B
40
45
  - Llama 3.2 3B
41
46
  - Phi-4
42
- - Qwen3 30B
43
- - GPT-OSS
44
47
 
45
48
  Simply enter the number of the model you want to use. If you select a model that hasn't been downloaded yet, it will be automatically downloaded from Hugging Face.
46
49
 
@@ -84,7 +87,14 @@ model.close
84
87
 
85
88
  #### Generation parameters
86
89
 
87
- Adjust the generation with parameters:
90
+ By default the sampling parameters are taken from the model's own
91
+ `general.sampling.*` GGUF metadata when present (falling back to llama.cpp's
92
+ standard defaults: `temperature: 0.8`, `top_k: 40`, `top_p: 0.95`,
93
+ `min_p: 0.05`). You can inspect the resolved values with
94
+ `model.sampling_defaults`, or read any metadata key directly with
95
+ `model.meta('general.sampling.temp')`.
96
+
97
+ Pass any parameter explicitly to override the model default:
88
98
 
89
99
  ```ruby
90
100
  result = model.generate(
@@ -97,6 +107,9 @@ result = model.generate(
97
107
  )
98
108
  ```
99
109
 
110
+ The parameters actually used are reported back in `result.stats`
111
+ (`temperature`, `top_k`, `top_p`, `min_p`, `seed`).
112
+
100
113
  #### Streaming generation
101
114
 
102
115
  Stream generated text token-by-token:
@@ -132,6 +145,95 @@ result = model.generate([
132
145
  puts result.text
133
146
  ```
134
147
 
148
+ #### Tool definitions
149
+
150
+ Pass tool definitions to include them in the model's prompt for function calling:
151
+
152
+ ```ruby
153
+ tools = [
154
+ {
155
+ type: 'function',
156
+ function: {
157
+ name: 'get_weather',
158
+ description: 'Get the current weather for a location',
159
+ parameters: {
160
+ type: 'object',
161
+ properties: {
162
+ location: { type: 'string', description: 'City name' }
163
+ },
164
+ required: ['location']
165
+ }
166
+ }
167
+ }
168
+ ]
169
+
170
+ result = model.generate('What is the weather in Paris?', tools:)
171
+ puts result.text
172
+ ```
173
+
174
+ #### Tool call results
175
+
176
+ Messages are passed to the model's chat template in the OpenAI format, so tool
177
+ calls and their results are wired through the standard message schema: the
178
+ assistant message carries a structured `tool_calls:` array, and the tool output
179
+ is sent back as a `role: 'tool'` message.
180
+
181
+ Pass `tools:` (and optionally `system:`) once when creating the context — they
182
+ are remembered for all subsequent turns. When the model emits a tool call, it
183
+ is parsed into `result.tool_calls` and stored in the conversation history in
184
+ structured form automatically. Execute the calls and send each result back as
185
+ a `role: 'tool'` message:
186
+
187
+ ```ruby
188
+ context = model.init_context(tools:)
189
+
190
+ result = context.generate('What is the weather in Paris?')
191
+ result.tool_calls
192
+ # => [{ name: 'get_weather', arguments: { 'location' => 'Paris' }, id: 'call_1' }]
193
+
194
+ result.tool_calls.each do |call|
195
+ weather = get_weather(call[:arguments]['location'])
196
+
197
+ result = context.generate(
198
+ { role: 'tool', tool_call_id: call[:id], name: call[:name], content: weather.to_json }
199
+ )
200
+ end
201
+
202
+ puts result.text
203
+ # => "The weather in Paris right now is 21°C."
204
+ ```
205
+
206
+ The tool call markup is parsed by llama.cpp's chat parser, which derives the
207
+ format automatically from the model's own chat template — any model with a
208
+ tool-capable template is supported. If no tool call is detected,
209
+ `result.tool_calls` is empty and `result.text` contains the raw output.
210
+
211
+ The context detects the edited history and automatically rolls the KV cache
212
+ back to the common prefix, so only the changed suffix is re-processed.
213
+
214
+ ### Reasoning
215
+
216
+ For models with reasoning support, pass `reasoning: true` to
217
+ enable the model's reasoning phase. The reasoning block is parsed out of the
218
+ output: `result.text` contains only the final answer and the reasoning is
219
+ available separately:
220
+
221
+ ```ruby
222
+ result = model.generate('Why is the sky blue?', reasoning: true)
223
+
224
+ result.reasoning
225
+ # => "Thinking Process:\n\n1. Analyze the request..."
226
+
227
+ result.text
228
+ # => "The sky appears blue because..."
229
+ ```
230
+
231
+ Like `tools:`, the flag can also be set once per context via
232
+ `model.init_context(reasoning: true)`. It defaults to off; `result.reasoning`
233
+ is `nil` when reasoning is disabled or the model produced no reasoning block.
234
+ Note that when streaming with a block, the raw reasoning tokens are yielded
235
+ as they are generated.
236
+
135
237
  ### Chat
136
238
 
137
239
  For ongoing conversations, use a context object that maintains the conversation history:
data/bin/rllama CHANGED
@@ -1,8 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'bundler/setup'
5
4
  require 'rllama'
6
- require 'rllama/cli'
7
5
 
8
6
  Rllama::Cli.start(ARGV)
@@ -0,0 +1 @@
1
+ libggml-base.so.0
@@ -0,0 +1 @@
1
+ libggml-base.so.0.17.0
@@ -0,0 +1 @@
1
+ libggml.so.0
@@ -0,0 +1 @@
1
+ libggml.so.0.17.0
@@ -0,0 +1 @@
1
+ libllama-common.so.0
@@ -0,0 +1 @@
1
+ libllama-common.so.0.0.10067
@@ -0,0 +1 @@
1
+ libllama.so.0
@@ -0,0 +1 @@
1
+ libllama.so.0.0.10067
data/lib/rllama/cli.rb CHANGED
@@ -5,13 +5,19 @@ require 'readline'
5
5
  module Rllama
6
6
  class Cli
7
7
  POPULAR_MODELS = [
8
- { path: 'lmstudio-community/gemma-3-1B-it-QAT-GGUF/gemma-3-1B-it-QAT-Q4_0.gguf', size: 720_425_472 },
9
- { path: 'lmstudio-community/gpt-oss-20b-GGUF/gpt-oss-20b-MXFP4.gguf', size: 12_109_565_632 },
10
- { path: 'bartowski/Llama-3.2-3B-Instruct-GGUF/Llama-3.2-3B-Instruct-Q4_K_M.gguf', size: 2_019_377_696 },
11
- { path: 'unsloth/Qwen3-30B-A3B-GGUF/Qwen3-30B-A3B-Q3_K_S.gguf', size: 13_292_468_800 },
8
+ { path: 'lmstudio-community/gemma-4-E4B-it-GGUF/gemma-4-E4B-it-Q4_K_M.gguf', size: 5_335_285_280 },
9
+ { path: 'lmstudio-community/gemma-4-26B-A4B-it-GGUF/gemma-4-26B-A4B-it-Q4_K_M.gguf', size: 16_796_010_624 },
10
+ { path: 'unsloth/NVIDIA-Nemotron-3-Nano-4B-GGUF/NVIDIA-Nemotron-3-Nano-4B-Q4_K_M.gguf', size: 2_900_295_712 },
11
+ { path: 'unsloth/Qwen3.5-35B-A3B-GGUF/Qwen3.5-35B-A3B-Q4_K_M.gguf', size: 22_016_023_168 },
12
+ { path: 'lmstudio-community/LFM2-24B-A2B-GGUF/LFM2-24B-A2B-Q4_K_M.gguf', size: 14_415_473_952 },
13
+ { path: 'unsloth/GLM-4.7-Flash-GGUF/GLM-4.7-Flash-Q4_K_M.gguf', size: 18_312_339_808 },
12
14
  { path: 'inclusionAI/Ling-mini-2.0-GGUF/Ling-mini-2.0-Q4_K_M.gguf', size: 9_911_575_072 },
15
+ { path: 'lmstudio-community/gpt-oss-20b-GGUF/gpt-oss-20b-MXFP4.gguf', size: 12_109_565_632 },
13
16
  { path: 'unsloth/gemma-3n-E4B-it-GGUF/gemma-3n-E4B-it-Q4_K_S.gguf', size: 4_404_697_216 },
14
- { path: 'microsoft/phi-4-gguf/phi-4-Q4_K_S.gguf', size: 8_440_762_560 }
17
+ { path: 'unsloth/Qwen3-30B-A3B-GGUF/Qwen3-30B-A3B-Q3_K_S.gguf', size: 13_292_468_800 },
18
+ { path: 'lmstudio-community/gemma-3-1B-it-QAT-GGUF/gemma-3-1B-it-QAT-Q4_0.gguf', size: 720_425_472 },
19
+ { path: 'microsoft/phi-4-gguf/phi-4-Q4_K_S.gguf', size: 8_440_762_560 },
20
+ { path: 'bartowski/Llama-3.2-3B-Instruct-GGUF/Llama-3.2-3B-Instruct-Q4_K_M.gguf', size: 2_019_377_696 }
15
21
  ].freeze
16
22
 
17
23
  COLOR_CODES = {
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi'
4
+ require 'json'
5
+
6
+ module Rllama
7
+ module Common
8
+ extend FFI::Library
9
+
10
+ REASONING_FORMAT_AUTO = 1
11
+
12
+ lib_file =
13
+ case FFI::Platform::OS
14
+ when 'darwin'
15
+ 'libllama_common.dylib'
16
+ when 'windows', 'mingw32'
17
+ 'llama_common.dll'
18
+ else
19
+ 'libllama_common.so'
20
+ end
21
+
22
+ ffi_lib File.join(Cpp::PLATFORM_DIR, lib_file)
23
+
24
+ attach_function :rlc_free, [:pointer], :void
25
+ attach_function :rlc_chat_templates_init, %i[pointer string string string pointer], :pointer
26
+ attach_function :rlc_chat_templates_free, [:pointer], :void
27
+ attach_function :rlc_chat_apply, %i[pointer string pointer], :pointer
28
+ attach_function :rlc_chat_parse, %i[string bool string pointer], :pointer
29
+ attach_function :rlc_sampler_init, %i[pointer string pointer], :pointer
30
+ attach_function :rlc_sampler_free, [:pointer], :void
31
+ attach_function :rlc_sampler_sample, %i[pointer pointer int32], :int32
32
+ attach_function :rlc_sampler_accept, %i[pointer int32 bool], :void
33
+ attach_function :rlc_sampler_reset, [:pointer], :void
34
+ attach_function :rlc_sampler_print, [:pointer], :pointer
35
+
36
+ module_function
37
+
38
+ def chat_templates_init(model, source, bos_token, eos_token)
39
+ with_err do |err|
40
+ handle = rlc_chat_templates_init(model, source, bos_token, eos_token, err)
41
+
42
+ raise Error, "Failed to parse chat template: #{read_err(err)}" if handle.null?
43
+
44
+ handle
45
+ end
46
+ end
47
+
48
+ def chat_templates_free(handle)
49
+ rlc_chat_templates_free(handle)
50
+ end
51
+
52
+ def chat_apply(handle, messages:, tools: nil, add_generation_prompt: true, enable_thinking: false,
53
+ add_bos: false, add_eos: false)
54
+ inputs = { messages:, tools:, add_generation_prompt:, enable_thinking:, add_bos:, add_eos:,
55
+ reasoning_format: enable_thinking ? REASONING_FORMAT_AUTO : 0 }.to_json
56
+
57
+ with_err do |err|
58
+ result = rlc_chat_apply(handle, inputs, err)
59
+
60
+ raise Error, "Failed to apply chat template: #{read_err(err)}" if result.null?
61
+
62
+ JSON.parse(take(result))
63
+ end
64
+ end
65
+
66
+ def chat_parse(text, params, is_partial: false, reasoning: false)
67
+ parse_params = params.slice('format', 'parser', 'generation_prompt')
68
+ parse_params['reasoning_format'] = REASONING_FORMAT_AUTO if reasoning
69
+ params_json = parse_params.to_json
70
+
71
+ with_err do |err|
72
+ result = rlc_chat_parse(text, is_partial, params_json, err)
73
+
74
+ raise Error, "Failed to parse output: #{read_err(err)}" if result.null?
75
+
76
+ JSON.parse(take(result))
77
+ end
78
+ end
79
+
80
+ def sampler_init(model, params)
81
+ with_err do |err|
82
+ handle = rlc_sampler_init(model, params.to_json, err)
83
+
84
+ raise Error, "Failed to initialize sampler: #{read_err(err)}" if handle.null?
85
+
86
+ handle
87
+ end
88
+ end
89
+
90
+ def sampler_free(handle)
91
+ rlc_sampler_free(handle)
92
+ end
93
+
94
+ def sampler_sample(handle, ctx, idx = -1)
95
+ rlc_sampler_sample(handle, ctx, idx)
96
+ end
97
+
98
+ def sampler_accept(handle, token, accept_grammar: true)
99
+ rlc_sampler_accept(handle, token, accept_grammar)
100
+ end
101
+
102
+ def sampler_reset(handle)
103
+ rlc_sampler_reset(handle)
104
+ end
105
+
106
+ def sampler_print(handle)
107
+ take(rlc_sampler_print(handle))
108
+ end
109
+
110
+ def with_err
111
+ yield FFI::MemoryPointer.new(:pointer)
112
+ end
113
+
114
+ def take(ptr)
115
+ str = ptr.read_string
116
+
117
+ rlc_free(ptr)
118
+
119
+ str
120
+ end
121
+
122
+ def read_err(err)
123
+ msg_ptr = err.read_pointer
124
+
125
+ return 'unknown error' if msg_ptr.null?
126
+
127
+ msg = msg_ptr.read_string
128
+
129
+ rlc_free(msg_ptr)
130
+
131
+ msg
132
+ end
133
+ end
134
+ end