Ctrl K
ComunidadGuías…

scripts/butler-ext5-pdf-ingest-sim.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env bash
# EXT-5 phrase card #3a — PDF attachment + ingest (handler sim, no iLink).
#
# Simulates:
#   1) WeChat PDF attachment → inbound_media text
#   2) 「把这份 PDF 转成 Markdown 放进记忆」
#
# Usage:
#   bash scripts/butler-ext5-pdf-ingest-sim.sh
#
# Skip (exit 0): BUTLER_EXTENSION_WECHAT_SIM=0 or no LLM key.
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
export PYTHONPATH="$ROOT"

if [[ -f "$ROOT/.env" ]]; then
  # shellcheck source=scripts/lib/butler-source-env.sh
  source "$ROOT/scripts/lib/butler-source-env.sh"
  butler_source_env "$ROOT/.env" 2>/dev/null || true
fi

export BUTLER_MCP_MAX_SERVERS="${BUTLER_MCP_MAX_SERVERS:-4}"

if [[ "${BUTLER_EXTENSION_WECHAT_SIM:-1}" == "0" ]]; then
  echo "skip: BUTLER_EXTENSION_WECHAT_SIM=0"
  exit 0
fi

_has_llm=0
for key in MINIMAX_API_KEY DEEPSEEK_API_KEY OPENAI_API_KEY ANTHROPIC_API_KEY; do
  if [[ -n "${!key:-}" ]]; then
    _has_llm=1
    break
  fi
done
if [[ "$_has_llm" -eq 0 ]]; then
  echo "skip: no LLM API key in env"
  exit 0
fi

PDF="$ROOT/tests/fixtures/ext5/sample.pdf"
if [[ ! -f "$PDF" ]]; then
  echo "FAIL: missing $PDF"
  exit 1
fi

exec python3 - "$PDF" <<'PY'
import os
import sys
import time
from pathlib import Path

ROOT = Path.cwd()
sys.path.insert(0, str(ROOT))

from butler.gateway.inbound_media import build_inbound_user_text
from butler.gateway.message_handler import ButlerMessageHandler
from butler.gateway.outbound_files import expand_reply_with_wechat_attachments
from butler.gateway.platforms.types import MessageEvent, MessageType, SessionSource
from butler.gateway.wechat_scenario_sim import evaluation_reply_text

PDF = Path(sys.argv[1]).resolve()
OWNER = os.getenv("BUTLER_OWNER_WECHAT_ID", "owner-wechat-sim")
SESSION = f"wechat:{OWNER}:ext5-pdf-{time.time_ns()}"
WS = ROOT / "projects/LingWen1"
handler = ButlerMessageHandler(channel="gateway")
failures: list[str] = []


def run(name: str, msg: str, *, expect_any=(), reject_any=()) -> str:
    print(f"\n== {name} ==")
    preview_in = msg.replace("\n", " ")[:180]
    print(f"IN:  {preview_in}{'…' if len(msg) > 180 else ''}")
    t0 = time.time()
    raw = handler.handle_message(msg, session_key=SESSION, platform="wechat", external_id=OWNER) or ""
    out = evaluation_reply_text(
        handler,
        owner_id=OWNER,
        session_key=SESSION,
        reply=expand_reply_with_wechat_attachments(raw),
        tools=[],
    )
    elapsed = time.time() - t0
    print(f"OUT ({elapsed:.1f}s): {out.replace(chr(10), ' ')[:420]}…")
    ok = True
    if expect_any and not any(n in out or n in raw for n in expect_any):
        print(f"  FAIL: need any of {expect_any!r}")
        ok = False
    for bad in reject_any:
        if bad in out or bad in raw:
            print(f"  FAIL: unwanted {bad!r}")
            ok = False
    if ok:
        print("  PASS")
    else:
        failures.append(name)
    return out


def ingest_has_pdf_marker() -> bool:
    for p in WS.glob(".butler/ingest/**/*.md"):
        try:
            body = p.read_text(encoding="utf-8", errors="replace").lower()
        except OSError:
            continue
        if "ext-5 fixture" in body or "ingest sim" in body or "pdf" in body:
            return True
    return False


run("/切换 灵文1号", "/切换 灵文1号", expect_any=("灵文1号",))

ev = MessageEvent(
    text="",
    message_type=MessageType.DOCUMENT,
    source=SessionSource(platform="wechat", chat_id=OWNER),
    media_urls=[str(PDF)],
    media_types=["application/pdf"],
)
pdf_inbound = build_inbound_user_text(ev).strip()
if not pdf_inbound or "EXT-5 fixture" not in pdf_inbound:
    print("FAIL: build_inbound_user_text did not extract PDF text")
    raise SystemExit(1)
print("  OK: inbound_media extracted PDF text")

run(
    "EXT-5 #3a PDF 附件入站",
    pdf_inbound,
    expect_any=("已完成", "ingest", "验收", "代理"),
    reject_any=("DEV_VERIFY_GATE", "未通过验证"),
)

run(
    "EXT-5 #3a 放进记忆",
    "把这份 PDF 转成 Markdown 放进记忆",
    expect_any=("ingest", "已完成", "记忆", "Markdown"),
    reject_any=("DEV_VERIFY_GATE", "未通过验证", "butler_remember", "长期记忆太短"),
)

if not ingest_has_pdf_marker():
    print("FAIL: no .butler/ingest/*.md with PDF/fixture content")
    failures.append("ingest_file_check")
else:
    print("\n  OK: .butler/ingest/ has markdown from PDF path")

print(f"\next5-pdf-ingest-sim: {'ALL PASS' if not failures else failures}")
raise SystemExit(1 if failures else 0)
PY