Commit db4deb6
Changed files (3)
ext
elelem
llama
ext/elelem/llama/elelem_llama.cpp
@@ -36,8 +36,7 @@ void *el_open(const char *path, int n_gpu_layers, int n_ctx, int n_threads, floa
llama_model *model = llama_model_load_from_file(path, mp);
if (!model) return nullptr;
- return new el_handle{model, common_chat_templates_init(model, ""), n_ctx, n_threads,
- temp, (uint32_t) seed};
+ return new el_handle{model, common_chat_templates_init(model, ""), n_ctx, n_threads, temp, (uint32_t) seed};
}
// Tool-call arguments arrive as either a JSON string or an object; llama.cpp's
lib/elelem/llama/client.rb
@@ -5,12 +5,6 @@ require "json"
module Elelem
module Net
- # In-process GGUF client: loads a local model inside the elelem process via a
- # thin C shim over llama.cpp + its common_chat layer (see
- # ext/elelem_llama/elelem_llama.cpp), bound with stdlib Fiddle. The shim takes
- # OpenAI-style messages + tools as JSON and returns {content, tool_calls} as
- # JSON, so the model participates in the normal agent tool loop. No subprocess,
- # no HTTP server, no third-party gem.
class GGUF
NATIVE = File.expand_path("../native", __dir__)
SHIM = File.join(NATIVE, "libelelem_llama.so")
@@ -18,20 +12,14 @@ module Elelem
I = Fiddle::TYPE_INT
F = Fiddle::TYPE_FLOAT
- # The GPU backend the extension compiled (extconf.rb stamps this at install);
- # "cpu" when absent. Lets the provider default GPU offload to what was built.
def self.backend
File.read(File.join(NATIVE, "backend")).strip
rescue SystemCallError
"cpu"
end
- # dlopen + bindings are process-wide resources -- memoize once, like Net.http.
def self.functions
@functions ||= begin
- unless File.exist?(SHIM)
- raise "gguf: native shim missing at #{SHIM}\n run: bundle exec rake compile"
- end
lib = Fiddle.dlopen(SHIM)
{
open: Fiddle::Function.new(lib["el_open"], [V, I, I, I, F, I], V),
@@ -40,28 +28,25 @@ module Elelem
end
end
- # temp <= 0 => greedy/deterministic; seed -1 keeps llama.cpp's default seed.
- def initialize(model_path:, n_ctx: 4096, n_threads: 16, max_tokens: 512,
- n_gpu_layers: 0, temp: 0.7, seed: -1)
+ def initialize(model:, n_ctx: 8192, n_threads: 16, max_tokens: 512, n_gpu_layers: 0, temp: 0.7, seed: -1)
@max_tokens = max_tokens
- @handle = self.class.functions[:open].call(model_path, n_gpu_layers, n_ctx, n_threads, temp, seed)
- raise "gguf: failed to load model at #{model_path}" if @handle.null?
+ @handle = self.class.functions[:open].call(model, n_gpu_layers, n_ctx, n_threads, temp, seed)
+ raise "gguf: failed to load model at #{model}" if @handle.null?
end
- # elelem provider contract: fetch(messages, tools=[]) { |event| } -> tool_calls.
- # Streams the reply as a "saying" event and each parsed tool call as a
- # "doing" event, which the agent loop executes and feeds back.
def fetch(messages, tools = [], &block)
- ptr = self.class.functions[:generate].call(
- @handle, JSON.generate(messages), JSON.generate(tools), @max_tokens
- )
+ ptr = self.class.functions[:generate].call(@handle, JSON.generate(messages), JSON.generate(tools), @max_tokens)
result = JSON.parse(Fiddle::Pointer.new(ptr).to_s)
content = result["content"].to_s
block&.call(type: "saying", text: content) unless content.empty?
result.fetch("tool_calls", []).map do |call|
- tool_call = { id: call["id"], name: call["name"], arguments: parse_args(call["arguments"]) }
+ tool_call = {
+ id: call["id"],
+ name: call["name"],
+ arguments: parse(call["arguments"])
+ }
block&.call(tool_call.merge(type: "doing"))
tool_call
end
@@ -69,7 +54,7 @@ module Elelem
private
- def parse_args(raw)
+ def parse(raw)
JSON.parse(raw.to_s)
rescue JSON::ParserError
{}
lib/elelem/llama/plugin.rb
@@ -1,18 +1,15 @@
# frozen_string_literal: true
-# In-process GGUF provider (see Elelem::Net::GGUF in lib/elelem/net/gguf.rb).
Elelem::Providers.register(:gguf) do
- # Offload all layers by default when a GPU backend was compiled (extconf.rb stamps
- # it); CPU builds stay at 0. GGUF_N_GPU_LAYERS overrides either way.
gpu = %w[vulkan cuda metal].include?(Elelem::Net::GGUF.backend)
Elelem::Net::GGUF.new(
- model_path: ENV.fetch("GGUF_MODEL", File.expand_path("~/models/Qwen2.5-Coder-7B-Instruct-Q4_K_M.gguf")),
- n_ctx: Integer(ENV.fetch("GGUF_N_CTX", "8192")), # room for system prompt + tool results
+ model: ENV.fetch("GGUF_MODEL", File.expand_path("~/models/Qwen2.5-Coder-7B-Instruct-Q4_K_M.gguf")),
+ n_ctx: Integer(ENV.fetch("GGUF_N_CTX", "8192")),
n_threads: Integer(ENV.fetch("GGUF_THREADS", "16")),
max_tokens: Integer(ENV.fetch("GGUF_MAX_TOKENS", "512")),
- n_gpu_layers: Integer(ENV.fetch("GGUF_N_GPU_LAYERS", gpu ? "999" : "0")), # 999 = offload all
- temp: Float(ENV.fetch("GGUF_TEMP", "0.7")), # 0 = greedy
- seed: Integer(ENV.fetch("GGUF_SEED", "-1")) # -1 = llama default
+ n_gpu_layers: Integer(ENV.fetch("GGUF_N_GPU_LAYERS", gpu ? "999" : "0")),
+ temp: Float(ENV.fetch("GGUF_TEMP", "0.7")),
+ seed: Integer(ENV.fetch("GGUF_SEED", "-1"))
)
end