convaiinnovations commited on
Commit
e2addc4
Β·
verified Β·
1 Parent(s): 3672ab9

Upload folder using huggingface_hub

Browse files
Files changed (5) hide show
  1. README.md +13 -0
  2. application.py +66 -0
  3. client.py +43 -0
  4. generate_token.py +24 -0
  5. push_to_hf.py +31 -0
README.md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Qwen FastAPI Inference API
2
+
3
+ This repository contains a secure, OpenAI-compatible REST API for the **Qwen2.5-0.5B-Instruct** model, built with FastAPI.
4
+
5
+ ## Features
6
+ - **OpenAI Compatible**: Implements the `/v1/chat/completions` endpoint.
7
+ - **Secure**: Uses JWT Authentication (HS256) for all inference requests.
8
+ - **Lightweight**: Optimized to run on consumer hardware or free-tier cloud environments like Google Colab.
9
+
10
+ ## Structure
11
+ - `application.py`: The main FastAPI server.
12
+ - `client.py`: A test client to verify the API.
13
+ - `generate_token.py`: Utility to generate JWT tokens for authentication.
application.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import jwt
2
+ import time
3
+ import os
4
+ from datetime import datetime, timedelta
5
+ from fastapi import FastAPI, Depends, HTTPException
6
+ from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
7
+ from pydantic import BaseModel
8
+ from transformers import AutoModelForCausalLM, AutoTokenizer
9
+ import torch
10
+ from dotenv import load_dotenv
11
+
12
+ # --- Load Environment Variables ---
13
+ load_dotenv()
14
+ SECRET_KEY = os.getenv("JWT_SECRET_KEY", "default-fallback-secret")
15
+ ALGORITHM = "HS256"
16
+ MODEL_NAME = "Qwen/Qwen2.5-0.5B-Instruct"
17
+
18
+ security = HTTPBearer()
19
+
20
+ def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
21
+ token = credentials.credentials
22
+ try:
23
+ payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
24
+ return payload
25
+ except Exception:
26
+ raise HTTPException(status_code=401, detail="Unauthorized")
27
+
28
+ # --- FastAPI Setup ---
29
+ app = FastAPI()
30
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
31
+ model = AutoModelForCausalLM.from_pretrained(MODEL_NAME, torch_dtype="auto", device_map="auto")
32
+
33
+ class ChatMessage(BaseModel):
34
+ role: str
35
+ content: str
36
+
37
+ class ChatCompletionRequest(BaseModel):
38
+ messages: list[ChatMessage]
39
+ max_tokens: int = 100
40
+
41
+ @app.get("/")
42
+ def read_root():
43
+ return {"message": "Qwen OpenAI-style API is running with .env auth"}
44
+
45
+ @app.post("/v1/chat/completions")
46
+ async def chat_generate(request: ChatCompletionRequest, user=Depends(verify_token)):
47
+ chat_msgs = [msg.dict() for msg in request.messages]
48
+ text = tokenizer.apply_chat_template(chat_msgs, tokenize=False, add_generation_prompt=True)
49
+ model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
50
+
51
+ generated_ids = model.generate(
52
+ **model_inputs,
53
+ max_new_tokens=request.max_tokens
54
+ )
55
+ generated_ids = [output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)]
56
+ response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
57
+
58
+ return {
59
+ "id": f"chatcmpl-{int(time.time())}",
60
+ "object": "chat.completion",
61
+ "model": MODEL_NAME,
62
+ "choices": [{
63
+ "message": {"role": "assistant", "content": response},
64
+ "finish_reason": "stop"
65
+ }]
66
+ }
client.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import argparse
3
+ import sys
4
+
5
+ BASE_URL = "http://127.0.0.1:8000"
6
+
7
+ def test_openai_style(token):
8
+ headers = {"Authorization": f"Bearer {token}"}
9
+
10
+ # Send OpenAI-style Chat Messages
11
+ payload = {
12
+ "messages": [
13
+ {"role": "system", "content": "You are a concise AI assistant."},
14
+ {"role": "user", "content": "What is the main benefit of open-source LLMs?"}
15
+ ],
16
+ "max_tokens": 60
17
+ }
18
+
19
+ print(f"πŸ€– Sending Chat Completion request to {BASE_URL}...")
20
+ try:
21
+ response = requests.post(f"{BASE_URL}/v1/chat/completions", json=payload, headers=headers)
22
+
23
+ if response.status_code == 200:
24
+ result = response.json()
25
+ print("\n✨ Model Response:")
26
+ print(result['choices'][0]['message']['content'])
27
+ elif response.status_code == 401:
28
+ print("❌ Error: 401 Unauthorized. Please check if your token is valid and matches the SECRET_KEY.")
29
+ else:
30
+ print(f"❌ Error {response.status_code}: {response.text}")
31
+ except Exception as e:
32
+ print(f"❌ Connection failed: {e}")
33
+
34
+ if __name__ == "__main__":
35
+ parser = argparse.ArgumentParser(description="Test the Qwen API with a JWT token.")
36
+ parser.add_argument("--token", type=str, help="The JWT token generated by generate_token.py")
37
+ args = parser.parse_args()
38
+
39
+ if not args.token:
40
+ print("❌ Error: Please provide a token using --token <YOUR_TOKEN>")
41
+ sys.exit(1)
42
+
43
+ test_openai_style(args.token)
generate_token.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import jwt
2
+ import os
3
+ from datetime import datetime, timedelta
4
+ from dotenv import load_dotenv
5
+
6
+ load_dotenv()
7
+ SECRET_KEY = os.getenv("JWT_SECRET_KEY")
8
+ ALGORITHM = "HS256"
9
+
10
+ def create_token(user_id: str):
11
+ if not SECRET_KEY:
12
+ print("❌ Error: JWT_SECRET_KEY not found in .env")
13
+ return
14
+
15
+ payload = {
16
+ "sub": user_id,
17
+ "exp": datetime.utcnow() + timedelta(hours=24)
18
+ }
19
+ token = jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
20
+ print(f"βœ… Token generated for {user_id}:\n{token}")
21
+ return token
22
+
23
+ if __name__ == "__main__":
24
+ create_token("admin_user")
push_to_hf.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import os
3
+ from huggingface_hub import HfApi, create_repo
4
+
5
+ def push_api_to_hf(token, repo_name="qwen-api-fastapi"):
6
+ username = "convaiinnovations"
7
+ repo_id = f"{username}/{repo_name}"
8
+ api = HfApi()
9
+
10
+ print(f"πŸš€ Creating/Accessing repo: {repo_id}...")
11
+ try:
12
+ create_repo(repo_id=repo_id, token=token, repo_type="model", exist_ok=True)
13
+
14
+ print(f"πŸ“€ Uploading files from 'api/' folder...")
15
+ api.upload_folder(
16
+ folder_path=".",
17
+ repo_id=repo_id,
18
+ repo_type="model",
19
+ token=token
20
+ )
21
+ print(f"βœ… Successfully pushed to https://huggingface.co/{repo_id}")
22
+ except Exception as e:
23
+ print(f"❌ Error: {e}")
24
+
25
+ if __name__ == "__main__":
26
+ parser = argparse.ArgumentParser(description="Push API folder to Hugging Face.")
27
+ parser.add_argument("--token", type=str, required=True, help="Your Hugging Face Write Token")
28
+ parser.add_argument("--repo", type=str, default="qwen-api-fastapi", help="Name of the repo")
29
+ args = parser.parse_args()
30
+
31
+ push_api_to_hf(args.token, args.repo)