-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathconfig_tool.py
More file actions
251 lines (210 loc) · 8.83 KB
/
Copy pathconfig_tool.py
File metadata and controls
251 lines (210 loc) · 8.83 KB
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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
import json, os, sys, subprocess, tkinter as tk
from tkinter import ttk, filedialog, messagebox
FIELD_LABELS = {
"har_path": "HAR 文件路径",
"target_url": "目标地址",
"stream_url": "流式地址",
"agent_id": "Agent ID",
"token": "Token",
"uuid": "UUID",
"device_id": "Device ID",
"user_id": "User ID",
"screen_width": "屏幕宽度",
"screen_height": "屏幕高度",
}
ENV_MAPPING = [
("TARGET_URL", "target_url", "目标网站地址"),
("STREAM_URL", "stream_url", "流式地址"),
("AGENT_ID", "agent_id", "Agent ID"),
("TOKEN", "token", "JWT Token"),
("UUID", "uuid", "浏览器 UUID"),
("DEVICE_ID", "device_id", "设备 ID"),
("USER_ID", "user_id", "用户 ID"),
]
DISPLAY_FIELDS = [
("目标地址", "target_url"),
("Agent ID", "agent_id"),
("Token", "token"),
("UUID", "uuid"),
("设备 ID", "device_id"),
]
MUTABLE_KEYS = ["TOKEN", "TARGET_URL", "STREAM_URL", "AGENT_ID",
"UUID", "DEVICE_ID", "USER_ID",
"SCREEN_WIDTH", "SCREEN_HEIGHT"]
DEFAULT_IMMUTABLE = """# ============================================
# 不易变部分 — 服务器配置
# ============================================
MODEL_NAME=MiniMax-M3
MODEL_PROVIDER=minimax
MODEL_ID=MiniMax-M3
MODEL_VARIANT=thinking
HOST=0.0.0.0
PORT=8000
API_KEY=sk-web2api-placeholder
USE_WORKTREE=false
"""
def parse_har_file(har_path: str) -> dict:
with open(har_path, 'r', encoding='utf-8') as f:
data = json.load(f)
entries = data['log']['entries']
ses_entry = msg_entry = None
for e in entries:
url = e['request']['url']
ct = ""
for h in e['response'].get('headers', []):
if h['name'].lower() == 'content-type':
ct = h['value']
if e['request']['method'] == "POST" and "/session" in url and "/message" not in url:
ses_entry = e
if ct == "text/event-stream" and e['request']['method'] == "POST":
msg_entry = e
if not ses_entry:
raise ValueError("HAR 中未找到 Session 创建请求")
from urllib.parse import urlparse, parse_qs
up = urlparse(ses_entry['request']['url'])
qs = {k: v[0] for k, v in parse_qs(up.query).items()}
agent_id = ""
parts = up.path.split('/')
for i, p in enumerate(parts):
if p == 'agent' and i + 1 < len(parts):
agent_id = parts[i + 1]
break
info = {
"har_path": har_path,
"target_url": f"{up.scheme}://{up.netloc}",
"stream_url": f"{up.scheme}://agent-stream.minimaxi.com",
"agent_id": agent_id,
"token": qs.get("token", ""),
"uuid": qs.get("uuid", ""),
"device_id": qs.get("device_id", ""),
"user_id": qs.get("user_id", "0"),
"screen_width": qs.get("screen_width", "1366"),
"screen_height": qs.get("screen_height", "768"),
}
if msg_entry:
m_up = urlparse(msg_entry['request']['url'])
info["stream_url"] = f"{m_up.scheme}://{m_up.netloc}"
return info
def merge_env_with_auth(info: dict) -> str:
env_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env")
existing = {}
if os.path.exists(env_path):
with open(env_path, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#') and '=' in line:
k, v = line.split('=', 1)
existing[k.strip()] = v.strip()
lines = []
lines.append("# ============================================")
lines.append("# Immutable — Server Configuration")
lines.append("# ============================================")
fixed = [
("MODEL_NAME", "MiniMax-M3"),
("MODEL_PROVIDER", "minimax"),
("MODEL_ID", "MiniMax-M3"),
("MODEL_VARIANT", "thinking"),
("HOST", "0.0.0.0"),
("PORT", "8000"),
("API_KEY", "sk-web2api-placeholder"),
("USE_WORKTREE", "false"),
]
for k, v in fixed:
if k in existing and k not in MUTABLE_KEYS:
v = existing[k]
lines.append(f"{k}={v}")
lines.append("")
lines.append("# ============================================")
lines.append("# Mutable — From HAR capture")
lines.append("# ============================================")
for env_key, info_key, _ in ENV_MAPPING:
val = info.get(info_key, "")
lines.append(f"{env_key}={val}")
lines.append(f"SCREEN_WIDTH={info.get('screen_width', '1366')}")
lines.append(f"SCREEN_HEIGHT={info.get('screen_height', '768')}")
lines.append(f"COOKIES=")
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
lines.append(f"USER_AGENT={ua}")
return "\n".join(lines) + "\n"
class ConfigTool:
def __init__(self, root):
self.root = root
self.root.title("MiniMax Agent Web2API 配置工具")
self.root.geometry("900x700")
self.info = {}
main_frame = ttk.Frame(root, padding=10)
main_frame.pack(fill=tk.BOTH, expand=True)
btn_frame = ttk.Frame(main_frame)
btn_frame.pack(fill=tk.X, pady=(0, 10))
ttk.Button(btn_frame, text="选择 HAR 文件", command=self.select_har).pack(side=tk.LEFT, padx=(0, 5))
ttk.Button(btn_frame, text="解析", command=self.parse_har).pack(side=tk.LEFT, padx=5)
ttk.Button(btn_frame, text="保存到 .env", command=self.save_env).pack(side=tk.LEFT, padx=5)
ttk.Button(btn_frame, text="启动代理服务器", command=self.start_server).pack(side=tk.LEFT, padx=5)
self.tree = ttk.Treeview(main_frame, columns=("value",), show="tree", height=10)
self.tree.heading("#0", text="字段")
self.tree.heading("value", text="值")
self.tree.column("#0", width=150)
self.tree.column("value", width=700)
tree_scroll = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self.tree.yview)
self.tree.configure(yscrollcommand=tree_scroll.set)
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
tree_scroll.pack(side=tk.RIGHT, fill=tk.Y)
ttk.Label(main_frame, text=".env 预览:", font=("", 10, "bold")).pack(anchor=tk.W, pady=(10, 0))
self.env_text = tk.Text(main_frame, height=15, font=("Consolas", 9))
env_scroll = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self.env_text.yview)
self.env_text.configure(yscrollcommand=env_scroll.set)
self.env_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
env_scroll.pack(side=tk.RIGHT, fill=tk.Y)
self.har_path = None
def select_har(self):
path = filedialog.askopenfilename(filetypes=[("HAR files", "*.har"), ("All files", "*.*")])
if path:
self.har_path = path
self.parse_har()
def parse_har(self):
path = self.har_path
if not path:
path = filedialog.askopenfilename(filetypes=[("HAR files", "*.har"), ("All files", "*.*")])
if not path:
return
self.har_path = path
try:
self.info = parse_har_file(path)
except Exception as e:
messagebox.showerror("解析失败", str(e))
return
for item in self.tree.get_children():
self.tree.delete(item)
for label, key in DISPLAY_FIELDS:
val = self.info.get(key, "")
display_val = str(val)[:100] + "..." if len(str(val)) > 100 else str(val)
self.tree.insert("", tk.END, text=label, values=(display_val,))
env_content = merge_env_with_auth(self.info)
self.env_text.delete(1.0, tk.END)
self.env_text.insert(1.0, env_content)
messagebox.showinfo("解析完成", "成功解析 HAR 文件")
def save_env(self):
if not self.info:
messagebox.showwarning("无数据", "请先解析 HAR 文件")
return
env_content = merge_env_with_auth(self.info)
env_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env")
with open(env_path, 'w', encoding='utf-8') as f:
f.write(env_content)
messagebox.showinfo("保存成功", f".env 已保存到:{env_path}")
def start_server(self):
script = os.path.join(os.path.dirname(os.path.abspath(__file__)), "server.py")
if not os.path.exists(script):
messagebox.showerror("错误", "server.py 不存在")
return
try:
subprocess.Popen([sys.executable, script], cwd=os.path.dirname(script))
messagebox.showinfo("启动成功", "代理服务器已启动!\n\nAPI: http://localhost:8000/v1")
except Exception as e:
messagebox.showerror("启动失败", str(e))
def main():
root = tk.Tk()
ConfigTool(root)
root.mainloop()
if __name__ == "__main__":
main()