hzruo commited on
Commit
08eec19
·
verified ·
1 Parent(s): 39f4070

Create main.py

Browse files
Files changed (1) hide show
  1. main.py +378 -0
main.py ADDED
@@ -0,0 +1,378 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Request, Depends, HTTPException
2
+ from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
3
+ from fastapi.responses import StreamingResponse
4
+ from fastapi.background import BackgroundTasks
5
+ import requests
6
+ import uuid
7
+ import json
8
+ import time
9
+ from typing import Optional
10
+ import asyncio
11
+ from curl_cffi import requests as cffi_requests
12
+ import re
13
+ import os
14
+
15
+ app = FastAPI()
16
+ security = HTTPBearer()
17
+
18
+ # OpenAI API Key 配置,可以通过环境变量覆盖
19
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", None) # 设置为 None 表示不校验,或设置具体值,如"sk-proj-1234567890"
20
+
21
+ # 修改全局数据存储
22
+ global_data = {
23
+ "cookie": None,
24
+ "cookies": None,
25
+ "last_update": 0
26
+ }
27
+
28
+ def get_cookie():
29
+ try:
30
+ # 使用 curl_cffi 发送请求
31
+ response = cffi_requests.get(
32
+ 'https://chat.akash.network/',
33
+ impersonate="chrome110",
34
+ timeout=30
35
+ )
36
+
37
+ # 获取所有 cookies
38
+ cookies = response.cookies.items()
39
+ if cookies:
40
+ cookie_str = '; '.join([f'{k}={v}' for k, v in cookies])
41
+ global_data["cookie"] = cookie_str
42
+ global_data["last_update"] = time.time()
43
+ print(f"Got cookies: {cookie_str}")
44
+ return cookie_str
45
+
46
+ except Exception as e:
47
+ print(f"Error fetching cookie: {e}")
48
+ return None
49
+
50
+ async def check_and_update_cookie(background_tasks: BackgroundTasks):
51
+ # 如果cookie超过30分钟,在后台更新
52
+ if time.time() - global_data["last_update"] > 1800:
53
+ background_tasks.add_task(get_cookie)
54
+
55
+ @app.on_event("startup")
56
+ async def startup_event():
57
+ get_cookie()
58
+
59
+ async def get_api_key(credentials: HTTPAuthorizationCredentials = Depends(security)):
60
+ token = credentials.credentials
61
+
62
+ # 如果设置了 OPENAI_API_KEY,则需要验证
63
+ if OPENAI_API_KEY is not None:
64
+ # 去掉 Bearer 前缀后再比较
65
+ clean_token = token.replace("Bearer ", "") if token.startswith("Bearer ") else token
66
+ if clean_token != OPENAI_API_KEY:
67
+ raise HTTPException(
68
+ status_code=401,
69
+ detail="Invalid API key"
70
+ )
71
+
72
+ # 返回去掉 "Bearer " 前缀的token
73
+ return token.replace("Bearer ", "") if token.startswith("Bearer ") else token
74
+
75
+ async def check_image_status(session: requests.Session, job_id: str, headers: dict) -> Optional[str]:
76
+ """
77
+ 检查图片生成状态并获取生成的图片
78
+
79
+ Args:
80
+ session: 请求会话
81
+ job_id: 任务ID
82
+ headers: 请求头
83
+
84
+ Returns:
85
+ Optional[str]: base64格式的图片数据,如果生成失败则返回None
86
+ """
87
+ max_retries = 30 # 最多等待30秒
88
+ for _ in range(max_retries):
89
+ try:
90
+ response = session.get(
91
+ f'https://chat.akash.network/api/image-status?ids={job_id}',
92
+ headers=headers
93
+ )
94
+ status_data = response.json()
95
+
96
+ if status_data and isinstance(status_data, list) and len(status_data) > 0:
97
+ job_info = status_data[0]
98
+
99
+ # 如果result不为空,说明图片已生成
100
+ if job_info.get("result"):
101
+ return job_info["result"] # 直接返回base64数据
102
+
103
+ # 如果状态是失败,则停止等待
104
+ if job_info.get("status") == "failed":
105
+ print(f"Image generation failed for job {job_id}")
106
+ return None
107
+
108
+ except Exception as e:
109
+ print(f"Error checking image status: {e}")
110
+
111
+ await asyncio.sleep(1) # 等待1秒后重试
112
+
113
+ print(f"Timeout waiting for image generation job {job_id}")
114
+ return None
115
+
116
+ @app.get("/")
117
+ async def health_check():
118
+ """Health check endpoint"""
119
+ return {"status": "ok"}
120
+
121
+ @app.post("/v1/chat/completions")
122
+ async def chat_completions(
123
+ request: Request,
124
+ api_key: str = Depends(get_api_key)
125
+ ):
126
+ try:
127
+ data = await request.json()
128
+ print(f"Chat request data: {data}")
129
+
130
+ chat_id = str(uuid.uuid4()).replace('-', '')[:16]
131
+
132
+ akash_data = {
133
+ "id": chat_id,
134
+ "messages": data.get('messages', []),
135
+ "model": data.get('model', "DeepSeek-R1"),
136
+ "system": data.get('system_message', "You are a helpful assistant."),
137
+ "temperature": data.get('temperature', 0.6),
138
+ "topP": data.get('top_p', 0.95)
139
+ }
140
+
141
+ headers = {
142
+ "Content-Type": "application/json",
143
+ "Cookie": f"session_token={api_key}",
144
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
145
+ "Accept": "*/*",
146
+ "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
147
+ "Accept-Encoding": "gzip, deflate, br",
148
+ "Origin": "https://chat.akash.network",
149
+ "Referer": "https://chat.akash.network/",
150
+ "Sec-Fetch-Dest": "empty",
151
+ "Sec-Fetch-Mode": "cors",
152
+ "Sec-Fetch-Site": "same-origin",
153
+ "Connection": "keep-alive",
154
+ "Priority": "u=1, i"
155
+ }
156
+
157
+ print(f"Sending request to Akash with headers: {headers}")
158
+ print(f"Request data: {akash_data}")
159
+
160
+ with requests.Session() as session:
161
+ response = session.post(
162
+ 'https://chat.akash.network/api/chat',
163
+ json=akash_data,
164
+ headers=headers,
165
+ stream=True
166
+ )
167
+
168
+ def generate():
169
+ content_buffer = ""
170
+ for line in response.iter_lines():
171
+ if not line:
172
+ continue
173
+
174
+ try:
175
+ line_str = line.decode('utf-8')
176
+ msg_type, msg_data = line_str.split(':', 1)
177
+
178
+ if msg_type == '0':
179
+ if msg_data.startswith('"') and msg_data.endswith('"'):
180
+ msg_data = msg_data.replace('\\"', '"')
181
+ msg_data = msg_data[1:-1]
182
+ msg_data = msg_data.replace("\\n", "\n")
183
+
184
+ # 在处理消息时先判断模型类型
185
+ if data.get('model') == 'AkashGen' and "<image_generation>" in msg_data:
186
+ # 图片生成模型的特殊处理
187
+ match = re.search(r"jobId='([^']+)' prompt='([^']+)' negative='([^']*)'", msg_data)
188
+ if match:
189
+ job_id, prompt, negative = match.groups()
190
+ print(f"Starting image generation process for job_id: {job_id}")
191
+
192
+ # 立即发送思考开始的消息
193
+ start_time = time.time()
194
+ think_msg = "<think>\n"
195
+ think_msg += "🎨 Generating image...\n\n"
196
+ think_msg += f"Prompt: {prompt}\n"
197
+
198
+ # 发送思考开始消息 (使用标准 OpenAI 格式)
199
+ chunk = {
200
+ "id": f"chatcmpl-{chat_id}",
201
+ "object": "chat.completion.chunk",
202
+ "created": int(time.time()),
203
+ "model": data.get('model'), # 使用请求中指定的模型
204
+ "choices": [{
205
+ "delta": {"content": think_msg},
206
+ "index": 0,
207
+ "finish_reason": None
208
+ }]
209
+ }
210
+ yield f"data: {json.dumps(chunk)}\n\n"
211
+
212
+ # 同步方式检查图片状态
213
+ max_retries = 10
214
+ retry_interval = 3
215
+ result = None
216
+
217
+ for attempt in range(max_retries):
218
+ try:
219
+ print(f"\nAttempt {attempt + 1}/{max_retries} for job {job_id}")
220
+ status_response = cffi_requests.get(
221
+ f'https://chat.akash.network/api/image-status?ids={job_id}',
222
+ headers=headers,
223
+ impersonate="chrome110"
224
+ )
225
+ print(f"Status response code: {status_response.status_code}")
226
+ status_data = status_response.json()
227
+ print(f"Status data: {json.dumps(status_data, indent=2)}")
228
+
229
+ if status_data and isinstance(status_data, list) and len(status_data) > 0:
230
+ job_info = status_data[0]
231
+ print(f"Job status: {job_info.get('status')}")
232
+
233
+ if job_info.get("result"):
234
+ result = job_info['result']
235
+ if result and not result.startswith("Failed"):
236
+ break
237
+ elif job_info.get("status") == "failed":
238
+ result = None
239
+ break
240
+ except Exception as e:
241
+ print(f"Error checking status: {e}")
242
+
243
+ if attempt < max_retries - 1:
244
+ time.sleep(retry_interval)
245
+
246
+ # 发送结束消息
247
+ elapsed_time = time.time() - start_time
248
+ end_msg = f"\n🤔 Thinking for {elapsed_time:.1f}s...\n"
249
+ end_msg += "</think>\n\n"
250
+ if result and not result.startswith("Failed"):
251
+ end_msg += f"![Generated Image]({result})"
252
+ else:
253
+ end_msg += "*Image generation failed or timed out.*\n"
254
+
255
+ # 发送结束消息 (使用标准 OpenAI 格式)
256
+ chunk = {
257
+ "id": f"chatcmpl-{chat_id}",
258
+ "object": "chat.completion.chunk",
259
+ "created": int(time.time()),
260
+ "model": data.get('model'), # 使用请求中指定的模型
261
+ "choices": [{
262
+ "delta": {"content": end_msg},
263
+ "index": 0,
264
+ "finish_reason": None
265
+ }]
266
+ }
267
+ yield f"data: {json.dumps(chunk)}\n\n"
268
+ continue
269
+
270
+ content_buffer += msg_data
271
+
272
+ chunk = {
273
+ "id": f"chatcmpl-{chat_id}",
274
+ "object": "chat.completion.chunk",
275
+ "created": int(time.time()),
276
+ "model": data.get('model'),
277
+ "choices": [{
278
+ "delta": {"content": msg_data},
279
+ "index": 0,
280
+ "finish_reason": None
281
+ }]
282
+ }
283
+ yield f"data: {json.dumps(chunk)}\n\n"
284
+
285
+ elif msg_type in ['e', 'd']:
286
+ chunk = {
287
+ "id": f"chatcmpl-{chat_id}",
288
+ "object": "chat.completion.chunk",
289
+ "created": int(time.time()),
290
+ "model": data.get('model'), # 使用请求中指定的模型
291
+ "choices": [{
292
+ "delta": {},
293
+ "index": 0,
294
+ "finish_reason": "stop"
295
+ }]
296
+ }
297
+ yield f"data: {json.dumps(chunk)}\n\n"
298
+ yield "data: [DONE]\n\n"
299
+ break
300
+
301
+ except Exception as e:
302
+ print(f"Error processing line: {e}")
303
+ continue
304
+
305
+ return StreamingResponse(
306
+ generate(),
307
+ media_type='text/event-stream',
308
+ headers={
309
+ 'Cache-Control': 'no-cache',
310
+ 'Connection': 'keep-alive',
311
+ 'Content-Type': 'text/event-stream'
312
+ }
313
+ )
314
+
315
+ except Exception as e:
316
+ return {"error": str(e)}
317
+
318
+ @app.get("/v1/models")
319
+ async def list_models(api_key: str = Depends(get_api_key)):
320
+ try:
321
+ headers = {
322
+ "Content-Type": "application/json",
323
+ "Cookie": f"session_token={api_key}",
324
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
325
+ "Accept": "*/*",
326
+ "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
327
+ "Accept-Encoding": "gzip, deflate, br",
328
+ "Origin": "https://chat.akash.network",
329
+ "Referer": "https://chat.akash.network/",
330
+ "Sec-Fetch-Dest": "empty",
331
+ "Sec-Fetch-Mode": "cors",
332
+ "Sec-Fetch-Site": "same-origin",
333
+ "Connection": "keep-alive"
334
+ }
335
+
336
+ response = requests.get(
337
+ 'https://chat.akash.network/api/models',
338
+ headers=headers
339
+ )
340
+
341
+ akash_response = response.json()
342
+
343
+ # 转换为标准 OpenAI 格式
344
+ openai_models = {
345
+ "object": "list",
346
+ "data": [
347
+ {
348
+ "id": model["id"],
349
+ "object": "model",
350
+ "created": int(time.time()),
351
+ "owned_by": "akash",
352
+ "permission": [{
353
+ "id": "modelperm-" + model["id"],
354
+ "object": "model_permission",
355
+ "created": int(time.time()),
356
+ "allow_create_engine": False,
357
+ "allow_sampling": True,
358
+ "allow_logprobs": True,
359
+ "allow_search_indices": False,
360
+ "allow_view": True,
361
+ "allow_fine_tuning": False,
362
+ "organization": "*",
363
+ "group": None,
364
+ "is_blocking": False
365
+ }]
366
+ } for model in akash_response.get("models", [])
367
+ ]
368
+ }
369
+
370
+ return openai_models
371
+
372
+ except Exception as e:
373
+ print(f"Error in list_models: {e}")
374
+ return {"error": str(e)}
375
+
376
+ if __name__ == '__main__':
377
+ import uvicorn
378
+ uvicorn.run(app, host='0.0.0.0', port=9000)