LLamaSharp/LLama/Native/NativeLogConfig.cs

93 lines
3.1 KiB
C#

using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Microsoft.Extensions.Logging;
namespace LLama.Native;
/// <summary>
/// Configure llama.cpp logging
/// </summary>
public static class NativeLogConfig
{
/// <summary>
/// Callback from llama.cpp with log messages
/// </summary>
/// <param name="level"></param>
/// <param name="message"></param>
public delegate void LLamaLogCallback(LLamaLogLevel level, string message);
/// <summary>
/// Register a callback to receive llama log messages
/// </summary>
/// <param name="logCallback"></param>
[DllImport(NativeApi.libraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "llama_log_set")]
private static extern void native_llama_log_set(LLamaLogCallback? logCallback);
/// <summary>
/// A GC handle for the current log callback to ensure the callback is not collected
/// </summary>
private static GCHandle? _currentLogCallbackHandle;
/// <summary>
/// Register a callback to receive llama log messages
/// </summary>
/// <param name="logCallback"></param>
#pragma warning disable IDE1006 // Naming Styles (name imitates llama.cpp)
public static void llama_log_set(LLamaLogCallback? logCallback)
#pragma warning restore IDE1006 // Naming Styles
{
if (NativeLibraryConfig.LibraryHasLoaded)
{
// The library is loaded, just pass the callback directly to llama.cpp
native_llama_log_set(logCallback);
// Save a GC handle, to ensure the callback is not collected
_currentLogCallbackHandle?.Free();
_currentLogCallbackHandle = null;
if (logCallback != null)
_currentLogCallbackHandle = GCHandle.Alloc(logCallback);
}
else
{
// We can't set the log method yet since that would cause the llama.dll to load.
// Instead configure it to be set when the native library loading is done
NativeLibraryConfig.Instance.WithLogCallback(logCallback);
}
}
/// <summary>
/// Register a callback to receive llama log messages
/// </summary>
/// <param name="logger"></param>
#pragma warning disable IDE1006 // Naming Styles (name imitates llama.cpp)
public static void llama_log_set(ILogger? logger)
#pragma warning restore IDE1006 // Naming Styles
{
// Clear the logger
if (logger == null)
{
llama_log_set((LLamaLogCallback?)null);
return;
}
var builderThread = new ThreadLocal<StringBuilder>(() => new StringBuilder());
// Bind a function that combines messages until a newline is encountered, then logs it all as one message
llama_log_set((level, message) =>
{
var builder = builderThread.Value!;
builder.Append(message);
if (!message.EndsWith("\n"))
return;
// Remove the newline from the end
builder.Remove(builder.Length - 1, 1);
logger.Log(level.ToLogLevel(), "{message}", builder.ToString());
builder.Clear();
});
}
}