File size: 14,315 Bytes
8eb015e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6354eae
8eb015e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
"""act → verify → correct: the half of the loop that was missing.

Every other tool in this engine reads. Turing could describe an edit in
perfect detail and never make one — the scientist still opened the editor and
typed it themselves. That gap is the whole difference between an agent and a
very good assistant.

This is also the first thing in the codebase that CHANGES a construct, so the
tests here are weighted toward refusal rather than capability. The dangerous
outcome is not "the edit failed"; it is "the edit succeeded, somewhere else,
quietly", and someone orders that DNA.

Four properties, each of which fails silently if broken:

  1. An edit that does not match the construct is REFUSED, and the refusal
     says what is actually there.
  2. Residue numbering is only used on an actual reading frame.
  3. The edited construct becomes the run's target — otherwise the verify
     step re-checks the old sequence and reports a clean bill of health for a
     change that never landed.
  4. The bases never travel through the model.
"""
import json
import time

import pytest

from dee.core import agent_tools as t
from dee.core import edits
from dee.core import llm as _llm
from dee.core import orchestrator as orch


# A real, minimal CDS: M R A K stop.  Residue 2 is R (CGT).
CDS = "ATG" + "CGT" + "GCA" + "AAA" + "TAA"


# --------------------------------------------------------------------------- #
# the engine
# --------------------------------------------------------------------------- #
def test_a_substitution_lands_on_the_right_codon():
    out = edits.apply_edits(CDS, "R2H")
    assert out["ok"] is True
    assert edits.translate(out["sequence"]) == "MHAK*"
    a = out["applied"][0]
    assert a["codon_before"] == "CGT"
    assert (a["dna_from"], a["dna_to"]) == (4, 6)


def test_the_codon_that_was_installed_is_reported():
    """H is CAT or CAC and the choice affects expression. Leaving it implicit
    would make the tool look more deterministic than it is."""
    out = edits.apply_edits(CDS, "R2H", host="e_coli")
    note = out["applied"][0]["codon_note"]
    assert out["applied"][0]["codon_after"] in ("CAT", "CAC")
    assert out["applied"][0]["codon_after"] in note
    assert "e_coli" in note


def test_a_wild_type_mismatch_is_refused_and_says_what_is_there():
    """THE test. Applying K2H to a construct whose residue 2 is R would
    mutate the wrong residue and look like a success. The refusal has to name
    the actual residue, or the agent cannot tell a numbering problem from a
    typo."""
    out = edits.apply_edits(CDS, "K2H")
    assert out["ok"] is False
    assert out["kind"] == "mismatch"
    assert "is R" in out["error"] and "not K" in out["error"]


def test_residue_numbering_is_refused_on_a_non_coding_sequence():
    """A residue number quoted against something that is not a reading frame
    is a number about nothing. Measuring from base 0 and hoping is how an
    edit lands 200 bp from where it was meant to."""
    out = edits.apply_edits("ACGTACGTACGTACGTAC", "R2H")
    assert out["ok"] is False
    assert out["kind"] == "not_coding"


def test_nothing_is_applied_unless_every_edit_validates():
    """A half-applied set is a construct nobody asked for, and the agent has
    no way to tell which half it got."""
    out = edits.apply_edits(CDS, ["R2H", "K99W"])
    assert out["ok"] is False
    # and the good one did not sneak through
    assert "sequence" not in out


def test_two_edits_at_one_position_are_refused():
    out = edits.apply_edits(CDS, ["R2H", "R2W"])
    assert out["ok"] is False
    assert "position 2" in out["error"]


def test_the_level_is_never_inferred():
    """'A123G' is a valid edit in both alphabets. Guessing which one was meant
    would silently edit the wrong thing on exactly the constructs where it
    matters most, so `level` is required and the parser respects it."""
    assert edits.parse_edit("A12G", "dna") == {"from": "A", "pos": 12, "to": "G"}
    assert edits.parse_edit("A12G", "protein") == {"from": "A", "pos": 12, "to": "G"}
    # ...but a residue that is not a base is rejected at the dna level
    assert edits.parse_edit("R12H", "dna") is None


def test_a_base_level_edit_works_on_anything():
    out = edits.apply_edits("ACGTACGT", "G3T", level="dna")
    assert out["ok"] is True
    assert out["sequence"] == "ACTTACGT"
    assert out["length_changed"] == 0


def test_a_base_level_mismatch_is_refused_too():
    out = edits.apply_edits("ACGTACGT", "A3T", level="dna")
    assert out["ok"] is False and out["kind"] == "mismatch"
    assert "is G" in out["error"]


def test_a_wholesale_replacement_is_refused_as_an_edit():
    out = edits.apply_edits(CDS, [f"R{i}H" for i in range(1, 40)])
    assert out["ok"] is False
    assert "cap is" in out["error"]


