"""Regression tests for SmolLM2 / hybrid pipeline (no model download required).""" from __future__ import annotations import os import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT)) from app.config import GENERATIVE_BACKEND, PIPELINE_MODE from app.pipeline.alignment import align_documents, align_paragraph_sentences from app.pipeline.candidate_validator import validate_candidate from app.pipeline.generative import backend_kind, _clean_gen_text from app.pipeline.grammar_fix import correct_text from app.pipeline.orchestrator import rewrite_text def test_backend_defaults() -> None: # Defaults come from env at import time; kind helper mirrors config assert backend_kind() in {"seq2seq", "causal"} assert GENERATIVE_BACKEND in {"seq2seq", "causal"} assert PIPELINE_MODE in {"hybrid", "generative", "classical"} print("backend defaults OK:", GENERATIVE_BACKEND, PIPELINE_MODE) def test_clean_causal_wrappers() -> None: assert _clean_gen_text('Assistant: Hello there.') == "Hello there." assert _clean_gen_text('"Rewritten sentence."') == "Rewritten sentence." assert _clean_gen_text("Paraphrase: People live healthier lives.") == ( "People live healthier lives." ) print("clean wrappers OK") def test_alignment_1to1() -> None: src = "Alpha is first. Beta is second." cand = "Alpha comes first. Beta comes second." units = align_paragraph_sentences(src, cand) assert len(units) == 2 assert all(u.kind == "1:1" for u in units) print("alignment 1:1 OK") def test_alignment_preserves_paragraphs() -> None: src = "One. Two.\n\nThree." cand = "Uno. Dos.\n\nTres." units = align_documents(src, cand) idxs = {u.paragraph_index for u in units} assert 0 in idxs and 1 in idxs print("alignment paragraphs OK:", len(units), "units") def test_truncated_rejected() -> None: orig = ( "Nowadays many people are living an unhealthy life because they don't have enough time. " "Eating fast foods is becoming very common and people don't realize how much it affects their health." ) truncated = ( "Fast food is becoming very common because many people don't realize " "how much it affects their health." ) v = validate_candidate(orig, truncated, min_meaning=0.5) assert not v.ok assert "length" in v.reasons or "coverage" in v.reasons print("truncated rejected OK") def test_hybrid_classical_without_ml() -> None: orig = ( "Nowadays many peoples are living unhealthy life because they don't have enough times. " "Eating fast foods are becoming very common and peoples don't realizes how much it affect their health." ) r = rewrite_text( orig, tone="Neutral", strength=1, preserve_length=True, ml_polish=False, ) out = r.text.lower() assert "peoples" not in out assert "an unhealthy life" in out assert "affects" in out assert "enough time" in out # Both claims should survive classical path assert "unhealthy" in out assert "fast food" in out or "fast foods" in out assert r.pipeline_mode == "classical" print("hybrid classical-without-ml OK:", r.engine) def test_hybrid_mode_with_ml_no_model() -> None: """When generative fails to load, hybrid still returns safe classical output.""" orig = ( "Nowadays many peoples are living unhealthy life because they don't have enough times. " "Eating fast foods are becoming very common and peoples don't realizes how much it affect their health." ) # Force unavailable generative by not requiring it; hybrid runs classical+validate r = rewrite_text( orig, tone="Neutral", strength=1, preserve_length=True, ml_polish=True, ) out = r.text.lower() assert "unhealthy" in out assert "health" in out # Must not collapse to single truncated claim only g = correct_text(orig).lower() assert "enough time" in out or "enough time" in g print("hybrid ml path OK mode=", r.pipeline_mode, "engine=", r.engine) if r.hybrid: print( " units=", r.hybrid.units, "kept=", r.hybrid.classical_kept, "regen=", r.hybrid.regenerated, "accepted=", r.hybrid.gen_accepted, "reverted=", r.hybrid.reverted_source, ) if __name__ == "__main__": test_backend_defaults() test_clean_causal_wrappers() test_alignment_1to1() test_alignment_preserves_paragraphs() test_truncated_rejected() test_hybrid_classical_without_ml() test_hybrid_mode_with_ml_no_model() print("\nALL SMOLLM2 / HYBRID PIPELINE TESTS PASSED")