|
| 1 | +"""llms.txt endpoint: GET /llms.txt |
| 2 | +
|
| 3 | +Serves a plain-text orientation document for AI agents, following the |
| 4 | +llms.txt convention (analogous to robots.txt). Describes what LNT is, |
| 5 | +its domain concepts, API structure, and common workflows. |
| 6 | +
|
| 7 | +Registered as a plain Flask blueprint (not flask-smorest) so it does not |
| 8 | +appear in the OpenAPI spec. |
| 9 | +""" |
| 10 | + |
| 11 | +from flask import Blueprint, make_response |
| 12 | +import hashlib |
| 13 | + |
| 14 | +llms_txt_bp = Blueprint('llms_txt', __name__) |
| 15 | + |
| 16 | +LLMS_TEXT = """\ |
| 17 | +# LNT — LLVM Nightly Test Infrastructure |
| 18 | +
|
| 19 | +LNT is a performance testing infrastructure designed for tracking software |
| 20 | +performance over time. It collects benchmark results from test runs, detects |
| 21 | +regressions, and provides tools for performance analysis. Originally built |
| 22 | +for the LLVM compiler project, it can be used for any software project. |
| 23 | +
|
| 24 | +## Key Concepts |
| 25 | +
|
| 26 | +- **Test Suite**: A schema defining what metrics to collect (e.g., "nts" for |
| 27 | + the LLVM nightly test suite). Each suite has its own set of machines, orders, |
| 28 | + runs, and tests. All data queries are scoped to a specific test suite. |
| 29 | +
|
| 30 | +- **Machine**: A build/test environment identified by name (e.g., |
| 31 | + "clang-x86_64-linux"). Machines have key-value info fields describing |
| 32 | + their configuration. |
| 33 | +
|
| 34 | +- **Order**: A point in the revision history (e.g., a commit hash or revision |
| 35 | + number). Orders define the sequence for time-series analysis. They may have |
| 36 | + multiple fields (e.g., primary revision + dependent project revisions). |
| 37 | +
|
| 38 | +- **Run**: A single test execution on a machine at a specific order. Contains |
| 39 | + samples (individual test results) with metric values. Identified by UUID. |
| 40 | +
|
| 41 | +- **Test**: A named benchmark or test case (e.g., "SingleSource/Benchmarks/ |
| 42 | + Dhrystone/dry"). Tests are created implicitly when runs are submitted. |
| 43 | +
|
| 44 | +- **Sample**: A single data point: one test's metric values from one run. |
| 45 | + Each sample records values for the metrics defined by the test suite schema |
| 46 | + (e.g., execution_time, compile_time, code_size). |
| 47 | +
|
| 48 | +- **Regression**: A detected performance change, grouping one or more field |
| 49 | + changes. Has a state (detected, active, fixed, ignored, etc.), optional |
| 50 | + title, and optional bug tracker link. |
| 51 | +
|
| 52 | +- **Field Change**: A statistically significant change in a metric value |
| 53 | + between two orders for a specific test on a specific machine. |
| 54 | +
|
| 55 | +## REST API (v5) |
| 56 | +
|
| 57 | +Base URL: /api/v5/ |
| 58 | +Authentication: Bearer token in Authorization header. Reads are unauthenticated |
| 59 | +by default. Write operations require tokens with appropriate scopes |
| 60 | +(submit, triage, manage, admin). |
| 61 | +
|
| 62 | +### Discovery |
| 63 | +
|
| 64 | + GET /api/v5/ List test suites and API links |
| 65 | +
|
| 66 | +### Per-Suite Endpoints (replace {ts} with suite name, e.g., "nts") |
| 67 | +
|
| 68 | + GET /api/v5/{ts}/machines List machines |
| 69 | + GET /api/v5/{ts}/machines/{name} Machine detail |
| 70 | + GET /api/v5/{ts}/orders List orders |
| 71 | + GET /api/v5/{ts}/orders/{value} Order detail (with prev/next) |
| 72 | + GET /api/v5/{ts}/runs List runs |
| 73 | + POST /api/v5/{ts}/runs Submit a run |
| 74 | + GET /api/v5/{ts}/runs/{uuid} Run detail |
| 75 | + GET /api/v5/{ts}/tests List tests |
| 76 | + POST /api/v5/{ts}/query Query time-series data |
| 77 | + GET /api/v5/{ts}/regressions List regressions |
| 78 | + GET /api/v5/{ts}/field-changes List unassigned field changes |
| 79 | +
|
| 80 | +### Global Endpoints |
| 81 | +
|
| 82 | + GET /api/v5/test-suites List all test suites |
| 83 | + GET /api/v5/test-suites/{name} Suite detail (schema + metrics) |
| 84 | + GET /api/v5/admin/api-keys List API keys (admin) |
| 85 | +
|
| 86 | +The endpoints above cover the most common read operations. The API also |
| 87 | +supports write operations (creating/updating/deleting machines, orders, |
| 88 | +runs, regressions, field changes, test suites, and API keys) which require |
| 89 | +appropriate authentication scopes. See the OpenAPI spec or Swagger UI for |
| 90 | +the complete endpoint list including all write operations. |
| 91 | +
|
| 92 | +### Interactive Documentation |
| 93 | +
|
| 94 | + OpenAPI spec: /api/v5/openapi/openapi.json |
| 95 | + Swagger UI: /api/v5/openapi/swagger-ui |
| 96 | +
|
| 97 | +### Pagination |
| 98 | +
|
| 99 | +List endpoints return cursor-paginated responses: |
| 100 | + { "items": [...], "cursor": { "next": "...", "previous": null } } |
| 101 | +Pass cursor=<next> to get the next page. Use limit= to control page size. |
| 102 | +
|
| 103 | +### Common Workflows |
| 104 | +
|
| 105 | +1. Discover available data: GET /api/v5/ to list test suites, then |
| 106 | + GET /api/v5/{ts}/machines to see what machines exist. |
| 107 | +
|
| 108 | +2. Query performance history: POST /api/v5/{ts}/query with |
| 109 | + { "metric": "execution_time", "machine": "machine-name", |
| 110 | + "test": ["test/name"] } to get time-series data points. |
| 111 | +
|
| 112 | +3. Submit a run: POST /api/v5/{ts}/runs with the LNT JSON report format. |
| 113 | + Requires a token with "submit" scope. |
| 114 | +
|
| 115 | +4. Check for regressions: GET /api/v5/{ts}/regressions?state=detected |
| 116 | + to find new regressions. PATCH to update state, title, or bug link. |
| 117 | +
|
| 118 | +5. Inspect a specific order: GET /api/v5/{ts}/orders/{value} returns |
| 119 | + the order detail with previous/next navigation links. |
| 120 | +""" |
| 121 | + |
| 122 | +_ETAG = hashlib.md5(LLMS_TEXT.encode()).hexdigest() |
| 123 | + |
| 124 | + |
| 125 | +@llms_txt_bp.route('/llms.txt') |
| 126 | +def llms_txt(): |
| 127 | + """Serve the LNT orientation document for AI agents.""" |
| 128 | + resp = make_response(LLMS_TEXT) |
| 129 | + resp.mimetype = 'text/plain' |
| 130 | + resp.charset = 'utf-8' |
| 131 | + resp.headers['Cache-Control'] = 'public, max-age=86400' |
| 132 | + resp.headers['ETag'] = _ETAG |
| 133 | + return resp |
0 commit comments