Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
| 1 |
from fastapi import FastAPI, Request, HTTPException, Depends
|
| 2 |
from fastapi.security import APIKeyHeader
|
| 3 |
from fastapi.middleware.cors import CORSMiddleware
|
| 4 |
-
from fastapi.responses import JSONResponse
|
|
|
|
| 5 |
import os
|
| 6 |
import json
|
| 7 |
import time
|
|
@@ -17,10 +18,30 @@ app.add_middleware(
|
|
| 17 |
allow_headers=["*"],
|
| 18 |
)
|
| 19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
API_KEY = os.environ.get("QWEN_API_KEY")
|
| 21 |
MODEL_PATH = "/models/Qwen2.5-7B-Instruct-Q4_K_M.gguf"
|
| 22 |
|
| 23 |
-
|
| 24 |
print(f"Loading model from {MODEL_PATH}...")
|
| 25 |
llm = Llama(
|
| 26 |
model_path=MODEL_PATH,
|
|
@@ -36,10 +57,55 @@ api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
|
|
| 36 |
async def verify_key(api_key: str = Depends(api_key_header)):
|
| 37 |
if not API_KEY:
|
| 38 |
raise HTTPException(status_code=500, detail="QWEN_API_KEY not set in HF Secrets")
|
| 39 |
-
if api_key!= API_KEY:
|
| 40 |
raise HTTPException(status_code=401, detail="Invalid API key")
|
| 41 |
return api_key
|
| 42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
@app.get("/health")
|
| 44 |
async def health():
|
| 45 |
return {
|
|
@@ -49,7 +115,6 @@ async def health():
|
|
| 49 |
}
|
| 50 |
|
| 51 |
def format_prompt(messages):
|
| 52 |
-
"""ChatML format dla Qwena"""
|
| 53 |
prompt = ""
|
| 54 |
for msg in messages:
|
| 55 |
role = msg["role"]
|
|
@@ -61,7 +126,6 @@ def format_prompt(messages):
|
|
| 61 |
@app.post("/v1/chat/completions")
|
| 62 |
async def chat_completions(request: Request, api_key: str = Depends(verify_key)):
|
| 63 |
body = await request.json()
|
| 64 |
-
|
| 65 |
messages = body.get("messages", [])
|
| 66 |
max_tokens = body.get("max_tokens", 512)
|
| 67 |
temperature = body.get("temperature", 0.7)
|
|
@@ -71,7 +135,6 @@ async def chat_completions(request: Request, api_key: str = Depends(verify_key))
|
|
| 71 |
raise HTTPException(status_code=400, detail="messages is required")
|
| 72 |
|
| 73 |
prompt = format_prompt(messages)
|
| 74 |
-
|
| 75 |
output = llm(
|
| 76 |
prompt,
|
| 77 |
max_tokens=max_tokens,
|
|
@@ -97,4 +160,4 @@ async def chat_completions(request: Request, api_key: str = Depends(verify_key))
|
|
| 97 |
"completion_tokens": output["usage"]["completion_tokens"],
|
| 98 |
"total_tokens": output["usage"]["total_tokens"]
|
| 99 |
}
|
| 100 |
-
})
|
|
|
|
| 1 |
from fastapi import FastAPI, Request, HTTPException, Depends
|
| 2 |
from fastapi.security import APIKeyHeader
|
| 3 |
from fastapi.middleware.cors import CORSMiddleware
|
| 4 |
+
from fastapi.responses import JSONResponse, HTMLResponse
|
| 5 |
+
from fastapi.openapi.utils import get_openapi
|
| 6 |
import os
|
| 7 |
import json
|
| 8 |
import time
|
|
|
|
| 18 |
allow_headers=["*"],
|
| 19 |
)
|
| 20 |
|
| 21 |
+
def custom_openapi():
|
| 22 |
+
if app.openapi_schema:
|
| 23 |
+
return app.openapi_schema
|
| 24 |
+
schema = get_openapi(
|
| 25 |
+
title="Qwen API",
|
| 26 |
+
version="0.1.0",
|
| 27 |
+
routes=app.routes,
|
| 28 |
+
)
|
| 29 |
+
schema["components"]["securitySchemes"] = {
|
| 30 |
+
"ApiKeyAuth": {
|
| 31 |
+
"type": "apiKey",
|
| 32 |
+
"in": "header",
|
| 33 |
+
"name": "X-API-Key"
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
schema["security"] = [{"ApiKeyAuth": []}]
|
| 37 |
+
app.openapi_schema = schema
|
| 38 |
+
return schema
|
| 39 |
+
|
| 40 |
+
app.openapi = custom_openapi
|
| 41 |
+
|
| 42 |
API_KEY = os.environ.get("QWEN_API_KEY")
|
| 43 |
MODEL_PATH = "/models/Qwen2.5-7B-Instruct-Q4_K_M.gguf"
|
| 44 |
|
|
|
|
| 45 |
print(f"Loading model from {MODEL_PATH}...")
|
| 46 |
llm = Llama(
|
| 47 |
model_path=MODEL_PATH,
|
|
|
|
| 57 |
async def verify_key(api_key: str = Depends(api_key_header)):
|
| 58 |
if not API_KEY:
|
| 59 |
raise HTTPException(status_code=500, detail="QWEN_API_KEY not set in HF Secrets")
|
| 60 |
+
if api_key != API_KEY:
|
| 61 |
raise HTTPException(status_code=401, detail="Invalid API key")
|
| 62 |
return api_key
|
| 63 |
|
| 64 |
+
@app.get("/", response_class=HTMLResponse)
|
| 65 |
+
async def ui():
|
| 66 |
+
return """
|
| 67 |
+
<!DOCTYPE html>
|
| 68 |
+
<html>
|
| 69 |
+
<head>
|
| 70 |
+
<title>Qwen API Tester</title>
|
| 71 |
+
<style>
|
| 72 |
+
body { font-family: Arial; max-width: 800px; margin: 40px auto; padding: 20px; background: #1a1a1a; color: #fff; }
|
| 73 |
+
input, textarea { width: 100%; padding: 10px; margin: 8px 0; background: #2a2a2a; color: #fff; border: 1px solid #444; border-radius: 6px; box-sizing: border-box; }
|
| 74 |
+
button { background: #ff6b35; color: white; padding: 12px 24px; border: none; border-radius: 6px; cursor: pointer; font-size: 16px; }
|
| 75 |
+
pre { background: #2a2a2a; padding: 15px; border-radius: 6px; overflow-x: auto; white-space: pre-wrap; }
|
| 76 |
+
label { color: #aaa; font-size: 14px; }
|
| 77 |
+
h1 { color: #ff6b35; }
|
| 78 |
+
</style>
|
| 79 |
+
</head>
|
| 80 |
+
<body>
|
| 81 |
+
<h1>🤖 Qwen API Tester</h1>
|
| 82 |
+
<label>API Key</label>
|
| 83 |
+
<input type="password" id="apikey" placeholder="Twój klucz API">
|
| 84 |
+
<label>System prompt</label>
|
| 85 |
+
<input type="text" id="system" value="You are a helpful assistant.">
|
| 86 |
+
<label>Wiadomość</label>
|
| 87 |
+
<textarea id="message" rows="4" placeholder="Napisz coś..."></textarea>
|
| 88 |
+
<button onclick="send()">Wyślij 🚀</button>
|
| 89 |
+
<pre id="output">Odpowiedź pojawi się tutaj...</pre>
|
| 90 |
+
<script>
|
| 91 |
+
async function send() {
|
| 92 |
+
const key = document.getElementById('apikey').value;
|
| 93 |
+
const system = document.getElementById('system').value;
|
| 94 |
+
const msg = document.getElementById('message').value;
|
| 95 |
+
document.getElementById('output').textContent = 'Ładowanie...';
|
| 96 |
+
const res = await fetch('/v1/chat/completions', {
|
| 97 |
+
method: 'POST',
|
| 98 |
+
headers: { 'Content-Type': 'application/json', 'X-API-Key': key },
|
| 99 |
+
body: JSON.stringify({ messages: [{ role: 'system', content: system }, { role: 'user', content: msg }] })
|
| 100 |
+
});
|
| 101 |
+
const data = await res.json();
|
| 102 |
+
document.getElementById('output').textContent = data.choices?.[0]?.message?.content || JSON.stringify(data, null, 2);
|
| 103 |
+
}
|
| 104 |
+
</script>
|
| 105 |
+
</body>
|
| 106 |
+
</html>
|
| 107 |
+
"""
|
| 108 |
+
|
| 109 |
@app.get("/health")
|
| 110 |
async def health():
|
| 111 |
return {
|
|
|
|
| 115 |
}
|
| 116 |
|
| 117 |
def format_prompt(messages):
|
|
|
|
| 118 |
prompt = ""
|
| 119 |
for msg in messages:
|
| 120 |
role = msg["role"]
|
|
|
|
| 126 |
@app.post("/v1/chat/completions")
|
| 127 |
async def chat_completions(request: Request, api_key: str = Depends(verify_key)):
|
| 128 |
body = await request.json()
|
|
|
|
| 129 |
messages = body.get("messages", [])
|
| 130 |
max_tokens = body.get("max_tokens", 512)
|
| 131 |
temperature = body.get("temperature", 0.7)
|
|
|
|
| 135 |
raise HTTPException(status_code=400, detail="messages is required")
|
| 136 |
|
| 137 |
prompt = format_prompt(messages)
|
|
|
|
| 138 |
output = llm(
|
| 139 |
prompt,
|
| 140 |
max_tokens=max_tokens,
|
|
|
|
| 160 |
"completion_tokens": output["usage"]["completion_tokens"],
|
| 161 |
"total_tokens": output["usage"]["total_tokens"]
|
| 162 |
}
|
| 163 |
+
})
|