Commit 573c2b0
2026-08-30 05:32:39
1 parent
c5d46c2
Changed files (3)
ext
elelem_llama
ext/elelem_llama/elelem_llama.cpp
@@ -8,6 +8,8 @@
#include "chat.h"
#include <nlohmann/json.hpp>
#include <algorithm>
+#include <cstdlib>
+#include <cstring>
#include <string>
#include <vector>
@@ -26,10 +28,33 @@ struct el_handle {
static bool g_backend = false;
+// llama.cpp/ggml log to stderr by default; gate that behind LOG_LEVEL so
+// GGUF model loads aren't noisy unless a caller opts in, matching the
+// verbosity Elelem.logger is configured with in lib/elelem.rb.
+static enum ggml_log_level el_log_threshold() {
+ const char *level = std::getenv("LOG_LEVEL");
+ if (!level) return GGML_LOG_LEVEL_WARN;
+ std::string v(level);
+ std::transform(v.begin(), v.end(), v.begin(), ::tolower);
+ if (v == "debug") return GGML_LOG_LEVEL_DEBUG;
+ if (v == "info") return GGML_LOG_LEVEL_INFO;
+ if (v == "error") return GGML_LOG_LEVEL_ERROR;
+ return GGML_LOG_LEVEL_WARN;
+}
+
+static void el_log_callback(enum ggml_log_level level, const char *text, void *user_data) {
+ if (level < el_log_threshold()) return;
+ fputs(text, stderr);
+}
+
// temp <= 0 selects greedy/deterministic sampling; seed is the RNG seed for the
// sampled path (both surfaced so callers -- notably the eval harness -- can pin them).
void *el_open(const char *path, int n_gpu_layers, int n_ctx, int n_threads, float temp, int seed) {
- if (!g_backend) { llama_backend_init(); g_backend = true; }
+ if (!g_backend) {
+ llama_log_set(el_log_callback, nullptr);
+ llama_backend_init();
+ g_backend = true;
+ }
llama_model_params mp = llama_model_default_params();
mp.n_gpu_layers = n_gpu_layers;
lib/elelem/net/gguf.rb
@@ -38,6 +38,11 @@ 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"]
+ 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")
+ end
+
content = result["content"].to_s
block&.call(type: "saying", text: content) unless content.empty?
lib/elelem.rb
@@ -38,9 +38,13 @@ require_relative "elelem/web_terminal"
module Elelem
def logger
- @logger ||= Logger.new("./elelem/current.log")
+ @logger ||= Logger.new("./elelem/current.log").tap do |log|
+ log.level = Logger.const_get(ENV.fetch("LOG_LEVEL", "warn").upcase)
+ end
end
+ OUTPUT_LIMIT = 10_000
+
def self.sh(cmd, args: [], cwd: Dir.pwd, env: {}, timeout: nil)
output = StringIO.new
options = { chdir: cwd }
@@ -63,10 +67,18 @@ module Elelem
status = wait_thr.value
note = timed_out ? "\n[command timed out after #{timeout}s]" : ""
- { exit_status: status.exitstatus || (timed_out ? 124 : 1), content: output.string + note }
+ { exit_status: status.exitstatus || (timed_out ? 124 : 1), content: truncate(output.string) + note }
end
end
+ def self.truncate(content, limit: OUTPUT_LIMIT)
+ return content if content.length <= limit
+
+ half = limit / 2
+ elided = content.length - limit
+ "#{content[0, half]}\n[#{elided} characters elided]\n#{content[-half, half]}"
+ end
+
def self.terminate(pid)
Process.kill("TERM", -pid)
sleep 0.5