Files
wehub-resource-sync 542cfa195c
CI / Frontend build (push) Failing after 9m6s
CI / Plugin validate (push) Failing after 9m27s
CI / Python lint (push) Failing after 16m1s
CI / Tests (push) Successful in 18m0s
Deploy / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:27 +08:00

104 lines
3.1 KiB
Python

#!/usr/bin/env python3
"""Convert training data JSONL from train_contrastors.py format to ms-swift embedding format.
Input format (train_contrastors.py):
{"query": "...", "chunk_path": "/path/to/pos.png"}
{"query": "...", "chunk_path": "/path/to/pos.png", "neg_chunk_paths": ["/path/to/neg1.png", ...]}
Output format (ms-swift embedding):
{
"messages": [
{"role": "system", "content": "Retrieve images or text relevant to the user's query."},
{"role": "user", "content": "<query>"}
],
"positive_messages": [[
{"role": "system", "content": "Represent the user's input."},
{"role": "user", "content": "<image>"}
]],
"positive_images": [["/path/to/pos.png"]],
"negative_messages": [
[{"role": "system", "content": "Represent the user's input."}, {"role": "user", "content": "<image>"}],
...
],
"negative_images": [["/path/to/neg1.png"], ...]
}
Usage:
uv run python convert_data_for_swift.py \
--input data/train_hn.jsonl --output data/train_hn_swift.jsonl
uv run python convert_data_for_swift.py \
--input data/eval.jsonl --output data/eval_swift.jsonl
"""
import argparse
import json
import os
# Match train_contrastors.py instructions exactly
QUERY_INSTRUCTION = "Retrieve images or text relevant to the user's query."
DOC_INSTRUCTION = "Represent the user's input."
def convert_line(item):
"""Convert one data item from contrastors format to swift format."""
query = item["query"]
pos_path = item["chunk_path"]
if not os.path.exists(pos_path):
return None
doc_messages = [
{"role": "system", "content": DOC_INSTRUCTION},
{"role": "user", "content": "<image>"},
]
out = {
"messages": [
{"role": "system", "content": QUERY_INSTRUCTION},
{"role": "user", "content": query},
],
"positive_messages": [doc_messages],
"positive_images": [[pos_path]],
}
neg_paths = item.get("neg_chunk_paths", [])
if neg_paths:
neg_messages = []
neg_images = []
for np_ in neg_paths:
if np_ and os.path.exists(np_):
neg_messages.append(doc_messages)
neg_images.append([np_])
if neg_messages:
out["negative_messages"] = neg_messages
out["negative_images"] = neg_images
return out
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"--input", required=True, help="Input JSONL (contrastors format)"
)
parser.add_argument("--output", required=True, help="Output JSONL (swift format)")
args = parser.parse_args()
converted = 0
skipped = 0
with open(args.input) as fin, open(args.output, "w") as fout:
for line in fin:
item = json.loads(line.strip())
out = convert_line(item)
if out is None:
skipped += 1
continue
fout.write(json.dumps(out, ensure_ascii=False) + "\n")
converted += 1
print(f"Converted {converted} samples, skipped {skipped}{args.output}")
if __name__ == "__main__":
main()