def test_is_coding_is_strict():
    assert edits.is_coding(CDS) is True
    assert edits.is_coding(CDS[:-1]) is False        # not whole codons
    assert edits.is_coding("CGT" * 4 + "TAA") is False   # no ATG
    assert edits.is_coding("ATG" + "TAA" + "GCA" + "TAA") is False  # internal stop


# --------------------------------------------------------------------------- #
# the tool
# --------------------------------------------------------------------------- #
def test_the_tool_returns_the_construct_to_the_browser_only():
    """_strip_ui removes `_ui` before the model sees the result. The bases
    belong to the editor; in the model's context they are pure cost, and for
    a construct over ~8 kB they would be truncated anyway."""
    out = t.execute_tool("edit_sequence",
                         {"sequence": CDS, "edits": ["R2H"], "level": "protein"},
                         auth_anonymous=False, user_id="u" * 36)
    assert out["ok"] is True
    assert out["_ui"]["sequence"]
    stripped = orch._strip_ui(out)
    assert "_ui" not in stripped
    assert not any(isinstance(v, str) and len(v) > 40 and set(v) <= set("ACGT")
                   for v in stripped.values()), "bases leaked into the model result"


def test_the_result_tells_the_agent_to_verify():
    """Carried in the RESULT, not only in the system prompt, so it survives
    compaction and arrives at the moment it is relevant."""
    out = t.execute_tool("edit_sequence",
                         {"sequence": CDS, "edits": ["R2H"], "level": "protein"},
                         auth_anonymous=False, user_id="u" * 36)
    assert "map_plasmid" in out["next"]
    assert "verify" in out["next"].lower()


def test_the_edited_spans_are_reported_for_the_editor():
    """So the editor can select what changed. Repainting a 20 kb construct
    that differs by one base and leaving the viewport at base 1 asks the
    scientist to take the edit on trust."""
    out = t.execute_tool("edit_sequence",
                         {"sequence": CDS, "edits": ["R2H"], "level": "protein"},
                         auth_anonymous=False, user_id="u" * 36)
    spans = out["_ui"]["edited_spans"]
    assert spans == [{"from": 4, "to": 6, "label": "R2H"}]


def test_the_tool_has_no_sequence_parameter():
    """The sequence is injected by the orchestrator from the bound target.
    If the schema advertised one, the model would fill it — from a context
    where a large construct has already been truncated to 8 kB."""
    spec = next(s for s in orch.TOOL_SPECS
                if s["function"]["name"] == "edit_sequence")
    props = spec["function"]["parameters"]["properties"]
    assert "sequence" not in props
    assert set(spec["function"]["parameters"]["required"]) == {"edits", "level"}


def test_the_tool_is_gated_and_target_bound():
    assert t._TOOLS["edit_sequence"]["requires_confirm"] is True
    assert t.needs_target("edit_sequence") is True
    assert t.needs_target("map_plasmid") is False


# --------------------------------------------------------------------------- #
# the loop, through the orchestrator
# --------------------------------------------------------------------------- #
def _msg_tool(name, args, call_id="c1"):
    return {"content": None, "tool_calls": [
        {"id": call_id, "function": {"name": name, "arguments": json.dumps(args)}}]}


def _msg_text(text):
    return {"content": text, "tool_calls": []}


def _script(monkeypatch, messages):
    seq = list(messages)

    def fake(config, body):
        return (seq.pop(0) if seq else _msg_text("done")), 0.001, 10, 0
    monkeypatch.setattr(_llm, "call", fake)


def _rest(run, timeout=5.0):
    deadline = time.time() + timeout
    while time.time() < deadline:
        if run.status in ("awaiting_input", "done", "error", "stopped"):
            return run.status
        time.sleep(0.01)
    raise AssertionError(f"never settled ({run.status})")


@pytest.fixture(autouse=True)
def _configured(monkeypatch):
    monkeypatch.setenv("OPENROUTER_API_KEY", "test-key")
    monkeypatch.setenv("AGENT_MAX_COST_USD", "0.5")


def test_the_sequence_is_injected_not_passed(monkeypatch):
    """The model calls edit_sequence with no bases; the orchestrator supplies
    them from run.target. This is what makes a 19,895 bp construct editable
    at all."""
    seen = {}

    def exec_tool(name, args, anon, **kw):
        seen.update(args)
        return {"ok": True, "_ui": {"sequence": "ATGCATGCAAAATAA"},
                "edits_applied": ["R2H"], "length": 15}
    monkeypatch.setattr(orch._tools, "execute_tool", exec_tool)
    _script(monkeypatch, [_msg_tool("edit_sequence",
                                    {"edits": ["R2H"], "level": "protein"}),
                          _msg_text("done")])
    run = orch.create_run(owner="u1", anonymous=False)
    run.target = {"label": "TP53", "gene_symbol": "TP53", "sequence": CDS,
                  "length": len(CDS), "uniprot": "P04637"}
    orch.start(run, "install R2H")
    assert _rest(run) == "awaiting_input", "the gate should have stopped it"
    orch.reply(run, "yes")
    _rest(run)
    assert seen.get("sequence") == CDS, "the construct was not injected"


