File size: 8,870 Bytes
c5d1fd7 5face16 c5d1fd7 5face16 0243fa2 c5d1fd7 0243fa2 c5d1fd7 13fbcb2 0243fa2 c5d1fd7 0243fa2 d1aade9 c5d1fd7 13fbcb2 d1aade9 702c4ec 13fbcb2 5face16 702c4ec d1aade9 13fbcb2 d1aade9 5face16 0243fa2 c5d1fd7 d1aade9 702c4ec d1aade9 702c4ec d1aade9 0243fa2 13fbcb2 d1aade9 13fbcb2 702c4ec 13fbcb2 702c4ec 13fbcb2 702c4ec d1aade9 13fbcb2 d1aade9 0243fa2 702c4ec d1aade9 ba47fa8 0243fa2 702c4ec 0243fa2 ba47fa8 702c4ec d1aade9 13fbcb2 c5d1fd7 702c4ec d1aade9 702c4ec c5d1fd7 0243fa2 13fbcb2 0243fa2 702c4ec 0243fa2 13fbcb2 702c4ec 13fbcb2 702c4ec 0243fa2 d1aade9 5face16 702c4ec 13fbcb2 702c4ec 13fbcb2 702c4ec 13fbcb2 702c4ec 13fbcb2 702c4ec 13fbcb2 702c4ec 13fbcb2 d1aade9 13fbcb2 d1aade9 13fbcb2 702c4ec 13fbcb2 702c4ec 13fbcb2 702c4ec 13fbcb2 702c4ec 13fbcb2 702c4ec 13fbcb2 702c4ec 13fbcb2 d1aade9 702c4ec d1aade9 13fbcb2 702c4ec 13fbcb2 702c4ec 13fbcb2 702c4ec 13fbcb2 ba47fa8 13fbcb2 ba47fa8 13fbcb2 0243fa2 c5d1fd7 0243fa2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
import json
import uuid
import time
import random
import os
import requests
import gradio as gr
from fastapi import FastAPI, Request, HTTPException, Depends, status
from fastapi.responses import StreamingResponse
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
# --- 1. 配置与状态缓存 ---
app = FastAPI()
security = HTTPBearer()
ACCESS_TOKEN = os.getenv("ACCESS_TOKEN", "sk-admin-123456")
API_KEY = "AIzaSyAZaD22Mzi9HkTcW3ErNxRA_sNEFolLBCA"
SUPPORTED_MODELS = ["auto", "gpt-5-mini", "gemini-3-flash", "pro"]
class TokenCache:
def __init__(self):
self.token = ""
self.balance = 0
cache = TokenCache()
# --- 2. 核心后端逻辑 ---
def fetch_account():
"""注册新账号"""
try:
reg_url = f"https://identitytoolkit.googleapis.com/v1/accounts:signUp?key={API_KEY}"
reg_resp = requests.post(reg_url, json={"returnSecureToken": True}, headers={"origin": "https://www.aidocmaker.com"}, timeout=10).json()
token = reg_resp.get("idToken")
if not token: return None
# 同步余额
bal_url = "https://level2labs-prod--adm-agent-helper-user-get-credits.modal.run/"
bal_resp = requests.post(bal_url, headers={"Authorization": f"Bearer {token}"}, timeout=10).json()
cache.token = token
cache.balance = bal_resp.get("premium_quota", 0)
return token
except:
return None
def get_valid_token():
if cache.token and cache.balance > 0:
return cache.token
return fetch_account()
def sync_balance():
if not cache.token: return 0
url = "https://level2labs-prod--adm-agent-helper-user-get-credits.modal.run/"
try:
data = requests.post(url, headers={"Authorization": f"Bearer {cache.token}"}, timeout=10).json()
cache.balance = data.get("premium_quota", 0)
return cache.balance
except:
cache.balance = 0
return 0
# --- 3. 稳健的 SSE 解析器 ---
def parse_upstream_line(line):
"""解析 upstream 返回的每一行,兼容 data: 前缀和纯 JSON"""
if not line: return None
decoded_line = line.decode("utf-8").strip()
if not decoded_line: return None
# 处理 SSE 常见的 data: 前缀
json_str = decoded_line
if decoded_line.startswith("data: "):
json_str = decoded_line[6:].strip()
if json_str == "[DONE]": return None
try:
data_obj = json.loads(json_str)
# 根据之前的日志,内容通常在 "data" 字段
return data_obj.get("data", "")
except:
return None
# --- 4. OpenAI 兼容接口 ---
async def verify_api(auth: HTTPAuthorizationCredentials = Depends(security)):
if auth.credentials != ACCESS_TOKEN:
raise HTTPException(status_code=401, detail="Invalid Access Token")
return auth.credentials
@app.get("/v1/models")
async def list_models(t: str = Depends(verify_api)):
return {"object": "list", "data": [{"id": m, "object": "model"} for m in SUPPORTED_MODELS]}
@app.post("/v1/chat/completions")
async def chat_completions(request: Request, t: str = Depends(verify_api)):
body = await request.json()
model = body.get("model", "auto")
stream = body.get("stream", False)
content = body.get("messages", [])[-1]["content"] if body.get("messages") else ""
jwt = get_valid_token()
if not jwt: raise HTTPException(status_code=500, detail="Failed to get provider token")
url = "https://level2labs-prod--adm-agent-send-chat-message.modal.run/"
data = {
"content": (None, content), "model": (None, model),
"conversation_id": (None, f"c-{uuid.uuid4()}"),
"message_id": (None, f"m-{uuid.uuid4()}"), "max_mode": (None, "true")
}
target_resp = requests.post(url, files=data, headers={"Authorization": f"Bearer {jwt}"}, stream=True, timeout=60)
def generate():
cid = f"chatcmpl-{uuid.uuid4()}"
for line in target_resp.iter_lines():
text_chunk = parse_upstream_line(line)
if text_chunk:
chunk = {
"id": cid, "object": "chat.completion.chunk", "created": int(time.time()),
"model": model, "choices": [{"index": 0, "delta": {"content": text_chunk}, "finish_reason": None}]
}
yield f"data: {json.dumps(chunk)}\n\n"
yield "data: [DONE]\n\n"
sync_balance()
if stream:
return StreamingResponse(generate(), media_type="text/event-stream")
else:
full_text = ""
for line in target_resp.iter_lines():
text_chunk = parse_upstream_line(line)
if text_chunk:
full_text += text_chunk
sync_balance()
return {
"id": f"chatcmpl-{uuid.uuid4()}", "object": "chat.completion", "created": int(time.time()),
"model": model, "choices": [{"index": 0, "message": {"role": "assistant", "content": full_text}, "finish_reason": "stop"}]
}
# --- 5. Gradio 管理面板 ---
with gr.Blocks(title="AI Doc Maker 管理面板", theme=gr.themes.Soft()) as demo:
# 鉴权门禁
with gr.Column(visible=True) as login_view:
gr.Markdown("## 🔐 访问受限\n请输入系统配置的 `ACCESS_TOKEN` 以解锁界面。")
pwd = gr.Textbox(label="ACCESS_TOKEN", type="password")
login_btn = gr.Button("解锁系统", variant="primary")
login_err = gr.Markdown()
# 主操作区
with gr.Column(visible=False) as main_view:
gr.Markdown("# 🚀 AI Doc Maker 后台管理")
with gr.Row():
ui_token = gr.Textbox(label="当前 Token (缓存)", interactive=False)
ui_balance = gr.Textbox(label="剩余额度", interactive=False)
ui_refresh = gr.Button("🔄 刷新账号")
with gr.Row():
with gr.Column():
ui_input = gr.Textbox(label="输入内容", lines=5, placeholder="写个笑话...")
with gr.Row():
ui_chat_btn = gr.Button("💬 发送对话", variant="primary")
ui_tts_btn = gr.Button("🎙️ 生成语音", variant="secondary")
with gr.Column():
ui_output = gr.Textbox(label="AI 回复", lines=8, interactive=False)
ui_audio = gr.Audio(label="语音预览", interactive=False)
# --- UI 交互逻辑 ---
def handle_login(token):
if token == ACCESS_TOKEN:
jwt = get_valid_token()
bal = sync_balance()
return gr.update(visible=False), gr.update(visible=True), jwt, str(bal), ""
return gr.update(visible=True), gr.update(visible=False), "", "", "❌ 密码错误"
login_btn.click(handle_login, inputs=pwd, outputs=[login_view, main_view, ui_token, ui_balance, login_err])
def handle_refresh():
jwt = fetch_account()
bal = sync_balance()
return jwt, str(bal)
ui_refresh.click(handle_refresh, outputs=[ui_token, ui_balance])
def handle_ui_chat(text):
jwt = get_valid_token()
if not jwt: yield "Token 获取失败"; return
url = "https://level2labs-prod--adm-agent-send-chat-message.modal.run/"
data = {"content": (None, text), "model": (None, "auto"), "conversation_id": (None, "ui"), "message_id": (None, "ui"), "max_mode": (None, "true")}
full_text = ""
try:
resp = requests.post(url, files=data, headers={"Authorization": f"Bearer {jwt}"}, stream=True, timeout=60)
for line in resp.iter_lines():
chunk = parse_upstream_line(line)
if chunk:
full_text += chunk
yield full_text
sync_balance()
except Exception as e:
yield f"请求出错: {e}"
ui_chat_btn.click(handle_ui_chat, inputs=ui_input, outputs=ui_output)
def handle_ui_tts(text):
jwt = get_valid_token()
if not jwt: return None
try:
url_create = "https://level2labs-prod--adm-agent-audio-create-audio-with-assistant.modal.run/"
headers = {"Authorization": f"Bearer {jwt}"}
res = requests.post(url_create, files={"prompt": (None, text), "voice_id": (None, "clear"), "speed": (None, "1")}, headers=headers).json()
name = res.get("name")
url_get = f"https://level2labs-prod--adm-agent-audio-get-audio-playback-url.modal.run/?name={name}"
audio_url = requests.get(url_get, headers=headers).json().get("url")
return audio_url
except:
return None
ui_tts_btn.click(handle_ui_tts, inputs=ui_input, outputs=ui_audio)
# 挂载并启动
app = gr.mount_gradio_app(app, demo, path="/")
if __name__ == "__main__":
import uvicorn
uvicorn.run("app:app", host="0.0.0.0", port=7860) |