Spaces:
Sleeping
Sleeping
Commit ·
0e79077
1
Parent(s): 34e1647
feat: added
Browse files
README.md
CHANGED
|
@@ -22,13 +22,24 @@ Text-only inference. Returns full response in one JSON body (no streaming).
|
|
| 22 |
|
| 23 |
**Response:** `{ "response": "..." }`
|
| 24 |
|
|
|
|
|
|
|
| 25 |
### `GET /health`
|
| 26 |
-
Health check and model load status.
|
| 27 |
|
| 28 |
## Usage
|
| 29 |
|
| 30 |
```bash
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
curl -X POST "http://localhost:7860/prompt" \
|
| 32 |
-H "Content-Type: application/json" \
|
|
|
|
| 33 |
-d '{"prompt": "What is 2+2?"}'
|
| 34 |
```
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
**Response:** `{ "response": "..." }`
|
| 24 |
|
| 25 |
+
**Auth:** If `API_KEY` env var is set, send it via header `X-API-Key: <key>` or `Authorization: Bearer <key>`. If unset, no auth.
|
| 26 |
+
|
| 27 |
### `GET /health`
|
| 28 |
+
Health check and model load status (no auth).
|
| 29 |
|
| 30 |
## Usage
|
| 31 |
|
| 32 |
```bash
|
| 33 |
+
# Without API key (when API_KEY is not set)
|
| 34 |
+
curl -X POST "http://localhost:7860/prompt" \
|
| 35 |
+
-H "Content-Type: application/json" \
|
| 36 |
+
-d '{"prompt": "What is 2+2?"}'
|
| 37 |
+
|
| 38 |
+
# With API key
|
| 39 |
curl -X POST "http://localhost:7860/prompt" \
|
| 40 |
-H "Content-Type: application/json" \
|
| 41 |
+
-H "X-API-Key: your-secret-key" \
|
| 42 |
-d '{"prompt": "What is 2+2?"}'
|
| 43 |
```
|
| 44 |
+
|
| 45 |
+
Set `API_KEY` in the environment to enable protection (e.g. `API_KEY=your-secret-key node server.js`).
|
server.js
CHANGED
|
@@ -9,6 +9,7 @@ import crypto from "crypto";
|
|
| 9 |
const app = express();
|
| 10 |
const PORT = 7860;
|
| 11 |
const MODEL_ID = "huggingworld/Qwen3.5-0.8B-ONNX";
|
|
|
|
| 12 |
|
| 13 |
let model = null;
|
| 14 |
let processor = null;
|
|
@@ -87,6 +88,16 @@ const swaggerDoc = {
|
|
| 87 |
version: "1.0.0",
|
| 88 |
description: "Text inference API using Qwen3.5-0.8B ONNX with transformers.js",
|
| 89 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
paths: {
|
| 91 |
"/": {
|
| 92 |
get: {
|
|
@@ -121,13 +132,28 @@ const swaggerDoc = {
|
|
| 121 |
responses: {
|
| 122 |
200: { description: "Inference result" },
|
| 123 |
400: { description: "Invalid input" },
|
|
|
|
| 124 |
503: { description: "Model not loaded" },
|
| 125 |
},
|
|
|
|
| 126 |
},
|
| 127 |
},
|
| 128 |
},
|
| 129 |
};
|
| 130 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerDoc));
|
| 132 |
|
| 133 |
app.use((req, res, next) => {
|
|
@@ -165,7 +191,7 @@ app.get("/health", (req, res) => {
|
|
| 165 |
res.json({ status: "healthy", model_loaded: model !== null });
|
| 166 |
});
|
| 167 |
|
| 168 |
-
app.post("/prompt", express.json(), async (req, res) => {
|
| 169 |
const prompt = req.body.prompt;
|
| 170 |
const maxTokens = parseInt(req.body.max_tokens) || 256;
|
| 171 |
log("info", "prompt_request_received", {
|
|
|
|
| 9 |
const app = express();
|
| 10 |
const PORT = 7860;
|
| 11 |
const MODEL_ID = "huggingworld/Qwen3.5-0.8B-ONNX";
|
| 12 |
+
const API_KEY = process.env.API_KEY;
|
| 13 |
|
| 14 |
let model = null;
|
| 15 |
let processor = null;
|
|
|
|
| 88 |
version: "1.0.0",
|
| 89 |
description: "Text inference API using Qwen3.5-0.8B ONNX with transformers.js",
|
| 90 |
},
|
| 91 |
+
components: {
|
| 92 |
+
securitySchemes: {
|
| 93 |
+
ApiKeyAuth: {
|
| 94 |
+
type: "apiKey",
|
| 95 |
+
in: "header",
|
| 96 |
+
name: "X-API-Key",
|
| 97 |
+
description: "Set API_KEY env var; send as X-API-Key or Authorization: Bearer <key>",
|
| 98 |
+
},
|
| 99 |
+
},
|
| 100 |
+
},
|
| 101 |
paths: {
|
| 102 |
"/": {
|
| 103 |
get: {
|
|
|
|
| 132 |
responses: {
|
| 133 |
200: { description: "Inference result" },
|
| 134 |
400: { description: "Invalid input" },
|
| 135 |
+
401: { description: "Invalid or missing API key" },
|
| 136 |
503: { description: "Model not loaded" },
|
| 137 |
},
|
| 138 |
+
security: [{ ApiKeyAuth: [] }],
|
| 139 |
},
|
| 140 |
},
|
| 141 |
},
|
| 142 |
};
|
| 143 |
|
| 144 |
+
function requireApiKey(req, res, next) {
|
| 145 |
+
if (!API_KEY) return next();
|
| 146 |
+
const bearer = req.headers.authorization?.startsWith("Bearer ")
|
| 147 |
+
? req.headers.authorization.slice(7)
|
| 148 |
+
: null;
|
| 149 |
+
const key = bearer ?? req.headers["x-api-key"] ?? null;
|
| 150 |
+
if (key !== API_KEY) {
|
| 151 |
+
log("warn", "api_key_rejected", { request_id: req.requestId, path: req.path });
|
| 152 |
+
return res.status(401).json({ detail: "Invalid or missing API key." });
|
| 153 |
+
}
|
| 154 |
+
next();
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerDoc));
|
| 158 |
|
| 159 |
app.use((req, res, next) => {
|
|
|
|
| 191 |
res.json({ status: "healthy", model_loaded: model !== null });
|
| 192 |
});
|
| 193 |
|
| 194 |
+
app.post("/prompt", requireApiKey, express.json(), async (req, res) => {
|
| 195 |
const prompt = req.body.prompt;
|
| 196 |
const maxTokens = parseInt(req.body.max_tokens) || 256;
|
| 197 |
log("info", "prompt_request_received", {
|