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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +123 -104
app.py CHANGED
@@ -9,149 +9,168 @@ from fastapi import FastAPI, Request, HTTPException, Depends, status
9
  from fastapi.responses import StreamingResponse
10
  from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
11
 
12
- # --- 1. 初始化 FastAPI 与 安全验证 ---
13
  app = FastAPI()
14
  security = HTTPBearer()
15
 
16
- # 建议在 HF Secret 中设置 ACCESS_TOKEN
17
  ACCESS_TOKEN = os.getenv("ACCESS_TOKEN", "sk-admin-123456")
18
  API_KEY = "AIzaSyAZaD22Mzi9HkTcW3ErNxRA_sNEFolLBCA"
19
  SUPPORTED_MODELS = ["auto", "gpt-5-mini", "gemini-3-flash", "pro"]
20
 
21
- async def verify_token(auth: HTTPAuthorizationCredentials = Depends(security)):
22
- if auth.credentials != ACCESS_TOKEN:
23
- raise HTTPException(
24
- status_code=status.HTTP_401_UNAUTHORIZED,
25
- detail="Invalid API Key",
26
- headers={"WWW-Authenticate": "Bearer"},
27
- )
28
- return auth.credentials
29
 
30
- # --- 2. 核心后端逻辑 ---
31
- def get_jwt():
32
- url = f"https://identitytoolkit.googleapis.com/v1/accounts:signUp?key={API_KEY}"
 
 
33
  try:
34
- resp = requests.post(url, json={"returnSecureToken": True}, headers={"origin": "https://www.aidocmaker.com"}, timeout=10)
35
- return resp.json().get("idToken")
 
 
 
 
 
 
 
 
 
 
36
  except:
37
  return None
38
 
39
- def generate_id():
40
- return ''.join(random.choice('0123456789abcdef') for _ in range(32))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
- # --- 3. OpenAI 兼容接口 (/v1/...) ---
 
 
 
 
 
43
 
44
  @app.get("/v1/models")
45
- async def list_models(token: str = Depends(verify_token)):
46
- models_data = [{"id": m, "object": "model", "created": int(time.time()), "owned_by": "aidocmaker"} for m in SUPPORTED_MODELS]
47
- return {"object": "list", "data": models_data}
48
 
49
  @app.post("/v1/chat/completions")
50
- async def chat_completions(request: Request, token: str = Depends(verify_token)):
51
  body = await request.json()
52
- messages = body.get("messages", [])
53
  model = body.get("model", "auto")
54
- stream = body.get("stream", False)
55
- content = messages[-1]["content"] if messages else ""
56
-
57
- jwt = get_jwt()
58
- if not jwt: raise HTTPException(status_code=500, detail="Failed to get JWT")
59
 
60
  url = "https://level2labs-prod--adm-agent-send-chat-message.modal.run/"
61
- headers = {"Authorization": f"Bearer {jwt}"}
62
  form_data = {
63
- "content": (None, content),
64
- "model": (None, model),
65
- "conversation_id": (None, generate_id()),
66
- "message_id": (None, generate_id()),
67
- "max_mode": (None, "true")
68
  }
69
 
70
- target_resp = requests.post(url, files=form_data, headers=headers, stream=True, timeout=60)
71
 
72
  def generate():
73
- chat_id = f"chatcmpl-{uuid.uuid4()}"
74
  for line in target_resp.iter_lines():
75
  if line:
76
  try:
77
- raw_data = json.loads(line.decode("utf-8"))
78
- chunk = {
79
- "id": chat_id, "object": "chat.completion.chunk", "created": int(time.time()),
80
- "model": model, "choices": [{"index": 0, "delta": {"content": raw_data.get("data", "")}, "finish_reason": None}]
81
- }
82
- yield f"data: {json.dumps(chunk)}\n\n"
83
  except: continue
84
  yield "data: [DONE]\n\n"
 
 
85
 
