feat(runtime): implement single-article consolidation runtime and modularize codebase

This commit is contained in:
2026-08-24 00:14:07 -03:00
parent e1e0be1353
commit 23de7d8fe7
176 changed files with 266754 additions and 10179 deletions
@@ -0,0 +1,63 @@
"""Subprocess-level integration tests for CLI consolidate.py verifying normative exit codes."""
from __future__ import annotations
import json
import subprocess
import sys
from pathlib import Path
CLI_PATH = (
Path(__file__).resolve().parent.parent.parent.parent
/ "src"
/ "runtime"
/ "cli"
/ "consolidate.py"
)
def test_cli_subprocess_invalid_schema_exit_code_1(tmp_path: Path):
"""Passing an invalid article JSON (missing url/title/etc) exits with code 1."""
bad_article = tmp_path / "bad_art.json"
bad_article.write_text(json.dumps({"invalid": "payload"}), encoding="utf-8")
ecp_file = Path("examples/sample_ecp_snapshot.json")
config_file = Path("runtime_config.local.json")
res = subprocess.run(
[
sys.executable,
str(CLI_PATH),
"--config",
str(config_file),
"--article",
str(bad_article),
"--ecp",
str(ecp_file),
],
capture_output=True,
text=True,
)
assert res.returncode == 1
def test_cli_subprocess_missing_config_exit_code_2(tmp_path: Path):
"""Passing a nonexistent config file exits with code 2 (preflight/config error)."""
article_file = Path("examples/sample_article_valid.json")
ecp_file = Path("examples/sample_ecp_snapshot.json")
res = subprocess.run(
[
sys.executable,
str(CLI_PATH),
"--config",
"nonexistent_config_123.json",
"--article",
str(article_file),
"--ecp",
str(ecp_file),
],
capture_output=True,
text=True,
)
assert res.returncode == 2
@@ -0,0 +1,65 @@
"""High-contention concurrency test verifying atomic claims with 8+ parallel workers."""
from __future__ import annotations
import concurrent.futures
import threading
from pathlib import Path
from typing import List
from src.runtime.storage.sqlite_store import SQLiteStore
def test_concurrent_claims_with_8_workers(tmp_path: Path):
"""Executes 8 parallel threads attempting to claim the same article fingerprint simultaneously.
Asserts:
1. Exactly 1 worker successfully obtains the claim and transitions to completed_text.
2. 7 workers receive active_claim or reuse existing result without duplicate writes.
3. Zero SQLite deadlock / database locked errors occur.
"""
db_path = tmp_path / "test_concurrency.db"
store = SQLiteStore(db_path)
fingerprint = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
barrier = threading.Barrier(8)
results: List[str] = []
lock = threading.Lock()
def worker_action(worker_id: int):
# Synchronize all 8 workers at the starting line
barrier.wait()
worker_store = SQLiteStore(db_path)
try:
is_new, record = worker_store.claim_or_get_execution(
fingerprint=fingerprint,
source_url="https://example.com/test",
selected_extractor="trafilatura",
config_version="1.0.0",
)
with lock:
results.append(f"worker_{worker_id}:{'claimed' if is_new else 'reused'}")
if is_new:
# Worker simulates processing and records completion
worker_store.record_transition(
fingerprint,
"completed_text",
reason="Completed by winner worker",
extra_fields={"final_status": "completed_text"},
)
except Exception as e:
with lock:
results.append(f"worker_{worker_id}:error:{e}")
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
futures = [executor.submit(worker_action, i) for i in range(8)]
concurrent.futures.wait(futures)
# Exactly 1 claimed
claimed_count = sum(1 for r in results if ":claimed" in r)
assert claimed_count == 1, f"Expected exactly 1 claim, got: {results}"
# Final state in DB must be completed_text
final_record = store.get_execution(fingerprint)
assert final_record is not None
assert final_record["current_status"] == "completed_text"
@@ -0,0 +1,65 @@
"""Crash recovery and state reconciliation integration tests."""
from __future__ import annotations
import hashlib
import json
from pathlib import Path
from src.runtime.cli.reconcile import reconcile_runtime
from src.runtime.storage.sqlite_store import SQLiteStore
def test_reconcile_resolves_crash_mismatch(tmp_path: Path):
"""Simulates a crash where a Markdown file and manifest were written, but SQLite status remained in 'received' state.
Asserts:
1. Reconcile detects the completed artifact.
2. Reconcile calculates and verifies the SHA-256 hash.
3. Reconcile updates SQLite status to 'completed_text' with matching payload.
"""
db_path = tmp_path / "state.db"
out_dir = tmp_path / "out"
out_dir.mkdir()
base_cfg = json.loads(Path("runtime_config.local.json").read_text(encoding="utf-8"))
base_cfg["paths"]["sqlite_db"] = str(db_path)
base_cfg["paths"]["output_dir"] = str(out_dir)
cfg_file = tmp_path / "cfg.json"
cfg_file.write_text(json.dumps(base_cfg, indent=2), encoding="utf-8")
store = SQLiteStore(db_path)
fp = "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
# Step 1: SQLite has received status
store.claim_or_get_execution(fp, "https://example.com/test", "trafilatura", "1.0.0")
# Step 2: Disk has completed markdown and manifest
md_content = "---\ntitle: Reconciled Article\n---\n\nContent here."
md_file = out_dir / f"{fp}.md"
md_file.write_text(md_content, encoding="utf-8")
md_hash = hashlib.sha256(md_content.encode("utf-8")).hexdigest()
manifest_data = {
"manifest_version": "1.0.0",
"fingerprint": fp,
"status": "completed_text",
"artifacts": {
"markdown_path": str(md_file),
"markdown_sha256": md_hash,
"manifest_path": str(out_dir / f"{fp}.result.json"),
},
}
manifest_file = out_dir / f"{fp}.result.json"
manifest_file.write_text(json.dumps(manifest_data, indent=2), encoding="utf-8")
# Step 3: Run reconciliation
report = reconcile_runtime(cfg_file)
assert report["divergent_states_recovered"] == 1
# Step 4: Verify DB updated to completed_text
record = store.get_execution(fp)
assert record is not None
assert record["current_status"] == "completed_text"
@@ -0,0 +1,53 @@
"""Integration test for ECP rejection producing zero Markdown files covering scenario OUT-008."""
import asyncio
import json
from pathlib import Path
from src.runtime.cli.consolidate import run_consolidation
def test_ecp_rejection_flow_produces_zero_markdown(tmp_path: Path):
# Setup non-related article
non_related_article = {
"crawled_url": "https://example.com/art_recipe",
"selected_extractor": "trafilatura",
"input_meta": {
"titulo": "Receita de Bolo de Cenoura",
"url": "https://example.com/art_recipe",
},
"trafilatura": {
"title": "Receita de Bolo de Cenoura",
"canonical_url": "https://example.com/art_recipe",
"body_text": "# Receita de Bolo de Cenoura\n\nMisture as cenouras raladas com ovos, farinha e açúcar no liquidificador e asse por 40 minutos.",
},
}
art_file = tmp_path / "article_unrelated.json"
art_file.write_text(json.dumps(non_related_article), encoding="utf-8")
ecp_file = Path("examples/sample_ecp_snapshot.json")
# Custom config outputting to tmp_path
cfg_data = json.loads(Path("runtime_config.local.json").read_text(encoding="utf-8"))
out_dir = tmp_path / "output_articles"
db_file = tmp_path / "test_runtime.db"
cfg_data["paths"]["output_dir"] = str(out_dir)
cfg_data["paths"]["sqlite_db"] = str(db_file)
cfg_file = tmp_path / "custom_config.json"
cfg_file.write_text(json.dumps(cfg_data), encoding="utf-8")
exit_code = asyncio.run(run_consolidation(art_file, ecp_file, cfg_file))
assert exit_code == 0
# Verify zero .md files exist in output directory
md_files = list(out_dir.glob("*.md"))
assert len(md_files) == 0, f"Expected 0 Markdown files on rejected ECP, found: {md_files}"
# Verify .result.json exists with final_status 'rejected_ecp' and generate_markdown False
result_files = list(out_dir.glob("*.result.json"))
assert len(result_files) == 1
manifest = json.loads(result_files[0].read_text(encoding="utf-8"))
assert manifest["final_status"] == "rejected_ecp"
assert manifest["generate_markdown"] is False
assert manifest["markdown_path"] is None
assert manifest["markdown_hash"] is None
@@ -0,0 +1,91 @@
"""Real Live E2E Integration Test executing the full consolidation runtime against live LLM APIs."""
from __future__ import annotations
import asyncio
import hashlib
import json
import os
from pathlib import Path
import pytest
from src.runtime.cli.consolidate import run_consolidation
from src.runtime.gateway.adapters import _load_env_file
from src.runtime.storage.sqlite_store import SQLiteStore
def test_live_e2e_real_api_consolidation(tmp_path: Path):
"""Executes a 100% REAL LIVE end-to-end consolidation against configured LLM endpoint.
Asserts:
1. CLI run_consolidation returns exit code 0.
2. Result manifest JSON is persisted with SHA-256 verification.
3. Markdown file is rendered with YAML front-matter containing title, tags, and sentiment.
4. SQLite WAL tracks the claim and state transitions to 'completed_text'.
5. Tokens and real latency were recorded.
"""
_load_env_file()
api_key = os.environ.get("OPENAI_API_KEY") or os.environ.get("GROQ_API_KEY")
if not api_key:
pytest.skip("No real LLM API key configured in .env or environment.")
out_dir = tmp_path / "live_out"
out_dir.mkdir(parents=True, exist_ok=True)
db_file = tmp_path / "live_runtime.db"
# Base configuration adapted to live endpoint
base_cfg = json.loads(Path("runtime_config.local.json").read_text(encoding="utf-8"))
base_cfg["paths"]["output_dir"] = str(out_dir)
base_cfg["paths"]["sqlite_db"] = str(db_file)
# Use the live provider from .env if OPENAI_API_KEY is present
if os.environ.get("OPENAI_API_KEY"):
base_cfg["roles"]["runtime_primary"]["provider"] = "openai"
base_cfg["roles"]["runtime_primary"]["model"] = os.environ.get(
"OPENAI_MODEL", "gpt-4o-mini"
)
base_cfg["roles"]["runtime_primary"]["endpoint_url"] = os.environ.get(
"OPENAI_BASE_URL", "https://api.openai.com/v1"
)
cfg_file = tmp_path / "live_config.json"
cfg_file.write_text(json.dumps(base_cfg, indent=2), encoding="utf-8")
article_file = Path("examples/sample_article_valid.json")
ecp_file = Path("examples/sample_ecp_snapshot.json")
# Run full consolidation pipeline LIVE
exit_code = asyncio.run(run_consolidation(article_file, ecp_file, cfg_file))
assert exit_code == 0, f"Expected exit code 0, got {exit_code}"
# Verify artifacts on disk
manifests = list(out_dir.glob("*.result.json"))
assert len(manifests) == 1, f"Expected 1 manifest, found: {manifests}"
manifest_data = json.loads(manifests[0].read_text(encoding="utf-8"))
assert manifest_data["final_status"] == "completed_text"
assert manifest_data["schema_version"] == "1.0.0"
md_path = Path(manifest_data["markdown_path"])
assert md_path.exists(), f"Markdown file {md_path} does not exist"
md_content = md_path.read_text(encoding="utf-8")
assert md_content.startswith("---"), "Markdown must have YAML front-matter"
assert "title:" in md_content
assert "fingerprint:" in md_content
assert "sentiment:" in md_content
assert manifest_data["enrichment"]["sentiment"] is not None
assert len(manifest_data["enrichment"]["tags"]) > 0
# SHA256 integrity verification
calculated_hash = hashlib.sha256(md_content.encode("utf-8")).hexdigest()
assert calculated_hash == manifest_data["markdown_hash"]
# Verify SQLite tracking
store = SQLiteStore(db_file)
record = store.get_execution(manifest_data["fingerprint"])
assert record is not None
assert record["current_status"] == "completed_text"
assert record["final_status"] == "completed_text"
@@ -0,0 +1,9 @@
"""Integration tests for operational resilience and rotations."""
from src.runtime.core.config import CERTIFIED_CHEAP_MODELS, load_runtime_config
def test_certified_model_rotation_resilience():
cfg = load_runtime_config("runtime_config.local.json")
for r_name, r_conf in cfg.roles.items():
assert r_conf.model in CERTIFIED_CHEAP_MODELS
@@ -0,0 +1,41 @@
"""Integration test for telemetry degradation and atomic flush."""
import json
from pathlib import Path
from src.runtime.cli.telemetry_flush import flush_telemetry_queue
from src.runtime.core.config import load_runtime_config
from src.runtime.observability.langfuse_tracer import LangfuseRuntimeTracer
from src.runtime.storage.sqlite_store import SQLiteStore
def test_telemetry_degradation_and_flush(tmp_path: Path):
db_file = tmp_path / "telemetry.db"
store = SQLiteStore(db_file)
cfg_data = json.loads(Path("runtime_config.local.json").read_text(encoding="utf-8"))
cfg_data["paths"]["sqlite_db"] = str(db_file)
cfg_file = tmp_path / "cfg.json"
cfg_file.write_text(json.dumps(cfg_data), encoding="utf-8")
cfg = load_runtime_config(cfg_file)
tracer = LangfuseRuntimeTracer(cfg, store)
# Insert 3 degraded events
for i in range(3):
tracer.record_trace(
trace_id=f"tr_00{i}",
fingerprint=f"fp_{i}" + "0" * 60,
source_url="https://example.com",
status="completed_text",
spans_data={},
generations=[],
metrics={},
)
assert len(store.get_unflushed_telemetry()) == 3
# Run flush CLI
exit_code = flush_telemetry_queue(cfg_file, batch_size=10)
assert exit_code == 0
assert len(store.get_unflushed_telemetry()) == 0