Files
agent_jrxml/start.py
T
panda bd5bfbac2d fix: band-level windowed refine_layout + programmatic map_fields to prevent 91.5% content loss
Root cause: LLM receiving full 34k-char JRXML would regenerate from scratch
instead of modifying coordinates in-place, shrinking output to ~3k chars.

Solution (programmatic node control, not prompt engineering):

- New agent/jrxml_windower.py: decompose JRXML into header (never sent to
  LLM) + individual bands. Split bands >4000 chars at element boundaries.
  Reassemble with element count validation (>10% change = rollback).

- Rewrite refine_layout: per-band windowed LLM processing (~2-4k chars
  each). LLM cannot "reimagine" the entire report.

- Rewrite map_fields: 100% programmatic regex $F{field_N} -> real name
  replacement. Zero LLM calls, zero content loss.

- _sanitize_field_name: non-ASCII chars escaped to _uXXXX_ format for
  valid JRXML identifiers.

- Tests: 48 new unit tests (windower 28 + map_fields 20). All passing.
  Full suite 385 tests, zero regressions.
2026-05-24 08:55:38 +08:00

134 lines
4.0 KiB
Python

"""Start all jrxml-agent services (validator, API, frontend)."""
import subprocess
import sys
import time
import urllib.request
from pathlib import Path
ROOT = Path(__file__).parent
VENV_PYTHON = ROOT / ".venv" / "Scripts" / "python.exe"
FRONTEND_DIR = ROOT / "frontend"
def kill_port(port: int):
"""Kill any process listening on the given port."""
import os
import signal
try:
result = subprocess.run(
["netstat", "-ano"], capture_output=True, text=True
)
for line in result.stdout.splitlines():
if f":{port}" in line and "LISTENING" in line:
parts = line.split()
pid = int(parts[-1])
print(f" Killing PID {pid} on port {port}")
os.kill(pid, signal.SIGTERM)
except Exception as e:
print(f" (cleanup note: {e})")
def wait_http(url: str, timeout: int = 60, interval: float = 2.0) -> bool:
"""Wait until an HTTP endpoint responds 200, or timeout."""
deadline = time.time() + timeout
while time.time() < deadline:
try:
urllib.request.urlopen(url, timeout=2)
return True
except Exception:
time.sleep(interval)
return False
def wait_port(port: int, timeout: int = 60, interval: float = 3.0) -> bool:
"""Wait until a TCP port is listening."""
deadline = time.time() + timeout
while time.time() < deadline:
try:
result = subprocess.run(
["netstat", "-ano"], capture_output=True, text=True
)
for line in result.stdout.splitlines():
if f":{port}" in line and "LISTENING" in line:
return True
except Exception:
pass
time.sleep(interval)
return False
def main():
if not VENV_PYTHON.exists():
print("[ERROR] .venv not found. Create a virtual environment first.")
sys.exit(1)
print("=" * 48)
print(" jrxml-agent launcher (full stack)")
print("=" * 48)
# -- cleanup --
print("\n[Cleanup] Checking residual processes...")
for port in (8000, 8001, 5173):
kill_port(port)
# -- 1. Validator --
print("\n[1/3] Starting validator on :8001 ...")
subprocess.Popen(
[str(VENV_PYTHON), "-c",
"import uvicorn; uvicorn.run('validation_service.main:app',host='0.0.0.0',port=8001,reload=False)"],
cwd=str(ROOT),
creationflags=subprocess.CREATE_NO_WINDOW,
)
if not wait_http("http://localhost:8001/health"):
print("[FAIL] Validator did not start in time.")
sys.exit(1)
print(" :8001 ready")
# -- 2. API --
print("[2/3] Starting API on :8000 ...")
subprocess.Popen(
[str(VENV_PYTHON), "-c",
"import uvicorn; uvicorn.run('api_server:app',host='0.0.0.0',port=8000,reload=False)"],
cwd=str(ROOT),
creationflags=subprocess.CREATE_NO_WINDOW,
)
if not wait_http("http://localhost:8000/api/health"):
print("[FAIL] API server did not start in time.")
sys.exit(1)
print(" :8000 ready")
# -- 3. Frontend --
print("[3/3] Starting frontend on :5173 ...")
if not (FRONTEND_DIR / "node_modules").exists():
print(" Installing npm dependencies...")
subprocess.run(["npm", "install"], cwd=str(FRONTEND_DIR), check=True)
subprocess.Popen(
"npm run dev",
cwd=str(FRONTEND_DIR),
creationflags=subprocess.CREATE_NO_WINDOW,
shell=True,
)
if not wait_port(5173):
print("[FAIL] Frontend did not start in time.")
sys.exit(1)
print(" :5173 ready")
print("\n" + "=" * 48)
print(" All services ready:")
print(" Frontend: http://localhost:5173")
print(" API: http://localhost:8000/docs")
print(" Validator: http://localhost:8001/health")
print(" Press Ctrl+C to stop all services")
print("=" * 48)
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\nShutting down...")
if __name__ == "__main__":
main()