Files
homelab-frontend/API.md
T

831 lines
17 KiB
Markdown
Raw Normal View History

# API Reference: api.riotpiao.com
Gateway to all homelab services. Single entry point for LLM inference, workflows, queues, memory, and cluster operations.
**Base URL:** `https://api.riotpiao.com`
---
## Table of Contents
1. [Health & Status](#health--status)
2. [LLM Services](#llm-services)
3. [Workflow Services (Temporal)](#workflow-services-temporal)
4. [Queue Services (SQS)](#queue-services-sqs)
5. [Memory Services](#memory-services)
6. [IAM Services](#iam-services)
7. [S3 Services](#s3-services)
8. [Authentication](#authentication)
9. [Error Handling](#error-handling)
---
## Health & Status
### GET /healthz
Liveness probe. Returns immediately without checks.
```bash
curl https://api.riotpiao.com/healthz
```
**Response:**
```json
{
"status": "alive"
}
```
**Status Code:** 200
---
### GET /readyz
Readiness probe. Returns 200 when gateway is ready (config loaded, connections available).
```bash
curl https://api.riotpiao.com/readyz
```
**Response:**
```json
{
"status": "ready"
}
```
**Status Code:** 200 (ready) or 503 (not ready)
---
## LLM Services
### GET /v1/models
List all available models for inference.
```bash
curl https://api.riotpiao.com/v1/models
```
**Response:**
```json
{
"object": "list",
"data": [
{
"id": "reasoning",
"object": "model",
"owned_by": "api.riotpiao.com",
"created": 1700000000
},
{
"id": "ornith:35b",
"object": "model",
"owned_by": "api.riotpiao.com",
"created": 1700000000
},
{
"id": "qwen2.5:3b-instruct",
"object": "model",
"owned_by": "api.riotpiao.com",
"created": 1700000000
},
{
"id": "nomic-ai/nomic-embed-text-v2-moe",
"object": "model",
"owned_by": "api.riotpiao.com",
"created": 1700000000
},
{
"id": "BAAI/bge-reranker-base",
"object": "model",
"owned_by": "api.riotpiao.com",
"created": 1700000000
}
]
}
```
**Status Code:** 200
---
### POST /v1/chat/completions
Chat with an LLM model. Supports streaming, tool calling, and multi-turn conversations.
```bash
curl -X POST https://api.riotpiao.com/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "reasoning",
"messages": [
{"role": "user", "content": "What is 2+2?"}
]
}'
```
**Request Body:**
```json
{
"model": "reasoning",
"messages": [
{
"role": "user",
"content": "string or array"
}
],
"temperature": 0.7,
"top_p": 1.0,
"max_tokens": 2048,
"stream": false,
"tools": [
{
"type": "function",
"function": {
"name": "function_name",
"description": "What it does",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
}
]
}
```
**Response (non-streaming):**
```json
{
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1704000000,
"model": "reasoning",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "2+2=4"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 5,
"total_tokens": 15
}
}
```
**Response (streaming, SSE format):**
```
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"2"}}]}
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"+"}}]}
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"2"}}]}
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"="}}]}
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"4"}}]}
data: [DONE]
```
**Status Codes:**
- 200 OK
- 400 Bad Request (invalid model, missing fields, invalid JSON)
- 500 Internal Server Error (upstream issue)
**Available Models:**
- `reasoning` — DeepSeek-R1-Distill reasoning model (8 concurrent slots)
- `ornith:35b` — Ollama 35B model
- `qwen2.5:3b-instruct` — Qwen 2.5 3B model
---
### POST /v1/embeddings
Generate text embeddings.
```bash
curl -X POST https://api.riotpiao.com/v1/embeddings \
-H 'Content-Type: application/json' \
-d '{
"model": "nomic-ai/nomic-embed-text-v2-moe",
"input": ["text to embed", "another text"]
}'
```
**Request Body:**
```json
{
"model": "nomic-ai/nomic-embed-text-v2-moe",
"input": "string or array of strings",
"encoding_format": "float"
}
```
**Response:**
```json
{
"object": "list",
"data": [
{
"object": "embedding",
"embedding": [0.1, 0.2, 0.3, ...],
"index": 0
},
{
"object": "embedding",
"embedding": [0.4, 0.5, 0.6, ...],
"index": 1
}
],
"model": "nomic-ai/nomic-embed-text-v2-moe",
"usage": {
"prompt_tokens": 20,
"total_tokens": 20
}
}
```
**Status Codes:**
- 200 OK
- 400 Bad Request (invalid model or input)
- 500 Internal Server Error
---
### POST /v1/rerank
Rerank documents by relevance to a query.
```bash
curl -X POST https://api.riotpiao.com/v1/rerank \
-H 'Content-Type: application/json' \
-d '{
"model": "BAAI/bge-reranker-base",
"query": "machine learning",
"texts": [
"Machine learning is AI",
"Python is a language",
"Deep learning is ML"
],
"top_k": 2
}'
```
**Request Body:**
```json
{
"model": "BAAI/bge-reranker-base",
"query": "search query",
"texts": ["text1", "text2", "text3"],
"top_k": 2,
"return_documents": true
}
```
**Response:**
```json
{
"results": [
{
"index": 0,
"score": 0.95,
"text": "Machine learning is AI"
},
{
"index": 2,
"score": 0.85,
"text": "Deep learning is ML"
}
]
}
```
**Status Codes:**
- 200 OK
- 400 Bad Request
- 500 Internal Server Error
---
## Workflow Services (Temporal)
Base path: `/workflow`
All operations use REST with JSON body. Internally translated to gRPC (port 7233).
### POST /workflow
Execute a workflow operation via action dispatch.
```bash
curl -X POST https://api.riotpiao.com/workflow \
-H 'Content-Type: application/json' \
-d '{
"action": "START_WORKFLOW",
"namespace": "default",
"payload": {
"workflow_id": "order-123",
"workflow_type": "ProcessOrder",
"task_queue": "orders_queue",
"input": {
"order_id": "123",
"amount": 99.99
}
}
}'
```
**Supported Actions:**
#### START_WORKFLOW
Start a new workflow execution.
**Payload:**
```json
{
"workflow_id": "unique-id",
"workflow_type": "WorkflowName",
"task_queue": "queue_name",
"input": {"field": "value"},
"options": {
"workflow_execution_timeout": 3600,
"workflow_run_timeout": 1800,
"workflow_task_timeout": 300
}
}
```
**Response:**
```json
{
"success": true,
"action": "START_WORKFLOW",
"data": {
"workflow_id": "order-123",
"run_id": "abc123def",
"start_time": "2026-08-27T..."
}
}
```
#### DESCRIBE_WORKFLOW
Get workflow execution details.
**Payload:**
```json
{
"workflow_id": "order-123",
"run_id": "abc123def"
}
```
**Response:**
```json
{
"success": true,
"action": "DESCRIBE_WORKFLOW",
"data": {
"workflow_id": "order-123",
"run_id": "abc123def",
"status": "RUNNING",
"start_time": "2026-08-27T..."
}
}
```
#### LIST_WORKFLOWS
List workflow executions with filtering.
**Payload:**
```json
{
"status": "RUNNING",
"page_size": 50,
"next_page_token": ""
}
```
#### GET_WORKFLOW_HISTORY
Retrieve workflow execution history (events).
**Payload:**
```json
{
"workflow_id": "order-123",
"run_id": "abc123def",
"max_events": 100
}
```
#### SIGNAL_WORKFLOW
Send a signal to a running workflow (trigger handler).
**Payload:**
```json
{
"workflow_id": "order-123",
"run_id": "abc123def",
"signal_name": "payment_received",
"input": {"amount": 99.99}
}
```
#### QUERY_WORKFLOW
Query workflow state without modifying it (read-only).
**Payload:**
```json
{
"workflow_id": "order-123",
"run_id": "abc123def",
"query_type": "get_status"
}
```
#### CANCEL_WORKFLOW
Request graceful cancellation.
**Payload:**
```json
{
"workflow_id": "order-123",
"run_id": "abc123def"
}
```
#### TERMINATE_WORKFLOW
Stop workflow immediately.
**Payload:**
```json
{
"workflow_id": "order-123",
"run_id": "abc123def",
"reason": "User cancelled"
}
```
#### Other Actions
- RESET_WORKFLOW
- UPDATE_WORKFLOW
- HEARTBEAT_ACTIVITY
- COMPLETE_ACTIVITY
- FAIL_ACTIVITY
**Status Codes:**
- 200 OK
- 400 Bad Request (invalid action or fields)
- 404 Not Found (workflow not found)
- 503 Service Unavailable (Temporal unreachable)
---
## Queue Services (SQS)
Base path: `/sqs` (via X-Service header routing)
Operations via ServiceAdapter CRD. Use header-based dispatch:
```bash
curl -X GET https://api.riotpiao.com/ \
-H 'X-Service: sqs' \
-H 'X-Resource: queue/my-queue/messages'
```
**Supported Resources:**
- `queue` — List queues
- `queue/{name}/message` — Send/receive messages
- `queue/{name}/messages` — Batch operations
---
## Memory Services
Base path: `/` (via X-Service header)
Query and manage semantic memory for knowledge retrieval.
```bash
curl -X GET https://api.riotpiao.com/ \
-H 'X-Service: memory' \
-H 'X-Resource: query' \
-G --data-urlencode 'query=explain machine learning' \
--data-urlencode 'level=L1,L2'
```
**Supported Resources:**
- `query` — Search memory (GET with params)
- `skill` — List/retrieve skills
- `project` — List/describe projects
- `ingest` — Add new documents (POST, requires memory:write capability)
---
## IAM Services
Base path: `/` (via X-Service header)
Manage Authentik users, groups, and permissions.
```bash
curl -X GET https://api.riotpiao.com/ \
-H 'Authorization: Bearer <token>' \
-H 'X-Service: iam' \
-H 'X-Resource: user'
```
**Supported Resources:**
- `user` — List/create users
- `user/{id}` — Get/update/delete specific user
- `role` (group) — Manage groups/roles
- `permission` — List permissions
- `flow` — List authentication flows
---
## S3 Services
Base path: `/` (via X-Service header)
Object storage operations via MinIO.
```bash
curl -X GET https://api.riotpiao.com/ \
-H 'X-Service: s3' \
-H 'X-Resource: bucket/my-bucket/objects'
```
**Supported Resources:**
- `bucket` — List buckets
- `bucket/{name}/objects` — List objects in bucket
- `bucket/{name}/object/{key}` — Get/put/delete object
---
## Authentication
### Bearer Token (JWT)
All operations except `/healthz` and `/readyz` require authentication via Authentik-issued JWT.
```bash
curl -H 'Authorization: Bearer <jwt-token>' \
https://api.riotpiao.com/v1/models
```
### Obtaining Tokens
**Service Account (client_credentials flow):**
For programmatic access (agents, CI, scripts), use a service account:
```bash
# Get credentials from k8s secret (once)
CLIENT_ID=$(kubectl -n portfolio get secret portfolio-agent-oidc -o jsonpath='{.data.CLIENT_ID}' | base64 -d)
CLIENT_SECRET=$(kubectl -n portfolio get secret portfolio-agent-oidc -o jsonpath='{.data.CLIENT_SECRET}' | base64 -d)
# Exchange for access token
TOKEN=$(curl -s -X POST https://authentik.riotpiao.com/application/o/token/ \
-d "grant_type=client_credentials" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
-d "scope=openid profile groups permissions memory" | jq -r '.access_token')
# Use token
curl -H "Authorization: Bearer $TOKEN" \
https://api.riotpiao.com/v1/models
```
**Available service accounts:**
- `portfolio-agent` — Portfolio app (memory:read on homelab/portfolio projects)
- `memory-agent` — Memory service internal (memory:* on all projects)
**Human login (authorization_code flow):**
Browser-based OAuth2 via Authentik. Used by web apps, not CLI.
```
1. Redirect to: https://authentik.riotpiao.com/application/o/authorize/
?client_id=<app>&response_type=code&redirect_uri=<callback>&scope=openid+profile+groups+permissions
2. User authenticates
3. Exchange code for token at /application/o/token/
```
### JWT Claims
Authentik tokens include these custom claims:
```json
{
"sub": "hashed-user-id",
"groups": ["homelab-admins", "llm-admins"],
"permissions": ["llm:inference", "llm:read", "llm:write"],
"memory_projects": ["homelab", "portfolio"],
"memory_visibility": "public",
"memory_role": "user"
}
```
### Capabilities (RBAC)
The `permissions` claim contains capabilities. Gateway checks these:
| Capability | Required for | Granted to |
|------------|--------------|------------|
| `llm:inference` | `/v1/chat/completions`, `/v1/embeddings`, `/v1/rerank` | llm-admins, llm-users |
| `memory:read` | Memory queries | memory-users, memory-writers, poimen-memory-admins |
| `memory:write` | Memory ingest | memory-writers, poimen-memory-admins |
| `memory:admin` | Memory admin ops | poimen-memory-admins |
| `*` | Everything | homelab-admins |
**Memory-specific claims:**
| Claim | Description |
|-------|-------------|
| `memory_projects` | Projects user can access (`["*"]` = all) |
| `memory_visibility` | Max visibility level (`public`, `private`) |
| `memory_role` | Role for memory service (`user`, `admin`, `portfolio-agent`) |
---
## Error Handling
### Problem Details (RFC 9457)
All error responses use standardized JSON format:
```json
{
"type": "https://api.example.com/problems/unknown-model",
"title": "Unknown Model",
"status": 400,
"detail": "Model 'gpt-4' is not available. See valid_models for available options.",
"valid_models": ["reasoning", "ornith:35b", ...]
}
```
### HTTP Status Codes
| Status | Meaning |
|--------|---------|
| 200 | Success |
| 400 | Bad Request (validation error, invalid model, missing fields) |
| 401 | Unauthorized (invalid/missing token) |
| 403 | Forbidden (insufficient permissions/capability) |
| 404 | Not Found (workflow, resource) |
| 409 | Conflict (duplicate workflow_id) |
| 429 | Too Many Requests (rate limit exceeded) |
| 503 | Service Unavailable (backend unreachable) |
| 504 | Gateway Timeout (request exceeded timeout) |
### Error Examples
**Unknown Model:**
```bash
curl -X POST https://api.riotpiao.com/v1/chat/completions \
-d '{"model":"gpt-4","messages":[]}'
# Returns 400
{
"type": "https://api.example.com/problems/unknown-model",
"title": "Unknown Model",
"status": 400,
"detail": "Model 'gpt-4' is not available",
"valid_models": ["reasoning", "ornith:35b", "qwen2.5:3b-instruct", ...]
}
```
**Temporal Unavailable:**
```bash
curl -X POST https://api.riotpiao.com/workflow \
-d '{"action":"START_WORKFLOW",...}'
# Returns 503 if Temporal server unreachable
{
"success": false,
"error": "TEMPORAL_UNAVAILABLE",
"message": "Temporal server connection not available"
}
```
---
## Examples
### Complete RAG Pipeline
1. **Query embedding:**
```bash
curl -X POST https://api.riotpiao.com/v1/embeddings \
-H 'Authorization: Bearer $TOKEN' \
-d '{
"model": "nomic-ai/nomic-embed-text-v2-moe",
"input": "how do neural networks work"
}' | jq '.data[0].embedding'
```
2. **Rerank documents:**
```bash
curl -X POST https://api.riotpiao.com/v1/rerank \
-H 'Authorization: Bearer $TOKEN' \
-d '{
"model": "BAAI/bge-reranker-base",
"query": "how do neural networks work",
"texts": ["doc1", "doc2", "doc3"],
"top_k": 2
}'
```
3. **Chat with context:**
```bash
curl -X POST https://api.riotpiao.com/v1/chat/completions \
-H 'Authorization: Bearer $TOKEN' \
-d '{
"model": "reasoning",
"messages": [
{
"role": "user",
"content": "Based on these documents: [top-2 from rerank]\nQuestion: how do neural networks work?"
}
]
}'
```
### Workflow Orchestration
```bash
# Start workflow
WORKFLOW_ID="order-$(date +%s)"
curl -X POST https://api.riotpiao.com/workflow \
-H 'Authorization: Bearer $TOKEN' \
-d "{
\"action\": \"START_WORKFLOW\",
\"namespace\": \"default\",
\"payload\": {
\"workflow_id\": \"$WORKFLOW_ID\",
\"workflow_type\": \"ProcessOrder\",
\"task_queue\": \"orders\",
\"input\": {\"order_id\": \"123\"}
}
}"
# Monitor execution
curl -X POST https://api.riotpiao.com/workflow \
-H 'Authorization: Bearer $TOKEN' \
-d "{
\"action\": \"DESCRIBE_WORKFLOW\",
\"namespace\": \"default\",
\"payload\": {\"workflow_id\": \"$WORKFLOW_ID\"}
}"
# Send signal when payment received
curl -X POST https://api.riotpiao.com/workflow \
-H 'Authorization: Bearer $TOKEN' \
-d "{
\"action\": \"SIGNAL_WORKFLOW\",
\"namespace\": \"default\",
\"payload\": {
\"workflow_id\": \"$WORKFLOW_ID\",
\"signal_name\": \"payment_received\",
\"input\": {\"amount\": 99.99}
}
}"
```
---
## Rate Limits & Quotas
- `/v1/chat/completions` — 8 concurrent slots (reasoning model)
- `/v1/embeddings` — 10 concurrent
- `/v1/rerank` — 10 concurrent
- `/workflow` — 100 concurrent operations
- Retry-After header set on 429 responses
---
## Timeouts
| Endpoint | Connect | Read | Write |
|----------|---------|------|-------|
| `/v1/chat/completions` | 10s | 1h | 1h |
| `/v1/embeddings` | 10s | 10m | 10m |
| `/v1/rerank` | 10s | 10m | 10m |
| `/workflow` | 10s | 30s | 10s |
---
## Support
- **Health:** `curl https://api.riotpiao.com/healthz`
- **Readiness:** `curl https://api.riotpiao.com/readyz`
- **Models:** `curl https://api.riotpiao.com/v1/models`
- **Logs:** `kubectl -n api logs deployment/homelab-frontend`