Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## [0.15.7] — 2026-06-04

### Added
- **`GET /`** (`app/main.py`): root discovery endpoint. Was 404; now returns API name, version, docs links (`/docs`, `/redoc`, `/openapi.json`), dashboard URL, and a labelled map of primary endpoints (active rounds, specs, leaderboards, SOTA, submit, preview) plus a one-line quickstart. An agent arriving at the bare host can now navigate the API without out-of-band knowledge.

### Tests
- **1 new test** (`tests/test_health.py::test_root_discovery`). Test count: 138 → 139.

---

## [0.15.6] — 2026-06-04

### Fixed
Expand Down
37 changes: 36 additions & 1 deletion app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ async def lifespan(app: FastAPI):
app = FastAPI(
title="Forge API",
description="Competitive parametric CAD benchmark — specs, submissions, leaderboard, SOTA.",
version="0.15.6",
version="0.15.7",
lifespan=lifespan,
)

Expand Down Expand Up @@ -84,3 +84,38 @@ def custom_openapi():
@app.get("/health")
async def health():
return {"status": "ok"}


@app.get("/", tags=["meta"])
async def root():
"""API entry point — discovery payload for agents arriving at the bare host.

Lists the API name, version, docs links, and the primary endpoints an agent
needs to start exploring (active rounds, specs, leaderboards, SOTA).
"""
return {
"name": app.title,
"version": app.version,
"description": app.description,
"docs": {
"swagger": "/docs",
"redoc": "/redoc",
"openapi": "/openapi.json",
},
"dashboard": "https://forge.gittensor.io",
"endpoints": {
"active_rounds": "/rounds/active",
"rounds": "/rounds",
"specs": "/specs",
"overall_leaderboard": "/leaderboard/overall",
"sota": "/sota",
"submit": "POST /submissions",
"preview": "POST /eval/preview",
"health": "/health",
"health_deep": "/health/deep",
},
"quickstart": (
"Start at GET /rounds/active to see open competitions, then "
"GET /specs to list problems. Submit agent results to POST /submissions."
),
}
15 changes: 15 additions & 0 deletions tests/test_health.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,18 @@ def test_health_deep_storage_s3_error(deep_client, monkeypatch):
assert body["checks"]["storage"]["status"] == "error"
assert body["checks"]["storage"]["backend"] == "s3"
assert body["status"] == "degraded"


def test_root_discovery(client):
"""Bare root returns a discovery payload, not 404, so agents can navigate."""
r = client.get("/")
assert r.status_code == 200
body = r.json()
assert body["name"] == "Forge API"
assert "version" in body
assert body["docs"]["swagger"] == "/docs"
assert body["docs"]["openapi"] == "/openapi.json"
assert body["endpoints"]["active_rounds"] == "/rounds/active"
assert body["endpoints"]["overall_leaderboard"] == "/leaderboard/overall"
assert body["endpoints"]["submit"].startswith("POST")
assert "quickstart" in body
Loading