RAGSpine
Reference

HTTP API

The FastAPI service endpoints — health, ask, async ingestion jobs, and job status — with exact paths, methods, and representative request/response payloads.

The service layer (src/ragspine/service/api/) is a thin HTTP adapter: schema validation, dependency injection, resource assembly, the FAQ short-circuit, error shaping, and tracing. It never rewrites the agent / retrieval / ingestion logic — it calls the existing workflows.

Start it from the repo root:

RAGSPINE_DB_PATH=data/fact_metric.db .venv/bin/python scripts/run_server.py --port 8000

Ingestion jobs run out-of-process on the worker (needs Redis):

RAGSPINE_REDIS_URL=redis://localhost:6379/0 .venv/bin/python scripts/run_worker.py

Endpoints

MethodPathPurpose
GET/healthzLiveness — always returns {"status": "ok"}.
GET/readyzReadiness — checks the fact DB (and queue if it exposes ping); 200 ready, 503 degraded.
POST/v1/askAnswer a question (FAQ short-circuit → dual-channel workflow).
POST/v1/ingest/structured/jobsEnqueue a structured (numeric) ingestion job.
POST/v1/ingest/narrative/jobsEnqueue a narrative (chunk) ingestion job.
GET/v1/jobs/{job_id}Poll an ingestion job's status.

GET /healthz

curl -s localhost:8000/healthz
{ "status": "ok" }

GET /readyz

Returns 200 with status: "ready" when all checks pass, 503 with status: "degraded" otherwise. The queue check appears only when the configured queue exposes a ping.

{ "status": "ready", "checks": { "fact_db": true, "queue": true } }

POST /v1/ask

The README curl example:

curl -s localhost:8000/v1/ask -H 'content-type: application/json' \
     -d '{"question":"中国内地FY2024的REVENUE是多少"}'

Request (AskRequest)

Prop

Type

Response (AskResponse) — a found structured answer:

{
  "request_id": "req_...",
  "answer": "ACME_CN FY2024 REVENUE 为 1320 USD_M(来源:ACME_FY2024_Review.pptx · slide=2,table=1,row=REVENUE,col=FY2024)",
  "route": "structured",
  "answer_kind": "normal",
  "clarification": null,
  "sources": [
    { "doc": "ACME_FY2024_Review.pptx", "locator": "slide=2,table=1,row=REVENUE,col=FY2024" }
  ],
  "tool_status_summary": { "found": 1, "not_found": 0, "unrecognized": 0 },
  "cache": { "hit": false, "type": null, "faq_id": null, "version": null, "source": null }
}

answer_kind is derived by the route layer: "refusal" when the structured/composite route found no fact (anti-fabrication), "clarification" when the agent asks first, "normal" otherwise. A FAQ short-circuit hit returns route: "faq", cache.hit: true, and never touches the provider, fact store, or retriever.

A FAQ short-circuit hit:

{
  "request_id": "req_...",
  "answer": "...vetted answer...",
  "route": "faq",
  "answer_kind": "normal",
  "clarification": null,
  "sources": [],
  "tool_status_summary": { "found": 0, "not_found": 0, "unrecognized": 0 },
  "cache": { "hit": true, "type": "exact", "faq_id": "faq_001", "version": 1, "source": "..." }
}

POST /v1/ingest/structured/jobs

Enqueues a structured ingestion job. Accepted suffixes: .xlsx, .xlsm, .pptx, .pdf. A path outside RAGSPINE_ALLOWED_UPLOAD_ROOT (when set) returns 400 PathNotAllowedError.

Request (IngestStructuredJobRequest)

Prop

Type

curl -s localhost:8000/v1/ingest/structured/jobs -H 'content-type: application/json' \
     -d '{"file":"/data/uploads/ACME_FY2024_Review.pptx"}'

Response (JobSubmitResponse)

{ "job_id": "..." }

POST /v1/ingest/narrative/jobs

Enqueues a narrative ingestion job. Accepted suffixes: .pptx, .pdf. inputs may be a single path string or a list.

Request (IngestNarrativeJobRequest)

Prop

Type

curl -s localhost:8000/v1/ingest/narrative/jobs -H 'content-type: application/json' \
     -d '{"inputs":["/data/uploads/report.pdf"]}'
{ "job_id": "..." }

GET /v1/jobs/{job_id}

Polls a job. Returns 404 JobNotFound when the id is unknown.

Response (JobStatusResponse) — status is one of queued / started / finished / failed:

{
  "id": "...",
  "status": "finished",
  "result": { "facts_written": 42 },
  "error": null
}

A failed job carries a structured error:

{
  "id": "...",
  "status": "failed",
  "result": null,
  "error": { "type": "JobError", "message": "...", "stage": "execution", "retryable": false }
}

Errors

Errors are shaped uniformly and never leak a traceback:

{ "error": { "type": "InternalError", "message": "internal error", "request_id": "req_..." } }
StatustypeWhen
400PathNotAllowedErrorIngest path is outside the allowlist or has an unsupported suffix.
404JobNotFoundGET /v1/jobs/{id} for an unknown id.
500InternalErrorAny unhandled exception in /v1/ask (defensive catch-all).

On this page