exable324 commited on
Commit
702c4ec
·
verified ·
1 Parent(s): d1aade9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +132 -85
app.py CHANGED
@@ -9,7 +9,7 @@ from fastapi import FastAPI, Request, HTTPException, Depends, status
9
  from fastapi.responses import StreamingResponse
10
  from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
11
 
12
- # --- 1. 配置与全局状态 ---
13
  app = FastAPI()
14
  security = HTTPBearer()
15
 
@@ -17,7 +17,6 @@ ACCESS_TOKEN = os.getenv("ACCESS_TOKEN", "sk-admin-123456")
17
  API_KEY = "AIzaSyAZaD22Mzi9HkTcW3ErNxRA_sNEFolLBCA"
18
  SUPPORTED_MODELS = ["auto", "gpt-5-mini", "gemini-3-flash", "pro"]
19
 
20
- # 全局缓存:存储当前可用的 Token 及其余额
21
  class TokenCache:
22
  def __init__(self):
23
  self.token = ""
@@ -25,18 +24,17 @@ class TokenCache:
25
 
26
  cache = TokenCache()
27
 
28
- # --- 2. 核心逻辑函数 ---
29
 
30
- def fetch_new_account():
31
  """注册新账号并初始化缓存"""
32
- reg_url = f"https://identitytoolkit.googleapis.com/v1/accounts:signUp?key={API_KEY}"
33
  try:
34
- # 注册
35
  reg_resp = requests.post(reg_url, json={"returnSecureToken": True}, headers={"origin": "https://www.aidocmaker.com"}, timeout=10).json()
36
  token = reg_resp.get("idToken")
37
  if not token: return None
38
 
39
- # 查询初始余额
40
  bal_url = "https://level2labs-prod--adm-agent-helper-user-get-credits.modal.run/"
41
  bal_resp = requests.post(bal_url, headers={"Authorization": f"Bearer {token}"}, timeout=10).json()
42
 
@@ -47,130 +45,179 @@ def fetch_new_account():
47
  return None
48
 
49
  def get_valid_token():
50
- """获取有效 Token:如果有缓存且有余额则复用,否则获取"""
51
  if cache.token and cache.balance > 0:
52
  return cache.token
53
- return fetch_new_account()
54
 
55
  def sync_balance():
56
- """同步当前 Token 的实际余额"""
57
  if not cache.token: return 0
58
  url = "https://level2labs-prod--adm-agent-helper-user-get-credits.modal.run/"
59
  try:
60
- data = requests.post(url, headers={"Authorization": f"Bearer {cache.token}"}).json()
61
  cache.balance = data.get("premium_quota", 0)
62
  return cache.balance
63
  except:
64
  cache.balance = 0
65
  return 0
66
 
67
- # --- 3. OpenAI 兼容接口 ---
68
 
69
- async def verify_api_token(auth: HTTPAuthorizationCredentials = Depends(security)):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  if auth.credentials != ACCESS_TOKEN:
71
- raise HTTPException(status_code=401, detail="Invalid API Key")
72
  return auth.credentials
73
 
74
  @app.get("/v1/models")
75
- async def list_models(token: str = Depends(verify_api_token)):
76
  return {"object": "list", "data": [{"id": m, "object": "model"} for m in SUPPORTED_MODELS]}
77
 
78
  @app.post("/v1/chat/completions")
79
- async def chat_completions(request: Request, token: str = Depends(verify_api_token)):
80
  body = await request.json()
81
  model = body.get("model", "auto")
 
82
  content = body.get("messages", [])[-1]["content"] if body.get("messages") else ""
83
 
84
- # 自动获取/刷新 Token
85
  jwt = get_valid_token()
86
  if not jwt: raise HTTPException(status_code=500, detail="Provider Error")
87
 
88
  url = "https://level2labs-prod--adm-agent-send-chat-message.modal.run/"
89
- form_data = {
 
90
  "content": (None, content), "model": (None, model),
91
- "conversation_id": (None, f"conv-{uuid.uuid4()}"),
92
- "message_id": (None, f"msg-{uuid.uuid4()}"), "max_mode": (None, "true")
93
  }
94
 
95
- target_resp = requests.post(url, files=form_data, headers={"Authorization": f"Bearer {jwt}"}, stream=True)
96
 
97
  def generate():
 
98
  for line in target_resp.iter_lines():
99
- if line:
100
- try:
101
- raw = json.loads(line.decode("utf-8"))
102
- yield f"data: {json.dumps({'choices': [{'delta': {'content': raw.get('data', '')}}]})}\n\n"
103
- except: continue
 
 
104
  yield "data: [DONE]\n\n"
