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.
- checksums.yaml +4 -4
- data/LICENSE +18 -0
- data/README.md +107 -5
- data/bin/rllama +0 -2
- data/lib/rllama/aarch64-linux/libggml-base.so +1 -0
- data/lib/rllama/aarch64-linux/libggml-base.so.0 +1 -0
- data/lib/rllama/aarch64-linux/libggml-base.so.0.17.0 +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-armv8.0_1.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-armv8.2_1.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-armv8.2_2.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-armv8.2_3.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-armv8.6_1.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-armv8.6_2.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-armv9.2_1.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-armv9.2_2.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-rpc.so +0 -0
- data/lib/rllama/aarch64-linux/libggml.so +1 -0
- data/lib/rllama/aarch64-linux/libggml.so.0 +1 -0
- data/lib/rllama/aarch64-linux/libggml.so.0.17.0 +0 -0
- data/lib/rllama/aarch64-linux/libllama-common.so +1 -0
- data/lib/rllama/aarch64-linux/libllama-common.so.0 +1 -0
- data/lib/rllama/aarch64-linux/libllama-common.so.0.0.10067 +0 -0
- data/lib/rllama/aarch64-linux/libllama.so +1 -0
- data/lib/rllama/aarch64-linux/libllama.so.0 +1 -0
- data/lib/rllama/aarch64-linux/libllama.so.0.0.10067 +0 -0
- data/lib/rllama/aarch64-linux/libllama_common.so +0 -0
- data/lib/rllama/cli.rb +11 -5
- data/lib/rllama/common.rb +134 -0
- data/lib/rllama/context.rb +220 -51
- data/lib/rllama/cpp.rb +18 -16
- data/lib/rllama/model.rb +67 -21
- data/lib/rllama/version.rb +1 -1
- data/lib/rllama.rb +2 -1
- data/licenses/LICENSE-boringssl +238 -0
- data/licenses/LICENSE-cpp-httplib +22 -0
- data/licenses/LICENSE-json +21 -0
- data/licenses/LICENSE-llama.cpp +21 -0
- metadata +28 -13
- data/lib/rllama/aarch64-linux/libggml-base.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-alderlake.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-haswell.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-icelake.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-sandybridge.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-sapphirerapids.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-skylakex.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-sse42.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu-x64.so +0 -0
- data/lib/rllama/aarch64-linux/libggml-cpu.so +0 -0
- data/lib/rllama/aarch64-linux/libggml.so +0 -0
- data/lib/rllama/aarch64-linux/libllama.so +0 -0
data/lib/rllama/context.rb
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'etc'
|
|
4
|
+
require 'json'
|
|
4
5
|
|
|
5
6
|
module Rllama
|
|
6
7
|
class Context
|
|
8
|
+
TOOL_CALL_ID_PREFIX = 'call_'
|
|
9
|
+
GRAMMAR_TYPE_TOOL_CALLS = 3
|
|
10
|
+
|
|
7
11
|
attr_reader :messages, :n_ctx, :n_batch, :n_past
|
|
8
12
|
|
|
9
|
-
def initialize(model, embeddings: false, n_ctx: nil, n_batch: nil, n_threads: Etc.nprocessors
|
|
13
|
+
def initialize(model, embeddings: false, n_ctx: nil, n_batch: nil, n_threads: Etc.nprocessors,
|
|
14
|
+
system: nil, tools: nil, reasoning: false)
|
|
10
15
|
@model = model
|
|
16
|
+
@tools = tools
|
|
17
|
+
@reasoning = reasoning
|
|
11
18
|
@n_ctx = n_ctx
|
|
12
19
|
@n_batch = n_batch
|
|
13
20
|
@embeddings = embeddings
|
|
@@ -38,13 +45,32 @@ module Rllama
|
|
|
38
45
|
|
|
39
46
|
raise Error, 'Failed to create the llama_context' if @pointer.null?
|
|
40
47
|
|
|
48
|
+
@n_ctx = Cpp.llama_n_ctx(@pointer)
|
|
49
|
+
@n_batch = Cpp.llama_n_batch(@pointer)
|
|
50
|
+
|
|
41
51
|
@n_past = 0
|
|
52
|
+
@cache_tokens = []
|
|
53
|
+
@tool_call_count = 0
|
|
42
54
|
@messages = []
|
|
55
|
+
@messages << { role: 'system', content: system } if system
|
|
56
|
+
|
|
57
|
+
prefill if !@embeddings && (system || tools)
|
|
43
58
|
end
|
|
44
59
|
|
|
45
|
-
def generate(message, role: 'user', max_tokens: @n_ctx, temperature:
|
|
46
|
-
seed: nil, system: nil)
|
|
47
|
-
|
|
60
|
+
def generate(message, role: 'user', max_tokens: @n_ctx, temperature: nil, top_k: nil, top_p: nil, min_p: nil,
|
|
61
|
+
seed: nil, system: nil, tools: nil, reasoning: nil, &block)
|
|
62
|
+
temperature, top_k, top_p, min_p = resolve_sampling(temperature, top_k, top_p, min_p)
|
|
63
|
+
|
|
64
|
+
@tools = tools unless tools.nil?
|
|
65
|
+
@reasoning = reasoning unless reasoning.nil?
|
|
66
|
+
|
|
67
|
+
if system
|
|
68
|
+
if @messages.dig(0, :role).to_s == 'system'
|
|
69
|
+
@messages.first[:content] = system
|
|
70
|
+
else
|
|
71
|
+
@messages.unshift(role: 'system', content: system)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
48
74
|
|
|
49
75
|
if message.is_a?(Array)
|
|
50
76
|
@messages.push(*message)
|
|
@@ -54,51 +80,19 @@ module Rllama
|
|
|
54
80
|
@messages << { role: role, content: message }
|
|
55
81
|
end
|
|
56
82
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
n_prompt_tokens = -Cpp.llama_tokenize(@model.vocab, prompt_string, prompt_string.bytesize, nil, 0, true, true)
|
|
60
|
-
|
|
61
|
-
raise Error, 'Prompt is too long.' if n_prompt_tokens.negative?
|
|
62
|
-
|
|
63
|
-
prompt_tokens_ptr = FFI::MemoryPointer.new(:int32, n_prompt_tokens)
|
|
64
|
-
tokens_written = Cpp.llama_tokenize(@model.vocab, prompt_string, prompt_string.bytesize, prompt_tokens_ptr,
|
|
65
|
-
n_prompt_tokens, true, true)
|
|
66
|
-
|
|
67
|
-
raise Error, 'Failed to tokenize prompt.' if tokens_written.negative?
|
|
68
|
-
|
|
69
|
-
new_token_count = tokens_written - @n_past
|
|
70
|
-
|
|
71
|
-
if new_token_count.positive?
|
|
72
|
-
new_tokens_ptr = prompt_tokens_ptr + (@n_past * FFI.type_size(:int32))
|
|
73
|
-
|
|
74
|
-
batch = Cpp.llama_batch_get_one(new_tokens_ptr, new_token_count)
|
|
75
|
-
|
|
76
|
-
raise Error, 'llama_decode failed.' if Cpp.llama_decode(@pointer, batch) != 0
|
|
77
|
-
|
|
78
|
-
@n_past = tokens_written
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
chain_params = Cpp.llama_sampler_chain_default_params
|
|
82
|
-
sampler_chain = Cpp.llama_sampler_chain_init(chain_params)
|
|
83
|
+
applied = @model.apply_chat_template(@messages, tools: @tools, enable_thinking: @reasoning)
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
Cpp.llama_sampler_chain_add(sampler_chain, Cpp.llama_sampler_init_top_k(top_k)) if top_k&.positive?
|
|
86
|
-
Cpp.llama_sampler_chain_add(sampler_chain, Cpp.llama_sampler_init_top_p(top_p, 1)) if top_p && top_p < 1.0
|
|
87
|
-
if temperature&.positive?
|
|
88
|
-
Cpp.llama_sampler_chain_add(sampler_chain,
|
|
89
|
-
Cpp.llama_sampler_init_temp(temperature))
|
|
90
|
-
end
|
|
85
|
+
decode_prompt(applied['prompt'])
|
|
91
86
|
|
|
92
|
-
is_probabilistic = temperature&.positive? || top_k&.positive? || (top_p && top_p < 1.0) || !min_p.nil?
|
|
93
87
|
rng_seed = seed || (Random.new_seed & 0xFFFFFFFF)
|
|
94
88
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
end
|
|
89
|
+
sampler = Common.sampler_init(@model.pointer, sampler_params(temperature, top_k, top_p, min_p, rng_seed, applied))
|
|
90
|
+
|
|
91
|
+
stops = applied['additional_stops'].to_a.map(&:b)
|
|
92
|
+
max_stop = stops.map(&:bytesize).max || 0
|
|
100
93
|
|
|
101
94
|
n_decoded = 0
|
|
95
|
+
n_yielded = 0
|
|
102
96
|
|
|
103
97
|
generated_text = ''.b
|
|
104
98
|
|
|
@@ -111,7 +105,9 @@ module Rllama
|
|
|
111
105
|
loop do
|
|
112
106
|
break if n_decoded >= max_tokens
|
|
113
107
|
|
|
114
|
-
new_token_id =
|
|
108
|
+
new_token_id = Common.sampler_sample(sampler, @pointer)
|
|
109
|
+
|
|
110
|
+
Common.sampler_accept(sampler, new_token_id)
|
|
115
111
|
|
|
116
112
|
break if Cpp.llama_vocab_is_eog(@model.vocab, new_token_id)
|
|
117
113
|
|
|
@@ -119,10 +115,10 @@ module Rllama
|
|
|
119
115
|
n_chars = Cpp.llama_token_to_piece(@model.vocab, new_token_id, buffer, buffer.size, 0, true)
|
|
120
116
|
|
|
121
117
|
if n_chars >= 0
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
118
|
+
stop_at, n_yielded = emit_piece(generated_text, buffer.read_string(n_chars), stops, max_stop, n_yielded,
|
|
119
|
+
&block)
|
|
120
|
+
|
|
121
|
+
break if stop_at
|
|
126
122
|
end
|
|
127
123
|
|
|
128
124
|
token_ptr = FFI::MemoryPointer.new(:int32, 1).put_int32(0, new_token_id)
|
|
@@ -132,24 +128,54 @@ module Rllama
|
|
|
132
128
|
raise Error, 'llama_decode failed.' if Cpp.llama_decode(@pointer, batch) != 0
|
|
133
129
|
|
|
134
130
|
@n_past += 1
|
|
131
|
+
@cache_tokens << new_token_id
|
|
135
132
|
n_decoded += 1
|
|
136
133
|
end
|
|
137
134
|
|
|
135
|
+
if block_given? && n_yielded < generated_text.bytesize
|
|
136
|
+
yield generated_text.byteslice(n_yielded..).force_encoding(Encoding::UTF_8)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
generated_text.force_encoding(Encoding::UTF_8)
|
|
140
|
+
|
|
138
141
|
end_time = Time.now
|
|
139
142
|
|
|
140
143
|
duration = end_time - start_time
|
|
141
144
|
|
|
142
145
|
tps = n_decoded.positive? && duration.positive? ? n_decoded / duration : 0
|
|
143
146
|
|
|
144
|
-
|
|
147
|
+
Common.sampler_free(sampler)
|
|
148
|
+
|
|
149
|
+
text = generated_text
|
|
150
|
+
reasoning = nil
|
|
151
|
+
tool_calls = []
|
|
152
|
+
|
|
153
|
+
if @tools || @reasoning
|
|
154
|
+
parsed = Common.chat_parse(generated_text, applied, reasoning: @reasoning)
|
|
155
|
+
|
|
156
|
+
reasoning = parsed['reasoning_content']
|
|
157
|
+
reasoning = nil if reasoning&.empty?
|
|
158
|
+
tool_calls = extract_tool_calls(parsed, assistant_message) if @tools
|
|
159
|
+
|
|
160
|
+
if @reasoning && tool_calls.empty?
|
|
161
|
+
text = parsed['content'].to_s
|
|
162
|
+
assistant_message[:content] = text
|
|
163
|
+
end
|
|
164
|
+
end
|
|
145
165
|
|
|
146
166
|
Result.new(
|
|
147
|
-
text
|
|
167
|
+
text:,
|
|
168
|
+
reasoning:,
|
|
169
|
+
tool_calls:,
|
|
148
170
|
stats: {
|
|
149
171
|
duration:,
|
|
150
172
|
tokens_generated: n_decoded,
|
|
151
173
|
tps:,
|
|
152
|
-
seed: rng_seed
|
|
174
|
+
seed: rng_seed,
|
|
175
|
+
temperature:,
|
|
176
|
+
top_k:,
|
|
177
|
+
top_p:,
|
|
178
|
+
min_p:
|
|
153
179
|
}
|
|
154
180
|
)
|
|
155
181
|
end
|
|
@@ -248,6 +274,149 @@ module Rllama
|
|
|
248
274
|
Cpp.llama_free(@pointer)
|
|
249
275
|
end
|
|
250
276
|
|
|
277
|
+
def prefill
|
|
278
|
+
decode_prompt(@model.build_chat_template(@messages, tools: @tools, add_generation_prompt: false,
|
|
279
|
+
enable_thinking: @reasoning))
|
|
280
|
+
rescue StandardError
|
|
281
|
+
nil
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def extract_tool_calls(parsed, assistant_message)
|
|
285
|
+
calls = (parsed['tool_calls'] || []).map do |call|
|
|
286
|
+
id = call['id'].to_s
|
|
287
|
+
id = "#{TOOL_CALL_ID_PREFIX}#{@tool_call_count += 1}" if id.empty?
|
|
288
|
+
|
|
289
|
+
arguments = begin
|
|
290
|
+
JSON.parse(call.dig('function', 'arguments').to_s)
|
|
291
|
+
rescue JSON::ParserError
|
|
292
|
+
{}
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
{ name: call.dig('function', 'name'), arguments:, id: }
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
return [] if calls.empty?
|
|
299
|
+
|
|
300
|
+
assistant_message.replace(
|
|
301
|
+
role: 'assistant',
|
|
302
|
+
content: nil,
|
|
303
|
+
tool_calls: calls.map do |call|
|
|
304
|
+
{ type: 'function', id: call[:id],
|
|
305
|
+
function: { name: call[:name], arguments: call[:arguments].to_json } }
|
|
306
|
+
end
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
calls
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def decode_prompt(prompt_string)
|
|
313
|
+
n_prompt_tokens = -Cpp.llama_tokenize(@model.vocab, prompt_string, prompt_string.bytesize, nil, 0, true, true)
|
|
314
|
+
|
|
315
|
+
raise Error, 'Prompt is too long.' if n_prompt_tokens.negative?
|
|
316
|
+
|
|
317
|
+
prompt_tokens_ptr = FFI::MemoryPointer.new(:int32, n_prompt_tokens)
|
|
318
|
+
tokens_written = Cpp.llama_tokenize(@model.vocab, prompt_string, prompt_string.bytesize, prompt_tokens_ptr,
|
|
319
|
+
n_prompt_tokens, true, true)
|
|
320
|
+
|
|
321
|
+
raise Error, 'Failed to tokenize prompt.' if tokens_written.negative?
|
|
322
|
+
|
|
323
|
+
prompt_tokens = prompt_tokens_ptr.read_array_of_int32(tokens_written)
|
|
324
|
+
|
|
325
|
+
common = common_prefix_length(prompt_tokens)
|
|
326
|
+
|
|
327
|
+
if common < @cache_tokens.length
|
|
328
|
+
memory = Cpp.llama_get_memory(@pointer)
|
|
329
|
+
|
|
330
|
+
if memory.null? || !Cpp.llama_memory_seq_rm(memory, 0, common, -1)
|
|
331
|
+
Cpp.llama_memory_clear(memory, true) unless memory.null?
|
|
332
|
+
|
|
333
|
+
common = 0
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
@n_past = common
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
while tokens_written > @n_past
|
|
340
|
+
n_eval = [tokens_written - @n_past, @n_batch].min
|
|
341
|
+
|
|
342
|
+
new_tokens_ptr = prompt_tokens_ptr + (@n_past * FFI.type_size(:int32))
|
|
343
|
+
|
|
344
|
+
batch = Cpp.llama_batch_get_one(new_tokens_ptr, n_eval)
|
|
345
|
+
|
|
346
|
+
raise Error, 'llama_decode failed.' if Cpp.llama_decode(@pointer, batch) != 0
|
|
347
|
+
|
|
348
|
+
@n_past += n_eval
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
@cache_tokens = prompt_tokens
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def common_prefix_length(tokens)
|
|
355
|
+
limit = [@cache_tokens.length, tokens.length - 1].min
|
|
356
|
+
|
|
357
|
+
common = 0
|
|
358
|
+
common += 1 while common < limit && @cache_tokens[common] == tokens[common]
|
|
359
|
+
|
|
360
|
+
common
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def sampler_params(temperature, top_k, top_p, min_p, seed, applied)
|
|
364
|
+
params = {
|
|
365
|
+
temp: temperature || 0.0,
|
|
366
|
+
top_k: top_k&.positive? ? top_k : 0,
|
|
367
|
+
top_p: top_p || 1.0,
|
|
368
|
+
min_p: min_p || 0.0,
|
|
369
|
+
seed:
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
grammar = applied['grammar'].to_s
|
|
373
|
+
|
|
374
|
+
unless grammar.empty?
|
|
375
|
+
params.merge!(grammar:, grammar_type: GRAMMAR_TYPE_TOOL_CALLS,
|
|
376
|
+
grammar_lazy: applied['grammar_lazy'],
|
|
377
|
+
grammar_triggers: applied['grammar_triggers'],
|
|
378
|
+
generation_prompt: applied['generation_prompt'])
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
params
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def emit_piece(text, piece, stops, max_stop, n_yielded, &block)
|
|
385
|
+
tail_from = [text.bytesize - max_stop + 1, 0].max
|
|
386
|
+
text << piece
|
|
387
|
+
|
|
388
|
+
stop_at = max_stop.positive? ? stops.filter_map { |stop| text.index(stop, tail_from) }.min : nil
|
|
389
|
+
text.slice!(stop_at..) if stop_at
|
|
390
|
+
|
|
391
|
+
if block
|
|
392
|
+
safe = text.bytesize - (stop_at ? 0 : stop_holdback(text, stops))
|
|
393
|
+
|
|
394
|
+
if safe > n_yielded
|
|
395
|
+
yield(text.byteslice(n_yielded, safe - n_yielded).force_encoding(Encoding::UTF_8))
|
|
396
|
+
n_yielded = safe
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
[stop_at, n_yielded]
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def stop_holdback(text, stops)
|
|
404
|
+
stops.map do |stop|
|
|
405
|
+
(stop.bytesize - 1).downto(1).find { |n| text.end_with?(stop.byteslice(0, n)) } || 0
|
|
406
|
+
end.max || 0
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def resolve_sampling(temperature, top_k, top_p, min_p)
|
|
410
|
+
defaults = @model.sampling_defaults
|
|
411
|
+
|
|
412
|
+
[
|
|
413
|
+
temperature.nil? ? defaults[:temperature] : temperature,
|
|
414
|
+
top_k.nil? ? defaults[:top_k] : top_k,
|
|
415
|
+
top_p.nil? ? defaults[:top_p] : top_p,
|
|
416
|
+
min_p.nil? ? defaults[:min_p] : min_p
|
|
417
|
+
]
|
|
418
|
+
end
|
|
419
|
+
|
|
251
420
|
def norm(vec)
|
|
252
421
|
Math.sqrt(vec.sum { |x| x**2 })
|
|
253
422
|
end
|
data/lib/rllama/cpp.rb
CHANGED
|
@@ -15,7 +15,11 @@ module Rllama
|
|
|
15
15
|
when 'windows', 'mingw32'
|
|
16
16
|
'x64-mingw32'
|
|
17
17
|
else
|
|
18
|
-
FFI::Platform::ARCH == 'aarch64' ? 'aarch64
|
|
18
|
+
arch = FFI::Platform::ARCH == 'aarch64' ? 'aarch64' : 'x86_64'
|
|
19
|
+
|
|
20
|
+
is_musl = defined?(FFI::Platform::IS_GNU) ? !FFI::Platform::IS_GNU : RbConfig::CONFIG['host_os'].include?('musl')
|
|
21
|
+
|
|
22
|
+
is_musl ? "#{arch}-linux-musl" : "#{arch}-linux"
|
|
19
23
|
end
|
|
20
24
|
|
|
21
25
|
lib_file =
|
|
@@ -324,9 +328,12 @@ module Rllama
|
|
|
324
328
|
:kv_overrides, :pointer, # const LlamaModelKvOverride*
|
|
325
329
|
:vocab_only, :bool,
|
|
326
330
|
:use_mmap, :bool,
|
|
331
|
+
:use_direct_io, :bool,
|
|
327
332
|
:use_mlock, :bool,
|
|
328
333
|
:check_tensors, :bool,
|
|
329
|
-
:use_extra_bufts, :bool
|
|
334
|
+
:use_extra_bufts, :bool,
|
|
335
|
+
:no_host, :bool,
|
|
336
|
+
:no_alloc, :bool
|
|
330
337
|
end
|
|
331
338
|
|
|
332
339
|
class LlamaContextParams < FFI::Struct
|
|
@@ -334,8 +341,11 @@ module Rllama
|
|
|
334
341
|
:n_batch, :uint32,
|
|
335
342
|
:n_ubatch, :uint32,
|
|
336
343
|
:n_seq_max, :uint32,
|
|
344
|
+
:n_rs_seq, :uint32,
|
|
345
|
+
:n_outputs_max, :uint32,
|
|
337
346
|
:n_threads, :int32,
|
|
338
347
|
:n_threads_batch, :int32,
|
|
348
|
+
:ctx_type, :int, # enum llama_context_type
|
|
339
349
|
:rope_scaling_type, :int, # enum llama_rope_scaling_type
|
|
340
350
|
:pooling_type, :int, # enum llama_pooling_type
|
|
341
351
|
:attention_type, :int, # enum llama_attention_type
|
|
@@ -359,7 +369,9 @@ module Rllama
|
|
|
359
369
|
:no_perf, :bool,
|
|
360
370
|
:op_offload, :bool,
|
|
361
371
|
:swa_full, :bool,
|
|
362
|
-
:kv_unified, :bool
|
|
372
|
+
:kv_unified, :bool,
|
|
373
|
+
:samplers, :pointer,
|
|
374
|
+
:n_samplers, :size_t
|
|
363
375
|
end
|
|
364
376
|
|
|
365
377
|
class LlamaModelQuantizeParams < FFI::Struct
|
|
@@ -372,6 +384,7 @@ module Rllama
|
|
|
372
384
|
:only_copy, :bool,
|
|
373
385
|
:pure, :bool,
|
|
374
386
|
:keep_split, :bool,
|
|
387
|
+
:dry_run, :bool,
|
|
375
388
|
:imatrix, :pointer,
|
|
376
389
|
:kv_overrides, :pointer,
|
|
377
390
|
:tensor_types, :pointer,
|
|
@@ -387,11 +400,6 @@ module Rllama
|
|
|
387
400
|
layout :no_perf, :bool
|
|
388
401
|
end
|
|
389
402
|
|
|
390
|
-
class LlamaChatMessage < FFI::Struct
|
|
391
|
-
layout :role, :pointer,
|
|
392
|
-
:content, :pointer
|
|
393
|
-
end
|
|
394
|
-
|
|
395
403
|
class LlamaSamplerI < FFI::Struct; end
|
|
396
404
|
|
|
397
405
|
class LlamaSampler < FFI::Struct
|
|
@@ -533,10 +541,8 @@ module Rllama
|
|
|
533
541
|
attach_function :llama_adapter_lora_free, [:llama_adapter_lora_p], :void
|
|
534
542
|
attach_function :llama_adapter_get_alora_n_invocation_tokens, [:llama_adapter_lora_p], :uint64
|
|
535
543
|
attach_function :llama_adapter_get_alora_invocation_tokens, [:llama_adapter_lora_p], :pointer # const llama_token*
|
|
536
|
-
attach_function :
|
|
537
|
-
attach_function :
|
|
538
|
-
attach_function :llama_clear_adapter_lora, [:llama_context_p], :void
|
|
539
|
-
attach_function :llama_apply_adapter_cvec, %i[llama_context_p pointer size_t int32 int32 int32], :int32
|
|
544
|
+
attach_function :llama_set_adapters_lora, %i[llama_context_p pointer size_t pointer], :int32
|
|
545
|
+
attach_function :llama_set_adapter_cvec, %i[llama_context_p pointer size_t int32 int32 int32], :int32
|
|
540
546
|
|
|
541
547
|
# Memory management
|
|
542
548
|
attach_function :llama_memory_clear, %i[llama_memory_t bool], :void
|
|
@@ -619,10 +625,6 @@ module Rllama
|
|
|
619
625
|
attach_function :llama_token_to_piece, %i[llama_vocab_p llama_token pointer int32 int32 bool], :int32
|
|
620
626
|
attach_function :llama_detokenize, %i[llama_vocab_p pointer int32 pointer int32 bool bool], :int32
|
|
621
627
|
|
|
622
|
-
# Chat templates
|
|
623
|
-
attach_function :llama_chat_apply_template, %i[string pointer size_t bool pointer int32], :int32
|
|
624
|
-
attach_function :llama_chat_builtin_templates, %i[pointer size_t], :int32
|
|
625
|
-
|
|
626
628
|
# Sampling API
|
|
627
629
|
attach_function :llama_sampler_init, %i[pointer llama_sampler_context_t], :llama_sampler_p
|
|
628
630
|
attach_function :llama_sampler_name, [:llama_sampler_p], :string
|
data/lib/rllama/model.rb
CHANGED
|
@@ -4,6 +4,8 @@ module Rllama
|
|
|
4
4
|
class Model
|
|
5
5
|
DEFAULT_CONTEXT_LENGTH = 2**13
|
|
6
6
|
|
|
7
|
+
DEFAULT_SAMPLING = { temperature: 0.8, top_k: 40, top_p: 0.95, min_p: 0.05 }.freeze
|
|
8
|
+
|
|
7
9
|
attr_reader :pointer
|
|
8
10
|
|
|
9
11
|
def initialize(path_or_name, dir: nil)
|
|
@@ -36,11 +38,35 @@ module Rllama
|
|
|
36
38
|
@n_ctx_train ||= Cpp.llama_model_n_ctx_train(@pointer)
|
|
37
39
|
end
|
|
38
40
|
|
|
39
|
-
def
|
|
40
|
-
|
|
41
|
+
def meta(key)
|
|
42
|
+
buffer = FFI::MemoryPointer.new(:char, 256)
|
|
43
|
+
length = Cpp.llama_model_meta_val_str(@pointer, key.to_s, buffer, buffer.size)
|
|
44
|
+
|
|
45
|
+
length.negative? ? nil : buffer.read_string
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def sampling_defaults
|
|
49
|
+
@sampling_defaults ||= {
|
|
50
|
+
temperature: fetch_meta_float('general.sampling.temp') || DEFAULT_SAMPLING[:temperature],
|
|
51
|
+
top_k: fetch_meta_int('general.sampling.top_k') || DEFAULT_SAMPLING[:top_k],
|
|
52
|
+
top_p: fetch_meta_float('general.sampling.top_p') || DEFAULT_SAMPLING[:top_p],
|
|
53
|
+
min_p: fetch_meta_float('general.sampling.min_p') || DEFAULT_SAMPLING[:min_p]
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def bos_token
|
|
58
|
+
@bos_token ||= token_to_string(Cpp.llama_vocab_bos(vocab))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def eos_token
|
|
62
|
+
@eos_token ||= token_to_string(Cpp.llama_vocab_eos(vocab))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def generate(prompt, max_tokens: DEFAULT_CONTEXT_LENGTH, temperature: nil, top_k: nil, top_p: nil, min_p: nil,
|
|
66
|
+
seed: nil, system: nil, tools: nil, reasoning: nil, &block)
|
|
41
67
|
init_context(n_ctx: max_tokens) do |ctx|
|
|
42
68
|
ctx.generate(prompt, max_tokens: ctx.n_ctx,
|
|
43
|
-
temperature:, top_k:, top_p:, seed:, system:, min_p:,
|
|
69
|
+
temperature:, top_k:, top_p:, seed:, system:, min_p:, tools:, reasoning:,
|
|
44
70
|
&block)
|
|
45
71
|
end
|
|
46
72
|
end
|
|
@@ -74,11 +100,18 @@ module Rllama
|
|
|
74
100
|
end
|
|
75
101
|
|
|
76
102
|
def close
|
|
103
|
+
if @chat_templates
|
|
104
|
+
Common.chat_templates_free(@chat_templates)
|
|
105
|
+
|
|
106
|
+
@chat_templates = nil
|
|
107
|
+
end
|
|
108
|
+
|
|
77
109
|
Cpp.llama_model_free(@pointer)
|
|
78
110
|
end
|
|
79
111
|
|
|
80
|
-
def init_context(embeddings: false, n_ctx: DEFAULT_CONTEXT_LENGTH, n_batch: 512
|
|
81
|
-
|
|
112
|
+
def init_context(embeddings: false, n_ctx: DEFAULT_CONTEXT_LENGTH, n_batch: 512, system: nil, tools: nil,
|
|
113
|
+
reasoning: false)
|
|
114
|
+
context = Context.new(self, embeddings:, n_ctx:, n_batch:, system:, tools:, reasoning:)
|
|
82
115
|
|
|
83
116
|
if block_given?
|
|
84
117
|
result = yield context
|
|
@@ -95,30 +128,43 @@ module Rllama
|
|
|
95
128
|
init_context(embeddings: true, n_ctx:, n_batch:, &)
|
|
96
129
|
end
|
|
97
130
|
|
|
98
|
-
def
|
|
131
|
+
def apply_chat_template(messages, tools: nil, add_generation_prompt: true, enable_thinking: false)
|
|
99
132
|
raise Error, 'Model does not provide a chat template' if chat_template.nil? || chat_template.empty?
|
|
100
133
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
134
|
+
Common.chat_apply(chat_templates, messages:, tools:, add_generation_prompt:, enable_thinking:,
|
|
135
|
+
add_bos: Cpp.llama_vocab_get_add_bos(vocab),
|
|
136
|
+
add_eos: Cpp.llama_vocab_get_add_eos(vocab))
|
|
137
|
+
end
|
|
104
138
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
end
|
|
139
|
+
def build_chat_template(messages, tools: nil, add_generation_prompt: true, enable_thinking: false)
|
|
140
|
+
apply_chat_template(messages, tools:, add_generation_prompt:, enable_thinking:)['prompt']
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
private
|
|
111
144
|
|
|
112
|
-
|
|
145
|
+
def fetch_meta_float(key)
|
|
146
|
+
value = meta(key)
|
|
113
147
|
|
|
114
|
-
|
|
148
|
+
value && Float(value, exception: false)
|
|
149
|
+
end
|
|
115
150
|
|
|
116
|
-
|
|
117
|
-
|
|
151
|
+
def fetch_meta_int(key)
|
|
152
|
+
value = meta(key)
|
|
153
|
+
|
|
154
|
+
return nil unless value
|
|
155
|
+
|
|
156
|
+
Integer(value, exception: false) || Float(value, exception: false)&.to_i
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def chat_templates
|
|
160
|
+
@chat_templates ||= Common.chat_templates_init(@pointer, chat_template, bos_token, eos_token)
|
|
161
|
+
end
|
|
118
162
|
|
|
119
|
-
|
|
163
|
+
def token_to_string(token_id)
|
|
164
|
+
buf = FFI::MemoryPointer.new(:char, 256)
|
|
165
|
+
n = Cpp.llama_token_to_piece(vocab, token_id, buf, buf.size, 0, true)
|
|
120
166
|
|
|
121
|
-
buf.read_string(
|
|
167
|
+
n.positive? ? buf.read_string(n) : ''
|
|
122
168
|
end
|
|
123
169
|
end
|
|
124
170
|
end
|
data/lib/rllama/version.rb
CHANGED
data/lib/rllama.rb
CHANGED
|
@@ -5,10 +5,11 @@ module Rllama
|
|
|
5
5
|
autoload :Loader, 'rllama/loader'
|
|
6
6
|
autoload :Context, 'rllama/context'
|
|
7
7
|
autoload :Cpp, 'rllama/cpp'
|
|
8
|
+
autoload :Common, 'rllama/common'
|
|
8
9
|
autoload :Cli, 'rllama/cli'
|
|
9
10
|
autoload :VERSION, 'rllama/version'
|
|
10
11
|
|
|
11
|
-
Result = Struct.new(:text, :stats, keyword_init: true)
|
|
12
|
+
Result = Struct.new(:text, :reasoning, :tool_calls, :stats, keyword_init: true)
|
|
12
13
|
Error = Class.new(StandardError)
|
|
13
14
|
|
|
14
15
|
module_function
|