File size: 3,461 Bytes
dfb775d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
"""Script authoring (actor / persona / script) + local-JSONL ingestion."""

from __future__ import annotations

import json
import tempfile
from pathlib import Path

from mindxtrain.config.loader import list_recipes, load_config, render_recipe
from mindxtrain.data import scripts as S


def test_persona_from_dict_maps_recognised_keys():
    p = S.persona_from_dict(
        {"name": "Codephreak", "description": "You are Codephreak.",
         "examples": ["yo.", "let's build."], "irrelevant": 123},
    )
    assert p.name == "Codephreak"
    assert p.system_prompt == "You are Codephreak."
    assert p.voice_examples == ["yo.", "let's build."]


def test_load_persona_from_env(monkeypatch, tmp_path):
    pj = tmp_path / "persona.json"
    pj.write_text(json.dumps({"persona": "Mentor", "system": "Teach plainly."}))
    monkeypatch.setenv("MINDXTRAIN_PERSONA_PATH", str(pj))
    p = S.load_persona()
    assert p.name == "Mentor"
    assert p.system_prompt == "Teach plainly."


def test_load_persona_falls_back_to_default(monkeypatch):
    monkeypatch.delenv("MINDXTRAIN_PERSONA_PATH", raising=False)
    p = S.load_persona()
    assert p.name == "actor"
    assert p.system_prompt


def test_build_script_rows_shape():
    persona = S.Persona(name="Codephreak", system_prompt="You are Codephreak.",
                        voice_examples=["augmentic intelligence."])
    rows = S.build_script_rows(
        persona,
        [S.Exchange(user="who are you?", assistant="i am codephreak.")],
        seed_voice=True,
    )
    # 1 exchange + 1 voice-seed row.
    assert len(rows) == 2
    msgs = rows[0]["messages"]
    assert [m["role"] for m in msgs] == ["system", "user", "assistant"]
    assert msgs[0]["content"] == "You are Codephreak."
    assert msgs[2]["content"] == "i am codephreak."
    # Voice-seed row carries the example as the assistant turn.
    assert rows[1]["messages"][2]["content"] == "augmentic intelligence."


def test_author_script_writes_ingestible_jsonl(tmp_path, monkeypatch):
    monkeypatch.delenv("MINDXTRAIN_PERSONA_PATH", raising=False)
    out = tmp_path / "ds" / "script.jsonl"
    path, n = S.author_script(
        out_path=out,
        exchanges=[
            S.Exchange(user="hi", assistant="hello, friend."),
            S.Exchange(user="what do you do?", assistant="i orchestrate agents."),
        ],
        persona=S.Persona(name="Codephreak", system_prompt="You are Codephreak."),
        seed_voice=False,
    )
    assert path == out and n == 2
    lines = [json.loads(line) for line in out.read_text().splitlines() if line.strip()]
    assert len(lines) == 2
    assert all("messages" in r for r in lines)

    # The authored JSONL is ingestible by the local data source.
    from mindxtrain.config.schema import DataCfg
    from mindxtrain.data.curate import load_streaming_dataset

    cfg = DataCfg(source="local", path=out, max_samples=10, packing=False, seq_len=128)
    ingested = list(load_streaming_dataset(cfg))
    assert len(ingested) == 2
    assert ingested[0]["messages"][0]["role"] == "system"


def test_persona_imprint_recipe_validates():
    assert "mindx_persona_imprint_local" in list_recipes()
    with tempfile.TemporaryDirectory() as d:
        p = Path(d) / "r.yaml"
        p.write_text(render_recipe("mindx_persona_imprint_local"))
        cfg = load_config(p)
    assert cfg.train.backend == "trl_local"
    assert cfg.data.source == "local"
    assert cfg.data.path is not None