161 lines
5.9 KiB
Nix
161 lines
5.9 KiB
Nix
|
{ self
|
||
|
, lib
|
||
|
, testers
|
||
|
, fetchzip
|
||
|
, fetchurl
|
||
|
, writers
|
||
|
, symlinkJoin
|
||
|
, linkFarmFromDrvs
|
||
|
, jq
|
||
|
}:
|
||
|
{
|
||
|
version = testers.testVersion {
|
||
|
package = self;
|
||
|
version = "v" + self.version;
|
||
|
};
|
||
|
|
||
|
health =
|
||
|
let
|
||
|
port = "8080";
|
||
|
in
|
||
|
testers.runNixOSTest {
|
||
|
name = self.name + "-health";
|
||
|
nodes.machine = {
|
||
|
systemd.services.local-ai = {
|
||
|
wantedBy = [ "multi-user.target" ];
|
||
|
serviceConfig.ExecStart = "${self}/bin/local-ai --debug --localai-config-dir . --address :${port}";
|
||
|
};
|
||
|
};
|
||
|
testScript = ''
|
||
|
machine.wait_for_open_port(${port})
|
||
|
machine.succeed("curl -f http://localhost:${port}/readyz")
|
||
|
'';
|
||
|
};
|
||
|
|
||
|
# https://localai.io/docs/getting-started/manual/
|
||
|
llama =
|
||
|
let
|
||
|
port = "8080";
|
||
|
gguf = fetchurl {
|
||
|
url = "https://huggingface.co/TheBloke/Luna-AI-Llama2-Uncensored-GGUF/resolve/main/luna-ai-llama2-uncensored.Q4_K_M.gguf";
|
||
|
sha256 = "6a9dc401c84f0d48996eaa405174999c3a33bf12c2bfd8ea4a1e98f376de1f15";
|
||
|
};
|
||
|
models = linkFarmFromDrvs "models" [
|
||
|
gguf
|
||
|
];
|
||
|
in
|
||
|
testers.runNixOSTest {
|
||
|
name = self.name + "-llama";
|
||
|
nodes.machine =
|
||
|
let
|
||
|
cores = 4;
|
||
|
in
|
||
|
{
|
||
|
virtualisation = {
|
||
|
inherit cores;
|
||
|
memorySize = 8192;
|
||
|
};
|
||
|
systemd.services.local-ai = {
|
||
|
wantedBy = [ "multi-user.target" ];
|
||
|
serviceConfig.ExecStart = "${self}/bin/local-ai --debug --threads ${toString cores} --models-path ${models} --localai-config-dir . --address :${port}";
|
||
|
};
|
||
|
};
|
||
|
testScript =
|
||
|
let
|
||
|
# https://localai.io/features/text-generation/#chat-completions
|
||
|
request-chat-completions = {
|
||
|
model = gguf.name;
|
||
|
messages = [{ role = "user"; content = "Say this is a test!"; }];
|
||
|
temperature = 0.7;
|
||
|
};
|
||
|
# https://localai.io/features/text-generation/#edit-completions
|
||
|
request-edit-completions = {
|
||
|
model = gguf.name;
|
||
|
instruction = "rephrase";
|
||
|
input = "Black cat jumped out of the window";
|
||
|
temperature = 0.7;
|
||
|
};
|
||
|
# https://localai.io/features/text-generation/#completions
|
||
|
request-completions = {
|
||
|
model = gguf.name;
|
||
|
prompt = "A long time ago in a galaxy far, far away";
|
||
|
temperature = 0.7;
|
||
|
};
|
||
|
in
|
||
|
''
|
||
|
machine.wait_for_open_port(${port})
|
||
|
machine.succeed("curl -f http://localhost:${port}/readyz")
|
||
|
machine.succeed("curl -f http://localhost:${port}/v1/models --output models.json")
|
||
|
machine.succeed("${jq}/bin/jq --exit-status 'debug | .data[].id == \"${gguf.name}\"' models.json")
|
||
|
machine.succeed("curl -f http://localhost:${port}/v1/chat/completions --json @${writers.writeJSON "request-chat-completions.json" request-chat-completions} --output chat-completions.json")
|
||
|
machine.succeed("${jq}/bin/jq --exit-status 'debug | .object == \"chat.completion\"' chat-completions.json")
|
||
|
machine.succeed("curl -f http://localhost:${port}/v1/edits --json @${writers.writeJSON "request-edit-completions.json" request-edit-completions} --output edit-completions.json")
|
||
|
machine.succeed("${jq}/bin/jq --exit-status 'debug | .object == \"edit\"' edit-completions.json")
|
||
|
machine.succeed("curl -f http://localhost:${port}/v1/completions --json @${writers.writeJSON "request-completions.json" request-completions} --output completions.json")
|
||
|
machine.succeed("${jq}/bin/jq --exit-status 'debug | .object ==\"text_completion\"' completions.json")
|
||
|
'';
|
||
|
};
|
||
|
|
||
|
} // lib.optionalAttrs self.features.with_tts {
|
||
|
# https://localai.io/features/text-to-audio/#piper
|
||
|
tts =
|
||
|
let
|
||
|
port = "8080";
|
||
|
voice-en-us = fetchzip {
|
||
|
url = "https://github.com/rhasspy/piper/releases/download/v0.0.2/voice-en-us-danny-low.tar.gz";
|
||
|
hash = "sha256-5wf+6H5HeQY0qgdqnAG1vSqtjIFM9lXH53OgouuPm0M=";
|
||
|
stripRoot = false;
|
||
|
};
|
||
|
ggml-tiny-en = fetchurl {
|
||
|
url = "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.en-q5_1.bin";
|
||
|
hash = "sha256-x3xXZvHO8JtrfUfyG1Rsvd1BV4hrO11tT3CekeZsfCs=";
|
||
|
};
|
||
|
whisper-en = {
|
||
|
name = "whisper-en";
|
||
|
backend = "whisper";
|
||
|
parameters.model = ggml-tiny-en.name;
|
||
|
};
|
||
|
models = symlinkJoin {
|
||
|
name = "models";
|
||
|
paths = [
|
||
|
voice-en-us
|
||
|
(linkFarmFromDrvs "whisper-en" [
|
||
|
(writers.writeYAML "whisper-en.yaml" whisper-en)
|
||
|
ggml-tiny-en
|
||
|
])
|
||
|
];
|
||
|
};
|
||
|
in
|
||
|
testers.runNixOSTest {
|
||
|
name = self.name + "-tts";
|
||
|
nodes.machine =
|
||
|
let
|
||
|
cores = 2;
|
||
|
in
|
||
|
{
|
||
|
virtualisation = {
|
||
|
inherit cores;
|
||
|
};
|
||
|
systemd.services.local-ai = {
|
||
|
wantedBy = [ "multi-user.target" ];
|
||
|
serviceConfig.ExecStart = "${self}/bin/local-ai --debug --threads ${toString cores} --models-path ${models} --localai-config-dir . --address :${port}";
|
||
|
};
|
||
|
};
|
||
|
testScript =
|
||
|
let
|
||
|
request = {
|
||
|
model = "en-us-danny-low.onnx";
|
||
|
backend = "piper";
|
||
|
input = "Hello, how are you?";
|
||
|
};
|
||
|
in
|
||
|
''
|
||
|
machine.wait_for_open_port(${port})
|
||
|
machine.succeed("curl -f http://localhost:${port}/readyz")
|
||
|
machine.succeed("curl -f http://localhost:${port}/tts --json @${writers.writeJSON "request.json" request} --output out.wav")
|
||
|
machine.succeed("curl -f http://localhost:${port}/v1/audio/transcriptions --header 'Content-Type: multipart/form-data' --form file=@out.wav --form model=${whisper-en.name} --output transcription.json")
|
||
|
machine.succeed("${jq}/bin/jq --exit-status 'debug | .segments | first.text == \"${request.input}\"' transcription.json")
|
||
|
'';
|
||
|
};
|
||
|
}
|