chore: import upstream snapshot with attribution
Build documentation / build (push) Failing after 0s
CI / check_code_quality (push) Has been cancelled
CI / test (deps-latest, ubuntu-latest, integration) (push) Has been cancelled
CI / test (deps-latest, ubuntu-latest, unit) (push) Has been cancelled
CI / test (deps-latest, windows-latest, integration) (push) Has been cancelled
CI / test (deps-latest, windows-latest, unit) (push) Has been cancelled
CI / test (deps-minimum, ubuntu-latest, integration) (push) Has been cancelled
CI / test (deps-minimum, ubuntu-latest, unit) (push) Has been cancelled
CI / test (deps-minimum, windows-latest, integration) (push) Has been cancelled
CI / test (deps-minimum, windows-latest, unit) (push) Has been cancelled
CI / test_py314 (deps-latest, ubuntu-latest, unit) (push) Has been cancelled
CI / test_py314 (deps-latest, windows-latest, unit) (push) Has been cancelled
CI / test_py314_future (deps-latest, ubuntu-latest, unit) (push) Has been cancelled
CI / test_py314_future (deps-latest, windows-latest, unit) (push) Has been cancelled
Secret Leaks / trufflehog (push) Has been cancelled
Build documentation / build (push) Failing after 0s
CI / check_code_quality (push) Has been cancelled
CI / test (deps-latest, ubuntu-latest, integration) (push) Has been cancelled
CI / test (deps-latest, ubuntu-latest, unit) (push) Has been cancelled
CI / test (deps-latest, windows-latest, integration) (push) Has been cancelled
CI / test (deps-latest, windows-latest, unit) (push) Has been cancelled
CI / test (deps-minimum, ubuntu-latest, integration) (push) Has been cancelled
CI / test (deps-minimum, ubuntu-latest, unit) (push) Has been cancelled
CI / test (deps-minimum, windows-latest, integration) (push) Has been cancelled
CI / test (deps-minimum, windows-latest, unit) (push) Has been cancelled
CI / test_py314 (deps-latest, ubuntu-latest, unit) (push) Has been cancelled
CI / test_py314 (deps-latest, windows-latest, unit) (push) Has been cancelled
CI / test_py314_future (deps-latest, ubuntu-latest, unit) (push) Has been cancelled
CI / test_py314_future (deps-latest, windows-latest, unit) (push) Has been cancelled
Secret Leaks / trufflehog (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,809 @@
|
||||
import json
|
||||
import textwrap
|
||||
|
||||
import pyarrow as pa
|
||||
import pytest
|
||||
|
||||
from datasets import Features, Value, load_dataset
|
||||
from datasets.builder import InvalidConfigName
|
||||
from datasets.data_files import DataFilesList
|
||||
from datasets.packaged_modules.json.json import AGENT_TRACES_FEATURES, Json, JsonConfig
|
||||
|
||||
from ..utils import require_teich
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def json_file(tmp_path):
|
||||
filename = tmp_path / "file.json"
|
||||
data = textwrap.dedent(
|
||||
"""\
|
||||
{
|
||||
"col_1": 1,
|
||||
"col_2": 2
|
||||
}
|
||||
"""
|
||||
)
|
||||
with open(filename, "w") as f:
|
||||
f.write(data)
|
||||
return str(filename)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def jsonl_file(tmp_path):
|
||||
filename = tmp_path / "file.jsonl"
|
||||
data = textwrap.dedent(
|
||||
"""\
|
||||
{"col_1": -1}
|
||||
{"col_1": 1, "col_2": 2}
|
||||
{"col_1": 10, "col_2": 20}
|
||||
"""
|
||||
)
|
||||
with open(filename, "w") as f:
|
||||
f.write(data)
|
||||
return str(filename)
|
||||
|
||||
|
||||
# ndjson format is no longer maintained (see: https://github.com/ndjson/ndjson-spec/issues/35#issuecomment-1285673417)
|
||||
@pytest.fixture
|
||||
def ndjson_file(tmp_path):
|
||||
filename = tmp_path / "file.ndjson"
|
||||
data = textwrap.dedent(
|
||||
"""\
|
||||
{"col_1": -1}
|
||||
{"col_1": 1, "col_2": 2}
|
||||
{"col_1": 10, "col_2": 20}
|
||||
"""
|
||||
)
|
||||
with open(filename, "w") as f:
|
||||
f.write(data)
|
||||
return str(filename)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def jsonl_file_utf16_encoded(tmp_path):
|
||||
filename = tmp_path / "file_utf16_encoded.jsonl"
|
||||
data = textwrap.dedent(
|
||||
"""\
|
||||
{"col_1": -1}
|
||||
{"col_1": 1, "col_2": 2}
|
||||
{"col_1": 10, "col_2": 20}
|
||||
"""
|
||||
)
|
||||
with open(filename, "w", encoding="utf-16") as f:
|
||||
f.write(data)
|
||||
return str(filename)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def json_file_with_list_of_dicts(tmp_path):
|
||||
filename = tmp_path / "file_with_list_of_dicts.json"
|
||||
data = textwrap.dedent(
|
||||
"""\
|
||||
[
|
||||
{"col_1": -1},
|
||||
{"col_1": 1, "col_2": 2},
|
||||
{"col_1": 10, "col_2": 20}
|
||||
]
|
||||
"""
|
||||
)
|
||||
with open(filename, "w") as f:
|
||||
f.write(data)
|
||||
return str(filename)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def json_file_with_list_of_strings(tmp_path):
|
||||
filename = tmp_path / "file_with_list_of_strings.json"
|
||||
data = textwrap.dedent(
|
||||
"""\
|
||||
[
|
||||
"First text.",
|
||||
"Second text.",
|
||||
"Third text."
|
||||
]
|
||||
"""
|
||||
)
|
||||
with open(filename, "w") as f:
|
||||
f.write(data)
|
||||
return str(filename)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def json_file_with_list_of_dicts_field(tmp_path):
|
||||
filename = tmp_path / "file_with_list_of_dicts_field.json"
|
||||
data = textwrap.dedent(
|
||||
"""\
|
||||
{
|
||||
"field1": 1,
|
||||
"field2": "aabb",
|
||||
"field3": [
|
||||
{"col_1": -1},
|
||||
{"col_1": 1, "col_2": 2},
|
||||
{"col_1": 10, "col_2": 20}
|
||||
]
|
||||
}
|
||||
"""
|
||||
)
|
||||
with open(filename, "w") as f:
|
||||
f.write(data)
|
||||
return str(filename)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def json_file_with_list_of_strings_field(tmp_path):
|
||||
path = tmp_path / "file.json"
|
||||
data = textwrap.dedent(
|
||||
"""\
|
||||
{
|
||||
"field1": 1,
|
||||
"field2": "aabb",
|
||||
"field3": [
|
||||
"First text.",
|
||||
"Second text.",
|
||||
"Third text."
|
||||
]
|
||||
}
|
||||
"""
|
||||
)
|
||||
with open(path, "w") as f:
|
||||
f.write(data)
|
||||
return str(path)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def json_file_with_dict_of_lists_field(tmp_path):
|
||||
path = tmp_path / "file.json"
|
||||
data = textwrap.dedent(
|
||||
"""\
|
||||
{
|
||||
"field1": 1,
|
||||
"field2": "aabb",
|
||||
"field3": {
|
||||
"col_1": [-1, 1, 10],
|
||||
"col_2": [null, 2, 20]
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
with open(path, "w") as f:
|
||||
f.write(data)
|
||||
return str(path)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def json_file_with_list_of_dicts_with_sorted_columns(tmp_path):
|
||||
path = tmp_path / "file.json"
|
||||
data = textwrap.dedent(
|
||||
"""\
|
||||
[
|
||||
{"ID": 0, "Language": "Language-0", "Topic": "Topic-0"},
|
||||
{"ID": 1, "Language": "Language-1", "Topic": "Topic-1"},
|
||||
{"ID": 2, "Language": "Language-2", "Topic": "Topic-2"}
|
||||
]
|
||||
"""
|
||||
)
|
||||
with open(path, "w") as f:
|
||||
f.write(data)
|
||||
return str(path)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def json_file_with_list_of_dicts_with_sorted_columns_field(tmp_path):
|
||||
path = tmp_path / "file.json"
|
||||
data = textwrap.dedent(
|
||||
"""\
|
||||
{
|
||||
"field1": 1,
|
||||
"field2": "aabb",
|
||||
"field3": [
|
||||
{"ID": 0, "Language": "Language-0", "Topic": "Topic-0"},
|
||||
{"ID": 1, "Language": "Language-1", "Topic": "Topic-1"},
|
||||
{"ID": 2, "Language": "Language-2", "Topic": "Topic-2"}
|
||||
]
|
||||
}
|
||||
"""
|
||||
)
|
||||
with open(path, "w") as f:
|
||||
f.write(data)
|
||||
return str(path)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def jsonl_file_with_mix_of_str_and_int(tmp_path):
|
||||
filename = tmp_path / "file.jsonl"
|
||||
data = textwrap.dedent(
|
||||
"""\
|
||||
{"col_1": -1}
|
||||
{"col_1": 1}
|
||||
{"col_1": "foo"}
|
||||
"""
|
||||
)
|
||||
with open(filename, "w") as f:
|
||||
f.write(data)
|
||||
return str(filename)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def jsonl_file_with_dicts_of_varying_keys(tmp_path):
|
||||
filename = tmp_path / "file.jsonl"
|
||||
data = textwrap.dedent(
|
||||
"""\
|
||||
{"col_1": {"a": 0}}
|
||||
{"col_1": {"b": 0}}
|
||||
{"col_1": {"c": 0}}
|
||||
"""
|
||||
)
|
||||
with open(filename, "w") as f:
|
||||
f.write(data)
|
||||
return str(filename)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def jsonl_file_with_lists_of_dicts_of_varying_keys(tmp_path):
|
||||
filename = tmp_path / "file.jsonl"
|
||||
data = textwrap.dedent(
|
||||
"""\
|
||||
{"col_1": [{"a": 0}, {"b": 0}]}
|
||||
{"col_1": [{"c": 0}, {"d": 0}]}
|
||||
"""
|
||||
)
|
||||
with open(filename, "w") as f:
|
||||
f.write(data)
|
||||
return str(filename)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def jsonl_file_with_lists_of_dicts_of_varying_keys_and_bom(tmp_path):
|
||||
# Same content as jsonl_file_with_lists_of_dicts_of_varying_keys, but the file
|
||||
# starts with a UTF-8 BOM (written via the utf-8-sig codec).
|
||||
filename = tmp_path / "file_with_bom.jsonl"
|
||||
data = textwrap.dedent(
|
||||
"""\
|
||||
{"col_1": [{"a": 0}, {"b": 0}]}
|
||||
{"col_1": [{"c": 0}, {"d": 0}]}
|
||||
"""
|
||||
)
|
||||
with open(filename, "w", encoding="utf-8-sig") as f:
|
||||
f.write(data)
|
||||
return str(filename)
|
||||
|
||||
|
||||
_messages = [
|
||||
{"role": "user", "content": "Turn on the living room lights and play my electronic music playlist."},
|
||||
{
|
||||
"role": "assistant",
|
||||
"tool_calls": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {"name": "control_light", "arguments": {"room": "living room", "state": "on"}},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "play_music",
|
||||
"arguments": {
|
||||
"playlist": "electronic"
|
||||
}, # mixed-type here since keys ["playlist"] and ["room", "state"] are different
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{"role": "tool", "name": "control_light", "content": "The lights in the living room are now on."},
|
||||
{"role": "tool", "name": "play_music", "content": "The music is now playing."},
|
||||
{"role": "assistant", "content": "Done!"},
|
||||
]
|
||||
|
||||
EXPECTED_ONE = {"col_1": [1], "col_2": [2]}
|
||||
EXPECTED_THREE = {"col_1": [-1, 1, 10], "col_2": [None, 2, 20]}
|
||||
EXPECTED_LIST_OF_STRINGS = {"text": ["First text.", "Second text.", "Third text."]}
|
||||
EXPECTED_MIX = {"col_1": [-1, 1, "foo"]}
|
||||
EXPECTED_DICTS_WITH_VARYING_KEYS = {"col_1": [{"a": 0}, {"b": 0}, {"c": 0}]}
|
||||
EXPECTED_LISTS_OF_DICTS_WITH_VARYING_KEYS = {"col_1": [[{"a": 0}, {"b": 0}], [{"c": 0}, {"d": 0}]]}
|
||||
EXPECTED_MESSAGES = {"messages": [_messages]}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def jsonl_file_with_messages(tmp_path):
|
||||
filename = tmp_path / "file.jsonl"
|
||||
data = json.dumps({"messages": _messages})
|
||||
with open(filename, "w") as f:
|
||||
f.write(data)
|
||||
return str(filename)
|
||||
|
||||
|
||||
def write_jsonl(path, rows):
|
||||
with open(path, "w") as f:
|
||||
for row in rows:
|
||||
f.write(json.dumps(row) + "\n")
|
||||
return str(path)
|
||||
|
||||
|
||||
def generate_agent_traces_output(trace_file):
|
||||
json_builder = Json(features=AGENT_TRACES_FEATURES)
|
||||
base_files = [trace_file]
|
||||
files_iterables = [[trace_file]]
|
||||
original_files = list(base_files)
|
||||
generator = json_builder._generate_tables(
|
||||
base_files=base_files, files_iterables=files_iterables, original_files=original_files
|
||||
)
|
||||
pa_table = pa.concat_tables([table for _, table in generator])
|
||||
return Features.from_arrow_schema(pa_table.schema).decode_batch(pa_table.to_pydict())
|
||||
|
||||
|
||||
def assert_agent_traces_output(tmp_path, filename, rows, expected, num_sessions=1):
|
||||
trace_file = write_jsonl(tmp_path / filename, rows)
|
||||
out = generate_agent_traces_output(trace_file)
|
||||
for key, value in zip(AGENT_TRACE_FIELD_NAMES_TO_CHECK, expected):
|
||||
assert out[key] == [value] * num_sessions
|
||||
assert out["file_path"] == [trace_file] * num_sessions
|
||||
assert isinstance(out["messages"], list)
|
||||
assert out["messages"]
|
||||
assert all(
|
||||
isinstance(message["role"], str) and isinstance(message["content"], str)
|
||||
for messages in out["messages"]
|
||||
for message in messages
|
||||
)
|
||||
assert isinstance(out["tools"], list)
|
||||
assert isinstance(out["metadata"], (dict, list))
|
||||
assert isinstance(out["trace"], (dict, list))
|
||||
return trace_file, out
|
||||
|
||||
|
||||
AGENT_TRACE_FIELD_NAMES_TO_CHECK = (
|
||||
"harness",
|
||||
"session_id",
|
||||
"prompt",
|
||||
"sent_at",
|
||||
"num_user_messages",
|
||||
"num_tool_calls",
|
||||
)
|
||||
CODEX_AGENT_TRACE_ROWS = [
|
||||
{"type": "session_meta", "payload": {"id": "codex-session"}},
|
||||
{
|
||||
"type": "response_item",
|
||||
"payload": {
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [{"type": "input_text", "text": "context-wrapped codex prompt"}],
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "response_item",
|
||||
"payload": {
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [{"type": "input_text", "text": "actual codex prompt"}],
|
||||
},
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-01T10:01:00.000Z",
|
||||
"type": "event_msg",
|
||||
"payload": {"type": "user_message", "message": "actual codex prompt"},
|
||||
},
|
||||
{
|
||||
"type": "response_item",
|
||||
"payload": {"type": "function_call", "name": "exec_command", "call_id": "call_1"},
|
||||
},
|
||||
{
|
||||
"type": "response_item",
|
||||
"payload": {"type": "function_call_output", "call_id": "call_1", "output": "done"},
|
||||
},
|
||||
]
|
||||
CODEX_EXPECTED_AGENT_TRACE_FIELDS = (
|
||||
"codex",
|
||||
"codex-session",
|
||||
"actual codex prompt",
|
||||
"2026-04-01T10:01:00.000Z",
|
||||
1,
|
||||
1,
|
||||
)
|
||||
|
||||
HERMES_SESSION = {
|
||||
"id": "20260605_092247_d018ec",
|
||||
"source": "cli",
|
||||
"model": "Qwen/Qwen3.5-35B-A3B",
|
||||
"system_prompt": "You are Hermes.",
|
||||
"started_at": 1_780_665_768.307,
|
||||
"message_count": 4,
|
||||
"tool_call_count": 1,
|
||||
"messages": [
|
||||
{
|
||||
"session_id": "20260605_092247_d018ec",
|
||||
"role": "user",
|
||||
"content": "Run pwd and date.",
|
||||
"timestamp": 1_780_665_767.307,
|
||||
},
|
||||
{
|
||||
"session_id": "20260605_092247_d018ec",
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"reasoning_content": "The user asked for two shell commands.",
|
||||
"timestamp": 1_780_665_767.308,
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_677f321e2b3047b4b8c7a1e1",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "terminal",
|
||||
"arguments": json.dumps({"command": "pwd && date -u +%Y-%m-%dT%H:%M:%SZ"}),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"session_id": "20260605_092247_d018ec",
|
||||
"role": "tool",
|
||||
"content": json.dumps({"output": "/tmp/work\n2026-06-05T13:22:47Z", "exit_code": 0, "error": None}),
|
||||
"tool_call_id": "call_677f321e2b3047b4b8c7a1e1",
|
||||
"timestamp": 1_780_665_767.309,
|
||||
},
|
||||
{
|
||||
"session_id": "20260605_092247_d018ec",
|
||||
"role": "assistant",
|
||||
"content": "Working directory: `/tmp/work`.",
|
||||
"reasoning": "The commands executed successfully.",
|
||||
"timestamp": 1_780_665_767.31,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
DROID_SESSION = [
|
||||
{
|
||||
"type": "session_start",
|
||||
"id": "droid-session",
|
||||
"title": "inspect the project",
|
||||
"sessionTitle": "Inspect project files",
|
||||
"owner": "caleb",
|
||||
"version": 2,
|
||||
"cwd": "/workspace/project",
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"id": "context-1",
|
||||
"timestamp": "2026-06-02T18:55:29.000Z",
|
||||
"message": {
|
||||
"role": "user",
|
||||
"visibility": "llm_only",
|
||||
"content": [{"type": "text", "text": "<system-reminder>injected context</system-reminder>"}],
|
||||
},
|
||||
"parentId": None,
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"id": "message-1",
|
||||
"timestamp": "2026-06-02T18:55:30.274Z",
|
||||
"message": {
|
||||
"role": "user",
|
||||
"content": [{"type": "text", "text": "Inspect the project"}],
|
||||
},
|
||||
"parentId": "context-1",
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"id": "message-2",
|
||||
"timestamp": "2026-06-02T18:55:35.000Z",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"type": "thinking",
|
||||
"thinking": "I should list the files first.",
|
||||
"signature": "reasoning_content",
|
||||
"signatureProvider": "generic-chat-completion-api",
|
||||
"durationMs": 1200,
|
||||
},
|
||||
{"type": "text", "text": "I'll list the files."},
|
||||
{"type": "tool_use", "id": "LS_0", "name": "LS", "input": {"directory_path": "/workspace/project"}},
|
||||
],
|
||||
"chatCompletionReasoningField": "reasoning_content",
|
||||
"chatCompletionReasoningContent": "I should list the files first.",
|
||||
},
|
||||
"parentId": "message-1",
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"id": "message-3",
|
||||
"timestamp": "2026-06-02T18:55:36.000Z",
|
||||
"message": {
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "tool_result", "tool_use_id": "LS_0", "is_error": False, "content": "README.md\nsrc"},
|
||||
],
|
||||
},
|
||||
"parentId": "message-2",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def test_config_raises_when_invalid_name() -> None:
|
||||
with pytest.raises(InvalidConfigName, match="Bad characters"):
|
||||
_ = JsonConfig(name="name-with-*-invalid-character")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("data_files", ["str_path", ["str_path"], DataFilesList(["str_path"], [()])])
|
||||
def test_config_raises_when_invalid_data_files(data_files) -> None:
|
||||
with pytest.raises(ValueError, match="Expected a DataFilesDict"):
|
||||
_ = JsonConfig(name="name", data_files=data_files)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"file_fixture, config_kwargs, expected",
|
||||
[
|
||||
("json_file", {}, EXPECTED_ONE),
|
||||
("jsonl_file", {}, EXPECTED_THREE),
|
||||
("ndjson_file", {}, EXPECTED_THREE),
|
||||
("jsonl_file_utf16_encoded", {"encoding": "utf-16"}, EXPECTED_THREE),
|
||||
("json_file_with_list_of_dicts", {}, EXPECTED_THREE),
|
||||
("json_file_with_list_of_dicts_field", {"field": "field3"}, EXPECTED_THREE),
|
||||
("json_file_with_list_of_strings", {}, EXPECTED_LIST_OF_STRINGS),
|
||||
("json_file_with_list_of_strings_field", {"field": "field3"}, EXPECTED_LIST_OF_STRINGS),
|
||||
("json_file_with_dict_of_lists_field", {"field": "field3"}, EXPECTED_THREE),
|
||||
("jsonl_file_with_mix_of_str_and_int", {}, EXPECTED_MIX),
|
||||
("jsonl_file_with_dicts_of_varying_keys", {}, EXPECTED_DICTS_WITH_VARYING_KEYS),
|
||||
("jsonl_file_with_lists_of_dicts_of_varying_keys", {}, EXPECTED_LISTS_OF_DICTS_WITH_VARYING_KEYS),
|
||||
(
|
||||
"jsonl_file_with_lists_of_dicts_of_varying_keys_and_bom",
|
||||
{},
|
||||
EXPECTED_LISTS_OF_DICTS_WITH_VARYING_KEYS,
|
||||
),
|
||||
("jsonl_file_with_messages", {}, EXPECTED_MESSAGES),
|
||||
],
|
||||
)
|
||||
def test_json_generate_tables(file_fixture, config_kwargs, expected, request):
|
||||
json = Json(**config_kwargs)
|
||||
base_files = [request.getfixturevalue(file_fixture)]
|
||||
files_iterables = [[file] for file in base_files]
|
||||
original_files = list(base_files)
|
||||
generator = json._generate_tables(
|
||||
base_files=base_files, files_iterables=files_iterables, original_files=original_files
|
||||
)
|
||||
pa_table = pa.concat_tables([table for _, table in generator])
|
||||
out = Features.from_arrow_schema(pa_table.schema).decode_batch(pa_table.to_pydict())
|
||||
assert out == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"file_fixture, config_kwargs",
|
||||
[
|
||||
(
|
||||
"jsonl_file",
|
||||
{"features": Features({"col_1": Value("int64"), "col_2": Value("int64"), "missing_col": Value("string")})},
|
||||
),
|
||||
(
|
||||
"json_file_with_list_of_dicts",
|
||||
{"features": Features({"col_1": Value("int64"), "col_2": Value("int64"), "missing_col": Value("string")})},
|
||||
),
|
||||
(
|
||||
"json_file_with_list_of_dicts_field",
|
||||
{
|
||||
"field": "field3",
|
||||
"features": Features(
|
||||
{"col_1": Value("int64"), "col_2": Value("int64"), "missing_col": Value("string")}
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_json_generate_tables_with_missing_features(file_fixture, config_kwargs, request):
|
||||
json = Json(**config_kwargs)
|
||||
base_files = [request.getfixturevalue(file_fixture)]
|
||||
files_iterables = [[file] for file in base_files]
|
||||
original_files = list(base_files)
|
||||
generator = json._generate_tables(
|
||||
base_files=base_files, files_iterables=files_iterables, original_files=original_files
|
||||
)
|
||||
pa_table = pa.concat_tables([table for _, table in generator])
|
||||
assert pa_table.to_pydict() == {"col_1": [-1, 1, 10], "col_2": [None, 2, 20], "missing_col": [None, None, None]}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"file_fixture, config_kwargs",
|
||||
[
|
||||
("json_file_with_list_of_dicts_with_sorted_columns", {}),
|
||||
("json_file_with_list_of_dicts_with_sorted_columns_field", {"field": "field3"}),
|
||||
],
|
||||
)
|
||||
def test_json_generate_tables_with_sorted_columns(file_fixture, config_kwargs, request):
|
||||
json = Json(**config_kwargs)
|
||||
base_files = [request.getfixturevalue(file_fixture)]
|
||||
files_iterables = [[file] for file in base_files]
|
||||
original_files = list(base_files)
|
||||
generator = json._generate_tables(
|
||||
base_files=base_files, files_iterables=files_iterables, original_files=original_files
|
||||
)
|
||||
pa_table = pa.concat_tables([table for _, table in generator])
|
||||
assert pa_table.column_names == ["ID", "Language", "Topic"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"filename, rows, expected",
|
||||
[
|
||||
pytest.param("codex.jsonl", CODEX_AGENT_TRACE_ROWS, CODEX_EXPECTED_AGENT_TRACE_FIELDS, id="codex"),
|
||||
pytest.param(
|
||||
"codex_response_item_only.jsonl",
|
||||
[
|
||||
{"type": "session_meta", "payload": {"id": "codex-session"}},
|
||||
{
|
||||
"type": "response_item",
|
||||
"payload": {
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [{"text": "codex response item prompt"}],
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "response_item",
|
||||
"payload": {"type": "function_call", "name": "exec_command", "call_id": "call_1"},
|
||||
},
|
||||
],
|
||||
("codex", "codex-session", None, None, 0, 1),
|
||||
id="codex-response-item-only",
|
||||
),
|
||||
pytest.param(
|
||||
"claude.jsonl",
|
||||
[
|
||||
{
|
||||
"timestamp": "2026-04-02T10:00:00.000Z",
|
||||
"type": "user",
|
||||
"sessionId": "claude-session",
|
||||
"message": {"role": "user", "content": "claude prompt"},
|
||||
},
|
||||
{
|
||||
"type": "assistant",
|
||||
"sessionId": "claude-session",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": [{"type": "tool_use", "id": "toolu_1"}, {"type": "tool_use", "id": "toolu_2"}],
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "user",
|
||||
"sessionId": "claude-session",
|
||||
"message": {"role": "user", "content": [{"type": "tool_result", "content": "done"}]},
|
||||
},
|
||||
],
|
||||
("claude_code", "claude-session", "claude prompt", "2026-04-02T10:00:00.000Z", 1, 2),
|
||||
id="claude",
|
||||
),
|
||||
pytest.param(
|
||||
"pi.jsonl",
|
||||
[
|
||||
{"type": "session", "id": "pi-session"},
|
||||
{
|
||||
"timestamp": "2026-04-03T10:01:00.000Z",
|
||||
"type": "message",
|
||||
"message": {"role": "user", "content": "pi prompt"},
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": [{"type": "toolCall", "id": "call_1"}, {"type": "toolCall", "id": "call_2"}],
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"message": {"role": "toolResult", "content": [{"type": "text", "text": "done"}]},
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"message": {"role": "user", "content": [{"type": "text", "text": "second pi prompt"}]},
|
||||
},
|
||||
],
|
||||
("pi", "pi-session", "pi prompt", "2026-04-03T10:01:00.000Z", 2, 2),
|
||||
id="pi",
|
||||
),
|
||||
pytest.param(
|
||||
"openclaw.jsonl",
|
||||
[
|
||||
{
|
||||
"type": "session",
|
||||
"id": "openclaw-session",
|
||||
"cwd": "/Users/test/.openclaw/agents/main",
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-03T10:01:00.000Z",
|
||||
"type": "message",
|
||||
"message": {"role": "user", "content": "openclaw prompt"},
|
||||
},
|
||||
],
|
||||
("openclaw", "openclaw-session", "openclaw prompt", "2026-04-03T10:01:00.000Z", 1, 0),
|
||||
id="openclaw",
|
||||
),
|
||||
pytest.param(
|
||||
"hermes.jsonl",
|
||||
[HERMES_SESSION],
|
||||
("hermes", "20260605_092247_d018ec", "Run pwd and date.", "2026-06-05T13:22:48.307Z", 1, 1),
|
||||
),
|
||||
pytest.param(
|
||||
"hermes_two_sessions.jsonl",
|
||||
[HERMES_SESSION] * 2,
|
||||
("hermes", "20260605_092247_d018ec", "Run pwd and date.", "2026-06-05T13:22:48.307Z", 1, 1),
|
||||
),
|
||||
pytest.param(
|
||||
"droid.jsonl",
|
||||
DROID_SESSION,
|
||||
("droid", "droid-session", "Inspect the project", "2026-06-02T18:55:30.274Z", 1, 1),
|
||||
id="droid",
|
||||
),
|
||||
pytest.param(
|
||||
"missing_prompt.jsonl",
|
||||
[
|
||||
{"type": "session_meta", "payload": {"id": "codex-session"}},
|
||||
{
|
||||
"type": "response_item",
|
||||
"payload": {"type": "message", "role": "assistant", "content": [{"text": "assistant response"}]},
|
||||
},
|
||||
],
|
||||
("codex", "codex-session", None, None, 0, 0),
|
||||
id="missing-prompt",
|
||||
),
|
||||
],
|
||||
)
|
||||
@require_teich
|
||||
def test_json_generate_tables_with_agent_trace_metadata(tmp_path, filename, rows, expected):
|
||||
num_sessions = 2 if filename == "hermes_two_sessions.jsonl" else 1
|
||||
_, out = assert_agent_traces_output(tmp_path, filename, rows, expected, num_sessions=num_sessions)
|
||||
if filename == "droid.jsonl":
|
||||
assert out["metadata"][0]["trace_type"] == "droid"
|
||||
assert "models" not in out
|
||||
|
||||
|
||||
@require_teich
|
||||
@pytest.mark.parametrize(
|
||||
"filename, rows, expected",
|
||||
[
|
||||
pytest.param("codex.jsonl", CODEX_AGENT_TRACE_ROWS, CODEX_EXPECTED_AGENT_TRACE_FIELDS, id="codex"),
|
||||
pytest.param(
|
||||
"droid.jsonl",
|
||||
DROID_SESSION,
|
||||
("droid", "droid-session", "Inspect the project", "2026-06-02T18:55:30.274Z", 1, 1),
|
||||
id="droid",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_json_load_dataset_with_agent_trace_metadata(tmp_path, filename, rows, expected):
|
||||
trace_file = write_jsonl(tmp_path / filename, rows)
|
||||
|
||||
dataset = load_dataset("json", data_files=trace_file, split="train", cache_dir=str(tmp_path / "cache"))
|
||||
row = dataset[0]
|
||||
|
||||
assert dataset.column_names == [
|
||||
"harness",
|
||||
"session_id",
|
||||
"prompt",
|
||||
"messages",
|
||||
"tools",
|
||||
"metadata",
|
||||
"sent_at",
|
||||
"num_user_messages",
|
||||
"num_tool_calls",
|
||||
"trace",
|
||||
"file_path",
|
||||
]
|
||||
for key, value in zip(AGENT_TRACE_FIELD_NAMES_TO_CHECK, expected):
|
||||
assert row[key] == value, key
|
||||
if filename == "droid.jsonl":
|
||||
assert row["metadata"]["trace_type"] == "droid"
|
||||
assert json.loads(row["trace"].splitlines()[0])["type"] == "session_start"
|
||||
|
||||
|
||||
def test_json_load_dataset_without_droid_marker_stays_ordinary_json(tmp_path):
|
||||
trace_file = write_jsonl(
|
||||
tmp_path / "droid_missing_marker.jsonl",
|
||||
[
|
||||
{"type": "session_start", "id": "droid-session", "version": 2},
|
||||
{
|
||||
"type": "message",
|
||||
"id": "message-1",
|
||||
"timestamp": "2026-06-02T18:55:30.274Z",
|
||||
"message": {"role": "user", "content": [{"type": "text", "text": "Inspect the project"}]},
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
dataset = load_dataset("json", data_files=trace_file, split="train", cache_dir=str(tmp_path / "cache"))
|
||||
|
||||
assert dataset.column_names == ["type", "id", "version", "timestamp", "message"]
|
||||
assert dataset[0]["type"] == "session_start"
|
||||
Reference in New Issue
Block a user