105
- # 响应结束后异步同步一下余额(简单处理,实际可根据响应次数扣减)
106
  sync_balance()
107
 
108
- return StreamingResponse(generate(), media_type="text/event-stream")
109
-
110
- # --- 4. Gradio UI (带门禁验证) ---
111
-
112
- with gr.Blocks(title="AI Doc Maker 管理系统") as demo:
113
- # 登录界面
114
- with gr.Column(visible=True) as login_panel:
115
- gr.Markdown("## 🔐 访问受限\n请输入系统的 ACCESS_TOKEN 以解锁管理面板。")
116
- pwd_input = gr.Textbox(label="ACCESS_TOKEN", type="password", placeholder="输入 sk-xxxx...")
117
- login_btn = gr.Button("解锁系统", variant="primary")
118
- login_msg = gr.Markdown()
119
-
120
- # 主管理界面 (初始隐藏)
121
- with gr.Column(visible=False) as main_panel:
122
- gr.Markdown("# 🚀 AI Doc Maker 后台管理")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
- with gr.Row():
125
- with gr.Column():
126
- token_view = gr.Textbox(label="当前活跃 Token (缓存中)", interactive=False)
127
- balance_view = gr.Textbox(label="剩余可用额度", interactive=False)
128
- refresh_btn = gr.Button("强制刷新账号/同步余额")
 
 
 
 
 
 
129
 
130
- with gr.Column():
131
- chat_input = gr.Textbox(label="测试对话", placeholder="输入消息...")
132
- chat_btn = gr.Button("发送测试", variant="primary")
133
- chat_output = gr.Textbox(label="AI 回复", interactive=False)
134
-
135
- # --- UI 逻辑绑定 ---
136
- def handle_login(entered_token):
137
- if entered_token == ACCESS_TOKEN:
138
- # 获取当前可用信息
139
- t = get_valid_token()
140
- b = sync_balance()
141
- return gr.update(visible=False), gr.update(visible=True), t, b, ""
142
- else:
143
- return gr.update(visible=True), gr.update(visible=False), "", "", "❌ 密码错误,请重试!"
144
-
145
- login_btn.click(
146
- handle_login,
147
- inputs=pwd_input,
148
- outputs=[login_panel, main_panel, token_view, balance_view, login_msg]
149
- )
150
-
151
- def manual_refresh():
152
- # 强制获取新账号
153
- t = fetch_new_account()
154
- b = sync_balance()
155
- return t, b
156
-
157
- refresh_btn.click(manual_refresh, outputs=[token_view, balance_view])
158
-
159
- def ui_chat_test(msg):
160
- jwt = get_valid_token()
161
  url = "https://level2labs-prod--adm-agent-send-chat-message.modal.run/"
162
- data = {"content": (None, msg), "model": (None, "auto"), "conversation_id": (None, "ui-test"), "message_id": (None, "ui-test"), "max_mode": (None, "true")}
163
  full_text = ""
164
  resp = requests.post(url, files=data, headers={"Authorization": f"Bearer {jwt}"}, stream=True)
165
  for line in resp.iter_lines():
166
- if line:
167
- full_text += json.loads(line.decode("utf-8")).get("data", "")
168
- yield full_text
169
- sync_balance() # 更新余额显示
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
- chat_btn.click(ui_chat_test, inputs=chat_input, outputs=chat_output)
172
 
173
- # 挂载
174
  app = gr.mount_gradio_app(app, demo, path="/")
175
 
176
  if __name__ == "__main__":
 
9
  from fastapi.responses import StreamingResponse
10
  from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
11
 
12
+ # --- 1. 全局配置与状态缓存 ---
13
  app = FastAPI()
14
  security = HTTPBearer()
15
 
 
17
  API_KEY = "AIzaSyAZaD22Mzi9HkTcW3ErNxRA_sNEFolLBCA"
18
  SUPPORTED_MODELS = ["auto", "gpt-5-mini", "gemini-3-flash", "pro"]
19
 
 
20
  class TokenCache:
21
  def __init__(self):
22
  self.token = ""
 
24
 
25
  cache = TokenCache()
26
 
27
+ # --- 2. 核心后端逻辑 (带自动刷新) ---
28
 
29
+ def fetch_account():
30
  """注册新账号并初始化缓存"""
 
31
  try:
32
+ reg_url = f"https://identitytoolkit.googleapis.com/v1/accounts:signUp?key={API_KEY}"
33
  reg_resp = requests.post(reg_url, json={"returnSecureToken": True}, headers={"origin": "https://www.aidocmaker.com"}, timeout=10).json()
34
  token = reg_resp.get("idToken")
35
  if not token: return None
