mirror of https://github.com/razor-ai/soup.git
175 lines
5.6 KiB
Python
175 lines
5.6 KiB
Python
"""Tests for data/formats.py — reverse conversion + edge cases."""
|
|
|
|
import pytest
|
|
|
|
from soup_cli.data.formats import (
|
|
detect_format,
|
|
format_to_messages,
|
|
messages_to_format,
|
|
)
|
|
|
|
# --- detect_format edge cases ---
|
|
|
|
def test_detect_format_empty():
|
|
"""Empty dataset should raise ValueError."""
|
|
with pytest.raises(ValueError, match="Empty"):
|
|
detect_format([])
|
|
|
|
|
|
def test_detect_format_unknown_keys():
|
|
"""Unrecognized keys should raise ValueError."""
|
|
with pytest.raises(ValueError, match="Cannot detect"):
|
|
detect_format([{"foo": "bar", "baz": 123}])
|
|
|
|
|
|
# --- format_to_messages edge cases ---
|
|
|
|
def test_format_to_messages_unknown_format():
|
|
"""Unknown format should raise ValueError."""
|
|
with pytest.raises(ValueError, match="Unknown format"):
|
|
format_to_messages({"a": "b"}, "unknown_fmt")
|
|
|
|
|
|
def test_format_to_messages_bad_row():
|
|
"""Missing required keys should return None (gracefully handled)."""
|
|
result = format_to_messages({"wrong": "keys"}, "alpaca")
|
|
assert result is None
|
|
|
|
|
|
def test_convert_chatml_passthrough():
|
|
"""ChatML format should pass through messages directly."""
|
|
row = {"messages": [{"role": "user", "content": "hi"}, {"role": "assistant", "content": "yo"}]}
|
|
result = format_to_messages(row, "chatml")
|
|
assert result["messages"] == row["messages"]
|
|
|
|
|
|
def test_convert_alpaca_with_system():
|
|
"""Alpaca row with system field should include system message."""
|
|
row = {
|
|
"instruction": "Translate",
|
|
"input": "",
|
|
"output": "Done",
|
|
"system": "You are a translator.",
|
|
}
|
|
result = format_to_messages(row, "alpaca")
|
|
assert len(result["messages"]) == 3
|
|
assert result["messages"][0]["role"] == "system"
|
|
assert result["messages"][0]["content"] == "You are a translator."
|
|
|
|
|
|
def test_convert_sharegpt_with_system():
|
|
"""ShareGPT with system turn should map correctly."""
|
|
row = {
|
|
"conversations": [
|
|
{"from": "system", "value": "Be helpful."},
|
|
{"from": "human", "value": "Hello"},
|
|
{"from": "gpt", "value": "Hi there!"},
|
|
]
|
|
}
|
|
result = format_to_messages(row, "sharegpt")
|
|
assert result["messages"][0]["role"] == "system"
|
|
assert result["messages"][1]["role"] == "user"
|
|
assert result["messages"][2]["role"] == "assistant"
|
|
|
|
|
|
# --- reverse conversion: messages_to_format ---
|
|
|
|
def test_messages_to_alpaca():
|
|
"""Convert messages back to alpaca format."""
|
|
row = {
|
|
"messages": [
|
|
{"role": "user", "content": "What is Python?"},
|
|
{"role": "assistant", "content": "A programming language."},
|
|
]
|
|
}
|
|
result = messages_to_format(row, "alpaca")
|
|
assert result is not None
|
|
assert result["instruction"] == "What is Python?"
|
|
assert result["output"] == "A programming language."
|
|
assert result["input"] == ""
|
|
|
|
|
|
def test_messages_to_alpaca_with_system():
|
|
"""Convert messages with system to alpaca format."""
|
|
row = {
|
|
"messages": [
|
|
{"role": "system", "content": "Be brief."},
|
|
{"role": "user", "content": "What is AI?"},
|
|
{"role": "assistant", "content": "Artificial Intelligence."},
|
|
]
|
|
}
|
|
result = messages_to_format(row, "alpaca")
|
|
assert result["system"] == "Be brief."
|
|
assert result["instruction"] == "What is AI?"
|
|
assert result["output"] == "Artificial Intelligence."
|
|
|
|
|
|
def test_messages_to_sharegpt():
|
|
"""Convert messages to sharegpt format."""
|
|
row = {
|
|
"messages": [
|
|
{"role": "user", "content": "Hello"},
|
|
{"role": "assistant", "content": "Hi!"},
|
|
]
|
|
}
|
|
result = messages_to_format(row, "sharegpt")
|
|
assert result is not None
|
|
convs = result["conversations"]
|
|
assert len(convs) == 2
|
|
assert convs[0]["from"] == "human"
|
|
assert convs[0]["value"] == "Hello"
|
|
assert convs[1]["from"] == "gpt"
|
|
assert convs[1]["value"] == "Hi!"
|
|
|
|
|
|
def test_messages_to_chatml():
|
|
"""Convert to chatml should return the row as-is."""
|
|
row = {
|
|
"messages": [
|
|
{"role": "user", "content": "Test"},
|
|
{"role": "assistant", "content": "OK"},
|
|
]
|
|
}
|
|
result = messages_to_format(row, "chatml")
|
|
assert result is row # should be the same object (passthrough)
|
|
|
|
|
|
def test_messages_to_unknown_format():
|
|
"""Unknown target format should raise ValueError."""
|
|
row = {"messages": [{"role": "user", "content": "hi"}]}
|
|
with pytest.raises(ValueError, match="Cannot convert"):
|
|
messages_to_format(row, "unknown")
|
|
|
|
|
|
def test_messages_to_format_bad_row():
|
|
"""Broken row should return None."""
|
|
result = messages_to_format({"bad": "data"}, "alpaca")
|
|
assert result is None
|
|
|
|
|
|
# --- round-trip tests ---
|
|
|
|
def test_roundtrip_alpaca():
|
|
"""alpaca → messages → alpaca should preserve data."""
|
|
original = {"instruction": "Explain ML", "input": "", "output": "ML is..."}
|
|
messages = format_to_messages(original, "alpaca")
|
|
back = messages_to_format(messages, "alpaca")
|
|
assert back["instruction"] == original["instruction"]
|
|
assert back["output"] == original["output"]
|
|
|
|
|
|
def test_roundtrip_sharegpt():
|
|
"""sharegpt → messages → sharegpt should preserve data."""
|
|
original = {
|
|
"conversations": [
|
|
{"from": "human", "value": "Hi"},
|
|
{"from": "gpt", "value": "Hello!"},
|
|
]
|
|
}
|
|
messages = format_to_messages(original, "sharegpt")
|
|
back = messages_to_format(messages, "sharegpt")
|
|
assert back["conversations"][0]["from"] == "human"
|
|
assert back["conversations"][0]["value"] == "Hi"
|
|
assert back["conversations"][1]["from"] == "gpt"
|
|
assert back["conversations"][1]["value"] == "Hello!"
|