def test_editing_with_nothing_loaded_fails_cleanly(monkeypatch):
    """And answers its tool call, so history stays valid."""
    monkeypatch.setattr(orch._tools, "execute_tool",
                        lambda *a, **k: pytest.fail("must not reach the tool"))
    _script(monkeypatch, [_msg_tool("edit_sequence",
                                    {"edits": ["R2H"], "level": "protein"},
                                    call_id="e1"),
                          _msg_text("Nothing loaded.")])
    run = orch.create_run(owner="u1", anonymous=False)
    orch.start(run, "install R2H")
    _rest(run)
    reply = next(m for m in run.history
                 if m.get("role") == "tool" and m["tool_call_id"] == "e1")
    assert json.loads(reply["content"])["kind"] == "no_target"


def test_the_edited_construct_becomes_the_target(monkeypatch):
    """Without this the verify step re-checks the sequence as it was BEFORE
    the edit and reports it healthy — a clean bill of health for a change
    that never entered the record."""
    edited = "ATG" + "CAT" + "GCA" + "AAA" + "TAA"

    def exec_tool(name, args, anon, **kw):
        return {"ok": True, "_ui": {"sequence": edited},
                "edits_applied": ["R2H"], "length": len(edited)}
    monkeypatch.setattr(orch._tools, "execute_tool", exec_tool)
    _script(monkeypatch, [_msg_tool("edit_sequence",
                                    {"edits": ["R2H"], "level": "protein"}),
                          _msg_text("done")])
    run = orch.create_run(owner="u1", anonymous=False)
    run.target = {"label": "TP53", "gene_symbol": "TP53", "sequence": CDS,
                  "length": len(CDS), "uniprot": "P04637"}
    orch.start(run, "install R2H")
    _rest(run); orch.reply(run, "yes"); _rest(run)
    assert run.target["sequence"] == edited
    assert run.target["edited"] is True
    assert "R2H" in run.target["label"]


def test_the_wild_type_accession_is_dropped_after_an_edit(monkeypatch):
    """P04637 describes wild-type TP53. Keeping it on a mutant would have the
    structure viewer confidently show an unmutated model for an edited
    construct — the TEM-1 incident's failure mode, one edit later."""
    def exec_tool(name, args, anon, **kw):
        return {"ok": True, "_ui": {"sequence": "ATGCATGCAAAATAA"},
                "edits_applied": ["R2H"], "length": 15}
    monkeypatch.setattr(orch._tools, "execute_tool", exec_tool)
    _script(monkeypatch, [_msg_tool("edit_sequence",
                                    {"edits": ["R2H"], "level": "protein"}),
                          _msg_text("done")])
    run = orch.create_run(owner="u1", anonymous=False)
    run.target = {"label": "TP53", "gene_symbol": "TP53", "sequence": CDS,
                  "length": len(CDS), "uniprot": "P04637"}
    orch.start(run, "install R2H")
    _rest(run); orch.reply(run, "yes"); _rest(run)
    assert not run.target.get("uniprot")


def test_the_lineage_does_not_compound_over_repeated_edits(monkeypatch):
    """Two edits must not produce 'TP53 + R2H + R2H + A3G'. The stem is kept
    and the labels replaced, so the name stays readable."""
    def exec_tool(name, args, anon, **kw):
        return {"ok": True, "_ui": {"sequence": "ATGCATGCAAAATAA"},
                "edits_applied": ["A3G"], "length": 15}
    monkeypatch.setattr(orch._tools, "execute_tool", exec_tool)
    _script(monkeypatch, [_msg_tool("edit_sequence",
                                    {"edits": ["A3G"], "level": "protein"}),
                          _msg_text("done")])
    run = orch.create_run(owner="u1", anonymous=False)
    run.target = {"label": "TP53 + R2H", "gene_symbol": "TP53",
                  "sequence": CDS, "length": len(CDS)}
    orch.start(run, "now A3G")
    _rest(run); orch.reply(run, "yes"); _rest(run)
    assert run.target["label"] == "TP53 + A3G"


def test_the_prompt_makes_verification_part_of_the_job():
    """A tool that changes something and a prompt that says "you're done" is
    how an unverified edit reaches a bench."""
    p = orch.build_system_prompt(False, {}, "")
    assert "edit_sequence" in p
    assert "NOT done when the tool returns" in p
    assert "map_plasmid" in p
    # and the refusal is framed as information, not an obstacle to route round
    assert "Never retry the same edit with a different guess" in p