DEG-5B-E2B-1018 / source /tests /test_datagen.py
gcoderw's picture
Rename package to deg (DEG_ env vars, deg.json); note upcoming modalities
16d760d verified
Raw History Blame Contribute Delete
10.3 kB
from __future__ import annotations
import pytest
from deg_cases import INVOICE, PRODUCT_LISTING
from deg.datagen.domains import TEST_DOMAINS, TRAIN_DOMAINS, sample_spec
from deg.datagen.labels import outcomes, parse_top, question_messages
from deg.datagen.run import item_hint
from deg.datagen.validate import Reject, check_fill
from deg.schema import compile_schema
CTX = ("Used Lego castle set (complete). Rated 4.5 stars by 120 buyers. Price: $1,045.00, "
"was $1,200. Ships today.")
def product(**over):
value = {"title": "Lego castle set", "category": "toys", "condition": "used",
"price": 1045, "rating": 4.5, "in_stock": True,
"description": "A complete Lego castle. Fun for builders of all ages.",
"tags": ["lego", "castle"]}
value.update(over)
return value
def test_valid_fill_recovers_spans_and_decides():
plan = compile_schema(PRODUCT_LISTING)
checked = check_fill(plan, CTX, product())
assert CTX[slice(*checked.spans["/title"])] == "Lego castle set"
assert CTX[slice(*checked.spans["/price"])] == "$1,045.00"
assert CTX[slice(*checked.spans["/rating"])] == "4.5"
assert checked.decide == {"/category": "toys", "/condition": "used", "/in_stock": True}
assert checked.generate_fields == ["/description", "/tags/0", "/tags/1"]
assert checked.value == product()
def test_case_repair_copies_context_text():
plan = compile_schema(PRODUCT_LISTING)
checked = check_fill(plan, CTX, product(title="lego Castle set"))
assert checked.value["title"] == "Lego castle set" and checked.repaired == 1
@pytest.mark.parametrize(
("over", "reason"),
[
({"title": "Duplo castle"}, "span_not_found"),
({"price": 999}, "number_no_span"),
({"category": "games"}, "decide_invalid"),
({"in_stock": "yes"}, "decide_invalid"),
({"description": "too short"}, "generate_bounds"),
({"extra": 1}, "undeclared_key"),
({"price": 1200, "rating": 120}, "extract_constraint"), # rating > maximum 5
({"tags": ["a", "b", "c", "d", "e", "f"]}, "schema_invalid_value"), # maxItems 5
],
)
def test_rejects(over, reason):
plan = compile_schema(PRODUCT_LISTING)
with pytest.raises(Reject) as info:
check_fill(plan, CTX, product(**over))
assert info.value.reason == reason
def test_missing_decide_and_required_extract():
plan = compile_schema(PRODUCT_LISTING)
value = product()
del value["in_stock"]
with pytest.raises(Reject, match="decide_missing"):
check_fill(plan, CTX, value)
value = product()
del value["title"]
with pytest.raises(Reject, match="required_missing"):
check_fill(plan, CTX, value)
def test_dates_and_arrays_in_invoice():
ctx = ("Invoice INV-0042 from Acme Corp (billing@acme.example, https://acme.example), "
"issued March 3, 2026, due 2026-04-02. Currency: USD.\n"
"Consulting 1,200.00\nSupport plan 300.5\nTotal 1,500.50")
plan = compile_schema(INVOICE)
value = {"invoice_number": "INV-0042", "issued": "2026-03-03", "due": "2026-04-02",
"vendor": {"name": "Acme Corp", "website": "https://acme.example"},
"currency": "USD",
"line_items": [{"description": "Consulting", "amount": 1200},
{"description": "Support plan", "amount": 300.5}],
"total": 1500.5}
checked = check_fill(plan, ctx, value)
assert ctx[slice(*checked.spans["/issued"])] == "March 3, 2026"
assert ctx[slice(*checked.spans["/line_items/1/amount"])] == "300.5"
assert item_hint(checked.value, "/line_items/1/amount").startswith("/line_items/1 = ")
assert item_hint(checked.value, "/total") is None
bad = {**value, "issued": "2026-03-04"}
with pytest.raises(Reject, match="date_no_span"):
check_fill(plan, ctx, bad)
def test_label_prompts_and_parse():
plan = compile_schema({"type": "object", "properties": {
"c": {"enum": ["a", "b", None]},
"s": {"type": ["integer", "null"], "minimum": 1, "maximum": 3},
"b": {"type": "boolean"}}})
assert outcomes(plan.field("/c"))[-1].startswith("null")
assert outcomes(plan.field("/s")) == ["1", "2", "3", "null (not stated / unknown)"]
msg = question_messages(plan.field("/c"), "/c", "doc", None, [2, 1, 0])[1]["content"]
assert "A. null" in msg and "C. a" in msg
top = [{"token": "A", "logprob": -0.1}, {"token": " B", "logprob": -2.5},
{"token": "x", "logprob": -3.0}]
probs, cov = parse_top(top, [{"A"}, {"B"}], case_insensitive=False)
assert sum(probs) == pytest.approx(1.0) and probs[0] > 0.9 and 0.9 < cov < 1.0
yes = parse_top([{"token": "Yes", "logprob": -0.2}], [{"yes"}, {"no"}],
case_insensitive=True)
assert yes[0] == [1.0, 0.0]
def test_spec_sampling_is_deterministic_and_covers_test_domains():
assert sample_spec(0, 7) == sample_spec(0, 7)
specs = [sample_spec(0, i) for i in range(20000)]
assert all(50 <= s.words <= 1500 for s in specs)
test = [s for s in specs if s.split_domain == "test"]
assert {s.domain for s in test} == set(TEST_DOMAINS)
assert 0.05 <= len(test) / len(specs) <= 0.08
assert len({s.domain for s in specs}) > 250
def test_domains_held_out_and_brainstorm_filters():
from deg.datagen.brainstorm import dedupe, leaks
from deg.datagen.domains import BASE_TEST_DOMAINS
assert set(BASE_TEST_DOMAINS) <= set(TEST_DOMAINS)
assert len(TEST_DOMAINS) >= 13 and not set(TEST_DOMAINS) & set(TRAIN_DOMAINS)
for train in TRAIN_DOMAINS[21:]: # brainstormed train domains never touch a held-out topic
assert not any(leaks(train, t) for t in TEST_DOMAINS), train
assert leaks("warranty claim forms", "insurance claims")
assert leaks("auto repair invoices", "vehicle service and repair records")
assert not leaks("sales invoices", "insurance claims")
assert dedupe(["Pharmacy Refill Requests", "pharmacy refill request", "x"],
["invoices and receipts"]) == ["pharmacy refill requests"]
def test_v2_knobs_sample_modes_without_changing_v1_draws():
base = [sample_spec(1, i) for i in range(3000)]
mixed = [sample_spec(1, i, real_schema_frac=0.3, hard_frac=0.3) for i in range(3000)]
for b, m in zip(base, mixed):
assert (b.domain, b.archetype, b.words, b.style) == (m.domain, m.archetype, m.words,
m.style)
assert not any(b.real_schema or b.hard for b in base)
real = sum(m.real_schema for m in mixed) / len(mixed)
hard = [m.hard for m in mixed if m.hard]
assert 0.25 < real < 0.35 and 0.25 < len(hard) / len(mixed) < 0.35
assert set(hard) == {"absent", "distractor", "correction"}
def test_clef_questions_and_parse():
from deg.datagen.clef import agree, build_request, parse_response
plan = compile_schema({"type": "object", "properties": {
"c": {"enum": ["a", 1, None]},
"b": {"type": "boolean"},
"s": {"type": "integer", "minimum": 1, "maximum": 5},
"w": {"type": "integer", "minimum": 0, "maximum": 10},
"n": {"type": ["integer", "null"], "minimum": 1, "maximum": 3}}})
items = [(f.pointer, f, None) for f in plan.fields]
req, keys = build_request("ctx", items)
q = req["questions"]
assert req["state"] == "ctx"
assert q["/c"]["type"] == "choice" and list(q["/c"]["criteria"]) == ["a", "1", "null"]
assert q["/b"] == {"type": "noul", "instructions": "JSON field /b"}
assert q["/s"]["type"] == "score" and q["/s"]["criteria"] == ["1", "2", "3", "4", "5"]
assert q["/w"]["type"] == "choice" and len(q["/w"]["criteria"]) == 11 # > 10 levels
assert list(q["/n"]["criteria"]) == ["1", "2", "3", "null"]
resp = {"answers": {
"/c": {"type": "choice", "probabilities": {"a": 0.2, "1": 0.7, "null": 0.1}},
"/b": {"type": "noul", "noul": 0.9},
"/s": {"type": "score", "probabilities": {str(i): 0.2 for i in range(5)}},
"/w": {"type": "choice", "probabilities": {str(i): 1 / 11 for i in range(11)}},
"/n": {"type": "choice", "probabilities": {"1": 0.1, "2": 0.1, "3": 0.1, "null": 0.7}},
}}
got = parse_response(resp, keys)
assert got["/c"] == pytest.approx([0.2, 0.7, 0.1]) and got["/b"] == pytest.approx([0.9, 0.1])
assert got["/n"][-1] == pytest.approx(0.7)
assert agree([0.6, 0.4], [0.8, 0.2]) is True
assert agree([0.5, 0.5], [0.9, 0.1]) is False # |dp| = 0.4
assert agree([0.4, 0.6], [0.6, 0.4]) is False and agree(None, [1.0]) is None
def test_ambiguous_spans_listed_and_marked():
from deg.datagen.labels import mark_spans
ctx = "1. Widget A, Qty 10, $5.00 ea\n2. Widget B, Qty 5, $2.00 ea"
plan = compile_schema({"type": "object", "properties": {"items": {
"type": "array", "items": {"type": "object", "properties": {
"name": {"type": "string"}, "qty": {"type": "integer"}}}}}})
checked = check_fill(plan, ctx, {"items": [{"name": "Widget A", "qty": 10},
{"name": "Widget B", "qty": 5}]})
alts = checked.alternatives["/items/1/qty"]
assert [ctx[a:b] for a, b in alts] == ["$5.00", "5"]
assert checked.alternatives["/items/0/qty"] == [checked.spans["/items/0/qty"]]
marked = mark_spans(ctx, alts)
assert "[[A]]$5.00" in marked and "Qty [[B]]5," in marked
def test_teacher_spec_parse(monkeypatch):
from deg.datagen.client import parse_teacher_spec
got = parse_teacher_spec("url=127.0.0.1:8780,model=Q27,concurrency=24")
assert got == {"base_url": "http://127.0.0.1:8780/v1", "api_key": "EMPTY",
"model": "Q27", "concurrency": 24}
monkeypatch.setenv("K", "secret")
assert parse_teacher_spec("url=http://h/v1,model=m,key_env=K")["api_key"] == "secret"
with pytest.raises(SystemExit):
parse_teacher_spec("url=http://h,concurrency=3")
with pytest.raises(SystemExit):
parse_teacher_spec("url=http://h,model=m,bogus=1")
def test_find_string_folds_typographic_quotes_and_dashes():
from deg.datagen.validate import _find_string
spans, text = _find_string("Contact: Kevin O’Connor – Sales", "kevin o'connor - sales")
assert text == "Kevin O’Connor – Sales" and len(spans) == 1