2023-08-25 08:56:48 +00:00
|
|
|
import pytest
|
2023-04-05 07:23:15 +00:00
|
|
|
import llama_cpp
|
|
|
|
|
2023-08-24 05:01:20 +00:00
|
|
|
MODEL = "./vendor/llama.cpp/models/ggml-vocab-llama.gguf"
|
2023-04-05 07:23:15 +00:00
|
|
|
|
|
|
|
|
2023-08-25 08:56:48 +00:00
|
|
|
def test_llama_cpp_tokenization():
|
|
|
|
llama = llama_cpp.Llama(model_path=MODEL, vocab_only=True, verbose=False)
|
2023-04-05 07:23:15 +00:00
|
|
|
|
|
|
|
assert llama
|
|
|
|
assert llama.ctx is not None
|
|
|
|
|
|
|
|
text = b"Hello World"
|
|
|
|
|
2023-08-25 08:56:48 +00:00
|
|
|
tokens = llama.tokenize(text)
|
|
|
|
assert tokens[0] == llama.token_bos()
|
2023-08-27 16:59:20 +00:00
|
|
|
assert tokens == [1, 15043, 2787]
|
2023-08-25 08:56:48 +00:00
|
|
|
detokenized = llama.detokenize(tokens)
|
|
|
|
assert detokenized == text
|
|
|
|
|
|
|
|
tokens = llama.tokenize(text, add_bos=False)
|
|
|
|
assert tokens[0] != llama.token_bos()
|
2023-08-27 16:59:20 +00:00
|
|
|
assert tokens == [15043, 2787]
|
2023-08-25 08:56:48 +00:00
|
|
|
|
|
|
|
detokenized = llama.detokenize(tokens)
|
2023-08-27 16:59:20 +00:00
|
|
|
assert detokenized != text
|
2023-04-05 07:23:15 +00:00
|
|
|
|
|
|
|
|
2023-08-25 08:56:48 +00:00
|
|
|
@pytest.mark.skip(reason="bug in tokenization where leading space is always inserted even if not after eos")
|
2023-04-05 07:23:15 +00:00
|
|
|
def test_llama_patch(monkeypatch):
|
|
|
|
llama = llama_cpp.Llama(model_path=MODEL, vocab_only=True)
|
2023-05-19 15:59:33 +00:00
|
|
|
n_vocab = llama_cpp.llama_n_vocab(llama.ctx)
|
2023-04-05 07:23:15 +00:00
|
|
|
|
|
|
|
## Set up mock function
|
|
|
|
def mock_eval(*args, **kwargs):
|
|
|
|
return 0
|
2023-05-02 02:38:46 +00:00
|
|
|
|
2023-05-01 19:46:03 +00:00
|
|
|
def mock_get_logits(*args, **kwargs):
|
2023-05-02 02:38:46 +00:00
|
|
|
return (llama_cpp.c_float * n_vocab)(
|
|
|
|
*[llama_cpp.c_float(0) for _ in range(n_vocab)]
|
|
|
|
)
|
2023-04-05 07:23:15 +00:00
|
|
|
|
|
|
|
monkeypatch.setattr("llama_cpp.llama_cpp.llama_eval", mock_eval)
|
2023-05-01 19:46:03 +00:00
|
|
|
monkeypatch.setattr("llama_cpp.llama_cpp.llama_get_logits", mock_get_logits)
|
2023-04-05 07:23:15 +00:00
|
|
|
|
|
|
|
output_text = " jumps over the lazy dog."
|
2023-04-29 10:19:22 +00:00
|
|
|
output_tokens = llama.tokenize(output_text.encode("utf-8"))
|
2023-04-05 07:23:15 +00:00
|
|
|
token_eos = llama.token_eos()
|
|
|
|
n = 0
|
|
|
|
|
|
|
|
def mock_sample(*args, **kwargs):
|
|
|
|
nonlocal n
|
|
|
|
if n < len(output_tokens):
|
|
|
|
n += 1
|
|
|
|
return output_tokens[n - 1]
|
|
|
|
else:
|
|
|
|
return token_eos
|
|
|
|
|
2023-05-01 19:46:03 +00:00
|
|
|
monkeypatch.setattr("llama_cpp.llama_cpp.llama_sample_token", mock_sample)
|
2023-04-05 07:23:15 +00:00
|
|
|
|
|
|
|
text = "The quick brown fox"
|
|
|
|
|
|
|
|
## Test basic completion until eos
|
|
|
|
n = 0 # reset
|
|
|
|
completion = llama.create_completion(text, max_tokens=20)
|
|
|
|
assert completion["choices"][0]["text"] == output_text
|
|
|
|
assert completion["choices"][0]["finish_reason"] == "stop"
|
|
|
|
|
|
|
|
## Test streaming completion until eos
|
|
|
|
n = 0 # reset
|
|
|
|
chunks = llama.create_completion(text, max_tokens=20, stream=True)
|
|
|
|
assert "".join(chunk["choices"][0]["text"] for chunk in chunks) == output_text
|
|
|
|
assert completion["choices"][0]["finish_reason"] == "stop"
|
|
|
|
|
|
|
|
## Test basic completion until stop sequence
|
|
|
|
n = 0 # reset
|
|
|
|
completion = llama.create_completion(text, max_tokens=20, stop=["lazy"])
|
|
|
|
assert completion["choices"][0]["text"] == " jumps over the "
|
|
|
|
assert completion["choices"][0]["finish_reason"] == "stop"
|
|
|
|
|
|
|
|
## Test streaming completion until stop sequence
|
|
|
|
n = 0 # reset
|
|
|
|
chunks = llama.create_completion(text, max_tokens=20, stream=True, stop=["lazy"])
|
|
|
|
assert (
|
|
|
|
"".join(chunk["choices"][0]["text"] for chunk in chunks) == " jumps over the "
|
|
|
|
)
|
|
|
|
assert completion["choices"][0]["finish_reason"] == "stop"
|
|
|
|
|
|
|
|
## Test basic completion until length
|
|
|
|
n = 0 # reset
|
|
|
|
completion = llama.create_completion(text, max_tokens=2)
|
|
|
|
assert completion["choices"][0]["text"] == " j"
|
|
|
|
assert completion["choices"][0]["finish_reason"] == "length"
|
|
|
|
|
|
|
|
## Test streaming completion until length
|
|
|
|
n = 0 # reset
|
|
|
|
chunks = llama.create_completion(text, max_tokens=2, stream=True)
|
|
|
|
assert "".join(chunk["choices"][0]["text"] for chunk in chunks) == " j"
|
|
|
|
assert completion["choices"][0]["finish_reason"] == "length"
|
2023-04-05 10:52:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_llama_pickle():
|
|
|
|
import pickle
|
|
|
|
import tempfile
|
2023-05-02 02:38:46 +00:00
|
|
|
|
2023-04-05 10:52:17 +00:00
|
|
|
fp = tempfile.TemporaryFile()
|
|
|
|
llama = llama_cpp.Llama(model_path=MODEL, vocab_only=True)
|
|
|
|
pickle.dump(llama, fp)
|
|
|
|
fp.seek(0)
|
|
|
|
llama = pickle.load(fp)
|
|
|
|
|
|
|
|
assert llama
|
|
|
|
assert llama.ctx is not None
|
|
|
|
|
|
|
|
text = b"Hello World"
|
|
|
|
|
2023-04-29 10:19:22 +00:00
|
|
|
assert llama.detokenize(llama.tokenize(text)) == text
|
|
|
|
|
2023-05-02 02:38:46 +00:00
|
|
|
|
2023-04-29 10:19:22 +00:00
|
|
|
def test_utf8(monkeypatch):
|
|
|
|
llama = llama_cpp.Llama(model_path=MODEL, vocab_only=True)
|
2023-05-19 15:59:33 +00:00
|
|
|
n_vocab = llama_cpp.llama_n_vocab(llama.ctx)
|
2023-04-29 10:19:22 +00:00
|
|
|
|
|
|
|
## Set up mock function
|
|
|
|
def mock_eval(*args, **kwargs):
|
|
|
|
return 0
|
|
|
|
|
2023-05-01 19:46:03 +00:00
|
|
|
def mock_get_logits(*args, **kwargs):
|
2023-05-02 02:38:46 +00:00
|
|
|
return (llama_cpp.c_float * n_vocab)(
|
|
|
|
*[llama_cpp.c_float(0) for _ in range(n_vocab)]
|
|
|
|
)
|
2023-05-01 19:46:03 +00:00
|
|
|
|
2023-04-29 10:19:22 +00:00
|
|
|
monkeypatch.setattr("llama_cpp.llama_cpp.llama_eval", mock_eval)
|
2023-05-01 19:46:03 +00:00
|
|
|
monkeypatch.setattr("llama_cpp.llama_cpp.llama_get_logits", mock_get_logits)
|
2023-04-29 10:19:22 +00:00
|
|
|
|
|
|
|
output_text = "😀"
|
|
|
|
output_tokens = llama.tokenize(output_text.encode("utf-8"))
|
|
|
|
token_eos = llama.token_eos()
|
|
|
|
n = 0
|
|
|
|
|
|
|
|
def mock_sample(*args, **kwargs):
|
|
|
|
nonlocal n
|
|
|
|
if n < len(output_tokens):
|
|
|
|
n += 1
|
|
|
|
return output_tokens[n - 1]
|
|
|
|
else:
|
|
|
|
return token_eos
|
|
|
|
|
2023-05-01 19:46:03 +00:00
|
|
|
monkeypatch.setattr("llama_cpp.llama_cpp.llama_sample_token", mock_sample)
|
2023-04-29 10:19:22 +00:00
|
|
|
|
|
|
|
## Test basic completion with utf8 multibyte
|
|
|
|
n = 0 # reset
|
|
|
|
completion = llama.create_completion("", max_tokens=4)
|
|
|
|
assert completion["choices"][0]["text"] == output_text
|
|
|
|
|
|
|
|
## Test basic completion with incomplete utf8 multibyte
|
|
|
|
n = 0 # reset
|
|
|
|
completion = llama.create_completion("", max_tokens=1)
|
|
|
|
assert completion["choices"][0]["text"] == ""
|
2023-04-29 06:26:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_llama_server():
|
|
|
|
from fastapi.testclient import TestClient
|
2023-05-02 02:38:46 +00:00
|
|
|
from llama_cpp.server.app import create_app, Settings
|
|
|
|
|
2023-05-02 02:41:54 +00:00
|
|
|
settings = Settings(
|
|
|
|
model=MODEL,
|
|
|
|
vocab_only=True,
|
|
|
|
)
|
2023-05-02 02:38:46 +00:00
|
|
|
app = create_app(settings)
|
2023-04-29 06:26:07 +00:00
|
|
|
client = TestClient(app)
|
|
|
|
response = client.get("/v1/models")
|
|
|
|
assert response.json() == {
|
|
|
|
"object": "list",
|
|
|
|
"data": [
|
|
|
|
{
|
|
|
|
"id": MODEL,
|
|
|
|
"object": "model",
|
|
|
|
"owned_by": "me",
|
|
|
|
"permissions": [],
|
|
|
|
}
|
|
|
|
],
|
|
|
|
}
|