squid-main-space / start.sh
mengxaingshuo's picture
fix: sync v4 runtime and fast inventory
dcea8ab
Raw
History Blame Contribute Delete
11.7 kB
#!/usr/bin/env bash
set -euo pipefail
# Fail fast on broken Space metadata, duplicate templates, or stale cache artifacts.
APP_ROOT="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
export APP_ROOT
python3 "${APP_ROOT}/scripts/preflight.py"
# ============================================================
# Required non-model settings
# ============================================================
: "${MARINE_API_URL:?Missing MARINE_API_URL variable}"
# ============================================================
# Model provider and key.
#
# This deployment is intentionally ZAI-only. The official Codex CLI remains
# the runtime, while ZAI/GLM-5.3 is its OpenAI-compatible model endpoint.
# ============================================================
CODEX_PROVIDER="${CODEX_PROVIDER:-zai}"
case "${CODEX_PROVIDER,,}" in
auto|glm|zhipu|zai)
CODEX_PROVIDER="zai"
;;
*)
echo "[startup] ERROR: this deployment only supports CODEX_PROVIDER=zai"
exit 1
;;
esac
MODEL_API_KEY="${ZAI_API_KEY:-${GLM_API_KEY:-${ZHIPU_API_KEY:-}}}"
export OPENAI_BASE_URL="${OPENAI_BASE_URL:-https://open.bigmodel.cn/api/v1}"
export CODEX_MODEL="${CODEX_MODEL:-glm-5.3}"
export CODEX_PROVIDER_ID="ZAI"
export CODEX_PROVIDER_NAME="ZAI"
echo "[startup] Codex provider=${CODEX_PROVIDER_NAME}; model=${CODEX_MODEL}; base_url=${OPENAI_BASE_URL}"
if [ -z "${MODEL_API_KEY}" ]; then
echo "[startup] ERROR: no ${CODEX_PROVIDER_NAME} API key was injected."
echo "[startup] Expected secret: ZAI_API_KEY (or GLM_API_KEY/ZHIPU_API_KEY)"
echo "[startup] Available environment variable names containing KEY/API:"
env | cut -d= -f1 | grep -E 'KEY|API' | sort || true
exit 1
fi
# ============================================================
# Official OpenAI Codex CLI runtime
# ============================================================
export OPENAI_API_KEY="${MODEL_API_KEY}"
# Both deployments use the official native Codex CLI Harness. Do not
# silently fall back to the legacy HTTP adapter.
export CODEX_HARNESS_RUNTIME="${CODEX_HARNESS_RUNTIME:-native}"
export REQUIRE_CODEX_HARNESS="${REQUIRE_CODEX_HARNESS:-1}"
if [ "${CODEX_HARNESS_RUNTIME}" != "native" ]; then
echo "[startup] ERROR: CODEX_HARNESS_RUNTIME must be native"
exit 1
fi
# Both providers use the official Codex Responses transport. Disable the
# optional WebSocket transport because the compatible endpoints expose HTTPS
# Responses reliably and do not require a WebSocket session.
export CODEX_DISABLE_WEBSOCKETS="${CODEX_DISABLE_WEBSOCKETS:-1}"
# This service is a public data interface, not an operations shell. Refuse a
# stale or accidental bypass setting before the official CLI is started.
case "${CODEX_ALLOW_MCP_BYPASS:-0}" in
0|false|FALSE|no|NO|off|OFF|"") ;;
*)
echo "[startup] ERROR: CODEX_ALLOW_MCP_BYPASS is forbidden for this deployment"
exit 1
;;
esac
export CODEX_ALLOW_MCP_BYPASS=0
export MARINE_EXPORT_RANGE_MAX_DAYS="${MARINE_EXPORT_RANGE_MAX_DAYS:-31}"
# ============================================================
# Web / Runtime
# ============================================================
export PORT="${PORT:-7860}"
export FISHERIES_EXPORT_ROOT="${FISHERIES_EXPORT_ROOT:-/tmp/squid_fisheries_exports}"
export HF_FISHERIES_CACHE_ROOT="${HF_FISHERIES_CACHE_ROOT:-/tmp/squid_hf_fisheries_cache}"
if [ -z "${PUBLIC_BASE_URL:-}" ] && [ -n "${SPACE_HOST:-}" ]; then
export PUBLIC_BASE_URL="https://${SPACE_HOST}"
fi
# ============================================================
# Build private Marine MCP runtime configuration
# ============================================================
RUNTIME_MCP_CONFIG="/tmp/codex-marine-mcp.json"
export RUNTIME_MCP_CONFIG
python3 - <<'PY_MCP_CONFIG'
import json
import os
from pathlib import Path
env = {
"MARINE_API_URL": os.environ["MARINE_API_URL"],
"HF_SQUID_DATASET_REPO": (
os.environ.get("HF_SQUID_DATASET_REPO")
or os.environ.get("HF_DATASET_REPO")
or "globalsquiddatabase/squid_dataset"
),
"HF_TUNA_DATASET_REPO": (
os.environ.get("HF_TUNA_DATASET_REPO")
or "globalsquiddatabase/Tuna-Fisheries-Dataset"
),
"FISHERIES_EXPORT_ROOT": os.environ.get(
"FISHERIES_EXPORT_ROOT", "/tmp/squid_fisheries_exports"
),
"HF_FISHERIES_CACHE_ROOT": os.environ.get(
"HF_FISHERIES_CACHE_ROOT", "/tmp/squid_hf_fisheries_cache"
),
"HF_SQUID_DATASET_REVISION": (
os.environ.get("HF_SQUID_DATASET_REVISION")
or os.environ.get("HF_DATASET_REVISION", "")
),
"HF_TUNA_DATASET_REVISION": os.environ.get("HF_TUNA_DATASET_REVISION", ""),
"PUBLIC_BASE_URL": os.environ.get("PUBLIC_BASE_URL", ""),
"MARINE_PUBLIC_BASE_URL": os.environ.get("MARINE_PUBLIC_BASE_URL", ""),
"MARINE_EXPORT_RANGE_MAX_DAYS": os.environ.get("MARINE_EXPORT_RANGE_MAX_DAYS", "31"),
}
hf_token = os.environ.get("HF_TOKEN", "").strip()
if hf_token:
env["HF_TOKEN"] = hf_token
for name in ("LOCAL_SQUID_DATA_ROOT", "LOCAL_TUNA_DATA_ROOT"):
if os.environ.get(name, "").strip():
env[name] = os.environ[name]
cfg = {
"timeouts": {
"connect_timeout": 15,
"execute_timeout": 900,
"read_timeout": 900,
},
"servers": {
"marine": {
"command": "python3",
"args": [os.path.join(os.environ["APP_ROOT"], "marine_mcp.py")],
"disabled": False,
"required": False,
"env": env,
}
},
}
p = Path(os.environ["RUNTIME_MCP_CONFIG"])
p.write_text(
json.dumps(cfg, ensure_ascii=False, indent=2) + "\n",
encoding="utf-8",
)
p.chmod(0o600)
print("[startup] private Marine MCP runtime config ready")
PY_MCP_CONFIG
# Official Codex CLI configuration. The JSON file above is retained for
# backwards compatibility with the adapter; native mode reads this TOML file.
export CODEX_HOME="${CODEX_HOME:-${HOME:-/tmp}/.codex-home}"
mkdir -p "${CODEX_HOME}"
python3 - <<'PY_CODEX_CONFIG'
import json
import os
from pathlib import Path
def q(value):
return json.dumps(str(value), ensure_ascii=False)
home = Path(os.environ["CODEX_HOME"])
home.mkdir(parents=True, exist_ok=True)
marine_env = {
"MARINE_API_URL": os.environ["MARINE_API_URL"],
"HF_SQUID_DATASET_REPO": os.environ.get("HF_SQUID_DATASET_REPO", "globalsquiddatabase/squid_dataset"),
"HF_TUNA_DATASET_REPO": os.environ.get("HF_TUNA_DATASET_REPO", "globalsquiddatabase/Tuna-Fisheries-Dataset"),
"FISHERIES_EXPORT_ROOT": os.environ.get("FISHERIES_EXPORT_ROOT", "/tmp/squid_fisheries_exports"),
"HF_FISHERIES_CACHE_ROOT": os.environ.get("HF_FISHERIES_CACHE_ROOT", "/tmp/squid_hf_fisheries_cache"),
"PUBLIC_BASE_URL": os.environ.get("PUBLIC_BASE_URL", ""),
"MARINE_PUBLIC_BASE_URL": os.environ.get("MARINE_PUBLIC_BASE_URL", ""),
"MARINE_EXPORT_RANGE_MAX_DAYS": os.environ.get("MARINE_EXPORT_RANGE_MAX_DAYS", "31"),
}
for name in ("HF_TOKEN", "HF_SQUID_DATASET_REVISION", "HF_TUNA_DATASET_REVISION"):
if os.environ.get(name, "").strip():
marine_env[name] = os.environ[name]
for name in ("LOCAL_SQUID_DATA_ROOT", "LOCAL_TUNA_DATA_ROOT"):
if os.environ.get(name, "").strip():
marine_env[name] = os.environ[name]
auth_line = (
"experimental_bearer_token = " + q(os.environ["OPENAI_API_KEY"])
if os.environ.get("CODEX_PROVIDER") == "zai"
else "env_key = \"OPENAI_API_KEY\""
)
lines = [
"model_provider = " + q(os.environ["CODEX_PROVIDER_ID"]),
"model = " + q(os.environ.get("CODEX_MODEL", "glm-5.3")),
"model_reasoning_effort = " + q(os.environ.get("CODEX_REASONING_EFFORT", "max")),
"cli_auth_credentials_store = \"file\"",
"",
"[model_providers." + os.environ["CODEX_PROVIDER_ID"] + "]",
"name = " + q(os.environ["CODEX_PROVIDER_NAME"]),
"base_url = " + q(os.environ.get("OPENAI_BASE_URL", "https://open.bigmodel.cn/api/v1")),
auth_line,
"requires_openai_auth = false",
"wire_api = \"responses\"",
"supports_websockets = false",
"",
"[mcp_servers.marine]",
"command = \"python3\"",
"args = [" + q(os.path.join(os.environ["APP_ROOT"], "marine_mcp.py")) + "]",
"required = false",
# Native/headless Codex runs cannot display an interactive MCP approval
# prompt. Marine is the deployment's explicitly configured, read-only
# data server, so approve its tool calls in the CLI config instead of
# leaving requests pending until the harness timeout.
"default_tools_approval_mode = \"approve\"",
"startup_timeout_sec = 30",
"tool_timeout_sec = 900",
"env = { " + ", ".join(f"{k} = {q(v)}" for k, v in marine_env.items()) + " }",
]
# Publish metadata for whichever OpenAI-compatible provider/model is active.
if os.environ.get("CODEX_MODEL", "").strip():
catalog = {
"models": [{
"slug": os.environ.get("CODEX_MODEL", "glm-5.3"),
"display_name": os.environ.get("CODEX_MODEL", "glm-5.3"),
"description": (
os.environ.get("CODEX_PROVIDER_NAME", "OpenAI-compatible")
+ " model"
),
"base_instructions": "You are a helpful coding assistant. Use the configured Marine MCP tools when the user asks for marine data.",
"model_messages": {
"instructions_template": "You are a helpful coding assistant. Follow the user's request and use available tools when needed."
},
"default_reasoning_level": os.environ.get("CODEX_REASONING_EFFORT", "max"),
"supported_reasoning_levels": [
{"effort": "low", "description": "Light reasoning"},
{"effort": "medium", "description": "Balanced reasoning"},
{"effort": "high", "description": "Deep reasoning"},
{"effort": "max", "description": "Maximum reasoning"},
],
# These fields are required by the official Codex model catalog
# parser. Keep the legacy shell_command spelling accepted by
# Codex 0.147.x while preserving the standard tool capability.
"shell_type": "shell_command",
"visibility": "list",
"supported_in_api": True,
"priority": 1,
"truncation_policy": {"mode": "tokens", "limit": 204800},
"supports_reasoning_summaries": True,
"default_reasoning_summary": "none",
"support_verbosity": False,
"default_verbosity": None,
"context_window": 204800,
"max_context_window": 204800,
"effective_context_window_percent": 95,
"supports_parallel_tool_calls": True,
"experimental_supported_tools": [],
"input_modalities": ["text"],
}]
}
(home / "models.json").write_text(
json.dumps(catalog, ensure_ascii=False, indent=2) + "\n",
encoding="utf-8",
)
lines.insert(3, "model_catalog_json = " + q(str(home / "models.json")))
path = home / "config.toml"
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
path.chmod(0o600)
print(f"[startup] official Codex config ready: {path}")
PY_CODEX_CONFIG
# ============================================================
# Marine API health check
# ============================================================
if ! curl -fsS --max-time 12 "${MARINE_API_URL%/}/health"; then
echo "[startup] WARNING: Marine API health check failed. Chat remains available; Ocean tools will be unavailable until MARINE_API_URL is corrected."
fi
echo
# ============================================================
# Start Squid Agent Web UI
# ============================================================
exec uvicorn ui_server:app \
--host 0.0.0.0 \
--port "${PORT}" \
--proxy-headers