peijun1 commited on
Commit
ea1e8b7
·
verified ·
1 Parent(s): 3b4aadd

Delete openai_ondemand_adapter.py

Browse files
Files changed (1) hide show
  1. openai_ondemand_adapter.py +0 -317
openai_ondemand_adapter.py DELETED
@@ -1,317 +0,0 @@
1
- from flask import Flask, request, Response, jsonify
2
- import requests
3
- import uuid
4
- import time
5
- import json
6
- import threading
7
- import logging
8
- import os
9
-
10
- # ====== 读取 Huggingface Secret 配置的私有key =======
11
- PRIVATE_KEY = os.environ.get("PRIVATE_KEY", "")
12
- PRIVATE_KEY = os.environ.get("PRIVATE_KEY", "")
13
- SAFE_HEADERS = ["Authorization", "X-API-KEY"]
14
-
15
- # 全局接口访问权限检查
16
- def check_private_key():
17
- # 可以在这里放宽部分接口,比如首页等
18
- if request.path in ["/", "/favicon.ico"]:
19
- return
20
- key = None
21
- for header in SAFE_HEADERS:
22
- key = request.headers.get(header)
23
- if key:
24
- if header == "Authorization" and key.startswith("Bearer "):
25
- key = key[len("Bearer "):].strip()
26
- break
27
- if not key or key != PRIVATE_KEY:
28
- return jsonify({"error": "Unauthorized, must provide correct Authorization or X-API-KEY"}), 401
29
-
30
- # 应用所有API鉴权
31
- app = Flask(__name__)
32
- app.before_request(check_private_key)
33
-
34
- # ========== KEY池(每行一个)==========
35
- ONDEMAND_APIKEYS = os.environ.get("ONDEMAND_APIKEYS", "").split(",") if os.environ.get("ONDEMAND_APIKEYS") else []
36
- BAD_KEY_RETRY_INTERVAL = 600 # 秒
37
- SESSION_TIMEOUT = 600 # 对话超时时间(10分钟)
38
-
39
- # ========== OnDemand模型映射 ==========
40
- MODEL_MAP = {
41
- "o3-mini": "predefined-openai-gpto3-mini",
42
- "gpt-4o": "predefined-openai-gpt4o",
43
- "gpt-4.1": "predefined-openai-gpt4.1",
44
- "gpt-4.1-mini": "predefined-openai-gpt4.1-mini",
45
- "gpt-4.1-nano": "predefined-openai-gpt4.1-nano",
46
- "gpt-4o-mini": "predefined-openai-gpt4o-mini",
47
- "deepseek-v3": "predefined-deepseek-v3",
48
- "deepseek-r1": "predefined-deepseek-r1",
49
- "claude-3-7-sonnet": "predefined-claude-3.7-sonnet",
50
- "gemini-2.0-flash": "predefined-gemini-2.0-flash",
51
- }
52
- DEFAULT_ONDEMAND_MODEL = "predefined-openai-gpt4o"
53
- # ==========================================
54
-
55
- class KeyManager:
56
- def __init__(self, key_list):
57
- self.key_list = list(key_list)
58
- self.lock = threading.Lock()
59
- self.key_status = {k: {"bad": False, "bad_ts": None} for k in self.key_list}
60
- self.idx = 0
61
- # 新增:当前正在使用的key和session
62
- self.current_key = None
63
- self.current_session = None
64
- self.last_used_time = None
65
-
66
- def display_key(self, key):
67
- return f"{key[:6]}...{key[-4:]}"
68
-
69
- def get(self):
70
- with self.lock:
71
- now = time.time()
72
- # 检查对话是否超时
73
- if self.current_key and self.last_used_time and (now - self.last_used_time > SESSION_TIMEOUT):
74
- print(f"【对话超时】上次使用时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_used_time))}")
75
- print(f"【对话超时】当前时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(now))}")
76
- print(f"【对话超时】超时{SESSION_TIMEOUT//60}分钟,切换新会话")
77
- self.current_key = None
78
- self.current_session = None
79
-
80
- # 如果已有正在使用的key,继续使用
81
- if self.current_key:
82
- if not self.key_status[self.current_key]["bad"]:
83
- print(f"【对话请求】【继续使用API KEY: {self.display_key(self.current_key)}】【状态:正常】")
84
- self.last_used_time = now
85
- return self.current_key
86
- else:
87
- # 当前key已标记为异常,需要切换
88
- self.current_key = None
89
- self.current_session = None
90
-
91
- # 如果没有当前key或当前key无效,选择新的key
92
- total = len(self.key_list)
93
- for _ in range(total):
94
- key = self.key_list[self.idx]
95
- self.idx = (self.idx + 1) % total
96
- s = self.key_status[key]
97
- if not s["bad"]:
98
- print(f"【对话请求】【使用新API KEY: {self.display_key(key)}】【状态:正常】")
99
- self.current_key = key
100
- self.current_session = None # 强制创建新会话
101
- self.last_used_time = now
102
- return key
103
- if s["bad"] and s["bad_ts"]:
104
- ago = now - s["bad_ts"]
105
- if ago >= BAD_KEY_RETRY_INTERVAL:
106
- print(f"【KEY自动尝试恢复】API KEY: {self.display_key(key)} 满足重试周期,标记为正常")
107
- self.key_status[key]["bad"] = False
108
- self.key_status[key]["bad_ts"] = None
109
- self.current_key = key
110
- self.current_session = None # 强制创建新会话
111
- self.last_used_time = now
112
- return key
113
-
114
- print("【警告】全部KEY已被禁用,强制选用第一个KEY继续尝试:", self.display_key(self.key_list[0]))
115
- for k in self.key_list:
116
- self.key_status[k]["bad"] = False
117
- self.key_status[k]["bad_ts"] = None
118
- self.idx = 0
119
- self.current_key = self.key_list[0]
120
- self.current_session = None # 强制创建新会话
121
- self.last_used_time = now
122
- print(f"【对话请求】【使用API KEY: {self.display_key(self.current_key)}】【状态:强制尝试(全部异常)】")
123
- return self.current_key
124
-
125
- def mark_bad(self, key):
126
- with self.lock:
127
- if key in self.key_status and not self.key_status[key]["bad"]:
128
- print(f"【禁用KEY】API KEY: {self.display_key(key)},接口返回无效(将在{BAD_KEY_RETRY_INTERVAL//60}分钟后自动重试)")
129
- self.key_status[key]["bad"] = True
130
- self.key_status[key]["bad_ts"] = time.time()
131
- if self.current_key == key:
132
- self.current_key = None
133
- self.current_session = None
134
-
135
- def get_session(self, apikey):
136
- with self.lock:
137
- if not self.current_session:
138
- try:
139
- self.current_session = create_session(apikey)
140
- print(f"【创建新会话】SESSION ID: {self.current_session}")
141
- except Exception as e:
142
- print(f"【创建会话失败】错误: {str(e)}")
143
- raise
144
- self.last_used_time = time.time()
145
- return self.current_session
146
-
147
- keymgr = KeyManager(ONDEMAND_APIKEYS)
148
-
149
- ONDEMAND_API_BASE = "https://api.on-demand.io/chat/v1"
150
-
151
- def get_endpoint_id(openai_model):
152
- m = str(openai_model or "").lower().replace(" ", "")
153
- return MODEL_MAP.get(m, DEFAULT_ONDEMAND_MODEL)
154
-
155
- def create_session(apikey, external_user_id=None, plugin_ids=None):
156
- url = f"{ONDEMAND_API_BASE}/sessions"
157
- payload = {"externalUserId": external_user_id or str(uuid.uuid4())}
158
- if plugin_ids is not None:
159
- payload["pluginIds"] = plugin_ids
160
- headers = {"apikey": apikey, "Content-Type": "application/json"}
161
- resp = requests.post(url, json=payload, headers=headers, timeout=20)
162
- resp.raise_for_status()
163
- return resp.json()["data"]["id"]
164
-
165
- def format_openai_sse_delta(chunk_str):
166
- return f"data: {json.dumps(chunk_str, ensure_ascii=False)}\n\n"
167
-
168
- @app.route("/v1/chat/completions", methods=["POST"])
169
- def chat_completions():
170
- data = request.json
171
- if not data or "messages" not in data:
172
- return jsonify({"error": "请求缺少messages字段"}), 400
173
-
174
- messages = data["messages"]
175
- openai_model = data.get("model", "gpt-4o")
176
- endpoint_id = get_endpoint_id(openai_model)
177
- is_stream = bool(data.get("stream", False))
178
-
179
- user_msg = None
180
- for msg in reversed(messages):
181
- if msg.get("role") == "user":
182
- user_msg = msg.get("content")
183
- break
184
- if user_msg is None:
185
- return jsonify({"error": "未找到用户消息"}), 400
186
-
187
- def with_valid_key(func):
188
- bad_cnt = 0
189
- max_retry = len(keymgr.key_list)*2
190
- while bad_cnt < max_retry:
191
- key = keymgr.get()
192
- try:
193
- return func(key)
194
- except Exception as e:
195
- if hasattr(e, 'response'):
196
- r = e.response
197
- if r.status_code in (401, 403, 429, 500):
198
- keymgr.mark_bad(key)
199
- bad_cnt += 1
200
- continue
201
- raise
202
- return jsonify({"error": "没有可用API KEY,请补充新KEY或联系技术支持"}), 500
203
-
204
- if is_stream:
205
- def generate():
206
- def do_once(apikey):
207
- # 使用KeyManager获取或创建session
208
- sid = keymgr.get_session(apikey)
209
- url = f"{ONDEMAND_API_BASE}/sessions/{sid}/query"
210
- payload = {
211
- "query": user_msg,
212
- "endpointId": endpoint_id,
213
- "pluginIds": [],
214
- "responseMode": "stream"
215
- }
216
- headers = {"apikey": apikey, "Content-Type": "application/json", "Accept": "text/event-stream"}
217
- with requests.post(url, json=payload, headers=headers, stream=True, timeout=120) as resp:
218
- if resp.status_code != 200:
219
- raise requests.HTTPError(response=resp)
220
- answer_acc = ""
221
- first_chunk = True
222
- for line in resp.iter_lines():
223
- if not line:
224
- continue
225
- line = line.decode("utf-8")
226
- if line.startswith("data:"):
227
- datapart = line[5:].strip()
228
- if datapart == "[DONE]":
229
- yield "data: [DONE]\n\n"
230
- break
231
- elif datapart.startswith("[ERROR]:"):
232
- err_json = datapart[len("[ERROR]:"):].strip()
233
- yield format_openai_sse_delta({"error": err_json})
234
- break
235
- else:
236
- try:
237
- js = json.loads(datapart)
238
- except Exception:
239
- continue
240
- if js.get("eventType") == "fulfillment":
241
- delta = js.get("answer", "")
242
- answer_acc += delta
243
- chunk = {
244
- "id": "chatcmpl-" + str(uuid.uuid4())[:8],
245
- "object": "chat.completion.chunk",
246
- "created": int(time.time()),
247
- "model": openai_model,
248
- "choices": [{
249
- "delta": {
250
- "role": "assistant",
251
- "content": delta
252
- } if first_chunk else {
253
- "content": delta
254
- },
255
- "index": 0,
256
- "finish_reason": None
257
- }]
258
- }
259
- yield format_openai_sse_delta(chunk)
260
- first_chunk = False
261
- yield "data: [DONE]\n\n"
262
- yield from with_valid_key(do_once)
263
- return Response(generate(), content_type='text/event-stream')
264
-
265
- def nonstream(apikey):
266
- # 使用KeyManager获取或创建session
267
- sid = keymgr.get_session(apikey)
268
- url = f"{ONDEMAND_API_BASE}/sessions/{sid}/query"
269
- payload = {
270
- "query": user_msg,
271
- "endpointId": endpoint_id,
272
- "pluginIds": [],
273
- "responseMode": "sync"
274
- }
275
- headers = {"apikey": apikey, "Content-Type": "application/json"}
276
- resp = requests.post(url, json=payload, headers=headers, timeout=120)
277
- if resp.status_code != 200:
278
- raise requests.HTTPError(response=resp)
279
- ai_response = resp.json()["data"]["answer"]
280
- resp_obj = {
281
- "id": "chatcmpl-" + str(uuid.uuid4())[:8],
282
- "object": "chat.completion",
283
- "created": int(time.time()),
284
- "model": openai_model,
285
- "choices": [
286
- {
287
- "index": 0,
288
- "message": {"role": "assistant", "content": ai_response},
289
- "finish_reason": "stop"
290
- }
291
- ],
292
- "usage": {}
293
- }
294
- return jsonify(resp_obj)
295
-
296
- return with_valid_key(nonstream)
297
-
298
- @app.route("/v1/models", methods=["GET"])
299
- def models():
300
- model_objs = []
301
- for mdl in MODEL_MAP.keys():
302
- model_objs.append({
303
- "id": mdl,
304
- "object": "model",
305
- "owned_by": "ondemand-proxy"
306
- })
307
- uniq = {m["id"]: m for m in model_objs}.values()
308
- return jsonify({
309
- "object": "list",
310
- "data": list(uniq)
311
- })
312
-
313
- if __name__ == "__main__":
314
- log_fmt = '[%(asctime)s] %(levelname)s: %(message)s'
315
- logging.basicConfig(level=logging.INFO, format=log_fmt)
316
- print("======== OnDemand KEY池数量:", len(ONDEMAND_APIKEYS), "========")
317
- app.run(host="0.0.0.0", port=7860, debug=False)