asa-api / README.md
cjerzak's picture
add files
f23dbdb
metadata
title: asa-api
emoji: 🔎
colorFrom: yellow
colorTo: indigo
sdk: docker
pinned: false
license: mit

asa-api (Self-Hosted Docker + Tor)

asa-api is a self-hosted Docker API + GUI wrapper around asa::run_task().

It uses:

  • R + plumber for HTTP endpoints
  • The asa package for orchestration (run_task, run_task_batch)
  • A lightweight password-protected browser GUI at /
  • A local Tor daemon for search/web egress inside the container

What This Service Exposes

  1. GET /healthz
  2. POST /v1/run for a single prompt
  3. POST /v1/batch for many prompts
  4. GET / GUI page
  5. POST /gui/query used by the GUI

Security Model

  • API bearer auth is required for /v1/*:
    • Include Authorization: Bearer 999
  • GUI auth is password-based:
    • /gui/query checks GUI_PASSWORD

Required Environment Variables

Set these when running the container:

  • GOOGLE_API_KEY (or the provider key you use)
  • GUI_PASSWORD

Optional secrets / vars:

  • ASA_DEFAULT_BACKEND (defaults to gemini if unset; examples: openai, groq, anthropic, gemini, openrouter)
  • ASA_DEFAULT_MODEL (example: gemini-2.5-flash)
  • ASA_CONDA_ENV (default: asa_env)
  • ASA_USE_BROWSER_DEFAULT (default: false, recommended for container stability)
  • CORS_ALLOW_ORIGIN (default: *)

Provider-specific keys supported by asa include:

  • OPENAI_API_KEY
  • GROQ_API_KEY
  • ANTHROPIC_API_KEY
  • GOOGLE_API_KEY (or GEMINI_API_KEY)
  • OPENROUTER_API_KEY
  • Bedrock credentials if using bedrock

API Usage

1) Health check

curl -s http://localhost:7860/healthz

2) Single query

curl -s http://localhost:7860/v1/run \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer 999" \
  -d '{
    "prompt": "What is the population of Tokyo?",
    "config": {
      "backend": "gemini",
      "model": "gemini-2.5-flash"
    },
    "run": {
      "output_format": "text",
      "recursion_limit": 20
    }
  }'

3) Structured JSON output

curl -s http://localhost:7860/v1/run \
  -H "Authorization: Bearer 999" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Find Marie Curie birth year and nationality. Return JSON.",
    "run": {
      "output_format": "json"
    }
  }'

4) Batch query

curl -s http://localhost:7860/v1/batch \
  -H "Authorization: Bearer 999" \
  -H "Content-Type: application/json" \
  -d '{
    "prompts": [
      "What is the capital of France?",
      "What is the capital of Japan?"
    ],
    "parallel": false,
    "run": {
      "output_format": "text"
    }
  }'

/v1/batch accepts only a plain JSON array of strings for prompts. Structured row objects that mirror upstream asa::run_task_batch() data-frame inputs are rejected by this adapter.

Request Shape

POST /v1/run

{
  "prompt": "string, required",
  "config": {
    "...": "any asa::asa_config argument"
  },
  "run": {
    "...": "any asa::run_task argument except prompt/config/agent"
  },
  "include_raw_output": false,
  "include_trace_json": false
}

POST /v1/batch

{
  "prompts": ["required", "array", "of strings"],
  "config": {
    "...": "any asa::asa_config argument"
  },
  "run": {
    "...": "batch-compatible shared options only; single-run-only keys are rejected"
  },
  "batch": {
    "...": "any asa::run_task_batch argument except prompts/config/agent"
  },
  "parallel": false,
  "include_raw_output": false,
  "include_trace_json": false
}

Future Compatibility Strategy

The adapter is intentionally resilient to asa changes:

  • It dynamically inspects formals(asa::asa_config), formals(asa::run_task), and formals(asa::run_task_batch).
  • It only forwards request keys that exist in current function signatures.
  • For /v1/batch, it rejects single-run-only passthrough keys instead of silently dropping them.
  • This avoids hard crashes when upstream adds/removes optional args.

GUI

  • Open / in your browser.
  • Enter GUI_PASSWORD.
  • Enter prompt and choose output format.
  • Leave Use direct provider call (skip ASA) unchecked to run the normal ASA agent flow.
  • Check Use direct provider call (skip ASA) to send the prompt straight to the configured provider/model without ASA search/tool orchestration.
  • Submit (Ctrl/Cmd + Enter is supported).

Notes:

  • The checkbox is GUI-only; /v1/run and /v1/batch remain ASA endpoints.
  • The provider/model selection stays server-side and is not user-selectable in the GUI.

Docker Build and Runtime

The Dockerfile clones asa-software during build and installs asa from source.

R dependency strategy in this image:

  • Base image is rocker/r2u:24.04.
  • Core R runtime packages (plumber, jsonlite, reticulate, remotes) are installed as binary apt packages (r-cran-*), not compiled from CRAN source.
  • This avoids source-compile failures such as sodium -> plumber install breaks.
  • The Docker build pre-creates asa_env with pinned python=3.12.3 and openssl=3.0.13 before calling asa::build_backend(), reducing rebuild drift that can break reticulate SSL imports on Ubuntu 24.04 / Hugging Face rebuilds.
  • Runtime linker guardrails are set so reticulate prefers conda environment libraries (/opt/conda/envs/asa_env/lib and /opt/conda/lib) to avoid C++ ABI loader mismatches.
  • Tor is installed in the image and started before the API. If Tor does not become ready, the container exits instead of serving direct search traffic.

Build args:

  • ASA_SOFTWARE_REPO (default: https://github.com/cjerzak/asa-software)
  • ASA_SOFTWARE_REF (default: main)
  • ASA_CONDA_PYTHON_VERSION (default: 3.12.3)
  • ASA_CONDA_OPENSSL_VERSION (default: 3.0.13)

Local build:

docker build -t asa-api .

Local run:

docker run --rm -p 7860:7860 \
  -e GOOGLE_API_KEY=... \
  -e GUI_PASSWORD=XXX \
  asa-api

Then open:

  • http://localhost:7860/healthz
  • http://localhost:7860/

Tor Deployment Defaults

The container deploys Tor locally and exports these runtime defaults before starting plumber:

  • ASA_PROXY=socks5h://127.0.0.1:9050
  • TOR_CONTROL_PORT=9051
  • ASA_TOR_CONTROL_COOKIE=/tmp/tor/control.authcookie

Tor scope is intentionally limited:

  • Search and webpage retrieval traffic uses Tor.
  • LLM provider API traffic remains direct, matching upstream asa behavior.
  • Browser/Selenium search remains disabled by default.

Startup behavior is fail-closed:

  • Tor must answer a probe to https://check.torproject.org/api/ip with IsTor=true.
  • If the SOCKS proxy, ControlPort, or cookie setup never becomes ready, the container exits non-zero.

GET /healthz now includes Tor readiness fields:

  • tor_enabled
  • tor_ready
  • tor_proxy
  • tor_control_port
  • tor_cookie_present
  • tor_cookie_readable

You can override the local proxy defaults if needed:

  • ASA_PROXY
  • TOR_CONTROL_PORT
  • ASA_TOR_CONTROL_COOKIE
  • ASA_TOR_PROBE_URL
  • ASA_TOR_STARTUP_TIMEOUT_SEC

Notes

  • Browser/Selenium tier is disabled by default (use_browser = FALSE) for better reliability in minimal containers.
  • If you want browser tier, set config.use_browser = true explicitly per request and ensure supporting binaries are installed.
  • If startup fails with Python import/linker errors (for example CXXABI_1.3.15 not found), inspect container logs and verify GET /healthz for boot_error details once the service is up.