feat(ai): 建立 PixelRAG VLM replay worker
This commit is contained in:
@@ -1434,11 +1434,12 @@ def test_collect_ai_automation_smoke_uses_worst_status(monkeypatch):
|
||||
monkeypatch.setattr(smoke, "_external_mcp_rag_integration_check", lambda: smoke._check("external mcp rag", "ok", "ok"))
|
||||
monkeypatch.setattr(smoke, "_pixelrag_rag_candidate_replay_check", lambda: smoke._check("pixelrag replay", "ok", "ok"))
|
||||
monkeypatch.setattr(smoke, "_pixelrag_ocr_vlm_replay_check", lambda: smoke._check("pixelrag ocr vlm", "ok", "ok"))
|
||||
monkeypatch.setattr(smoke, "_pixelrag_vlm_replay_worker_check", lambda: smoke._check("pixelrag vlm worker", "ok", "ok"))
|
||||
|
||||
result = smoke.collect_ai_automation_smoke(record_history=False)
|
||||
|
||||
assert result["status"] == "critical"
|
||||
assert result["summary"] == {"ok": 32, "warning": 1, "critical": 1, "total": 34}
|
||||
assert result["summary"] == {"ok": 33, "warning": 1, "critical": 1, "total": 35}
|
||||
|
||||
|
||||
def test_pchome_controlled_apply_drift_monitor_reports_verified_zero_drift(monkeypatch):
|
||||
@@ -3968,6 +3969,7 @@ def test_collect_ai_automation_smoke_persists_recent_history(tmp_path, monkeypat
|
||||
monkeypatch.setattr(smoke, "_external_mcp_rag_integration_check", lambda: smoke._check("external mcp rag", "ok", "ok"))
|
||||
monkeypatch.setattr(smoke, "_pixelrag_rag_candidate_replay_check", lambda: smoke._check("pixelrag replay", "ok", "ok"))
|
||||
monkeypatch.setattr(smoke, "_pixelrag_ocr_vlm_replay_check", lambda: smoke._check("pixelrag ocr vlm", "ok", "ok"))
|
||||
monkeypatch.setattr(smoke, "_pixelrag_vlm_replay_worker_check", lambda: smoke._check("pixelrag vlm worker", "ok", "ok"))
|
||||
|
||||
first = smoke.collect_ai_automation_smoke(history_limit=5)
|
||||
second = smoke.collect_ai_automation_smoke(history_limit=5)
|
||||
@@ -4023,7 +4025,7 @@ def test_scheduled_automation_health_summary_reads_history_without_side_effects(
|
||||
json.dumps({
|
||||
"generated_at": datetime.now().isoformat(timespec="seconds"),
|
||||
"status": "ok",
|
||||
"summary": {"ok": 34, "warning": 0, "critical": 0, "total": 34},
|
||||
"summary": {"ok": 35, "warning": 0, "critical": 0, "total": 35},
|
||||
"checks": [
|
||||
{
|
||||
"name": "PChome 受控落地 drift monitor",
|
||||
@@ -4148,6 +4150,27 @@ def test_scheduled_automation_health_summary_reads_history_without_side_effects(
|
||||
"blocked_pages_are_not_product_data": True,
|
||||
"next_machine_action": "run_ollama_first_vlm_replay_worker",
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "PixelRAG VLM replay worker",
|
||||
"status": "ok",
|
||||
"summary": "PixelRAG VLM replay worker receipts=1, ready=1, dry_run=1, skipped=0, executed=0",
|
||||
"details": {
|
||||
"policy": "controlled_pixelrag_ollama_vlm_replay_worker_v1",
|
||||
"receipt_count": 1,
|
||||
"ready_count": 1,
|
||||
"skipped_count": 0,
|
||||
"dry_run_count": 1,
|
||||
"executed_count": 0,
|
||||
"executed_ok_count": 0,
|
||||
"executed_warning_count": 0,
|
||||
"model_error_count": 0,
|
||||
"parse_error_count": 0,
|
||||
"receipt_written_count": 0,
|
||||
"model_call_performed": False,
|
||||
"artifact_write_performed": False,
|
||||
"next_machine_action": "run_pixelrag_vlm_replay_worker_execute",
|
||||
},
|
||||
}
|
||||
],
|
||||
}, ensure_ascii=False) + "\n",
|
||||
@@ -4166,7 +4189,7 @@ def test_scheduled_automation_health_summary_reads_history_without_side_effects(
|
||||
)
|
||||
assert summary["policy"] == "read_only_ai_automation_scheduled_health_summary"
|
||||
assert summary["status"] == "ok"
|
||||
assert summary["summary"]["total"] == 31
|
||||
assert summary["summary"]["total"] == 32
|
||||
assert summary["summary"]["primary_human_gate_count"] == 0
|
||||
assert summary["summary"]["writes_database_count"] == 0
|
||||
assert pchome_family["status"] == "ok"
|
||||
@@ -5849,6 +5872,15 @@ def test_scheduled_automation_health_summary_reads_history_without_side_effects(
|
||||
assert pixelrag_ocr_vlm_replay_family["status"] == "ok"
|
||||
assert pixelrag_ocr_vlm_replay_family["details"]["replay_ready_count"] == 1
|
||||
assert pixelrag_ocr_vlm_replay_family["details"]["extraction_execution_performed"] is False
|
||||
pixelrag_vlm_replay_worker_family = next(
|
||||
item for item in summary["families"]
|
||||
if item["key"] == "pixelrag_vlm_replay_worker"
|
||||
)
|
||||
assert pixelrag_vlm_replay_worker_family["status"] == "ok"
|
||||
assert pixelrag_vlm_replay_worker_family["details"]["ready_count"] == 1
|
||||
assert pixelrag_vlm_replay_worker_family["details"]["dry_run_count"] == 1
|
||||
assert pixelrag_vlm_replay_worker_family["details"]["model_call_performed"] is False
|
||||
assert pixelrag_vlm_replay_worker_family["details"]["writes_database_count"] == 0
|
||||
assert summary["scheduled_outputs"]["telegram_send_in_preview"] is False
|
||||
assert summary["scheduled_outputs"]["writes_database"] is False
|
||||
assert summary["automation_policy"]["primary_flow"] == "ai_controlled"
|
||||
@@ -6470,6 +6502,11 @@ def test_surface_html_readback_check_is_part_of_ai_smoke(monkeypatch):
|
||||
"ok",
|
||||
"pixelrag ocr vlm ok",
|
||||
))
|
||||
monkeypatch.setattr(smoke, "_pixelrag_vlm_replay_worker_check", lambda: smoke._check(
|
||||
"PixelRAG VLM replay worker",
|
||||
"ok",
|
||||
"pixelrag vlm worker ok",
|
||||
))
|
||||
|
||||
result = smoke.collect_ai_automation_smoke(record_history=False)
|
||||
|
||||
@@ -6485,7 +6522,7 @@ def test_surface_html_readback_check_is_part_of_ai_smoke(monkeypatch):
|
||||
item for item in result["checks"]
|
||||
if item["name"] == "Sitewide visual QA readback"
|
||||
)
|
||||
assert result["summary"]["total"] == 34
|
||||
assert result["summary"]["total"] == 35
|
||||
assert surface_check["status"] == "ok"
|
||||
assert surface_check["details"]["checked_surface_count"] == 10
|
||||
assert sitewide_check["status"] == "ok"
|
||||
|
||||
181
tests/test_pixelrag_vlm_replay_worker_service.py
Normal file
181
tests/test_pixelrag_vlm_replay_worker_service.py
Normal file
@@ -0,0 +1,181 @@
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from types import SimpleNamespace
|
||||
|
||||
from tests.test_pixelrag_ocr_vlm_replay_service import _write_receipt
|
||||
|
||||
|
||||
def test_pixelrag_vlm_replay_worker_dry_run_keeps_blocked_pages_out(tmp_path):
|
||||
from services.pixelrag_vlm_replay_worker_service import (
|
||||
POLICY,
|
||||
run_pixelrag_ollama_vlm_replay_worker,
|
||||
)
|
||||
|
||||
_write_receipt(
|
||||
tmp_path,
|
||||
platform="shopee_tw",
|
||||
manifest_id="shopee-ok",
|
||||
title="Shopee 防曬乳",
|
||||
url="https://shopee.tw/search?keyword=sunscreen",
|
||||
)
|
||||
_write_receipt(
|
||||
tmp_path,
|
||||
platform="coupang_tw",
|
||||
manifest_id="coupang-403",
|
||||
title="Access Denied",
|
||||
url="https://www.tw.coupang.com/search?q=iphone",
|
||||
http_status=403,
|
||||
)
|
||||
|
||||
payload = run_pixelrag_ollama_vlm_replay_worker(
|
||||
artifact_root=tmp_path,
|
||||
platform=("shopee_tw", "coupang_tw"),
|
||||
)
|
||||
|
||||
assert payload["policy"] == POLICY
|
||||
assert payload["status"] == "warning"
|
||||
assert payload["execute"] is False
|
||||
assert payload["summary"]["receipt_count"] == 2
|
||||
assert payload["summary"]["ready_count"] == 1
|
||||
assert payload["summary"]["dry_run_count"] == 1
|
||||
assert payload["summary"]["skipped_count"] == 1
|
||||
assert payload["summary"]["model_call_performed"] is False
|
||||
assert payload["summary"]["artifact_write_performed"] is False
|
||||
assert payload["summary"]["writes_database_count"] == 0
|
||||
assert payload["controlled_apply"]["primary_human_gate_count"] == 0
|
||||
assert payload["next_machine_action"] == "run_pixelrag_vlm_replay_worker_execute"
|
||||
by_platform = {item["platform"]: item for item in payload["worker_items"]}
|
||||
assert by_platform["shopee_tw"]["worker_status"] == "dry_run_ready"
|
||||
assert by_platform["coupang_tw"]["worker_status"] == "skipped_blocked_or_not_ready"
|
||||
|
||||
|
||||
def test_pixelrag_vlm_replay_worker_execute_writes_artifact_receipt(tmp_path, monkeypatch):
|
||||
from services import pixelrag_vlm_replay_worker_service as service
|
||||
|
||||
_write_receipt(
|
||||
tmp_path,
|
||||
platform="shopee_tw",
|
||||
manifest_id="shopee-ok",
|
||||
title="Shopee 防曬乳",
|
||||
url="https://shopee.tw/search?keyword=sunscreen",
|
||||
)
|
||||
|
||||
class FakeOllama:
|
||||
def __init__(self, model):
|
||||
self.model = model
|
||||
|
||||
def generate(self, *args, **kwargs):
|
||||
return SimpleNamespace(
|
||||
success=True,
|
||||
content=json.dumps({
|
||||
"blocked_page_detected": False,
|
||||
"fields": {
|
||||
"title": {
|
||||
"value": "防曬乳 SPF50",
|
||||
"confidence": 0.92,
|
||||
"evidence_refs": ["tile:1"],
|
||||
},
|
||||
"price": {
|
||||
"value": "399",
|
||||
"confidence": 0.88,
|
||||
"evidence_refs": ["tile:2"],
|
||||
},
|
||||
},
|
||||
"quality": {
|
||||
"overall_confidence": 0.89,
|
||||
"missing_required_fields": [],
|
||||
"requires_identity_matcher_replay": True,
|
||||
"requires_promotion_gate": True,
|
||||
},
|
||||
"notes": [],
|
||||
}),
|
||||
model="minicpm-v:latest",
|
||||
error=None,
|
||||
total_duration=1.5,
|
||||
host="http://34.87.90.216:11434",
|
||||
input_tokens=12,
|
||||
output_tokens=80,
|
||||
)
|
||||
|
||||
monkeypatch.setattr(service, "OllamaService", FakeOllama)
|
||||
payload = service.run_pixelrag_ollama_vlm_replay_worker(
|
||||
artifact_root=tmp_path,
|
||||
output_root=tmp_path / "receipts",
|
||||
platform="shopee_tw",
|
||||
execute=True,
|
||||
write_receipt=True,
|
||||
tile_limit=1,
|
||||
)
|
||||
|
||||
assert payload["status"] == "ok"
|
||||
assert payload["summary"]["executed_count"] == 1
|
||||
assert payload["summary"]["executed_ok_count"] == 1
|
||||
assert payload["summary"]["receipt_written_count"] == 1
|
||||
assert payload["summary"]["writes_database_count"] == 0
|
||||
item = payload["worker_items"][0]
|
||||
assert item["worker_status"] == "executed_ok"
|
||||
assert item["provider"] == "gcp_ollama"
|
||||
assert item["validation"]["valid_for_identity_matcher_replay"] is True
|
||||
receipt_path = tmp_path / "receipts" / "shopee_tw" / "shopee-ok" / "vlm_replay_receipt.json"
|
||||
assert receipt_path.exists()
|
||||
receipt = json.loads(receipt_path.read_text(encoding="utf-8"))
|
||||
assert receipt["parsed_output"]["fields"]["title"]["value"] == "防曬乳 SPF50"
|
||||
|
||||
|
||||
def test_pixelrag_vlm_replay_worker_cli_outputs_machine_readable_json(tmp_path):
|
||||
_write_receipt(
|
||||
tmp_path,
|
||||
platform="shopee_tw",
|
||||
manifest_id="shopee-ok",
|
||||
title="Shopee 防曬乳",
|
||||
url="https://shopee.tw/search?keyword=sunscreen",
|
||||
)
|
||||
|
||||
completed = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"scripts/ops/run_pixelrag_vlm_replay_worker.py",
|
||||
"--artifact-root",
|
||||
str(tmp_path),
|
||||
"--platform",
|
||||
"shopee_tw",
|
||||
],
|
||||
capture_output=True,
|
||||
check=False,
|
||||
text=True,
|
||||
)
|
||||
|
||||
assert completed.returncode == 0
|
||||
payload = json.loads(completed.stdout)
|
||||
assert payload["summary"]["receipt_count"] == 1
|
||||
assert payload["summary"]["dry_run_count"] == 1
|
||||
assert payload["controlled_apply"]["model_call"] is False
|
||||
assert payload["controlled_apply"]["writes_database"] is False
|
||||
|
||||
|
||||
def test_pixelrag_vlm_replay_worker_route_returns_readback(tmp_path, monkeypatch):
|
||||
from flask import Flask
|
||||
from routes import system_public_routes as routes
|
||||
from services import pixelrag_vlm_replay_worker_service as service
|
||||
|
||||
_write_receipt(
|
||||
tmp_path,
|
||||
platform="shopee_tw",
|
||||
manifest_id="shopee-ok",
|
||||
title="Shopee 防曬乳",
|
||||
url="https://shopee.tw/search?keyword=sunscreen",
|
||||
)
|
||||
monkeypatch.setattr(service, "DEFAULT_ARTIFACT_ROOT", str(tmp_path))
|
||||
|
||||
app = Flask(__name__)
|
||||
with app.test_request_context(
|
||||
"/api/ai-automation/pixelrag-vlm-replay-worker?platform=shopee_tw"
|
||||
):
|
||||
response = routes.ai_automation_pixelrag_vlm_replay_worker_api.__wrapped__()
|
||||
payload = response.get_json()
|
||||
|
||||
assert payload["policy"] == "controlled_pixelrag_ollama_vlm_replay_worker_v1"
|
||||
assert payload["summary"]["receipt_count"] == 1
|
||||
assert payload["summary"]["dry_run_count"] == 1
|
||||
assert payload["controlled_apply"]["model_call"] is False
|
||||
Reference in New Issue
Block a user