1 module llama.adapter;
2 
3 import llama.llama;
4 import llama.model : LlamaModel;
5 import llama.ctx  : LlamaContext;
6 
7 /++
8 A LoRA adapter loaded from a GGUF file.
9 Freed automatically on destruction; you may also call `free()` early.
10 The associated model must remain alive for the adapter's lifetime.
11 +/
12 struct LlamaAdapterLora
13 {
14     private llama_adapter_lora* _adapter;
15 
16     @disable this();
17     @disable this(this);
18 
19     private this(llama_adapter_lora* a) @nogc nothrow { _adapter = a; }
20 
21     ~this() @nogc nothrow
22     {
23         if (_adapter) { llama_adapter_lora_free(_adapter); _adapter = null; }
24     }
25 
26     bool opCast(T : bool)() @nogc nothrow { return _adapter !is null; }
27 
28     /// Raw C pointer.
29     @property llama_adapter_lora* ptr() @trusted @nogc nothrow { return _adapter; }
30 
31     /++ Number of GGUF metadata entries. +/
32     @property int metaCount() @nogc nothrow { return llama_adapter_meta_count(_adapter); }
33 
34     /// Metadata value by key. Returns `""` on failure.
35     string metaVal(string key) @trusted
36     {
37         import std.string : toStringz;
38         char[4096] buf;
39         int n = llama_adapter_meta_val_str(_adapter, key.toStringz, buf.ptr, buf.length);
40         return n >= 0 ? buf[0 .. n].idup : "";
41     }
42 
43     /// Metadata key name at `index`. Returns `""` on failure.
44     string metaKeyAt(int index) @trusted
45     {
46         char[512] buf;
47         int n = llama_adapter_meta_key_by_index(_adapter, index, buf.ptr, buf.length);
48         return n >= 0 ? buf[0 .. n].idup : "";
49     }
50 
51     /// Metadata value at `index`. Returns `""` on failure.
52     string metaValAt(int index) @trusted
53     {
54         char[4096] buf;
55         int n = llama_adapter_meta_val_str_by_index(_adapter, index, buf.ptr, buf.length);
56         return n >= 0 ? buf[0 .. n].idup : "";
57     }
58 
59     /// Free the adapter early (safe to call multiple times).
60     void free() @nogc nothrow
61     {
62         if (_adapter) { llama_adapter_lora_free(_adapter); _adapter = null; }
63     }
64 }
65 
66 /// Load a LoRA adapter from a GGUF file. Check `if (adapter)` after loading.
67 LlamaAdapterLora loadAdapterLora(ref LlamaModel model, string path)
68 {
69     import std.string : toStringz;
70     return LlamaAdapterLora(llama_adapter_lora_init(model.ptr, path.toStringz));
71 }
72 
73 /++
74 Apply a set of LoRA adapters to a context.
75 `adapters` is a slice of raw C pointers (use `adapter.ptr` on each `LlamaAdapterLora`).
76 `scales` must have the same length as `adapters`.
77 Pass an empty slice to clear all adapters.
78 Returns 0 on success.
79 +/
80 int setAdaptersLora(ref LlamaContext ctx,
81                     llama_adapter_lora*[] adapters,
82                     float[] scales) @trusted @nogc nothrow
83 in (adapters.length == scales.length)
84 {
85     return llama_set_adapters_lora(ctx.ptr, adapters.ptr, adapters.length, scales.ptr);
86 }