diff --git a/docs/static/files/run_openrag_with_prereqs.sh b/docs/static/files/run_openrag_with_prereqs.sh index d620256e..8b6cf3f5 100755 --- a/docs/static/files/run_openrag_with_prereqs.sh +++ b/docs/static/files/run_openrag_with_prereqs.sh @@ -1,4 +1,29 @@ #!/usr/bin/env bash + + +# --- support 'curl ... | bash' without breaking TUI -------------------------- +# If this script is being read from a PIPE (curl | bash), slurp it to a temp file +# and re-exec from that file. After that, it's safe to reattach to /dev/tty. +if [ -p /dev/stdin ]; then + tmp="$(mktemp -t openrag.XXXXXX)" + # Read the entire piped script into the temp file + cat > "$tmp" + chmod +x "$tmp" + exec bash "$tmp" "$@" +fi + +# Now we are running from a real file, not stdin. It is safe to reattach. +# (Only if not already attached to a TTY) +if [ ! -t 0 ] || [ ! -t 1 ] || [ ! -t 2 ]; then + if [ -e /dev/tty ]; then + exec /dev/tty 2>&1 + else + echo "This installer needs an interactive terminal. Try: bash <(curl -fsSL ...)" >&2 + exit 1 + fi +fi + + set -euo pipefail say() { printf "%s\n" "$*" >&2; } diff --git a/frontend/components/provider-health-banner.tsx b/frontend/components/provider-health-banner.tsx index d2755051..0782e7f9 100644 --- a/frontend/components/provider-health-banner.tsx +++ b/frontend/components/provider-health-banner.tsx @@ -23,8 +23,12 @@ export function useProviderHealth() { } = useProviderHealthQuery(); const isHealthy = health?.status === "healthy" && !isError; + // Only consider unhealthy if backend is up but provider validation failed + // Don't show banner if backend is unavailable const isUnhealthy = - health?.status === "unhealthy" || health?.status === "error" || isError; + health?.status === "unhealthy" || health?.status === "error"; + const isBackendUnavailable = + health?.status === "backend-unavailable" || isError; return { health, @@ -34,17 +38,23 @@ export function useProviderHealth() { isError, isHealthy, isUnhealthy, + isBackendUnavailable, }; } export function ProviderHealthBanner({ className }: ProviderHealthBannerProps) { - const { isLoading, isHealthy, error } = useProviderHealth(); + const { isLoading, isHealthy, isUnhealthy, health } = useProviderHealth(); const router = useRouter(); const { data: settings = {} } = useGetSettingsQuery(); - if (!isHealthy && !isLoading) { - const errorMessage = error?.message || "Provider validation failed"; + // Only show banner when provider is unhealthy (not when backend is unavailable) + if (isLoading || isHealthy) { + return null; + } + + if (isUnhealthy) { + const errorMessage = health?.message || "Provider validation failed"; const settingsUrl = settings.provider?.model_provider ? `/settings?setup=${settings.provider?.model_provider}` : "/settings"; diff --git a/frontend/src/app/api/queries/useProviderHealthQuery.ts b/frontend/src/app/api/queries/useProviderHealthQuery.ts index 980ec5f9..f7a0fb2d 100644 --- a/frontend/src/app/api/queries/useProviderHealthQuery.ts +++ b/frontend/src/app/api/queries/useProviderHealthQuery.ts @@ -12,7 +12,7 @@ export interface ProviderHealthDetails { } export interface ProviderHealthResponse { - status: "healthy" | "unhealthy" | "error"; + status: "healthy" | "unhealthy" | "error" | "backend-unavailable"; message: string; provider: string; details?: ProviderHealthDetails; @@ -38,21 +38,44 @@ export const useProviderHealthQuery = ( const queryClient = useQueryClient(); async function checkProviderHealth(): Promise { - const url = new URL("/api/provider/health", window.location.origin); - - // Add provider query param if specified - if (params?.provider) { - url.searchParams.set("provider", params.provider); - } + try { + const url = new URL("/api/provider/health", window.location.origin); - const response = await fetch(url.toString()); - - if (response.ok) { - return await response.json(); - } else { - // For 400 and 503 errors, still parse JSON for error details - const errorData = await response.json().catch(() => ({})); - throw new Error(`${providerTitleMap[errorData.provider as ModelProvider] || "Provider"} error: ${errorData.message || "Failed to check provider health"}`); + // Add provider query param if specified + if (params?.provider) { + url.searchParams.set("provider", params.provider); + } + + const response = await fetch(url.toString()); + + if (response.ok) { + return await response.json(); + } else if (response.status === 503) { + // Backend is up but provider validation failed + const errorData = await response.json().catch(() => ({})); + return { + status: "unhealthy", + message: errorData.message || "Provider validation failed", + provider: errorData.provider || params?.provider || "unknown", + details: errorData.details, + }; + } else { + // Other backend errors (400, etc.) - treat as provider issues + const errorData = await response.json().catch(() => ({})); + return { + status: "error", + message: errorData.message || "Failed to check provider health", + provider: errorData.provider || params?.provider || "unknown", + details: errorData.details, + }; + } + } catch (error) { + // Network error - backend is likely down, don't show provider banner + return { + status: "backend-unavailable", + message: error instanceof Error ? error.message : "Connection failed", + provider: params?.provider || "unknown", + }; } } diff --git a/pyproject.toml b/pyproject.toml index 36031ae4..1ea39a50 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "openrag" -version = "0.1.30" +version = "0.1.31" description = "Add your description here" readme = "README.md" requires-python = ">=3.13" diff --git a/uv.lock b/uv.lock index 977e447c..cfca6372 100644 --- a/uv.lock +++ b/uv.lock @@ -2352,7 +2352,7 @@ wheels = [ [[package]] name = "openrag" -version = "0.1.30" +version = "0.1.31" source = { editable = "." } dependencies = [ { name = "agentd" },