nacho commited on
Commit
f979e26
·
1 Parent(s): 3f9fa4b

chore: 清理无用测试脚本和老旧的 proxy.py

Browse files
Files changed (1) hide show
  1. proxy.py +0 -353
proxy.py DELETED
@@ -1,353 +0,0 @@
1
- import json
2
- import logging
3
- import os
4
- import time
5
- import uuid
6
- from typing import Optional
7
-
8
- import httpx
9
- from fastapi import FastAPI, HTTPException, Header, Request
10
- from fastapi.middleware.cors import CORSMiddleware
11
- from fastapi.responses import StreamingResponse
12
- from pydantic import BaseModel
13
-
14
- logging.basicConfig(
15
- level=logging.INFO,
16
- format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
17
- )
18
- logger = logging.getLogger(__name__)
19
-
20
- app = FastAPI(title="DS2API Browser Proxy")
21
-
22
- app.add_middleware(
23
- CORSMiddleware,
24
- allow_origins=["*"],
25
- allow_credentials=True,
26
- allow_methods=["*"],
27
- allow_headers=["*"],
28
- )
29
-
30
- DS2API_URL = os.getenv("DS2API_UPSTREAM_URL", "http://127.0.0.1:5001")
31
- API_KEYS = os.getenv("DS2API_PROXY_KEYS", os.getenv("DS2API_KEYS", "sk-default")).split(",")
32
- ADMIN_KEY = os.getenv("DS2API_ADMIN_KEY", "admin")
33
- # Internal key used to talk to the upstream DS2API instance
34
- _UPSTREAM_KEY = os.getenv("DS2API_UPSTREAM_KEY", API_KEYS[0] if API_KEYS else "sk-default")
35
-
36
-
37
- class Message(BaseModel):
38
- role: str
39
- content: str
40
-
41
-
42
- class ChatCompletionRequest(BaseModel):
43
- model: str
44
- messages: list[Message]
45
- stream: bool = False
46
- temperature: Optional[float] = None
47
- max_tokens: Optional[int] = None
48
-
49
-
50
- def verify_api_key(authorization: Optional[str] = Header(None)) -> str:
51
- if not authorization:
52
- raise HTTPException(status_code=401, detail="Missing API key")
53
-
54
- token = authorization.replace("Bearer ", "").strip()
55
- if token not in API_KEYS:
56
- raise HTTPException(status_code=401, detail="Invalid API key")
57
-
58
- return token
59
-
60
-
61
- def _upstream_headers() -> dict:
62
- return {"Authorization": f"Bearer {_UPSTREAM_KEY}"}
63
-
64
-
65
- @app.get("/v1/models")
66
- async def list_models(authorization: str = Header(...)):
67
- verify_api_key(authorization)
68
-
69
- async with httpx.AsyncClient() as client:
70
- resp = await client.get(f"{DS2API_URL}/v1/models", headers=_upstream_headers())
71
- return resp.json()
72
-
73
-
74
- @app.get("/v1/models/{model_id}")
75
- async def get_model(model_id: str, authorization: str = Header(...)):
76
- verify_api_key(authorization)
77
-
78
- async with httpx.AsyncClient() as client:
79
- resp = await client.get(f"{DS2API_URL}/v1/models/{model_id}", headers=_upstream_headers())
80
- return resp.json()
81
-
82
-
83
- @app.post("/v1/chat/completions")
84
- async def chat_completions(
85
- request: ChatCompletionRequest,
86
- authorization: str = Header(...),
87
- ):
88
- verify_api_key(authorization)
89
-
90
- if not request.messages:
91
- raise HTTPException(status_code=400, detail="No messages provided")
92
-
93
- async with httpx.AsyncClient() as client:
94
- if request.stream:
95
- async def stream_with_cleanup():
96
- async with httpx.AsyncClient() as stream_client:
97
- async with stream_client.stream(
98
- "POST",
99
- f"{DS2API_URL}/v1/chat/completions",
100
- json=request.model_dump(),
101
- headers=_upstream_headers(),
102
- timeout=120,
103
- ) as resp:
104
- async for line in resp.aiter_lines():
105
- yield line + "\n"
106
-
107
- return StreamingResponse(
108
- stream_with_cleanup(),
109
- media_type="text/event-stream",
110
- )
111
-
112
- resp = await client.post(
113
- f"{DS2API_URL}/v1/chat/completions",
114
- json=request.model_dump(),
115
- headers=_upstream_headers(),
116
- timeout=120,
117
- )
118
- return resp.json()
119
-
120
-
121
- @app.get("/anthropic/v1/models")
122
- async def anthropic_models(authorization: str = Header(...)):
123
- verify_api_key(authorization)
124
-
125
- return {
126
- "data": [
127
- {"id": "claude-sonnet-4-6", "object": "model", "created": int(time.time()), "owned_by": "anthropic"},
128
- {"id": "claude-opus-4-6", "object": "model", "created": int(time.time()), "owned_by": "anthropic"},
129
- {"id": "claude-haiku-4-5", "object": "model", "created": int(time.time()), "owned_by": "anthropic"},
130
- ],
131
- "object": "list",
132
- }
133
-
134
-
135
- @app.post("/anthropic/v1/messages")
136
- async def anthropic_messages(request: Request, authorization: str = Header(...)):
137
- verify_api_key(authorization)
138
-
139
- body = await request.json()
140
- messages = body.get("messages", [])
141
- model = body.get("model", "claude-sonnet-4-6")
142
- stream = body.get("stream", False)
143
-
144
- if not messages:
145
- raise HTTPException(status_code=400, detail="No messages provided")
146
-
147
- prompt = messages[-1].get("content", "")
148
-
149
- async with httpx.AsyncClient() as client:
150
- if stream:
151
- async def stream_with_cleanup():
152
- async with httpx.AsyncClient() as stream_client:
153
- async with stream_client.stream(
154
- "POST",
155
- f"{DS2API_URL}/v1/chat/completions",
156
- json={"model": "deepseek-flash", "messages": [{"role": "user", "content": prompt}], "stream": True},
157
- headers=_upstream_headers(),
158
- timeout=120,
159
- ) as resp:
160
- async for line in resp.aiter_lines():
161
- if line.startswith("data: "):
162
- data_str = line[6:].strip()
163
- if data_str == "[DONE]":
164
- continue
165
- try:
166
- data = json.loads(data_str)
167
- content = data.get("choices", [{}])[0].get("delta", {}).get("content", "")
168
- if content:
169
- yield f"event: content_block_delta\ndata: {json.dumps({'type': 'content_block_delta', 'index': 0, 'delta': {'type': 'text_delta', 'text': content}})}\n\n"
170
- except json.JSONDecodeError:
171
- pass
172
-
173
- yield f"event: message_stop\ndata: {json.dumps({'type': 'message_stop'})}\n\n"
174
-
175
- return StreamingResponse(
176
- stream_with_cleanup(),
177
- media_type="text/event-stream",
178
- )
179
-
180
- resp = await client.post(
181
- f"{DS2API_URL}/v1/chat/completions",
182
- json={"model": "deepseek-flash", "messages": [{"role": "user", "content": prompt}], "stream": False},
183
- headers=_upstream_headers(),
184
- timeout=120,
185
- )
186
- data = resp.json()
187
- content = data.get("choices", [{}])[0].get("message", {}).get("content", "")
188
-
189
- return {
190
- "id": f"msg_{uuid.uuid4().hex[:8]}",
191
- "type": "message",
192
- "role": "assistant",
193
- "model": model,
194
- "content": [{"type": "text", "text": content}],
195
- "stop_reason": "end_turn",
196
- "usage": {
197
- "input_tokens": len(prompt.split()),
198
- "output_tokens": len(content.split()),
199
- },
200
- }
201
-
202
-
203
- @app.post("/v1beta/models/{model}:generateContent")
204
- async def gemini_generate(model: str, request: Request, authorization: str = Header(...)):
205
- verify_api_key(authorization)
206
-
207
- body = await request.json()
208
- contents = body.get("contents", [])
209
-
210
- if not contents:
211
- raise HTTPException(status_code=400, detail="No contents provided")
212
-
213
- prompt = contents[-1].get("parts", [{}])[0].get("text", "")
214
-
215
- async with httpx.AsyncClient() as client:
216
- resp = await client.post(
217
- f"{DS2API_URL}/v1/chat/completions",
218
- json={"model": "deepseek-flash", "messages": [{"role": "user", "content": prompt}], "stream": False},
219
- headers=_upstream_headers(),
220
- timeout=120,
221
- )
222
- data = resp.json()
223
- content = data.get("choices", [{}])[0].get("message", {}).get("content", "")
224
-
225
- return {
226
- "candidates": [
227
- {
228
- "content": {
229
- "parts": [{"text": content}],
230
- "role": "model",
231
- },
232
- "finishReason": "STOP",
233
- }
234
- ],
235
- "usageMetadata": {
236
- "promptTokenCount": len(prompt.split()),
237
- "candidatesTokenCount": len(content.split()),
238
- "totalTokenCount": len(prompt.split()) + len(content.split()),
239
- },
240
- }
241
-
242
-
243
- @app.post("/v1beta/models/{model}:streamGenerateContent")
244
- async def gemini_stream_generate(model: str, request: Request, authorization: str = Header(...)):
245
- verify_api_key(authorization)
246
-
247
- body = await request.json()
248
- contents = body.get("contents", [])
249
-
250
- if not contents:
251
- raise HTTPException(status_code=400, detail="No contents provided")
252
-
253
- prompt = contents[-1].get("parts", [{}])[0].get("text", "")
254
-
255
- async def stream_with_cleanup():
256
- async with httpx.AsyncClient() as stream_client:
257
- async with stream_client.stream(
258
- "POST",
259
- f"{DS2API_URL}/v1/chat/completions",
260
- json={"model": "deepseek-flash", "messages": [{"role": "user", "content": prompt}], "stream": True},
261
- headers=_upstream_headers(),
262
- timeout=120,
263
- ) as resp:
264
- async for line in resp.aiter_lines():
265
- if line.startswith("data: "):
266
- data_str = line[6:].strip()
267
- if data_str == "[DONE]":
268
- continue
269
- try:
270
- data = json.loads(data_str)
271
- content = data.get("choices", [{}])[0].get("delta", {}).get("content", "")
272
- if content:
273
- yield f"data: {json.dumps({'candidates': [{'content': {'parts': [{'text': content}], 'role': 'model'}}]})}\n\n"
274
- except json.JSONDecodeError:
275
- pass
276
-
277
- yield f"data: {json.dumps({'candidates': [{'content': {'parts': [], 'role': 'model'}, 'finishReason': 'STOP'}], 'usageMetadata': {'promptTokenCount': 0, 'candidatesTokenCount': 0, 'totalTokenCount': 0}})}\n\n"
278
-
279
- return StreamingResponse(
280
- stream_with_cleanup(),
281
- media_type="text/event-stream",
282
- )
283
-
284
-
285
- @app.get("/api/version")
286
- async def ollama_version():
287
- return {"version": "0.1.0"}
288
-
289
-
290
- @app.get("/api/tags")
291
- async def ollama_tags():
292
- return {
293
- "models": [
294
- {"name": "deepseek-chat", "model": "deepseek-chat"},
295
- {"name": "deepseek-reasoner", "model": "deepseek-reasoner"},
296
- ]
297
- }
298
-
299
-
300
- @app.post("/api/show")
301
- async def ollama_show(request: Request):
302
- body = await request.json()
303
- model = body.get("model", "deepseek-chat")
304
-
305
- return {
306
- "id": model,
307
- "capabilities": ["tools", "thinking"],
308
- }
309
-
310
-
311
- @app.get("/healthz")
312
- async def healthz():
313
- return {"status": "ok"}
314
-
315
-
316
- @app.get("/readyz")
317
- async def readyz():
318
- return {"status": "ok", "accounts": {"total": 1, "in_use": 0, "available": 1}}
319
-
320
-
321
- @app.get("/admin/stats")
322
- async def admin_stats(admin_key: str = Header(...)):
323
- if admin_key != ADMIN_KEY:
324
- raise HTTPException(status_code=401, detail="Invalid admin key")
325
-
326
- return {"total": 1, "in_use": 0, "available": 1, "logged_in": 1, "queue_size": 0}
327
-
328
-
329
- @app.get("/admin/config")
330
- async def get_config(admin_key: str = Header(...)):
331
- if admin_key != ADMIN_KEY:
332
- raise HTTPException(status_code=401, detail="Invalid admin key")
333
-
334
- return {
335
- "server": {"host": "0.0.0.0", "port": 5002},
336
- "browser": {"headless": True, "max_concurrent_per_account": 1, "timeout": 60000},
337
- "default_proxy": None,
338
- "account_count": 1,
339
- }
340
-
341
-
342
- def main():
343
- import uvicorn
344
-
345
- uvicorn.run(
346
- app,
347
- host="0.0.0.0",
348
- port=int(os.getenv("DS2API_PROXY_PORT", "5002")),
349
- )
350
-
351
-
352
- if __name__ == "__main__":
353
- main()