36
 
37
+ # 初始同步余额
38
  bal_url = "https://level2labs-prod--adm-agent-helper-user-get-credits.modal.run/"
39
  bal_resp = requests.post(bal_url, headers={"Authorization": f"Bearer {token}"}, timeout=10).json()
40
 
 
45
  return None
46
 
47
  def get_valid_token():
48
+ """复用缓存或静默刷新"""
49
  if cache.token and cache.balance > 0:
50
  return cache.token
51
+ return fetch_account()
52
 
53
  def sync_balance():
54
+ """查询并更新当前 Token 的余额"""
55
  if not cache.token: return 0
56
  url = "https://level2labs-prod--adm-agent-helper-user-get-credits.modal.run/"
57
  try:
58
+ data = requests.post(url, headers={"Authorization": f"Bearer {cache.token}"}, timeout=10).json()
59
  cache.balance = data.get("premium_quota", 0)
60
  return cache.balance
61
  except:
62
  cache.balance = 0
63
  return 0
64
 
65
+ # --- 3. API 解析辅助函数 ---
66
 
67
+ def clean_sse_line(line):
68
+ """解析 upstream 返回的每一行,处理可能存在的 data: 前缀"""
69
+ line = line.decode("utf-8").strip()
70
+ if not line: return None
71
+ # 移除 data: 前缀(如果存在)
72
+ if line.startswith("data: "):
73
+ line = line[6:]
74
+ if line == "[DONE]":
75
+ return None
76
+ try:
77
+ return json.loads(line)
78
+ except:
79
+ return None
80
+
81
+ # --- 4. OpenAI 兼容接口 ---
82
+
83
+ async def verify_api(auth: HTTPAuthorizationCredentials = Depends(security)):
84
  if auth.credentials != ACCESS_TOKEN:
85
+ raise HTTPException(status_code=401, detail="Unauthorized")
86
  return auth.credentials
87
 
88
  @app.get("/v1/models")
89
+ async def list_models(t: str = Depends(verify_api)):
90
  return {"object": "list", "data": [{"id": m, "object": "model"} for m in SUPPORTED_MODELS]}
91
 
92
  @app.post("/v1/chat/completions")
93
+ async def chat_completions(request: Request, t: str = Depends(verify_api)):
94
  body = await request.json()
95
  model = body.get("model", "auto")
96
+ stream = body.get("stream", False)
97
  content = body.get("messages", [])[-1]["content"] if body.get("messages") else ""
98
 
 
99
  jwt = get_valid_token()
100
  if not jwt: raise HTTPException(status_code=500, detail="Provider Error")
101
 
102
  url = "https://level2labs-prod--adm-agent-send-chat-message.modal.run/"
103
+ headers = {"Authorization": f"Bearer {jwt}"}
104
+ data = {
105
  "content": (None, content), "model": (None, model),
106
+ "conversation_id": (None, f"c-{uuid.uuid4()}"),
107
+ "message_id": (None, f"m-{uuid.uuid4()}"), "max_mode": (None, "true")
108
  }
109
 
110
+ target_resp = requests.post(url, files=data, headers=headers, stream=True, timeout=60)
111
 
112
  def generate():
113
+ cid = f"chatcmpl-{uuid.uuid4()}"
114
  for line in target_resp.iter_lines():
115
+ item = clean_sse_line(line)
116
+ if item and "data" in item:
117
+ chunk = {
118
+ "id": cid, "object": "chat.completion.chunk", "created": int(time.time()),
119
+ "model": model, "choices": [{"index": 0, "delta": {"content": item["data"]}, "finish_reason": None}]
120
+ }
121
+ yield f"data: {json.dumps(chunk)}\n\n"
122
  yield "data: [DONE]\n\n"
 
123
  sync_balance()
124
 
125
+ if stream:
126
+ return StreamingResponse(generate(), media_type="text/event-stream")
127
+ else:
128
+ full_text = ""
129
+ for line in target_resp.iter_lines():
130
+ item = clean_sse_line(line)
131
+ if item and "data" in item:
132
+ full_text += item["data"]
133
+ sync_balance()
134
+ return {
135
+ "id": f"chatcmpl-{uuid.uuid4()}", "object": "chat.completion", "created": int(time.time()),
136
+ "model": model, "choices": [{"index": 0, "message": {"role": "assistant", "content": full_text}, "finish_reason": "stop"}]
137
+ }
138
+
139
+ # --- 5. Gradio UI 管理面板 ---
140
+
141
+ with gr.Blocks(title="AI Doc Maker 管理面板") as demo:
142
+ # 状态存储
143
+ current_jwt_state = gr.State("")
144
+
145
+ # 登录门禁
146
+ with gr.Column(visible=True) as login_view:
147
+ gr.Markdown("## 🔐 访问受限\n请输入 `ACCESS_TOKEN` 以解锁管理面板。")
148
+ pwd = gr.Textbox(label="Token", type="password")
149
+ login_btn = gr.Button("验证并进入", variant="primary")
150
+ err_msg = gr.Markdown()
151
+
152
+ # 主面板
153
+ with gr.Column(visible=False) as main_view:
154
+ gr.Markdown("# 🚀 AI Doc Maker 管理面板")
155
 
