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.
68 lines
2.9 KiB
Bash
Executable File
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[@]}"
|