Commit 8580278
Changed files (6)
ext
elelem
llama
lib
elelem
llama
ext/elelem/llama/extconf.rb
@@ -6,7 +6,7 @@ EXT_DIR = __dir__
GEM_ROOT = File.expand_path("../../..", EXT_DIR)
VENDOR = File.join(GEM_ROOT, "vendor", "llama.cpp")
BUILD_DIR = File.join(EXT_DIR, "build")
-NATIVE = File.join(GEM_ROOT, "lib", "elelem", "gguf")
+NATIVE = File.join(GEM_ROOT, "ext", "elelem", "llama", "native")
STAMP = File.join(BUILD_DIR, ".elelem_backend") # last backend built here
VALID_BACKENDS = %w[cpu vulkan cuda].freeze
@@ -75,8 +75,7 @@ def configure_and_build(backend)
end
unless File.exist?(File.join(VENDOR, "CMakeLists.txt"))
- abort "elelem: vendored llama.cpp missing at #{VENDOR}\n" \
- " run: git submodule update --init --recursive"
+ abort "elelem: vendored llama.cpp missing at #{VENDOR}\n run: git submodule update --init --recursive"
end
backend = detect_backend
@@ -108,7 +107,7 @@ run(cxx, "-std=c++17", "-O2", "-Wall", "-Wextra", "-shared", "-fPIC",
"-I", File.join(VENDOR, "ggml", "include"),
"-I", File.join(VENDOR, "common"), # chat.h (common_chat)
"-I", File.join(VENDOR, "vendor"), # nlohmann/json.hpp
- File.join(EXT_DIR, "elelem/llama/elelem.cpp"),
+ File.join(EXT_DIR, "elelem.cpp"),
"-o", File.join(NATIVE, "libelelem_llama.so"),
"-L", libdir, "-lllama-common", "-lllama",
"-Wl,-rpath,$ORIGIN")
lib/elelem/llama/client.rb
@@ -4,9 +4,9 @@ require "fiddle"
require "json"
module Elelem
- module Net
- class GGUF
- NATIVE = File.expand_path("gguf", __dir__)
+ module Llama
+ class Client
+ NATIVE = File.expand_path("../../../ext/elelem/llama/native", __dir__)
SHIM = File.join(NATIVE, "libelelem_llama.so")
V = Fiddle::TYPE_VOIDP
I = Fiddle::TYPE_INT
@@ -32,7 +32,7 @@ module Elelem
def initialize(model:, n_ctx: 0, n_threads: 0, max_tokens: 0, n_gpu_layers: 0, temp: 0.7, seed: -1)
@max_tokens = max_tokens
@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?
+ raise "llama: failed to load model at #{model}" if @handle.null?
at_exit { self.class.functions[:close].call(@handle) }
end
@@ -41,21 +41,21 @@ module Elelem
ptr = self.class.functions[:generate].call(@handle, JSON.generate(messages), JSON.generate(tools), @max_tokens)
result = JSON.parse(Fiddle::Pointer.new(ptr).to_s)
- Elelem.logger.debug("gguf: tool-call fallback used") if result["fallback"]
- Elelem.logger.debug("gguf: harmony tag fallback used") if result["harmony_tag_fallback"]
- Elelem.logger.warn("gguf: generate error: #{result["error"]}") if result["error"]
+ Elelem.logger.debug("llama: tool-call fallback used") if result["fallback"]
+ Elelem.logger.debug("llama: harmony tag fallback used") if result["harmony_tag_fallback"]
+ Elelem.logger.warn("llama: generate error: #{result["error"]}") if result["error"]
if result["ms_decode"]
Elelem.logger.debug(format(
- "gguf: reset=%.0fms prefill=%.0fms (n=%d, %d reused) decode=%.0fms (n=%d)",
+ "llama: reset=%.0fms prefill=%.0fms (n=%d, %d reused) decode=%.0fms (n=%d)",
result["ms_reset"], result["ms_prefill"], result["n_prompt"], result["n_reused"].to_i,
result["ms_decode"], result["n_decoded"]
))
end
if result["tool_calls"].to_a.empty? && !tools.empty? && result["content"].to_s.include?("\"name\"")
- Elelem.logger.debug("gguf: no tool calls parsed, tools offered")
+ Elelem.logger.debug("llama: no tool calls parsed, tools offered")
end
reasoning = result["reasoning"].to_s
- Elelem.logger.debug("gguf: reasoning: #{reasoning}") unless reasoning.empty?
+ Elelem.logger.debug("llama: reasoning: #{reasoning}") unless reasoning.empty?
block&.call(type: "thinking", text: reasoning) unless reasoning.empty?
content = result["content"].to_s
@@ -63,7 +63,7 @@ module Elelem
content = "[error: #{result["error"]}]"
elsif result["truncated"] && content.empty? && result.fetch("tool_calls", []).empty?
limit = @max_tokens.positive? ? "max_tokens" : "available context"
- Elelem.logger.warn("gguf: hit #{limit} (#{result["n_decoded"]}/#{result["budget"]}) before producing a reply")
+ Elelem.logger.warn("llama: hit #{limit} (#{result["n_decoded"]}/#{result["budget"]}) before producing a reply")
content = "[no reply: ran out of output tokens before finishing]"
end
block&.call(type: "saying", text: content) unless content.empty?
lib/elelem/llama/plugin.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
-Elelem::Providers.register(:gguf) do
- gpu = %w[vulkan cuda metal].include?(Elelem::Net::GGUF.backend)
+require_relative "../llama"
+
+Elelem::Providers.register(:llama) do
+ gpu = %w[vulkan cuda metal].include?(Elelem::Llama::Client.backend)
default_model = -> {
dir = File.expand_path("~/.agents/models")
@@ -16,7 +18,7 @@ Elelem::Providers.register(:gguf) do
end
}
- Elelem::Net::GGUF.new(
+ Elelem::Llama::Client.new(
model: ENV.fetch("ELELEM_GGUF_MODEL") { default_model.call },
n_ctx: 0,
n_threads: 0,
lib/elelem/llama/version.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module Elelem
+ module Llama
+ VERSION = "0.1.0"
+ end
+end
lib/elelem/llama.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+require_relative "llama/version"
+require_relative "llama/client"
elelem-llama.gemspec
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require_relative "lib/elelem/llama/version"
+
+Gem::Specification.new do |spec|
+ spec.name = "elelem-llama"
+ spec.version = Elelem::Llama::VERSION
+ spec.authors = ["mo khan"]
+ spec.email = ["mo@mokhan.ca"]
+
+ spec.summary = "A llama.cpp plugin for elelem."
+ spec.description = "A llama.cpp plugin for elelem."
+ spec.homepage = "https://src.mokhan.ca/xlgmokha/elelem"
+ spec.license = "MIT"
+ spec.required_ruby_version = ">= 4.0.0"
+ spec.required_rubygems_version = ">= 4.0.0"
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
+ spec.metadata["homepage_uri"] = spec.homepage
+ spec.metadata["source_code_uri"] = "https://git.mokhan.ca/xlgmokha/elelem.git"
+
+ spec.files = [
+ "LICENSE.txt",
+ "lib/elelem/llama.rb",
+ "lib/elelem/llama/version.rb",
+ "lib/elelem/llama/client.rb",
+ "lib/elelem/llama/plugin.rb",
+ "ext/elelem/llama/elelem.cpp",
+ "ext/elelem/llama/extconf.rb",
+ ]
+ # Vendored llama.cpp source (git submodule) is compiled at install time by the
+ # extension below, so it must ship in the gem.
+ spec.files += Dir["vendor/llama.cpp/**/*"].select { |path| File.file?(path) }
+ spec.extensions = ["ext/elelem/llama/extconf.rb"]
+ spec.require_paths = ["lib"]
+
+ spec.add_dependency "elelem", "~> 0.10"
+ spec.add_dependency "fiddle", "~> 1.1"
+ spec.add_dependency "json", "~> 2.0"
+end