#!/usr/bin/env bash set -Eeuo pipefail # Direct SuperAgent Open API stream test. # Purpose: bypass TH Hotel backend Debug EML / OSS / SourceMessage and test # whether SuperAgent Open API itself times out or returns usable SSE events. # # Usage on test server: # cd /home/th-hotel-simple # bash ./superagent-direct-stream-test.sh # # You can either edit the variables below, or override them before running: # ENV_FILE=/home/th-hotel-simple/th-hotel-server.env bash ./superagent-direct-stream-test.sh ####################################### # Editable parameters ####################################### # Your test server env file. It can be plain key=value format. ENV_FILE="${ENV_FILE:-/home/th-hotel-simple/th-hotel-server.env}" # Output directory for generated request bodies, response headers, and SSE body. OUTPUT_DIR="${OUTPUT_DIR:-/tmp/th-hotel-superagent-direct}" # Curl timeout. This is total curl max time, not SuperAgent's own timeout. TIMEOUT_SECONDS="${TIMEOUT_SECONDS:-1900}" CONNECT_TIMEOUT_SECONDS="${CONNECT_TIMEOUT_SECONDS:-30}" # Optional hard overrides. Leave empty to read from ENV_FILE variables. BASE_URL="${BASE_URL:-}" API_KEY="${API_KEY:-}" EXTERNAL_SUBJECT_ID="${EXTERNAL_SUBJECT_ID:-}" # The direct test message. Keep it simple first; switch MESSAGE_PAYLOAD_FILE to # a larger JSON later if you want to mimic Debug EML more closely. If you only # have an .eml file, set EML_FILE and the script will embed the raw EML text in # the message payload JSON. MESSAGE_SUBJECT="${MESSAGE_SUBJECT:-timeout test}" MESSAGE_TEXT="${MESSAGE_TEXT:-This is a direct curl timeout test. Please return a JSON result.}" MESSAGE_PAYLOAD_FILE="${MESSAGE_PAYLOAD_FILE:-}" EML_FILE="${EML_FILE:-}" MESSAGE_PREFIX="${MESSAGE_PREFIX:-请基于以下 Debug 邮件 JSON 输出结构化任务抽取结果,只返回 JSON,不要创建订单或任务:}" ####################################### # Helpers ####################################### require_command() { if ! command -v "$1" >/dev/null 2>&1; then echo "Missing required command: $1" >&2 exit 1 fi } mask_secret() { local value="${1:-}" if [ -z "$value" ]; then echo "" return fi if [ "${#value}" -le 8 ]; then echo "****" return fi echo "${value:0:4}****${value: -4}" } load_env_file() { if [ ! -f "$ENV_FILE" ]; then echo "ENV_FILE not found: $ENV_FILE" >&2 echo "Edit ENV_FILE at the top of this script, or pass ENV_FILE=/path/to/file." >&2 exit 1 fi # shellcheck disable=SC1090 set -a source "$ENV_FILE" set +a } normalize_config() { BASE_URL="${BASE_URL:-${DEERFLOW_TEST_BASE_URL:-${DEERFLOW_BASE_URL:-}}}" API_KEY="${API_KEY:-${DEERFLOW_TEST_OPEN_API_KEY:-${DEERFLOW_OPEN_API_KEY:-}}}" if [ -z "$EXTERNAL_SUBJECT_ID" ]; then EXTERNAL_SUBJECT_ID="${SUPERAGENT_TEST_DEBUG_EML_EXTERNAL_SUBJECT_ID:-${SUPERAGENT_DEBUG_EML_EXTERNAL_SUBJECT_ID:-th-hotel-debug-eml-upload}}" fi if [ -z "$BASE_URL" ]; then echo "BASE_URL is empty. Set DEERFLOW_TEST_BASE_URL / DEERFLOW_BASE_URL or edit BASE_URL." >&2 exit 1 fi if [ -z "$API_KEY" ]; then echo "API_KEY is empty. Set DEERFLOW_TEST_OPEN_API_KEY / DEERFLOW_OPEN_API_KEY or edit API_KEY." >&2 exit 1 fi if [ -z "$EXTERNAL_SUBJECT_ID" ]; then echo "EXTERNAL_SUBJECT_ID is empty. Set SUPERAGENT_TEST_DEBUG_EML_EXTERNAL_SUBJECT_ID or edit it." >&2 exit 1 fi if [ -n "$MESSAGE_PAYLOAD_FILE" ] && [ ! -f "$MESSAGE_PAYLOAD_FILE" ]; then echo "MESSAGE_PAYLOAD_FILE not found: $MESSAGE_PAYLOAD_FILE" >&2 exit 1 fi if [ -n "$EML_FILE" ] && [ ! -f "$EML_FILE" ]; then echo "EML_FILE not found: $EML_FILE" >&2 exit 1 fi BASE_URL="${BASE_URL%/}" } print_config() { echo "=== Direct SuperAgent stream test ===" echo "ENV_FILE=$ENV_FILE" echo "OUTPUT_DIR=$OUTPUT_DIR" echo "BASE_URL=$BASE_URL" echo "EXTERNAL_SUBJECT_ID=$EXTERNAL_SUBJECT_ID" echo "API_KEY=$(mask_secret "$API_KEY")" echo "TIMEOUT_SECONDS=$TIMEOUT_SECONDS" echo "CONNECT_TIMEOUT_SECONDS=$CONNECT_TIMEOUT_SECONDS" echo "MESSAGE_SUBJECT=$MESSAGE_SUBJECT" if [ -n "$MESSAGE_PAYLOAD_FILE" ]; then echo "MESSAGE_PAYLOAD_FILE=$MESSAGE_PAYLOAD_FILE" fi if [ -n "$EML_FILE" ]; then echo "EML_FILE=$EML_FILE" fi echo } write_create_body() { python3 - <<'PY' > "$CREATE_BODY" import json import os run_id = os.environ["RUN_ID"] subject = os.environ["EXTERNAL_SUBJECT_ID"] print(json.dumps({ "external_subject_id": subject, "idempotency_key": f"{run_id}-session", "metadata": { "source": "direct-curl", "debug_run_id": run_id } }, ensure_ascii=False)) PY } write_stream_body() { python3 - <<'PY' > "$STREAM_BODY" import json import os from pathlib import Path run_id = os.environ["RUN_ID"] payload_file = os.environ.get("MESSAGE_PAYLOAD_FILE", "").strip() eml_file = os.environ.get("EML_FILE", "").strip() prefix = os.environ["MESSAGE_PREFIX"] if payload_file: payload_text = Path(payload_file).read_text(encoding="utf-8") elif eml_file: eml_path = Path(eml_file) raw_eml = eml_path.read_text(encoding="utf-8", errors="replace") payload_text = json.dumps({ "source": "direct-curl", "schema_version": "direct-raw-eml-v1", "raw_eml_file_name": eml_path.name, "raw_eml_size_bytes": eml_path.stat().st_size, "raw_eml": raw_eml, }, ensure_ascii=False) else: payload_text = json.dumps({ "source": "direct-curl", "subject": os.environ["MESSAGE_SUBJECT"], "text": os.environ["MESSAGE_TEXT"], }, ensure_ascii=False) message = prefix + "\n" + payload_text print(json.dumps({ "message": message, "idempotency_key": f"{run_id}-message", "metadata": { "source": "direct-curl", "debug_run_id": run_id, "source_message_id": "direct-curl", "hotel_id": "DIRECT" } }, ensure_ascii=False)) PY } extract_session_id() { python3 - "$CREATE_RESPONSE" <<'PY' import json import sys path = sys.argv[1] try: with open(path, "r", encoding="utf-8") as f: data = json.load(f) except Exception: print("") raise SystemExit(0) print(data.get("session_id") or data.get("id") or "") PY } curl_create_session() { local csrf="$RUN_ID-create" echo "=== Creating SuperAgent session ===" set +e CREATE_HTTP_CODE="$( curl -sS \ --connect-timeout "$CONNECT_TIMEOUT_SECONDS" \ --max-time "$TIMEOUT_SECONDS" \ -w "%{http_code}" \ -D "$CREATE_HEADERS" \ -o "$CREATE_RESPONSE" \ -X POST "$BASE_URL/api/open/agent-sessions" \ -H "Authorization: Bearer $API_KEY" \ -H "X-CSRF-Token: $csrf" \ -H "Cookie: csrf_token=$csrf" \ -H "Content-Type: application/json" \ --data-binary @"$CREATE_BODY" )" CREATE_CURL_EXIT=$? set -e echo "create curl exit=$CREATE_CURL_EXIT http=$CREATE_HTTP_CODE" echo "create headers: $CREATE_HEADERS" echo "create response: $CREATE_RESPONSE" if [ "$CREATE_CURL_EXIT" -ne 0 ] || [ "$CREATE_HTTP_CODE" -lt 200 ] || [ "$CREATE_HTTP_CODE" -ge 300 ]; then echo "Create session failed. Response body:" >&2 cat "$CREATE_RESPONSE" >&2 || true exit 1 fi SESSION_ID="$(extract_session_id)" if [ -z "$SESSION_ID" ]; then echo "Create session response does not contain session_id or id. Body:" >&2 cat "$CREATE_RESPONSE" >&2 || true exit 1 fi echo "SESSION_ID=$SESSION_ID" echo } curl_stream_message() { local csrf="$RUN_ID-message" echo "=== Calling SuperAgent message stream ===" echo "This may run for a long time. Output is saved to: $STREAM_RESPONSE" local started_at ended_at elapsed started_at="$(date +%s)" set +e STREAM_HTTP_CODE="$( curl -sS -N --no-buffer \ --connect-timeout "$CONNECT_TIMEOUT_SECONDS" \ --max-time "$TIMEOUT_SECONDS" \ -w "%{http_code}" \ -D "$STREAM_HEADERS" \ -o "$STREAM_RESPONSE" \ -X POST "$BASE_URL/api/open/agent-sessions/$SESSION_ID/messages/stream" \ -H "Authorization: Bearer $API_KEY" \ -H "X-CSRF-Token: $csrf" \ -H "Cookie: csrf_token=$csrf" \ -H "Content-Type: application/json" \ -H "Accept: text/event-stream" \ --data-binary @"$STREAM_BODY" )" STREAM_CURL_EXIT=$? set -e ended_at="$(date +%s)" elapsed="$((ended_at - started_at))" echo "stream curl exit=$STREAM_CURL_EXIT http=$STREAM_HTTP_CODE elapsed_seconds=$elapsed" echo "stream headers: $STREAM_HEADERS" echo "stream response: $STREAM_RESPONSE" echo } summarize_stream() { echo "=== Stream headers ===" cat "$STREAM_HEADERS" || true echo echo "=== Stream event summary ===" if [ -s "$STREAM_RESPONSE" ]; then grep -E '^event:' "$STREAM_RESPONSE" | sort | uniq -c || true echo echo "Last 120 lines:" tail -n 120 "$STREAM_RESPONSE" || true else echo "Stream response file is empty." fi echo } ####################################### # Main ####################################### require_command curl require_command python3 load_env_file normalize_config RUN_ID="${RUN_ID:-direct-curl-$(date +%Y%m%d-%H%M%S)}" export BASE_URL API_KEY EXTERNAL_SUBJECT_ID RUN_ID export MESSAGE_SUBJECT MESSAGE_TEXT MESSAGE_PAYLOAD_FILE EML_FILE MESSAGE_PREFIX mkdir -p "$OUTPUT_DIR" CREATE_BODY="$OUTPUT_DIR/$RUN_ID-create-body.json" CREATE_HEADERS="$OUTPUT_DIR/$RUN_ID-create.headers" CREATE_RESPONSE="$OUTPUT_DIR/$RUN_ID-create.json" STREAM_BODY="$OUTPUT_DIR/$RUN_ID-stream-body.json" STREAM_HEADERS="$OUTPUT_DIR/$RUN_ID-stream.headers" STREAM_RESPONSE="$OUTPUT_DIR/$RUN_ID-stream.sse" export CREATE_BODY STREAM_BODY CREATE_RESPONSE print_config write_create_body write_stream_body echo "create request body: $CREATE_BODY" echo "stream request body: $STREAM_BODY" echo curl_create_session curl_stream_message summarize_stream echo "Done."