You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

NativeLibraryConfig.cs 9.2 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. namespace LLama.Native
  5. {
  6. #if NET6_0_OR_GREATER
  7. /// <summary>
  8. /// Allows configuration of the native llama.cpp libraries to load and use.
  9. /// All configuration must be done before using **any** other LLamaSharp methods!
  10. /// </summary>
  11. public sealed class NativeLibraryConfig
  12. {
  13. private static readonly Lazy<NativeLibraryConfig> _instance = new(() => new NativeLibraryConfig());
  14. /// <summary>
  15. /// Get the config instance
  16. /// </summary>
  17. public static NativeLibraryConfig Instance => _instance.Value;
  18. /// <summary>
  19. /// Whether there's already a config for native library.
  20. /// </summary>
  21. public static bool LibraryHasLoaded { get; internal set; } = false;
  22. private string _libraryPath = string.Empty;
  23. private bool _useCuda = true;
  24. private AvxLevel _avxLevel;
  25. private bool _allowFallback = true;
  26. private bool _skipCheck = false;
  27. private bool _logging = false;
  28. /// <summary>
  29. /// search directory -> priority level, 0 is the lowest.
  30. /// </summary>
  31. private List<string> _searchDirectories = new List<string>();
  32. private static void ThrowIfLoaded()
  33. {
  34. if (LibraryHasLoaded)
  35. throw new InvalidOperationException("NativeLibraryConfig must be configured before using **any** other LLamaSharp methods!");
  36. }
  37. /// <summary>
  38. /// Load a specified native library as backend for LLamaSharp.
  39. /// When this method is called, all the other configurations will be ignored.
  40. /// </summary>
  41. /// <param name="libraryPath"></param>
  42. /// <exception cref="InvalidOperationException">Thrown if `LibraryHasLoaded` is true.</exception>
  43. public NativeLibraryConfig WithLibrary(string libraryPath)
  44. {
  45. ThrowIfLoaded();
  46. _libraryPath = libraryPath;
  47. return this;
  48. }
  49. /// <summary>
  50. /// Configure whether to use cuda backend if possible.
  51. /// </summary>
  52. /// <param name="enable"></param>
  53. /// <returns></returns>
  54. /// <exception cref="InvalidOperationException">Thrown if `LibraryHasLoaded` is true.</exception>
  55. public NativeLibraryConfig WithCuda(bool enable = true)
  56. {
  57. ThrowIfLoaded();
  58. _useCuda = enable;
  59. return this;
  60. }
  61. /// <summary>
  62. /// Configure the prefferred avx support level of the backend.
  63. /// </summary>
  64. /// <param name="level"></param>
  65. /// <returns></returns>
  66. /// <exception cref="InvalidOperationException">Thrown if `LibraryHasLoaded` is true.</exception>
  67. public NativeLibraryConfig WithAvx(AvxLevel level)
  68. {
  69. ThrowIfLoaded();
  70. _avxLevel = level;
  71. return this;
  72. }
  73. /// <summary>
  74. /// Configure whether to allow fallback when there's no match for preferred settings.
  75. /// </summary>
  76. /// <param name="enable"></param>
  77. /// <returns></returns>
  78. /// <exception cref="InvalidOperationException">Thrown if `LibraryHasLoaded` is true.</exception>
  79. public NativeLibraryConfig WithAutoFallback(bool enable = true)
  80. {
  81. ThrowIfLoaded();
  82. _allowFallback = enable;
  83. return this;
  84. }
  85. /// <summary>
  86. /// Whether to skip the check when you don't allow fallback. This option
  87. /// may be useful under some complex conditions. For example, you're sure
  88. /// you have your cublas configured but LLamaSharp take it as invalid by mistake.
  89. /// </summary>
  90. /// <param name="enable"></param>
  91. /// <returns></returns>
  92. /// <exception cref="InvalidOperationException">Thrown if `LibraryHasLoaded` is true.</exception>
  93. public NativeLibraryConfig SkipCheck(bool enable = true)
  94. {
  95. ThrowIfLoaded();
  96. _skipCheck = enable;
  97. return this;
  98. }
  99. /// <summary>
  100. /// Whether to output the logs to console when loading the native library with your configuration.
  101. /// </summary>
  102. /// <param name="enable"></param>
  103. /// <returns></returns>
  104. /// <exception cref="InvalidOperationException">Thrown if `LibraryHasLoaded` is true.</exception>
  105. public NativeLibraryConfig WithLogs(bool enable = true)
  106. {
  107. ThrowIfLoaded();
  108. _logging = enable;
  109. return this;
  110. }
  111. /// <summary>
  112. /// Add self-defined search directories. Note that the file stucture of the added
  113. /// directories must be the same as the default directory. Besides, the directory
  114. /// won't be used recursively.
  115. /// </summary>
  116. /// <param name="directories"></param>
  117. /// <returns></returns>
  118. public NativeLibraryConfig WithSearchDirectories(IEnumerable<string> directories)
  119. {
  120. ThrowIfLoaded();
  121. _searchDirectories.AddRange(directories);
  122. return this;
  123. }
  124. /// <summary>
  125. /// Add self-defined search directories. Note that the file stucture of the added
  126. /// directories must be the same as the default directory. Besides, the directory
  127. /// won't be used recursively.
  128. /// </summary>
  129. /// <param name="directory"></param>
  130. /// <returns></returns>
  131. public NativeLibraryConfig WithSearchDirectory(string directory)
  132. {
  133. ThrowIfLoaded();
  134. _searchDirectories.Add(directory);
  135. return this;
  136. }
  137. internal static Description CheckAndGatherDescription()
  138. {
  139. if (Instance._allowFallback && Instance._skipCheck)
  140. {
  141. throw new ArgumentException("Cannot skip the check when fallback is allowed.");
  142. }
  143. return new Description(
  144. Instance._libraryPath,
  145. Instance._useCuda,
  146. Instance._avxLevel,
  147. Instance._allowFallback,
  148. Instance._skipCheck,
  149. Instance._logging,
  150. Instance._searchDirectories.Concat(new string[] { "./" }).ToArray());
  151. }
  152. internal static string AvxLevelToString(AvxLevel level)
  153. {
  154. return level switch
  155. {
  156. AvxLevel.None => string.Empty,
  157. AvxLevel.Avx => "avx",
  158. AvxLevel.Avx2 => "avx2",
  159. AvxLevel.Avx512 => "avx512",
  160. _ => throw new ArgumentException($"Unknown AvxLevel '{level}'")
  161. };
  162. }
  163. /// <summary>
  164. /// Private constructor prevents new instances of this class being created
  165. /// </summary>
  166. private NativeLibraryConfig()
  167. {
  168. // Automatically detect the highest supported AVX level
  169. if (System.Runtime.Intrinsics.X86.Avx.IsSupported)
  170. _avxLevel = AvxLevel.Avx;
  171. if (System.Runtime.Intrinsics.X86.Avx2.IsSupported)
  172. _avxLevel = AvxLevel.Avx2;
  173. #if NET8_0_OR_GREATER
  174. if (System.Runtime.Intrinsics.X86.Avx512F.IsSupported)
  175. _avxLevel = AvxLevel.Avx512;
  176. #endif
  177. }
  178. /// <summary>
  179. /// Avx support configuration
  180. /// </summary>
  181. public enum AvxLevel
  182. {
  183. /// <summary>
  184. /// No AVX
  185. /// </summary>
  186. None,
  187. /// <summary>
  188. /// Advanced Vector Extensions (supported by most processors after 2011)
  189. /// </summary>
  190. Avx,
  191. /// <summary>
  192. /// AVX2 (supported by most processors after 2013)
  193. /// </summary>
  194. Avx2,
  195. /// <summary>
  196. /// AVX512 (supported by some processors after 2016, not widely supported)
  197. /// </summary>
  198. Avx512,
  199. }
  200. internal record Description(string Path, bool UseCuda, AvxLevel AvxLevel, bool AllowFallback, bool SkipCheck, bool Logging, string[] SearchDirectories)
  201. {
  202. public override string ToString()
  203. {
  204. string avxLevelString = AvxLevel switch
  205. {
  206. AvxLevel.None => "NoAVX",
  207. AvxLevel.Avx => "AVX",
  208. AvxLevel.Avx2 => "AVX2",
  209. AvxLevel.Avx512 => "AVX512",
  210. _ => "Unknown"
  211. };
  212. string searchDirectoriesString = "{ " + string.Join(", ", SearchDirectories) + " }";
  213. return $"NativeLibraryConfig Description:\n" +
  214. $"- Path: {Path}\n" +
  215. $"- PreferCuda: {UseCuda}\n" +
  216. $"- PreferredAvxLevel: {avxLevelString}\n" +
  217. $"- AllowFallback: {AllowFallback}\n" +
  218. $"- SkipCheck: {SkipCheck}\n" +
  219. $"- Logging: {Logging}\n" +
  220. $"- SearchDirectories and Priorities: {searchDirectoriesString}";
  221. }
  222. }
  223. }
  224. #endif
  225. }