86
- if stream:
87
- return StreamingResponse(generate(), media_type="text/event-stream")
88
- else:
89
- full_text = "".join([json.loads(l.decode("utf-8")).get("data", "") for l in target_resp.iter_lines() if l])
90
- return {
91
- "id": f"chatcmpl-{uuid.uuid4()}", "object": "chat.completion", "created": int(time.time()),
92
- "model": model, "choices": [{"index": 0, "message": {"role": "assistant", "content": full_text}, "finish_reason": "stop"}]
93
- }
94
 
95
- # --- 4. Gradio 管理界面逻辑 ---
96
 
97
- def ui_check_balance(token):
98
- if not token: return "请先获取 Token"
99
- url = "https://level2labs-prod--adm-agent-helper-user-get-credits.modal.run/"
100
- headers = {"Authorization": f"Bearer {token}"}
101
- try:
102
- data = requests.post(url, headers=headers).json()
103
- return f"当前账号余额: {data.get('premium_quota', 0)}"
104
- except: return "查询失败"
105
 
106
- def ui_chat(token, content):
107
- if not token: yield "请先获取 Token", ""; return
108
- url = "https://level2labs-prod--adm-agent-send-chat-message.modal.run/"
109
- headers = {"Authorization": f"Bearer {token}"}
110
- data = {"content": (None, content), "model": (None, "gemini-3-flash"), "conversation_id": (None, generate_id()), "message_id": (None, generate_id()), "max_mode": (None, "true")}
111
- full_text = ""
112
- try:
113
- resp = requests.post(url, files=data, headers=headers, stream=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  for line in resp.iter_lines():
115
  if line:
116
- raw = json.loads(line.decode("utf-8"))
117
- full_text += raw.get("data", "")
118
- yield full_text, json.dumps(raw, ensure_ascii=False)
119
- except Exception as e: yield f"错误: {e}", ""
120
-
121
- def ui_tts(token, prompt):
122
- if not token: return "请先获取 Token", None
123
- url = "https://level2labs-prod--adm-agent-audio-create-audio-with-assistant.modal.run/"
124
- headers = {"Authorization": f"Bearer {token}"}
125
- try:
126
- resp = requests.post(url, files={"prompt": (None, prompt), "voice_id": (None, "clear"), "speed": (None, "1")}, headers=headers).json()
127
- audio_url = requests.get(f"https://level2labs-prod--adm-agent-audio-get-audio-playback-url.modal.run/?name={resp.get('name')}", headers=headers).json().get("url")
128
- return json.dumps(resp, indent=2), audio_url
129
- except: return "生成失败", None
130
-
131
- # --- 5. 构建与挂载 Gradio ---
132
-
133
- with gr.Blocks(title="AI Doc Maker 管理面板") as demo:
134
- gr.Markdown("# 🚀 AI Doc Maker 管理面板")
135
- with gr.Group():
136
- btn_reg = gr.Button("第一步:创建/刷新 Token", variant="primary")
137
- token_display = gr.Textbox(label="当前 IdToken", interactive=True)
138
- with gr.Group():
139
- btn_balance = gr.Button("查询余额")
140
- balance_display = gr.Textbox(label="余额详情", interactive=False)
141
- with gr.Group():
142
- msg_input = gr.Textbox(label="输入消息", placeholder="a joke")
143
- with gr.Row():
144
- btn_msg = gr.Button("发送消息", variant="primary")
145
- btn_tts = gr.Button("生成语音")
146
- reply_out = gr.Textbox(label="AI 回复")
147
- audio_out = gr.Audio(label="语音播放")
148
 
149
- btn_reg.click(get_jwt, outputs=token_display)
150
- btn_balance.click(ui_check_balance, inputs=token_display, outputs=balance_display)
151
- btn_msg.click(ui_chat, inputs=[token_display, msg_input], outputs=[reply_out, gr.State()])
152
- btn_tts.click(ui_tts, inputs=[token_display, msg_input], outputs=[gr.State(), audio_out])
153
 
154
- # 挂载到根路径,这样你打开 Space 首页就能看到面板
155
  app = gr.mount_gradio_app(app, demo, path="/")
156
 
157
  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
 
 
16
  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 = ""
24
+ self.balance = 0
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
+
43
+ cache.token = token
44
+ cache.balance = bal_resp.get("premium_quota", 0)
45
+ return token
46
  except:
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__":