Skip to content

Commit 6cd87aa

Browse files
feat(api): add OpenAPI 3.1 spec with Swagger UI at /docs (#91)
- OpenAPI 3.1 spec covering all endpoints: dedupe, pipeline, batch, memory (store/recall/forget/expire/supersede/stats), sessions, health - Swagger UI served at /docs via CDN (no dependencies) - Raw spec available at /openapi.yaml - Spec also at repo root for discoverability - Root endpoint updated with docs link and full endpoint list Closes #23 Co-authored-by: Ona <no-reply@ona.com>
1 parent ef6b401 commit 6cd87aa

3 files changed

Lines changed: 1900 additions & 2 deletions

File tree

cmd/api.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cmd
22

33
import (
44
"context"
5+
_ "embed"
56
"encoding/json"
67
"fmt"
78
"net/http"
@@ -25,6 +26,9 @@ import (
2526
"github.com/spf13/viper"
2627
)
2728

29+
//go:embed openapi.yaml
30+
var openapiSpec []byte
31+
2832
var apiCmd = &cobra.Command{
2933
Use: "api",
3034
Short: "Start the Distill API server (standalone, no vector DB required)",
@@ -274,6 +278,8 @@ func runAPI(cmd *cobra.Command, args []string) error {
274278
mux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
275279
m.Handler().ServeHTTP(w, r)
276280
})
281+
mux.HandleFunc("/openapi.yaml", server.handleOpenAPISpec)
282+
mux.HandleFunc("/docs", server.handleDocs)
277283
mux.HandleFunc("/", server.handleRoot)
278284

279285
// CORS middleware
@@ -349,17 +355,53 @@ func (s *APIServer) handleRoot(w http.ResponseWriter, r *http.Request) {
349355
w.Header().Set("Content-Type", "application/json")
350356
_ = json.NewEncoder(w).Encode(map[string]interface{}{
351357
"name": "Distill API",
352-
"version": "1.0.0",
353-
"docs": "https://distill.siddhantkhare.com/docs",
358+
"version": "0.9.0",
359+
"docs": "/docs",
360+
"openapi": "/openapi.yaml",
354361
"endpoints": map[string]string{
355362
"dedupe": "POST /v1/dedupe",
356363
"dedupe_stream": "POST /v1/dedupe/stream",
364+
"pipeline": "POST /v1/pipeline",
365+
"memory_store": "POST /v1/memory/store",
366+
"memory_recall": "POST /v1/memory/recall",
357367
"health": "GET /health",
358368
"metrics": "GET /metrics",
359369
},
360370
})
361371
}
362372

373+
func (s *APIServer) handleOpenAPISpec(w http.ResponseWriter, r *http.Request) {
374+
w.Header().Set("Content-Type", "application/yaml")
375+
w.Header().Set("Access-Control-Allow-Origin", "*")
376+
_, _ = w.Write(openapiSpec)
377+
}
378+
379+
func (s *APIServer) handleDocs(w http.ResponseWriter, r *http.Request) {
380+
w.Header().Set("Content-Type", "text/html; charset=utf-8")
381+
_, _ = w.Write([]byte(`<!DOCTYPE html>
382+
<html>
383+
<head>
384+
<title>Distill API Docs</title>
385+
<meta charset="utf-8"/>
386+
<meta name="viewport" content="width=device-width, initial-scale=1">
387+
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
388+
</head>
389+
<body>
390+
<div id="swagger-ui"></div>
391+
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
392+
<script>
393+
SwaggerUIBundle({
394+
url: "/openapi.yaml",
395+
dom_id: "#swagger-ui",
396+
deepLinking: true,
397+
presets: [SwaggerUIBundle.presets.apis, SwaggerUIBundle.SwaggerUIStandalonePreset],
398+
layout: "BaseLayout"
399+
});
400+
</script>
401+
</body>
402+
</html>`))
403+
}
404+
363405
func (s *APIServer) handleDedupe(w http.ResponseWriter, r *http.Request) {
364406
if r.Method != http.MethodPost {
365407
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)

0 commit comments

Comments
 (0)