snailyp commited on
Commit
4d8ee18
1 Parent(s): 5375484

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +19 -0
  2. app/config.py +19 -0
  3. main.py +123 -0
  4. requirements.txt +5 -0
Dockerfile ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # 复制所需文件到容器中
6
+ COPY ./app /app/app
7
+ COPY ./main.py /app
8
+ COPY ./requirements.txt /app
9
+
10
+ RUN pip install --no-cache-dir -r requirements.txt
11
+ ENV API_KEYS=["your_api_key_1"]
12
+ ENV ALLOWED_TOKENS=["your_token_1"]
13
+ ENV BASE_URL=https://api.groq.com/openai/v1
14
+
15
+ # Expose port
16
+ EXPOSE 8000
17
+
18
+ # Run the application
19
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
app/config.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic_settings import BaseSettings
2
+ import os
3
+ from typing import List
4
+
5
+ class Settings(BaseSettings):
6
+ API_KEYS: List[str]
7
+ ALLOWED_TOKENS: List[str]
8
+ BASE_URL: str
9
+
10
+ class Config:
11
+ env_file = ".env"
12
+ env_file_encoding = "utf-8"
13
+ case_sensitive = True
14
+ # 同时从环境变量和.env文件获取配置
15
+ env_nested_delimiter = "__"
16
+ extra = "ignore"
17
+
18
+ # 优先从环境变量获取,如果没有则从.env文件获取
19
+ settings = Settings(_env_file=os.getenv("ENV_FILE", ".env"))
main.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException, Header, Request
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.responses import StreamingResponse
4
+ from pydantic import BaseModel
5
+ import openai
6
+ from typing import List, Optional
7
+ import logging
8
+ from itertools import cycle
9
+ import asyncio
10
+
11
+ import uvicorn
12
+
13
+ from app import config
14
+
15
+ # 配置日志
16
+ logging.basicConfig(
17
+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
18
+ )
19
+ logger = logging.getLogger(__name__)
20
+
21
+ app = FastAPI()
22
+
23
+ # 允许跨域
24
+ app.add_middleware(
25
+ CORSMiddleware,
26
+ allow_origins=["*"],
27
+ allow_credentials=True,
28
+ allow_methods=["*"],
29
+ allow_headers=["*"],
30
+ )
31
+
32
+ # API密钥配置
33
+ API_KEYS = config.settings.API_KEYS
34
+
35
+ # 创建一个循环迭代器
36
+ key_cycle = cycle(API_KEYS)
37
+ key_lock = asyncio.Lock()
38
+
39
+
40
+ class ChatRequest(BaseModel):
41
+ messages: List[dict]
42
+ model: str = "llama-3.2-90b-text-preview"
43
+ temperature: Optional[float] = 0.7
44
+ max_tokens: Optional[int] = 8000
45
+ stream: Optional[bool] = False
46
+
47
+
48
+ async def verify_authorization(authorization: str = Header(None)):
49
+ if not authorization:
50
+ logger.error("Missing Authorization header")
51
+ raise HTTPException(status_code=401, detail="Missing Authorization header")
52
+ if not authorization.startswith("Bearer "):
53
+ logger.error("Invalid Authorization header format")
54
+ raise HTTPException(
55
+ status_code=401, detail="Invalid Authorization header format"
56
+ )
57
+ token = authorization.replace("Bearer ", "")
58
+ if token not in config.settings.ALLOWED_TOKENS:
59
+ logger.error("Invalid token")
60
+ raise HTTPException(status_code=401, detail="Invalid token")
61
+ return token
62
+
63
+
64
+ @app.get("/v1/models")
65
+ async def list_models(authorization: str = Header(None)):
66
+ await verify_authorization(authorization)
67
+ async with key_lock:
68
+ api_key = next(key_cycle)
69
+ logger.info(f"Using API key: {api_key[:8]}...")
70
+ try:
71
+ client = openai.OpenAI(api_key=api_key, base_url=config.settings.BASE_URL)
72
+ response = client.models.list()
73
+ logger.info("Successfully retrieved models list")
74
+ return response
75
+ except Exception as e:
76
+ logger.error(f"Error listing models: {str(e)}")
77
+ raise HTTPException(status_code=500, detail=str(e))
78
+
79
+
80
+ @app.post("/v1/chat/completions")
81
+ async def chat_completion(request: ChatRequest, authorization: str = Header(None)):
82
+ await verify_authorization(authorization)
83
+ async with key_lock:
84
+ api_key = next(key_cycle)
85
+ logger.info(f"Using API key: {api_key[:8]}...")
86
+
87
+ try:
88
+ logger.info(f"Chat completion request - Model: {request.model}")
89
+ client = openai.OpenAI(api_key=api_key, base_url=config.settings.BASE_URL)
90
+ response = client.chat.completions.create(
91
+ model=request.model,
92
+ messages=request.messages,
93
+ temperature=request.temperature,
94
+ max_tokens=request.max_tokens,
95
+ stream=request.stream if hasattr(request, "stream") else False,
96
+ )
97
+
98
+ if hasattr(request, "stream") and request.stream:
99
+ logger.info("Streaming response enabled")
100
+
101
+ async def generate():
102
+ for chunk in response:
103
+ yield f"data: {chunk.model_dump_json()}\n\n"
104
+
105
+ return StreamingResponse(content=generate(), media_type="text/event-stream")
106
+
107
+ logger.info("Chat completion successful")
108
+ return response
109
+
110
+ except Exception as e:
111
+ logger.error(f"Error in chat completion: {str(e)}")
112
+ raise HTTPException(status_code=500, detail=str(e))
113
+
114
+
115
+ @app.get("/health")
116
+ async def health_check(authorization: str = Header(None)):
117
+ await verify_authorization(authorization)
118
+ logger.info("Health check endpoint called")
119
+ return {"status": "healthy"}
120
+
121
+
122
+ if __name__ == "__main__":
123
+ uvicorn.run(app, host="0.0.0.0", port=8000)
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ fastapi
2
+ openai
3
+ pydantic
4
+ pydantic_settings
5
+ uvicorn