#!/usr/bin/env bash # Call one tool by hand, the way Open WebUI would. # # ./tests/try-tool.sh is the server up? # ./tests/try-tool.sh web_search '{"query":"systemd socket activation"}' # ./tests/try-tool.sh subnet_calc '{"network":"192.168.1.0/24"}' # # WHY THIS IS NOT `curl http://localhost:8000/...` # # Because that does not work, and worse, it can appear to. NOTHING in # this stack publishes a port to the host: the app container has no # default route and the gateway is only on Open WebUI's docker network. # A curl to localhost:8000 reaches whatever else happens to be listening # there - on the machine this was written on, something answered "OK" and # looked for all the world like a healthy tool server. # # So the call is made from inside the api container, which is where the # token already is. Same code path a real tool call takes, minus nginx. set -euo pipefail cd "$(dirname "$0")/.." TOOL="${1:-}" BODY="${2:-{\}}" if [ -z "$TOOL" ]; then docker compose exec -T api python -c \ "import urllib.request;print(urllib.request.urlopen('http://127.0.0.1:8000/health').read().decode())" exit 0 fi docker compose exec -T -e TOOL="$TOOL" -e BODY="$BODY" api python - <<'PY' import json, os, sys, urllib.error, urllib.request tool, body = os.environ["TOOL"], os.environ["BODY"] try: json.loads(body) except ValueError as exc: sys.exit(f"the second argument is not valid JSON: {exc}") request = urllib.request.Request( f"http://127.0.0.1:8000/tools/{tool}", data=body.encode(), headers={"Authorization": "Bearer " + os.environ["OWUITOOLS_AUTH_TOKEN"], "Content-Type": "application/json"}, ) try: answer = urllib.request.urlopen(request, timeout=120).read().decode() except urllib.error.HTTPError as exc: if exc.code == 404: sys.exit(f"there is no tool called {tool!r}. It may be switched off in " "configs/tools.toml - see docs/tools.md.") sys.exit(f"HTTP {exc.code}: {exc.read().decode()[:400]}") # Tools return a JSON string; print what the model would actually read. try: answer = json.loads(answer) except ValueError: pass print(answer if isinstance(answer, str) else json.dumps(answer, indent=2)) PY