Scaffold agent API with Railway deployment

FastAPI server with /health, /agents, /jobs endpoints.
Includes railway.toml, Dockerfile, CI/CD workflows, and pytest tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Alexa Amundson
2026-02-20 16:04:15 -06:00
parent fa92a71129
commit 96c3d04dc6
11 changed files with 208 additions and 0 deletions

26
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install --upgrade pip
pip install -r requirements.txt
pip install pytest
- name: Validate syntax
run: find . -name "*.py" -type f -exec python3 -m py_compile {} \;
- name: Test
run: pytest tests/ -v

37
.github/workflows/deploy-railway.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Deploy to Railway
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy:
name: Deploy to Railway
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Railway CLI
run: npm install -g @railway/cli
- name: Deploy to Railway
run: railway up --detach
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
RAILWAY_PROJECT_ID: ${{ secrets.RAILWAY_PROJECT_ID }}
- name: Wait for deployment
run: sleep 30
- name: Health Check
if: success()
run: |
HEALTH_URL="${{ secrets.RAILWAY_SERVICE_URL }}/health"
if [ -n "${{ secrets.RAILWAY_SERVICE_URL }}" ]; then
echo "Checking health at: $HEALTH_URL"
curl -f --retry 3 --retry-delay 10 "$HEALTH_URL" || echo "Health check pending"
else
echo "RAILWAY_SERVICE_URL not set yet, skipping health check"
fi

1
.python-version Normal file
View File

@@ -0,0 +1 @@
3.11

7
Dockerfile Normal file
View File

@@ -0,0 +1,7 @@
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8080
CMD ["python", "server.py"]

View File

@@ -1,2 +1,33 @@
# blackroad-agents # blackroad-agents
Agent definitions, prompts, and orchestration schemas for BlackRoad OS. Agent definitions, prompts, and orchestration schemas for BlackRoad OS.
## Quick Start
```bash
pip install -r requirements.txt
python server.py # Start server on :8080
pytest tests/ -v # Run tests
```
## Endpoints
| Method | Path | Description |
|--------|------|-------------|
| GET | `/health` | Health check |
| GET | `/agents` | List agents |
| POST | `/jobs` | Submit agent job |
## Deployment
Deploys to Railway on push to `main`. See `railway.toml` for config.
## Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `PORT` | `8080` | Server port |
## License
Proprietary - BlackRoad OS, Inc. All rights reserved.

1
agent/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""BlackRoad Agent API package."""

46
agent/api.py Normal file
View File

@@ -0,0 +1,46 @@
"""FastAPI application for BlackRoad Agent API."""
from __future__ import annotations
from datetime import datetime, timezone
from typing import Any
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(title="BlackRoad Agent API", version="0.1.0")
VERSION = "0.1.0"
class JobRequest(BaseModel):
agent: str
task: str
payload: dict[str, Any] | None = None
@app.get("/health")
@app.get("/healthz")
def healthcheck() -> dict[str, Any]:
return {
"status": "ok",
"service": "blackroad-agents",
"version": VERSION,
"timestamp": datetime.now(timezone.utc).isoformat(),
}
@app.get("/agents")
def list_agents() -> dict[str, Any]:
return {"status": "ok", "agents": []}
@app.post("/jobs")
def submit_job(request: JobRequest) -> dict[str, Any]:
return {
"status": "ok",
"job_id": None,
"agent": request.agent,
"task": request.task,
"message": "Job submission not yet implemented",
}

9
railway.toml Normal file
View File

@@ -0,0 +1,9 @@
[build]
builder = "NIXPACKS"
[deploy]
startCommand = "python server.py"
healthcheckPath = "/health"
healthcheckTimeout = 300
restartPolicyType = "ON_FAILURE"
restartPolicyMaxRetries = 10

4
requirements.txt Normal file
View File

@@ -0,0 +1,4 @@
fastapi>=0.115.0
uvicorn>=0.34.0
pydantic>=2.0.0
httpx>=0.27.0

9
server.py Normal file
View File

@@ -0,0 +1,9 @@
"""BlackRoad Agents API Server."""
import os
import uvicorn
from agent.api import app
if __name__ == "__main__":
port = int(os.environ.get("PORT", 8080))
uvicorn.run(app, host="0.0.0.0", port=port)

37
tests/test_api.py Normal file
View File

@@ -0,0 +1,37 @@
"""Tests for the BlackRoad Agent API."""
from fastapi.testclient import TestClient
from agent.api import app
client = TestClient(app)
def test_health():
response = client.get("/health")
assert response.status_code == 200
data = response.json()
assert data["status"] == "ok"
assert data["service"] == "blackroad-agents"
def test_healthz():
response = client.get("/healthz")
assert response.status_code == 200
def test_list_agents():
response = client.get("/agents")
assert response.status_code == 200
data = response.json()
assert isinstance(data["agents"], list)
def test_submit_job():
response = client.post("/jobs", json={
"agent": "test-agent",
"task": "test-task",
})
assert response.status_code == 200
data = response.json()
assert data["status"] == "ok"
assert data["agent"] == "test-agent"