|
1 | | -from fastapi import FastAPI |
| 1 | +from fastapi import FastAPI, Request |
2 | 2 | from fastapi.responses import HTMLResponse, PlainTextResponse, Response |
3 | 3 |
|
4 | 4 | from app.discovery import ( |
5 | 5 | AGENTS_MD, |
| 6 | + DISCOVERY_BODY_HTML, |
6 | 7 | HEAD_DISCOVERY_HTML, |
7 | 8 | LLMS_FULL_TXT, |
8 | 9 | LLMS_TXT, |
9 | 10 | PLAYGROUND_MD, |
| 11 | + RELEASE_VERSION, |
10 | 12 | ROBOTS_TXT, |
11 | 13 | SITEMAP_MD, |
12 | 14 | SITEMAP_XML, |
|
23 | 25 |
|
24 | 26 | app = FastAPI( |
25 | 27 | title="Safe Agent API", |
26 | | - version="0.5.0", |
| 28 | + version=RELEASE_VERSION, |
27 | 29 | description="Deterministic policy, approval and audit layer for agent tool execution demos.", |
28 | 30 | ) |
29 | 31 |
|
30 | 32 |
|
31 | | -@app.get("/", response_class=HTMLResponse, include_in_schema=False) |
32 | | -def playground() -> HTMLResponse: |
| 33 | +MARKDOWN_HEADERS = { |
| 34 | + "Link": '</>; rel="canonical", </sitemap.md>; rel="sitemap"', |
| 35 | + "X-Robots-Tag": "index, follow, max-snippet:-1, max-image-preview:large", |
| 36 | +} |
| 37 | + |
| 38 | + |
| 39 | +@app.api_route("/", methods=["GET", "HEAD"], response_class=HTMLResponse, include_in_schema=False) |
| 40 | +def playground(request: Request) -> Response: |
| 41 | + accept = request.headers.get("accept", "").lower() |
| 42 | + if request.method == "GET" and "text/markdown" in accept: |
| 43 | + return PlainTextResponse( |
| 44 | + PLAYGROUND_MD, |
| 45 | + media_type="text/markdown", |
| 46 | + headers=MARKDOWN_HEADERS, |
| 47 | + ) |
| 48 | + |
33 | 49 | html = PLAYGROUND_HTML.replace("</head>", f"{HEAD_DISCOVERY_HTML}</head>") |
| 50 | + html = html.replace('<p class="footer">', f'{DISCOVERY_BODY_HTML}<p class="footer">') |
34 | 51 | return HTMLResponse( |
35 | 52 | html, |
36 | 53 | headers={ |
37 | 54 | "Cache-Control": "no-store", |
38 | 55 | "Content-Security-Policy": ( |
39 | 56 | "default-src 'self'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; " |
40 | | - "connect-src 'self'; img-src 'self' data:; frame-ancestors 'none'" |
| 57 | + "connect-src 'self'; img-src 'self' data: https://raw.githubusercontent.com; " |
| 58 | + "frame-ancestors 'none'" |
41 | 59 | ), |
42 | | - "Link": '</llms.txt>; rel="describedby", </playground.md>; rel="alternate"; type="text/markdown"', |
| 60 | + "Link": '</llms.txt>; rel="describedby", </index.md>; rel="alternate"; type="text/markdown"', |
43 | 61 | "Referrer-Policy": "no-referrer", |
44 | 62 | "X-Content-Type-Options": "nosniff", |
| 63 | + "X-Robots-Tag": "index, follow, max-snippet:-1, max-image-preview:large", |
45 | 64 | }, |
46 | 65 | ) |
47 | 66 |
|
48 | 67 |
|
49 | 68 | @app.get("/health") |
50 | 69 | def health() -> dict[str, str]: |
51 | | - return {"status": "ok", "service": "safe-agent-api", "version": "0.5.0"} |
| 70 | + return {"status": "ok", "service": "safe-agent-api", "version": RELEASE_VERSION} |
52 | 71 |
|
53 | 72 |
|
54 | 73 | @app.get("/llms.txt", response_class=PlainTextResponse, include_in_schema=False) |
@@ -81,12 +100,13 @@ def agents_md() -> PlainTextResponse: |
81 | 100 | return PlainTextResponse(AGENTS_MD, media_type="text/markdown") |
82 | 101 |
|
83 | 102 |
|
| 103 | +@app.get("/index.md", response_class=PlainTextResponse, include_in_schema=False) |
84 | 104 | @app.get("/playground.md", response_class=PlainTextResponse, include_in_schema=False) |
85 | 105 | def playground_md() -> PlainTextResponse: |
86 | 106 | return PlainTextResponse( |
87 | 107 | PLAYGROUND_MD, |
88 | 108 | media_type="text/markdown", |
89 | | - headers={"Link": '</sitemap.md>; rel="sitemap"'}, |
| 109 | + headers=MARKDOWN_HEADERS, |
90 | 110 | ) |
91 | 111 |
|
92 | 112 |
|
|
0 commit comments