confluence_web/scripts/run-backend.sh
Artur Mukhamadiev 6743c358f5 diagnostics: explain why a run failed instead of just "execution_failed"
Every internal failure was mapped to a fixed sanitized code before anything
recorded the cause, so an intermittent execution_failed was undebuggable: an
exhausted model-call budget, a model turn with no text, and a genuine crash all
looked identical.

The bridge now writes one bounded line to container stderr on failure with the
code, the internal reason, the state and both call counters, and the failure
sites pass a reason (budget exhausted, empty final answer with its content block
types, token counts against the limits). The wire error is unchanged.

The backend logs the agent's terminal code together with its own call counters,
and the sanitized tail of container stderr rather than only its byte count.
deploy/logging.json gives every logger a timestamp (uvicorn's default config
leaves non-uvicorn loggers on logging's fallback handler); override with
CONFLUENCE_WEB_LOG_CONFIG.
2026-09-16 19:48:50 +03:00

68 lines
2.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# Start the Confluence Research backend (serves the frontend on the same origin).
#
# scripts/run-backend.sh [deploy/confluence-web.env]
#
# Loads the env file, checks the runtime image and rootless daemon, then runs a
# single Uvicorn worker bound to CONFLUENCE_WEB_BIND_HOST:PORT (loopback by default).
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ENV_FILE="${1:-$ROOT/deploy/confluence-web.env}"
PYTHON="${PYTHON:-$ROOT/.venv/bin/python}"
if [[ ! -f "$ENV_FILE" ]]; then
echo "env file not found: $ENV_FILE (copy deploy/confluence-web.env.example)" >&2
exit 1
fi
set -a
# shellcheck disable=SC1090
. "$ENV_FILE"
set +a
# Resolve a relative frontend directory against the repository root.
if [[ -n "${CONFLUENCE_WEB_FRONTEND_DIST_DIR:-}" && "${CONFLUENCE_WEB_FRONTEND_DIST_DIR}" != /* ]]; then
export CONFLUENCE_WEB_FRONTEND_DIST_DIR="$ROOT/${CONFLUENCE_WEB_FRONTEND_DIST_DIR#./}"
fi
if [[ "${CONFLUENCE_WEB_DEV_MODE:-false}" != "true" ]]; then
if ! docker info --format '{{json .SecurityOptions}}' 2>/dev/null | grep -q rootless; then
echo "rootless Docker daemon is required (docker context use rootless)" >&2
exit 1
fi
if ! docker image inspect "${CONFLUENCE_WEB_RUNTIME_IMAGE:?}" >/dev/null 2>&1; then
echo "runtime image ${CONFLUENCE_WEB_RUNTIME_IMAGE} not found; run: make build-image" >&2
exit 1
fi
fi
# Optional TLS termination by Uvicorn (see scripts/make-tls.sh). Both or neither.
TLS_ARGS=()
if [[ -n "${CONFLUENCE_WEB_TLS_CERT:-}" || -n "${CONFLUENCE_WEB_TLS_KEY:-}" ]]; then
if [[ -z "${CONFLUENCE_WEB_TLS_CERT:-}" || -z "${CONFLUENCE_WEB_TLS_KEY:-}" ]]; then
echo "CONFLUENCE_WEB_TLS_CERT and CONFLUENCE_WEB_TLS_KEY must be set together" >&2
exit 1
fi
# Relative paths resolve against the repository root, like the frontend directory.
[[ "$CONFLUENCE_WEB_TLS_CERT" != /* ]] && CONFLUENCE_WEB_TLS_CERT="$ROOT/${CONFLUENCE_WEB_TLS_CERT#./}"
[[ "$CONFLUENCE_WEB_TLS_KEY" != /* ]] && CONFLUENCE_WEB_TLS_KEY="$ROOT/${CONFLUENCE_WEB_TLS_KEY#./}"
for f in "$CONFLUENCE_WEB_TLS_CERT" "$CONFLUENCE_WEB_TLS_KEY"; do
if [[ ! -r "$f" ]]; then
echo "TLS file not readable: $f" >&2
exit 1
fi
done
TLS_ARGS=(--ssl-certfile "$CONFLUENCE_WEB_TLS_CERT" --ssl-keyfile "$CONFLUENCE_WEB_TLS_KEY")
fi
# Timestamped logging for every logger, not just uvicorn's own (a backend warning would
# otherwise reach stderr through logging's fallback handler, without a timestamp).
LOG_ARGS=()
LOG_CONFIG="${CONFLUENCE_WEB_LOG_CONFIG:-$ROOT/deploy/logging.json}"
[[ -r "$LOG_CONFIG" ]] && LOG_ARGS=(--log-config "$LOG_CONFIG")
exec "$PYTHON" -m uvicorn backend.app:create_app --factory --workers 1 \
--host "${CONFLUENCE_WEB_BIND_HOST:-127.0.0.1}" --port "${CONFLUENCE_WEB_BIND_PORT:-8000}" \
--no-server-header --timeout-keep-alive 5 --limit-concurrency 32 --app-dir "$ROOT" \
"${LOG_ARGS[@]}" "${TLS_ARGS[@]}"