156
+ with gr.Tab("余额与账号"):
157
+ with gr.Row():
158
+ ui_token = gr.Textbox(label="当前活跃 Token", interactive=False)
159
+ ui_balance = gr.Textbox(label="剩余可用额度", interactive=False)
160
+ ui_refresh = gr.Button("手动刷新/注册新账号")
161
+
162
+ with gr.Tab("对话与语音"):
163
+ ui_input = gr.Textbox(label="输入内容", lines=3, placeholder="输入文字...")
164
+ with gr.Row():
165
+ ui_chat_btn = gr.Button("发送消息", variant="primary")
166
+ ui_tts_btn = gr.Button("✨ 生成语音", variant="secondary")
167
 
168
+ ui_reply = gr.Textbox(label="AI 回复", interactive=False)
169
+ with gr.Row():
170
+ ui_raw_json = gr.Textbox(label="原始 JSON (聊天)", interactive=False)
171
+ ui_audio_json = gr.Textbox(label="音频 JSON", interactive=False)
172
+ ui_audio_play = gr.Audio(label="播放器", interactive=False)
173
+
174
+ # --- 逻辑处理 ---
175
+ def on_login(token):
176
+ if token == ACCESS_TOKEN:
177
+ jwt = get_valid_token()
178
+ bal = sync_balance()
179
+ return gr.update(visible=False), gr.update(visible=True), jwt, bal, jwt, ""
180
+ return gr.update(visible=True), gr.update(visible=False), "", "", "", "❌ 验证失败"
181
+
182
+ login_btn.click(on_login, inputs=pwd, outputs=[login_view, main_view, ui_token, ui_balance, current_jwt_state, err_msg])
183
+
184
+ def on_refresh():
185
+ jwt = fetch_account()
186
+ bal = sync_balance()
187
+ return jwt, bal, jwt
188
+
189
+ ui_refresh.click(on_refresh, outputs=[ui_token, ui_balance, current_jwt_state])
190
+
191
+ def on_ui_chat(jwt, text):
192
+ if not jwt: yield "请先登录", ""; return
 
 
 
 
 
 
193
  url = "https://level2labs-prod--adm-agent-send-chat-message.modal.run/"
194
+ data = {"content": (None, text), "model": (None, "auto"), "conversation_id": (None, "ui"), "message_id": (None, "ui"), "max_mode": (None, "true")}
195
  full_text = ""
196
  resp = requests.post(url, files=data, headers={"Authorization": f"Bearer {jwt}"}, stream=True)
197
  for line in resp.iter_lines():
198
+ item = clean_sse_line(line)
199
+ if item and "data" in item:
200
+ full_text += item["data"]
201
+ yield full_text, json.dumps(item, ensure_ascii=False)
202
+ sync_balance()
203
+
204
+ ui_chat_btn.click(on_ui_chat, inputs=[current_jwt_state, ui_input], outputs=[ui_reply, ui_raw_json])
205
+
206
+ def on_ui_tts(jwt, text):
207
+ if not jwt: return "请先登录", None
208
+ try:
209
+ url_create = "https://level2labs-prod--adm-agent-audio-create-audio-with-assistant.modal.run/"
210
+ headers = {"Authorization": f"Bearer {jwt}"}
211
+ res = requests.post(url_create, files={"prompt": (None, text), "voice_id": (None, "clear"), "speed": (None, "1")}, headers=headers).json()
212
+ name = res.get("name")
213
+ url_get = f"https://level2labs-prod--adm-agent-audio-get-audio-playback-url.modal.run/?name={name}"
214
+ audio_url = requests.get(url_get, headers=headers).json().get("url")
215
+ return json.dumps(res, indent=2), audio_url
216
+ except Exception as e:
217
+ return f"生成失败: {e}", None
218
 
219
+ ui_tts_btn.click(on_ui_tts, inputs=[current_jwt_state, ui_input], outputs=[ui_audio_json, ui_audio_play])
220
 
 
221
  app = gr.mount_gradio_app(app, demo, path="/")
222
 
223
  if __name__ == "__main__":