Files
AR-House/scripts/test_wave2_green_cove.py
2026-07-03 12:24:58 -04:00

175 lines
6.5 KiB
Python

"""Test focalizado de Wave 2 (ValueEstimator + OfferStrategist) con deal real.
DEAL: 446 VERMONT Avenue, Green Cove Springs, FL 32043 — $219K MLS retail
- Clay County (NO court_records scraper — soft-fail NOT_IMPLEMENTED esperado)
- 2005 build, 3/2, 1461 sqft, tax $3348, insurance $876, rent $1789, no HOA
- ARV $240K (estimacion razonable para zona Class B/C 2005 build)
Criterios validados (8 puntos del spec del usuario):
1. ValueEstimator: comparison listing vs comps
2. ValueEstimator: rango low/mid/high
3. OfferStrategist: Strike/Stretch/Walk-Away
4. OfferStrategist: angulo de ataque (psychological points)
5. OfferStrategist: contra-ofertas anticipadas
6. OfferStrategist: si auction → MAB, si MLS → Strike (este deal es MLS)
7. Briefing: seccion "Valor Real vs Listing"
8. Briefing: seccion "Oferta Recomendada"
"""
from __future__ import annotations
import io
import sys
import time
from pathlib import Path
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))
from orchestrator import DealInputs, BuyerProfile, analyze_deal # noqa: E402
def status_cb(msg: str) -> None:
print(f"[{time.strftime('%H:%M:%S')}] {msg}", flush=True)
def main() -> int:
print("=" * 70)
print("Wave 2 Validation — 446 Vermont Ave, Green Cove Springs FL $219K MLS")
print("=" * 70)
deal = DealInputs(
address="446 VERMONT Avenue, Green Cove Springs, FL 32043",
price=219_000,
rent=1_789,
property_tax=3_348,
insurance=876,
hoa=0,
sqft=1_461,
beds=3,
baths=2.0,
year_built=2005,
arv=240_000, # estimacion razonable; sin rehab significativo para 2005 build
rehab_override=8_000, # cosmetics solo, no major rehab
deal_type="mls", # NORMAL MLS retail, no auction
)
profile = BuyerProfile(
profile_class="C",
fico=720,
capital_available=65_000,
nationality="Argentina",
)
print(f"DEAL: {deal.address}")
print(f" price=${deal.price:,} rent=${deal.rent:,}/mo arv=${deal.arv:,}")
print(f" beds={deal.beds}/baths={deal.baths} sqft={deal.sqft} year={deal.year_built}")
print(f" tax=${deal.property_tax:,}/y insurance=${deal.insurance:,}/y hoa=${deal.hoa}/mo")
print(f" deal_type={deal.deal_type}, rehab_override=${deal.rehab_override:,}")
print()
t0 = time.perf_counter()
result = analyze_deal(deal, profile, photo_bytes=None, status_cb=status_cb)
elapsed = time.perf_counter() - t0
print()
print("=" * 70)
print(f"COMPLETADO en {elapsed:.0f}s ({elapsed/60:.1f} min)")
print("=" * 70)
# Get outputs
ve_out = (result.value_estimate or {}).get("output", "") or ""
os_out = (result.offer_strategy or {}).get("output", "") or ""
briefing_out = (result.executive_briefing or {}).get("output", "") or ""
# ===========================================================
# 8 CRITERIOS DE VALIDACION (spec del usuario)
# ===========================================================
print()
print("─" * 70)
print("VALIDACION 8 CRITERIOS")
print("─" * 70)
# 1. ValueEstimator: comparison listing vs comps
c1 = any(kw in ve_out.lower() for kw in [
"listing", "precio listado", "$219", "comp", "comparable"
])
print(f" 1. ValueEstimator: comparison listing vs comps: {'✅' if c1 else '❌'}")
# 2. ValueEstimator: rango low/mid/high
has_low = "low" in ve_out.lower() or "bajo:" in ve_out.lower()
has_mid = "mid" in ve_out.lower() or "medio:" in ve_out.lower()
has_high = "high" in ve_out.lower() or "alto:" in ve_out.lower()
c2 = has_low and has_mid and has_high
print(f" 2. ValueEstimator: rango low/mid/high: {'✅' if c2 else '⚠️ (parcial)'}")
# 3. OfferStrategist: Strike/Stretch/Walk-Away
has_strike = "strike" in os_out.lower()
has_stretch = "stretch" in os_out.lower()
has_walkaway = "walk-away" in os_out.lower() or "walk away" in os_out.lower()
c3 = has_strike and has_stretch and has_walkaway
print(f" 3. OfferStrategist: Strike/Stretch/Walk-Away: {'✅' if c3 else '⚠️ (parcial)'}")
# 4. OfferStrategist: angulo de ataque
c4 = any(kw in os_out.lower() for kw in [
"angulo de ataque", "ángulo de ataque", "angulo", "psicologic", "psicológ",
"presentacion", "argumento",
])
print(f" 4. OfferStrategist: angulo de ataque (psicologico): {'✅' if c4 else '❌'}")
# 5. OfferStrategist: contra-ofertas anticipadas
c5 = any(kw in os_out.lower() for kw in [
"contra-oferta", "contra oferta", "counter-offer", "counter offer",
"contraoferta", "anticipad",
])
print(f" 5. OfferStrategist: contra-ofertas anticipadas: {'✅' if c5 else '❌'}")
# 6. MLS deal → debe usar Strike NO MAB (este test es MLS)
has_mab_only = "mab" in os_out.lower() and not has_strike
c6 = has_strike and not has_mab_only
print(f" 6. MLS deal usa Strike (no MAB exclusivo): {'✅' if c6 else '❌'}")
# 7. Briefing: seccion "Valor Real" / "Valor vs Listing"
c7 = any(kw in briefing_out.lower() for kw in [
"valor real", "valor estimado", "estimación de valor", "estimacion de valor",
"valor vs listing", "valor del inmueble",
])
print(f" 7. Briefing incluye seccion Valor Real: {'✅' if c7 else '❌'}")
# 8. Briefing: seccion "Oferta Recomendada"
c8 = any(kw in briefing_out.lower() for kw in [
"oferta recomendada", "recomendación de oferta", "recomendacion de oferta",
"estrategia de oferta", "oferta sugerida", "strike", "walk-away",
])
print(f" 8. Briefing incluye seccion Oferta Recomendada: {'✅' if c8 else '❌'}")
print()
score = sum([c1, c2, c3, c4, c5, c6, c7, c8])
print(f"SCORE: {score}/8")
# ===========================================================
# Excerpts para revisión humana
# ===========================================================
print()
print("=" * 70)
print("EXCERPTS — ValueEstimator (primeras 800 chars)")
print("=" * 70)
print(ve_out[:800])
print()
print("=" * 70)
print("EXCERPTS — OfferStrategist (primeras 1000 chars)")
print("=" * 70)
print(os_out[:1000])
print()
print("=" * 70)
print("EXCERPTS — ContextualGlossaryAgent (primeras 1200 chars)")
print("=" * 70)
print(briefing_out[:1200])
return 0 if score >= 7 else 1
if __name__ == "__main__":
sys.exit(main())