Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Logging interceptor #649

Merged
merged 8 commits into from
Apr 5, 2024
15 changes: 12 additions & 3 deletions LLama.Examples/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,20 @@ __ __ ____ __

""");

// Configure native library to use
// Configure native library to use. This must be done before any other llama.cpp methods are called!
NativeLibraryConfig
.Instance
.WithCuda()
.WithLogs(LLamaLogLevel.Info);
.WithCuda();

// Configure logging. Change this to `true` to see log messages from llama.cpp
var showLLamaCppLogs = false;
NativeLibraryConfig
.Instance
.WithLogCallback((level, message) =>
{
if (showLLamaCppLogs)
Console.WriteLine($"[llama {level}]: {message.TrimEnd('\n')}");
});

// Calling this method forces loading to occur now.
NativeApi.llama_empty_call();
Expand Down
2 changes: 2 additions & 0 deletions LLama/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@
[assembly: SuppressMessage("Interoperability", "CA1401:P/Invokes should not be visible", Justification = "LLamaSharp intentionally exports the native llama.cpp API")]

[assembly: SuppressMessage("Style", "IDE0070:Use 'System.HashCode'", Justification = "Not compatible with netstandard2.0")]

[assembly: SuppressMessage("Interoperability", "SYSLIB1054:Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time", Justification = "Not compatible with netstandard2.0")]
28 changes: 23 additions & 5 deletions LLama/Native/LLamaLogLevel.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
namespace LLama.Native
{
/// <summary>
/// Severity level of a log message
/// </summary>
using System;
using Microsoft.Extensions.Logging;

namespace LLama.Native
{
/// <summary>
/// Severity level of a log message
/// </summary>
public enum LLamaLogLevel
{
/// <summary>
Expand All @@ -25,4 +28,19 @@ public enum LLamaLogLevel
/// </summary>
Debug = 5,
}

internal static class LLamaLogLevelExtensions
{
public static LogLevel ToLogLevel(this LLamaLogLevel llama)
{
return (llama) switch
{
LLamaLogLevel.Error => LogLevel.Error,
LLamaLogLevel.Warning => LogLevel.Warning,
LLamaLogLevel.Info => LogLevel.Information,
LLamaLogLevel.Debug => LogLevel.Debug,
_ => throw new ArgumentOutOfRangeException(nameof(llama), llama, null)
};
}
}
}
55 changes: 10 additions & 45 deletions LLama/Native/NativeApi.Load.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ static NativeApi()
// which llama.dll is used.
SetDllImportResolver();

// Set flag to indicate that this point has been passed. No native library config can be done after this point.
NativeLibraryConfig.LibraryHasLoaded = true;

// Immediately make a call which requires loading the llama DLL. This method call
// can't fail unless the DLL hasn't been loaded.
try
Expand All @@ -34,6 +37,10 @@ static NativeApi()
"to specify it at the very beginning of your code. For more informations about compilation, please refer to LLamaSharp repo on github.\n");
}

// Now that the "loaded" flag is set configure logging in llama.cpp
if (NativeLibraryConfig.Instance.LogCallback != null)
NativeLogConfig.llama_log_set(NativeLibraryConfig.Instance.LogCallback);

// Init llama.cpp backend
llama_backend_init();
}
Expand Down Expand Up @@ -80,47 +87,10 @@ private static void SetDllImportResolver()

private static void Log(string message, LLamaLogLevel level)
{
if (!enableLogging)
return;

if ((int)level > (int)logLevel)
return;
if (!message.EndsWith("\n"))
message += "\n";

var fg = Console.ForegroundColor;
var bg = Console.BackgroundColor;
try
{
ConsoleColor color;
string levelPrefix;
if (level == LLamaLogLevel.Debug)
{
color = ConsoleColor.Cyan;
levelPrefix = "[Debug]";
}
else if (level == LLamaLogLevel.Info)
{
color = ConsoleColor.Green;
levelPrefix = "[Info]";
}
else if (level == LLamaLogLevel.Error)
{
color = ConsoleColor.Red;
levelPrefix = "[Error]";
}
else
{
color = ConsoleColor.Yellow;
levelPrefix = "[UNK]";
}

Console.ForegroundColor = color;
Console.WriteLine($"{loggingPrefix} {levelPrefix} {message}");
}
finally
{
Console.ForegroundColor = fg;
Console.BackgroundColor = bg;
}
NativeLibraryConfig.Instance.LogCallback?.Invoke(level, message);
martindevans marked this conversation as resolved.
Show resolved Hide resolved
}

#region CUDA version
Expand Down Expand Up @@ -362,8 +332,6 @@ private static IntPtr TryLoadLibraries(LibraryName lib)
{
#if NET6_0_OR_GREATER
var configuration = NativeLibraryConfig.CheckAndGatherDescription(lib);
enableLogging = configuration.Logging;
logLevel = configuration.LogLevel;

// Set the flag to ensure the NativeLibraryConfig can no longer be modified
NativeLibraryConfig.LibraryHasLoaded = true;
Expand Down Expand Up @@ -455,8 +423,5 @@ string TryFindPath(string filename)
internal const string libraryName = "llama";
internal const string llavaLibraryName = "llava_shared";
private const string cudaVersionFile = "version.json";
private const string loggingPrefix = "[LLamaSharp Native]";
private static bool enableLogging = false;
private static LLamaLogLevel logLevel = LLamaLogLevel.Info;
}
}
14 changes: 5 additions & 9 deletions LLama/Native/NativeApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@

namespace LLama.Native
{
/// <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>
/// Direct translation of the llama.cpp API
/// </summary>
Expand Down Expand Up @@ -364,8 +357,11 @@ public static int llama_token_to_piece(SafeLlamaModelHandle model, LLamaToken ll
/// Register a callback to receive llama log messages
/// </summary>
/// <param name="logCallback"></param>
[DllImport(libraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern void llama_log_set(LLamaLogCallback logCallback);
[Obsolete("Use `NativeLogConfig.llama_log_set` instead")]
public static void llama_log_set(NativeLogConfig.LLamaLogCallback logCallback)
{
NativeLogConfig.llama_log_set(logCallback);
}

/// <summary>
/// Clear the KV cache
Expand Down
105 changes: 52 additions & 53 deletions LLama/Native/NativeLibraryConfig.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;

namespace LLama.Native
{
Expand All @@ -9,39 +10,21 @@ namespace LLama.Native
/// Allows configuration of the native llama.cpp libraries to load and use.
/// All configuration must be done before using **any** other LLamaSharp methods!
/// </summary>
public sealed class NativeLibraryConfig
public sealed partial class NativeLibraryConfig
{
/// <summary>
/// Get the config instance
/// </summary>
public static NativeLibraryConfig Instance { get; } = new();

/// <summary>
/// Check if the native library has already been loaded. Configuration cannot be modified if this is true.
/// </summary>
public static bool LibraryHasLoaded { get; internal set; } = false;

private string? _libraryPath;
private string? _libraryPathLLava;

private bool _useCuda = true;
private AvxLevel _avxLevel;
private bool _allowFallback = true;
private bool _skipCheck = false;
private bool _logging = false;
private LLamaLogLevel _logLevel = LLamaLogLevel.Info;

/// <summary>
/// search directory -> priority level, 0 is the lowest.
/// </summary>
private readonly List<string> _searchDirectories = new List<string>();

private static void ThrowIfLoaded()
{
if (LibraryHasLoaded)
throw new InvalidOperationException("NativeLibraryConfig must be configured before using **any** other LLamaSharp methods!");
}

#region configurators
/// <summary>
/// Load a specified native library as backend for LLamaSharp.
Expand Down Expand Up @@ -117,35 +100,6 @@ public NativeLibraryConfig SkipCheck(bool enable = true)
return this;
}

/// <summary>
/// Whether to output the logs to console when loading the native library with your configuration.
/// </summary>
/// <param name="enable"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException">Thrown if `LibraryHasLoaded` is true.</exception>
public NativeLibraryConfig WithLogs(bool enable)
{
ThrowIfLoaded();

_logging = enable;
return this;
}

/// <summary>
/// Enable console logging with the specified log logLevel.
/// </summary>
/// <param name="logLevel"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException">Thrown if `LibraryHasLoaded` is true.</exception>
public NativeLibraryConfig WithLogs(LLamaLogLevel logLevel = LLamaLogLevel.Info)
{
ThrowIfLoaded();

_logging = true;
_logLevel = logLevel;
return this;
}

/// <summary>
/// Add self-defined search directories. Note that the file stucture of the added
/// directories must be the same as the default directory. Besides, the directory
Expand Down Expand Up @@ -196,8 +150,6 @@ internal static Description CheckAndGatherDescription(LibraryName library)
Instance._avxLevel,
Instance._allowFallback,
Instance._skipCheck,
Instance._logging,
Instance._logLevel,
Instance._searchDirectories.Concat(new[] { "./" }).ToArray()
);
}
Expand Down Expand Up @@ -279,7 +231,7 @@ public enum AvxLevel
Avx512,
}

internal record Description(string? Path, LibraryName Library, bool UseCuda, AvxLevel AvxLevel, bool AllowFallback, bool SkipCheck, bool Logging, LLamaLogLevel LogLevel, string[] SearchDirectories)
internal record Description(string? Path, LibraryName Library, bool UseCuda, AvxLevel AvxLevel, bool AllowFallback, bool SkipCheck, string[] SearchDirectories)
{
public override string ToString()
{
Expand All @@ -301,14 +253,61 @@ public override string ToString()
$"- PreferredAvxLevel: {avxLevelString}\n" +
$"- AllowFallback: {AllowFallback}\n" +
$"- SkipCheck: {SkipCheck}\n" +
$"- Logging: {Logging}\n" +
$"- LogLevel: {LogLevel}\n" +
$"- SearchDirectories and Priorities: {searchDirectoriesString}";
}
}
}
#endif

public sealed partial class NativeLibraryConfig
{
/// <summary>
/// Get the config instance
/// </summary>
public static NativeLibraryConfig Instance { get; } = new();

/// <summary>
/// Check if the native library has already been loaded. Configuration cannot be modified if this is true.
/// </summary>
public static bool LibraryHasLoaded { get; internal set; }

internal NativeLogConfig.LLamaLogCallback? LogCallback;

private static void ThrowIfLoaded()
{
if (LibraryHasLoaded)
throw new InvalidOperationException("NativeLibraryConfig must be configured before using **any** other LLamaSharp methods!");
}

/// <summary>
/// Set the log callback that will be used for all llama.cpp log messages
/// </summary>
/// <param name="callback"></param>
/// <exception cref="NotImplementedException"></exception>
public NativeLibraryConfig WithLogCallback(NativeLogConfig.LLamaLogCallback? callback)
{
ThrowIfLoaded();

LogCallback = callback;
return this;
}

/// <summary>
/// Set the log callback that will be used for all llama.cpp log messages
/// </summary>
/// <param name="logger"></param>
/// <exception cref="NotImplementedException"></exception>
public NativeLibraryConfig WithLogCallback(ILogger? logger)
{
ThrowIfLoaded();

// Redirect to llama_log_set. This will wrap the logger in a delegate and bind that as the log callback instead.
NativeLogConfig.llama_log_set(logger);

return this;
}
}

internal enum LibraryName
{
Llama,
Expand Down
Loading
Loading