KiroProxy User commited on
Commit
deeaf30
·
1 Parent(s): a990d27

重新设计监控界面:紧凑布局优化

Browse files

- 将监控面板重新设计为紧凑的3列布局
- 大幅减少卡片间距和内边距,节省垂直空间
- 优化统计数据字体大小和表格行高
- 添加滚动限制,确保所有数据在一页内完整显示
- 保持所有功能完整性和视觉风格一致性
- 更新核心思维系统和API处理器

KiroProxy/kiro_proxy/converters/__init__.py CHANGED
@@ -282,28 +282,8 @@ def convert_anthropic_messages_to_kiro(messages: List[dict], system="") -> Tuple
282
  current_tool_results = []
283
 
284
  def _strip_thinking(text: str) -> str:
285
- if text is None:
286
- return ""
287
- if not isinstance(text, str):
288
- text = str(text)
289
- if not text:
290
- return ""
291
- cleaned = text
292
- while True:
293
- start = find_real_thinking_start_tag(cleaned)
294
- if start == -1:
295
- break
296
- end = find_real_thinking_end_tag(cleaned, start + len("<thinking>"))
297
- if end == -1:
298
- cleaned = cleaned[:start].rstrip()
299
- break
300
- before = cleaned[:start].rstrip()
301
- after = cleaned[end + len("</thinking>"):].lstrip()
302
- if before and after:
303
- cleaned = before + "\n" + after
304
- else:
305
- cleaned = before or after
306
- return cleaned.strip()
307
 
308
  # 处理 system
309
  system_text = ""
 
282
  current_tool_results = []
283
 
284
  def _strip_thinking(text: str) -> str:
285
+ return text
286
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
 
288
  # 处理 system
289
  system_text = ""
KiroProxy/kiro_proxy/core/thinking.py CHANGED
@@ -327,57 +327,38 @@ def build_thinking_prompt(
327
  ) -> str:
328
  """Build a thinking prompt for internal reasoning phase.
329
 
330
- Key: This is planning-only, no actual tool execution.
331
- The model will receive full conversation history via the API.
332
  """
333
- budget_str = ""
334
- if budget_tokens:
335
- budget_str = f" (Budget: {budget_tokens} tokens)"
336
-
337
- # Detect language from current content or history if current is a placeholder
338
- detect_text = user_content or ""
339
- is_placeholder = any(p in detect_text for p in ["Tool results provided", "Please continue", "Continue"])
340
-
341
- if (not detect_text or is_placeholder) and history:
342
- for msg in reversed(history):
343
- if "userInputMessage" in msg:
344
- detect_text = msg["userInputMessage"].get("content", "")
345
- if detect_text:
346
- break
347
-
348
- language_instruction = ""
349
- if detect_text and any("\u4e00" <= c <= "\u9fff" for c in detect_text):
350
- language_instruction = "你必须用中文进行思考和推理。输出必须是中文,不要使用英文。"
351
- else:
352
- language_instruction = "Your reasoning MUST be in the same language as the user's message."
353
-
354
- common_rules = (
355
- f"[Internal Reasoning - Not Visible to User]{budget_str}\n\n"
356
- "Role: This is the THINKING/PLANNING phase. It is ONLY for internal reasoning before producing the final answer.\n"
357
- "This text will be injected as hidden context to improve the next response.\n\n"
358
- "Hard rules (must follow):\n"
359
- "1) Output plain-text reasoning ONLY. Do NOT output any structured tags/markup.\n"
360
- "2) Do NOT output any tool calls or tool-call formats (e.g. <tool_use>, tool_use, function_call, Bash, TodoWrite, JSON action blocks).\n"
361
- "3) Do NOT promise or describe executing commands/tools. Only analyze, break down, plan, and assess risks.\n"
362
- f"4) {language_instruction}\n"
363
  )
364
 
365
  if has_tool_results:
366
  return (
367
- f"{common_rules}\n\n"
368
- "Context: The user has provided tool results. The original query and previous steps are in the conversation history above.\n"
369
- "Task: Analyze the tool results from the conversation history. Summarize what you learned and plan next steps.\n"
370
- "Do NOT complain about missing info; use the history to find the context.\n"
371
- "Suggested structure:\n"
372
- "- Key findings\n"
373
- "- Unknowns / missing info\n"
374
- "- Next-step plan (plan only; do not write tool calls)"
375
  )
376
 
377
  return (
378
- f"{common_rules}\n\n"
379
- "Task: Analyze the user's request and plan your approach (planning only; do not execute).\n"
380
- "If the current input is a generic 'Continue', refer to the conversation history for the actual task.\n"
381
  f"User input: {user_content}"
382
  )
383
 
@@ -394,7 +375,11 @@ def build_user_prompt_with_thinking(user_content: str, thinking_content: str) ->
394
  if not thinking_block:
395
  return user_content
396
 
397
- return f"{thinking_block}\n\n{user_content}"
 
 
 
 
398
 
399
 
400
  async def iter_aws_event_stream_text(byte_iter: AsyncIterator[bytes]) -> AsyncIterator[str]:
@@ -445,18 +430,17 @@ async def fetch_thinking_text(
445
  timeout_s: float = 600.0,
446
  ) -> str:
447
  """Non-streaming helper to get thinking content (best-effort)."""
448
- clean_history = strip_thinking_from_history(history)
449
  has_tool_results = bool(tool_results)
450
  thinking_prompt = build_thinking_prompt(
451
  user_content,
452
  budget_tokens=budget_tokens,
453
- history=clean_history,
454
  has_tool_results=has_tool_results
455
  )
456
  thinking_request = build_kiro_request(
457
  thinking_prompt,
458
  model,
459
- clean_history,
460
  tools=tools,
461
  images=images,
462
  tool_results=tool_results,
@@ -485,18 +469,17 @@ async def stream_thinking_text(
485
  timeout_s: float = 600.0,
486
  ) -> AsyncIterator[str]:
487
  """Streaming helper to yield thinking content incrementally (best-effort)."""
488
- clean_history = strip_thinking_from_history(history)
489
  has_tool_results = bool(tool_results)
490
  thinking_prompt = build_thinking_prompt(
491
  user_content,
492
  budget_tokens=budget_tokens,
493
- history=clean_history,
494
  has_tool_results=has_tool_results
495
  )
496
  thinking_request = build_kiro_request(
497
  thinking_prompt,
498
  model,
499
- clean_history,
500
  tools=tools,
501
  images=images,
502
  tool_results=tool_results,
 
327
  ) -> str:
328
  """Build a thinking prompt for internal reasoning phase.
329
 
330
+ This phase is for the model to deeply analyze the situation and plan its strategy.
331
+ The reasoning is internal and should be in plain text.
332
  """
333
+ budget_str = f" (Budget: {budget_tokens} tokens)" if budget_tokens else ""
334
+ lang_instruction = "Your reasoning MUST be in the same language as the user's message."
335
+
336
+ common_intro = (
337
+ f"[Internal Reasoning - Hidden Context]{budget_str}\n\n"
338
+ "You are in your THINKING phase. Before providing a final response, take a moment to "
339
+ "analyze the conversation, break down the user's request, and plan your approach.\n\n"
340
+ "Your thinking process should focus on:\n"
341
+ "1. **Understanding Intent**: What is the user truly asking for?\n"
342
+ "2. **Context Analysis**: What information is available in the history? What's missing?\n"
343
+ "3. **Risk Assessment**: Are there any dangerous operations or edge cases to consider?\n"
344
+ "4. **Strategic Planning**: What steps are needed? Which tools are most appropriate (if any)?\n\n"
345
+ "Guidelines for this phase:\n"
346
+ "- Write your thoughts as clear, structured plain text.\n"
347
+ "- Do NOT output any structured tool calls or tags (like <tool_use>) here; save them for the next phase.\n"
348
+ "- Do NOT provide the final answer or a formal summary to the user yet.\n"
349
+ f"- {lang_instruction}\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
350
  )
351
 
352
  if has_tool_results:
353
  return (
354
+ f"{common_intro}\n"
355
+ "Current Focus: Analyze the tool results provided in the history above. "
356
+ "Evaluate what was accomplished, handle any errors, and determine the next logical steps."
 
 
 
 
 
357
  )
358
 
359
  return (
360
+ f"{common_intro}\n"
361
+ "Current Focus: Evaluate the user's latest request and formulate a high-level execution plan.\n"
 
362
  f"User input: {user_content}"
363
  )
364
 
 
375
  if not thinking_block:
376
  return user_content
377
 
378
+ # Dynamically instruct the model to respond in the same language as its thinking
379
+ # This covers all languages (English, Chinese, Japanese, etc.)
380
+ lang_hint = "\n(Please provide your final response in the same language as your internal reasoning above.)\n"
381
+
382
+ return f"{thinking_block}\n\n{lang_hint}{user_content}"
383
 
384
 
385
  async def iter_aws_event_stream_text(byte_iter: AsyncIterator[bytes]) -> AsyncIterator[str]:
 
430
  timeout_s: float = 600.0,
431
  ) -> str:
432
  """Non-streaming helper to get thinking content (best-effort)."""
 
433
  has_tool_results = bool(tool_results)
434
  thinking_prompt = build_thinking_prompt(
435
  user_content,
436
  budget_tokens=budget_tokens,
437
+ history=history,
438
  has_tool_results=has_tool_results
439
  )
440
  thinking_request = build_kiro_request(
441
  thinking_prompt,
442
  model,
443
+ history,
444
  tools=tools,
445
  images=images,
446
  tool_results=tool_results,
 
469
  timeout_s: float = 600.0,
470
  ) -> AsyncIterator[str]:
471
  """Streaming helper to yield thinking content incrementally (best-effort)."""
 
472
  has_tool_results = bool(tool_results)
473
  thinking_prompt = build_thinking_prompt(
474
  user_content,
475
  budget_tokens=budget_tokens,
476
+ history=history,
477
  has_tool_results=has_tool_results
478
  )
479
  thinking_request = build_kiro_request(
480
  thinking_prompt,
481
  model,
482
+ history,
483
  tools=tools,
484
  images=images,
485
  tool_results=tool_results,
KiroProxy/kiro_proxy/handlers/anthropic/__init__.py CHANGED
@@ -310,8 +310,7 @@ async def handle_messages(request: Request):
310
 
311
  # 构建 Kiro 请求
312
  kiro_tools = convert_anthropic_tools_to_kiro(tools) if tools else None
313
- clean_history = strip_thinking_from_history(history)
314
- kiro_request = build_kiro_request(user_content, model, clean_history, kiro_tools, images, tool_results)
315
 
316
  if stream:
317
  return await _handle_stream(
@@ -385,13 +384,12 @@ async def _handle_stream(kiro_request, headers, account, model, log_id, start_ti
385
  ) -> list:
386
  """构建用于思维链请求的历史记录。
387
 
388
- 使用 strip_thinking_from_history 移除历史中的思维链块
389
- 但保留工具调用和结果的结构,以便模型理解上下文。
390
  """
391
  if not base_history:
392
  return []
393
 
394
- return strip_thinking_from_history(base_history)
395
 
396
  # 思考功能:使用“独立请求”生成思维链(不向主请求注入提示词/标签)
397
  if thinking_enabled:
@@ -589,8 +587,7 @@ async def _handle_stream(kiro_request, headers, account, model, log_id, start_ti
589
  yield f'event: content_block_stop\ndata: {{"type":"content_block_stop","index":{thinking_index}}}\n\n'
590
 
591
  main_user_content = build_user_prompt_with_thinking(user_content, thinking_accumulated)
592
- clean_history = strip_thinking_from_history(history)
593
- kiro_request = build_kiro_request(main_user_content, model, clean_history, kiro_tools, images, tool_results)
594
 
595
  # ========== 主响应流式处理(text block,独立请求) ==========
596
  main_retry = 0
@@ -689,8 +686,7 @@ async def _handle_stream(kiro_request, headers, account, model, log_id, start_ti
689
  if should_retry:
690
  print(f"[Stream] 内容长度超限,{history_manager.truncate_info}")
691
  history = truncated_history
692
- clean_history = strip_thinking_from_history(history)
693
- kiro_request = build_kiro_request(main_user_content, model, clean_history, kiro_tools, images, tool_results)
694
  main_retry += 1
695
  continue
696
 
@@ -950,8 +946,7 @@ async def _handle_stream(kiro_request, headers, account, model, log_id, start_ti
950
  print(f"[Stream] 内容长度超限,{history_manager.truncate_info}")
951
  history = truncated_history
952
  # 重新构建请求
953
- clean_history = strip_thinking_from_history(history)
954
- kiro_request = build_kiro_request(user_content, model, clean_history, kiro_tools, images, tool_results)
955
  retry_count += 1
956
  continue
957
 
@@ -1148,10 +1143,9 @@ async def _handle_non_stream(kiro_request, headers, account, model, log_id, star
1148
  retry_ctx = RetryableRequest(max_retries=2)
1149
 
1150
  thinking_content = ""
1151
- clean_history = strip_thinking_from_history(history)
1152
  if thinking_enabled:
1153
  thinking_prompt = build_thinking_prompt(user_content, budget_tokens=budget_tokens)
1154
- thinking_request = build_kiro_request(thinking_prompt, model, clean_history, None, images, tool_results)
1155
 
1156
  for retry in range(max_retries + 1):
1157
  try:
@@ -1195,7 +1189,7 @@ async def _handle_non_stream(kiro_request, headers, account, model, log_id, star
1195
  break
1196
 
1197
  main_user_content = build_user_prompt_with_thinking(user_content, thinking_content)
1198
- kiro_request = build_kiro_request(main_user_content, model, clean_history, kiro_tools, images, tool_results)
1199
 
1200
  for retry in range(max_retries + 1):
1201
  try:
 
310
 
311
  # 构建 Kiro 请求
312
  kiro_tools = convert_anthropic_tools_to_kiro(tools) if tools else None
313
+ kiro_request = build_kiro_request(user_content, model, history, kiro_tools, images, tool_results)
 
314
 
315
  if stream:
316
  return await _handle_stream(
 
384
  ) -> list:
385
  """构建用于思维链请求的历史记录。
386
 
387
+ 直接返回原始历史记录,不再移除思维链块
 
388
  """
389
  if not base_history:
390
  return []
391
 
392
+ return base_history
393
 
394
  # 思考功能:使用“独立请求”生成思维链(不向主请求注入提示词/标签)
395
  if thinking_enabled:
 
587
  yield f'event: content_block_stop\ndata: {{"type":"content_block_stop","index":{thinking_index}}}\n\n'
588
 
589
  main_user_content = build_user_prompt_with_thinking(user_content, thinking_accumulated)
590
+ kiro_request = build_kiro_request(main_user_content, model, history, kiro_tools, images, tool_results)
 
591
 
592
  # ========== 主响应流式处理(text block,独立请求) ==========
593
  main_retry = 0
 
686
  if should_retry:
687
  print(f"[Stream] 内容长度超限,{history_manager.truncate_info}")
688
  history = truncated_history
689
+ kiro_request = build_kiro_request(main_user_content, model, history, kiro_tools, images, tool_results)
 
690
  main_retry += 1
691
  continue
692
 
 
946
  print(f"[Stream] 内容长度超限,{history_manager.truncate_info}")
947
  history = truncated_history
948
  # 重新构建请求
949
+ kiro_request = build_kiro_request(user_content, model, history, kiro_tools, images, tool_results)
 
950
  retry_count += 1
951
  continue
952
 
 
1143
  retry_ctx = RetryableRequest(max_retries=2)
1144
 
1145
  thinking_content = ""
 
1146
  if thinking_enabled:
1147
  thinking_prompt = build_thinking_prompt(user_content, budget_tokens=budget_tokens)
1148
+ thinking_request = build_kiro_request(thinking_prompt, model, history, None, images, tool_results)
1149
 
1150
  for retry in range(max_retries + 1):
1151
  try:
 
1189
  break
1190
 
1191
  main_user_content = build_user_prompt_with_thinking(user_content, thinking_content)
1192
+ kiro_request = build_kiro_request(main_user_content, model, history, kiro_tools, images, tool_results)
1193
 
1194
  for retry in range(max_retries + 1):
1195
  try:
KiroProxy/kiro_proxy/handlers/gemini.py CHANGED
@@ -138,11 +138,10 @@ async def handle_generate_content(model_name: str, request: Request):
138
  get_rate_limiter().record_request(account.id)
139
  main_user_content = build_user_prompt_with_thinking(user_content, thinking_content)
140
 
141
- clean_history = strip_thinking_from_history(history)
142
  kiro_request = build_kiro_request(
143
  main_user_content,
144
  model,
145
- clean_history,
146
  tools=kiro_tools if kiro_tools else None,
147
  tool_results=tool_results if tool_results else None,
148
  )
 
138
  get_rate_limiter().record_request(account.id)
139
  main_user_content = build_user_prompt_with_thinking(user_content, thinking_content)
140
 
 
141
  kiro_request = build_kiro_request(
142
  main_user_content,
143
  model,
144
+ history,
145
  tools=kiro_tools if kiro_tools else None,
146
  tool_results=tool_results if tool_results else None,
147
  )
KiroProxy/kiro_proxy/handlers/openai.py CHANGED
@@ -192,12 +192,10 @@ async def handle_chat_completions(request: Request):
192
  get_rate_limiter().record_request(account.id)
193
 
194
  main_user_content = build_user_prompt_with_thinking(user_content, thinking_content)
195
- clean_history = strip_thinking_from_history(history)
196
-
197
  kiro_request = build_kiro_request(
198
  main_user_content,
199
  model,
200
- clean_history,
201
  images=images,
202
  tools=kiro_tools if kiro_tools else None,
203
  tool_results=tool_results if tool_results else None,
 
192
  get_rate_limiter().record_request(account.id)
193
 
194
  main_user_content = build_user_prompt_with_thinking(user_content, thinking_content)
 
 
195
  kiro_request = build_kiro_request(
196
  main_user_content,
197
  model,
198
+ history,
199
  images=images,
200
  tools=kiro_tools if kiro_tools else None,
201
  tool_results=tool_results if tool_results else None,
KiroProxy/kiro_proxy/main.py CHANGED
@@ -1,4 +1,5 @@
1
  import time
 
2
  from contextlib import asynccontextmanager
3
 
4
  from fastapi import FastAPI, Request
@@ -9,33 +10,68 @@ from .core import get_quota_scheduler, get_refresh_manager, scheduler, state
9
  from .routers import admin, protocols, web
10
 
11
 
12
- @asynccontextmanager
13
- async def lifespan(app: FastAPI):
14
- await scheduler.start()
15
 
16
- refresh_manager = get_refresh_manager()
17
- refresh_manager.set_accounts_getter(lambda: state.accounts)
 
 
 
 
 
 
 
 
 
18
 
19
- accounts = state.accounts
20
- if accounts:
21
- print(f"[Startup] 检查 {len(accounts)} 个账号的 Token 状态...")
22
  for account in accounts:
23
- if account.enabled and refresh_manager.should_refresh_token(account):
 
 
 
 
24
  try:
 
25
  success, msg = await refresh_manager.refresh_token_if_needed(account)
26
  if success:
27
- print(f"[Startup] 账号 {account.name} Token 刷新成功")
 
28
  else:
29
- print(f"[Startup] 账号 {account.name} Token 刷新失败: {msg}")
30
  except Exception as e:
31
- print(f"[Startup] 账号 {account.name} Token 刷新异常: {e}")
 
 
 
 
 
 
 
 
32
 
 
 
 
 
 
 
 
 
 
33
  quota_scheduler = get_quota_scheduler()
34
  quota_scheduler.set_accounts_getter(lambda: state.accounts)
35
  await quota_scheduler.start()
36
 
 
37
  await refresh_manager.start_auto_refresh()
38
 
 
 
 
 
 
 
39
  yield
40
 
41
  await refresh_manager.stop_auto_refresh()
 
1
  import time
2
+ import asyncio
3
  from contextlib import asynccontextmanager
4
 
5
  from fastapi import FastAPI, Request
 
10
  from .routers import admin, protocols, web
11
 
12
 
13
+ async def _background_initial_token_check(refresh_manager, accounts):
14
+ """后台执行初始Token检查和刷新
 
15
 
16
+ 这个函数在服务启动后异步执行,不会阻塞启动流程。
17
+ 对每个启用的账号检查Token状态,如果需要则进行刷新。
18
+ """
19
+ try:
20
+ # 稍微延迟一下,确保服务完全启动
21
+ await asyncio.sleep(1)
22
+
23
+ print(f"[Background] 开始检查 {len(accounts)} 个账号的 Token 状态...")
24
+
25
+ refresh_count = 0
26
+ success_count = 0
27
 
 
 
 
28
  for account in accounts:
29
+ if not account.enabled:
30
+ continue
31
+
32
+ if refresh_manager.should_refresh_token(account):
33
+ refresh_count += 1
34
  try:
35
+ print(f"[Background] 检查账号 {account.name} Token...")
36
  success, msg = await refresh_manager.refresh_token_if_needed(account)
37
  if success:
38
+ success_count += 1
39
+ print(f"[Background] 账号 {account.name} Token 刷新成功")
40
  else:
41
+ print(f"[Background] 账号 {account.name} Token 刷新失败: {msg}")
42
  except Exception as e:
43
+ print(f"[Background] 账号 {account.name} Token 刷新异常: {e}")
44
+
45
+ if refresh_count > 0:
46
+ print(f"[Background] Token 检查完成: {success_count}/{refresh_count} 个账号刷新成功")
47
+ else:
48
+ print("[Background] 所有账号 Token 状态正常,无需刷新")
49
+
50
+ except Exception as e:
51
+ print(f"[Background] 后台 Token 检查异常: {e}")
52
 
53
+
54
+ @asynccontextmanager
55
+ async def lifespan(app: FastAPI):
56
+ await scheduler.start()
57
+
58
+ refresh_manager = get_refresh_manager()
59
+ refresh_manager.set_accounts_getter(lambda: state.accounts)
60
+
61
+ # 启动配额调度器
62
  quota_scheduler = get_quota_scheduler()
63
  quota_scheduler.set_accounts_getter(lambda: state.accounts)
64
  await quota_scheduler.start()
65
 
66
+ # 启动自动刷新定时器(会在后台异步检查和刷新Token)
67
  await refresh_manager.start_auto_refresh()
68
 
69
+ # 创建后台任务进行初始Token检查(非阻塞)
70
+ accounts = state.accounts
71
+ if accounts:
72
+ print(f"[Startup] 服务已启动,将在后台检查 {len(accounts)} 个账号的 Token 状态...")
73
+ asyncio.create_task(_background_initial_token_check(refresh_manager, accounts))
74
+
75
  yield
76
 
77
  await refresh_manager.stop_auto_refresh()
KiroProxy/kiro_proxy/web/html.py CHANGED
@@ -196,6 +196,41 @@ CSS_COMPONENTS = '''
196
  font-weight: 500;
197
  }
198
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  /* Badges */
200
  .badge {
201
  display: inline-flex;
@@ -380,6 +415,20 @@ th, td {
380
  text-align: left;
381
  border-bottom: 1px solid var(--border);
382
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  th {
384
  font-weight: 600;
385
  color: var(--muted);
@@ -635,16 +684,16 @@ HTML_HEADER = '''
635
  </header>
636
 
637
  <div class="tabs">
638
- <div class="tab active" data-tab="help">📚 帮助</div>
639
  <div class="tab" data-tab="monitor">📊 监控</div>
640
- <div class="tab" data-tab="accounts">👥 账号</div>
641
  <div class="tab" data-tab="api">🔌 API</div>
642
  <div class="tab" data-tab="settings">⚙️ 设置</div>
643
  </div>
644
  '''
645
 
646
  HTML_HELP = '''
647
- <div class="panel active" id="help">
648
  <div class="card" style="padding:1rem">
649
  <div class="docs-container">
650
  <nav class="docs-nav" id="docsNav"></nav>
@@ -693,70 +742,86 @@ HTML_FLOWS = '''
693
 
694
  HTML_MONITOR = '''
695
  <div class="panel" id="monitor">
696
- <!-- 服务状态统计 -->
697
- <div class="card">
698
- <h3>🚀 服务状态 <button class="secondary small" onclick="loadStats()">刷新</button></h3>
699
- <div class="stats-grid" id="statsGrid"></div>
700
- </div>
 
 
 
 
 
 
701
 
702
- <!-- 流量统计 -->
703
- <div class="card">
704
- <h3>📈 流量统计 <button class="secondary small" onclick="loadFlowStats()">刷新</button></h3>
705
- <div class="stats-grid" id="flowStatsGrid"></div>
706
- </div>
 
 
 
707
 
708
- <!-- 配额状态 -->
709
- <div class="card">
710
- <h3>⚡ 配额状态</h3>
711
- <div id="quotaStatus"></div>
712
- </div>
 
713
 
714
- <!-- 速度测试 -->
715
- <div class="card">
716
- <h3>🎯 速度测试</h3>
717
- <div style="display: flex; align-items: center; gap: 1rem;">
718
- <button class="circle" onclick="runSpeedtest()" id="speedtestBtn">▶</button>
719
- <span id="speedtestResult" style="color: var(--muted)">点击开始测试</span>
 
720
  </div>
721
  </div>
722
 
723
- <!-- 请求监控 -->
724
- <div class="card">
725
- <h3>📋 请求监控</h3>
726
- <div style="display: flex; gap: 0.5rem; margin-bottom: 1rem; flex-wrap: wrap;">
727
- <select id="flowProtocol" onchange="loadFlows()">
728
- <option value="">全部协议</option>
729
- <option value="anthropic">Anthropic</option>
730
- <option value="openai">OpenAI</option>
731
- <option value="gemini">Gemini</option>
732
- </select>
733
- <select id="flowState" onchange="loadFlows()">
734
- <option value="">全部状态</option>
735
- <option value="completed">完成</option>
736
- <option value="error">错误</option>
737
- <option value="streaming">流式中</option>
738
- <option value="pending">等待中</option>
739
- </select>
740
- <input type="text" id="flowSearch" placeholder="搜索内容..." style="flex: 1; min-width: 150px;" onkeydown="if(event.key==='Enter')loadFlows()">
741
- <button class="secondary small" onclick="loadFlows()">搜索</button>
742
- <button class="secondary small" onclick="exportFlows()">导出</button>
 
 
743
  </div>
744
- <div id="flowList"></div>
745
  </div>
746
 
747
- <!-- 请求日志 -->
748
- <div class="card">
749
- <h3>📝 请求日志 <button class="secondary small" onclick="loadLogs()">刷新</button></h3>
750
- <div style="overflow-x: auto;">
751
- <table>
 
 
 
752
  <thead>
753
- <tr>
754
- <th>时间</th>
755
- <th>路径</th>
756
- <th>模型</th>
757
- <th>账号</th>
758
- <th>状态</th>
759
- <th>耗时</th>
760
  </tr>
761
  </thead>
762
  <tbody id="logTable"></tbody>
@@ -765,16 +830,19 @@ HTML_MONITOR = '''
765
  </div>
766
 
767
  <!-- Flow 详情弹窗 -->
768
- <div class="card" id="flowDetail" style="display: none;">
769
- <h3>🔍 Flow 详情 <button class="secondary small" onclick="$('#flowDetail').style.display='none'">关闭</button></h3>
770
- <div id="flowDetailContent"></div>
 
 
 
771
  </div>
772
  </div>
773
  '''
774
 
775
 
776
  HTML_ACCOUNTS = '''
777
- <div class="panel" id="accounts">
778
  <!-- 紧凑的工具栏 + 汇总面板 -->
779
  <div class="card" style="padding:1rem">
780
  <div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:0.75rem;margin-bottom:1rem">
@@ -3484,6 +3552,29 @@ function stopAutoRefresh() {
3484
  // 页面加载时启动自动刷新
3485
  startAutoRefresh();
3486
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3487
  // ==================== 加载状态指示器 (任务 10.1) ====================
3488
  function showLoading(container, message = '加载中...') {
3489
  const el = typeof container === 'string' ? document.querySelector(container) : container;
 
196
  font-weight: 500;
197
  }
198
 
199
+ /* Compact Stats Grid for Monitor Panel */
200
+ .stats-grid-compact {
201
+ display: grid;
202
+ grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
203
+ gap: 0.5rem;
204
+ }
205
+ .stats-grid-compact .stat-item {
206
+ text-align: center;
207
+ padding: 0.5rem;
208
+ background: linear-gradient(135deg, rgba(59,130,246,0.08), rgba(139,92,246,0.08));
209
+ border-radius: 8px;
210
+ border: 1px solid rgba(59,130,246,0.15);
211
+ transition: all 0.2s ease;
212
+ }
213
+ .stats-grid-compact .stat-item:hover {
214
+ transform: translateY(-2px);
215
+ box-shadow: 0 4px 12px rgba(59,130,246,0.15);
216
+ }
217
+ .stats-grid-compact .stat-value {
218
+ font-size: 1.2rem;
219
+ font-weight: 600;
220
+ background: linear-gradient(135deg, var(--primary), var(--secondary));
221
+ -webkit-background-clip: text;
222
+ -webkit-text-fill-color: transparent;
223
+ background-clip: text;
224
+ margin-bottom: 0.25rem;
225
+ line-height: 1.2;
226
+ }
227
+ .stats-grid-compact .stat-label {
228
+ font-size: 0.7rem;
229
+ color: var(--muted);
230
+ font-weight: 500;
231
+ line-height: 1.2;
232
+ }
233
+
234
  /* Badges */
235
  .badge {
236
  display: inline-flex;
 
415
  text-align: left;
416
  border-bottom: 1px solid var(--border);
417
  }
418
+
419
+ /* Compact table styles for monitor panel */
420
+ #monitor table th,
421
+ #monitor table td {
422
+ padding: 0.25rem 0.5rem;
423
+ font-size: 0.75rem;
424
+ line-height: 1.3;
425
+ }
426
+ #monitor table tbody tr {
427
+ height: 32px;
428
+ }
429
+ #monitor table tbody tr:hover {
430
+ background: rgba(59,130,246,0.05);
431
+ }
432
  th {
433
  font-weight: 600;
434
  color: var(--muted);
 
684
  </header>
685
 
686
  <div class="tabs">
687
+ <div class="tab" data-tab="help">📚 帮助</div>
688
  <div class="tab" data-tab="monitor">📊 监控</div>
689
+ <div class="tab active" data-tab="accounts">👥 账号</div>
690
  <div class="tab" data-tab="api">🔌 API</div>
691
  <div class="tab" data-tab="settings">⚙️ 设置</div>
692
  </div>
693
  '''
694
 
695
  HTML_HELP = '''
696
+ <div class="panel" id="help">
697
  <div class="card" style="padding:1rem">
698
  <div class="docs-container">
699
  <nav class="docs-nav" id="docsNav"></nav>
 
742
 
743
  HTML_MONITOR = '''
744
  <div class="panel" id="monitor">
745
+ <!-- 紧凑的顶部统计面板 -->
746
+ <div class="card" style="padding: 0.75rem; margin-bottom: 0.75rem;">
747
+ <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1rem; margin-bottom: 0.75rem;">
748
+ <!-- 服务状态 -->
749
+ <div>
750
+ <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.5rem;">
751
+ <h4 style="font-size: 0.9rem; margin: 0;">🚀 服务状态</h4>
752
+ <button class="secondary" style="padding: 0.25rem 0.5rem; font-size: 0.7rem;" onclick="loadStats()">刷新</button>
753
+ </div>
754
+ <div class="stats-grid-compact" id="statsGrid"></div>
755
+ </div>
756
 
757
+ <!-- 流量统计 -->
758
+ <div>
759
+ <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.5rem;">
760
+ <h4 style="font-size: 0.9rem; margin: 0;">📈 流量统计</h4>
761
+ <button class="secondary" style="padding: 0.25rem 0.5rem; font-size: 0.7rem;" onclick="loadFlowStats()">刷新</button>
762
+ </div>
763
+ <div class="stats-grid-compact" id="flowStatsGrid"></div>
764
+ </div>
765
 
766
+ <!-- 配额状态 -->
767
+ <div>
768
+ <h4 style="font-size: 0.9rem; margin: 0 0 0.5rem 0;">⚡ 配额状态</h4>
769
+ <div id="quotaStatus" style="font-size: 0.8rem;"></div>
770
+ </div>
771
+ </div>
772
 
773
+ <!-- 速度测试 - 移到底部一行 -->
774
+ <div style="display: flex; align-items: center; justify-content: space-between; padding-top: 0.5rem; border-top: 1px solid var(--border);">
775
+ <div style="display: flex; align-items: center; gap: 0.75rem;">
776
+ <span style="font-size: 0.9rem; font-weight: 500;">🎯 速度测试</span>
777
+ <button class="circle" onclick="runSpeedtest()" id="speedtestBtn" style="width: 28px; height: 28px; font-size: 0.8rem;">▶</button>
778
+ <span id="speedtestResult" style="color: var(--muted); font-size: 0.8rem;">点击开始测试</span>
779
+ </div>
780
  </div>
781
  </div>
782
 
783
+ <!-- 请求监控 - 紧凑布局 -->
784
+ <div class="card" style="padding: 0.75rem; margin-bottom: 0.75rem;">
785
+ <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.75rem;">
786
+ <h4 style="font-size: 0.9rem; margin: 0;">📋 请求监控</h4>
787
+ <div style="display: flex; gap: 0.5rem; align-items: center;">
788
+ <select id="flowProtocol" onchange="loadFlows()" style="padding: 0.25rem; font-size: 0.8rem;">
789
+ <option value="">全部协议</option>
790
+ <option value="anthropic">Anthropic</option>
791
+ <option value="openai">OpenAI</option>
792
+ <option value="gemini">Gemini</option>
793
+ </select>
794
+ <select id="flowState" onchange="loadFlows()" style="padding: 0.25rem; font-size: 0.8rem;">
795
+ <option value="">全部状态</option>
796
+ <option value="completed">完成</option>
797
+ <option value="error">错误</option>
798
+ <option value="streaming">流式中</option>
799
+ <option value="pending">等待中</option>
800
+ </select>
801
+ <input type="text" id="flowSearch" placeholder="搜索..." style="width: 120px; padding: 0.25rem; font-size: 0.8rem;" onkeydown="if(event.key==='Enter')loadFlows()">
802
+ <button class="secondary" style="padding: 0.25rem 0.5rem; font-size: 0.7rem;" onclick="loadFlows()">搜索</button>
803
+ <button class="secondary" style="padding: 0.25rem 0.5rem; font-size: 0.7rem;" onclick="exportFlows()">导出</button>
804
+ </div>
805
  </div>
806
+ <div id="flowList" style="max-height: 200px; overflow-y: auto;"></div>
807
  </div>
808
 
809
+ <!-- 请求日志 - 紧凑表格 -->
810
+ <div class="card" style="padding: 0.75rem; margin-bottom: 0.75rem;">
811
+ <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.75rem;">
812
+ <h4 style="font-size: 0.9rem; margin: 0;">📝 请求日志</h4>
813
+ <button class="secondary" style="padding: 0.25rem 0.5rem; font-size: 0.7rem;" onclick="loadLogs()">刷新</button>
814
+ </div>
815
+ <div style="overflow-x: auto; max-height: 300px; overflow-y: auto;">
816
+ <table style="font-size: 0.75rem;">
817
  <thead>
818
+ <tr style="height: 32px;">
819
+ <th style="padding: 0.25rem 0.5rem;">时间</th>
820
+ <th style="padding: 0.25rem 0.5rem;">路径</th>
821
+ <th style="padding: 0.25rem 0.5rem;">模型</th>
822
+ <th style="padding: 0.25rem 0.5rem;">账号</th>
823
+ <th style="padding: 0.25rem 0.5rem;">状态</th>
824
+ <th style="padding: 0.25rem 0.5rem;">耗时</th>
825
  </tr>
826
  </thead>
827
  <tbody id="logTable"></tbody>
 
830
  </div>
831
 
832
  <!-- Flow 详情弹窗 -->
833
+ <div class="card" id="flowDetail" style="display: none; padding: 0.75rem;">
834
+ <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.75rem;">
835
+ <h4 style="font-size: 0.9rem; margin: 0;">🔍 Flow 详情</h4>
836
+ <button class="secondary" style="padding: 0.25rem 0.5rem; font-size: 0.7rem;" onclick="$('#flowDetail').style.display='none'">关闭</button>
837
+ </div>
838
+ <div id="flowDetailContent" style="font-size: 0.8rem;"></div>
839
  </div>
840
  </div>
841
  '''
842
 
843
 
844
  HTML_ACCOUNTS = '''
845
+ <div class="panel active" id="accounts">
846
  <!-- 紧凑的工具栏 + 汇总面板 -->
847
  <div class="card" style="padding:1rem">
848
  <div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:0.75rem;margin-bottom:1rem">
 
3552
  // 页面加载时启动自动刷新
3553
  startAutoRefresh();
3554
 
3555
+ // 页面初始化:如果默认显示账号页面,则加载账号数据
3556
+ function initializeDefaultTab() {
3557
+ const accountsTab = document.querySelector('.tab[data-tab="accounts"]');
3558
+ const accountsPanel = document.querySelector('#accounts');
3559
+
3560
+ // 检查账号标签页和面板是否都处于激活状态(默认页面)
3561
+ if (accountsTab && accountsTab.classList.contains('active') &&
3562
+ accountsPanel && accountsPanel.classList.contains('active')) {
3563
+ // 延迟一点时间确保DOM完全加载
3564
+ setTimeout(() => {
3565
+ loadAccounts();
3566
+ loadAccountsEnhanced();
3567
+ }, 100);
3568
+ }
3569
+ }
3570
+
3571
+ // 页面加载完成后初始化默认标签页
3572
+ if (document.readyState === 'loading') {
3573
+ document.addEventListener('DOMContentLoaded', initializeDefaultTab);
3574
+ } else {
3575
+ initializeDefaultTab();
3576
+ }
3577
+
3578
  // ==================== 加载状态指示器 (任务 10.1) ====================
3579
  function showLoading(container, message = '加载中...') {
3580
  const el = typeof container === 'string' ? document.querySelector(container) : container;
run.bat CHANGED
@@ -1,3 +1,60 @@
 
 
 
 
 
 
1
  cd KiroProxy
2
- start http://127.0.0.1:6696
3
- python run.py 6696
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @echo off
2
+ chcp 65001 >nul
3
+ echo ========================================
4
+ echo 启动 KiroProxy 服务...
5
+ echo ========================================
6
+
7
  cd KiroProxy
8
+
9
+ echo [1/3] 正在启动服务...
10
+ start /B python run.py 6696
11
+
12
+ echo [2/3] 等待服务就绪...
13
+ set /a count=0
14
+ set /a max_attempts=30
15
+
16
+ :check_service
17
+ set /a count+=1
18
+ echo 检查服务状态... (%count%/%max_attempts%)
19
+
20
+ :: 使用 curl 检查服务是否可用 (如果没有curl则使用powershell)
21
+ curl -s -o nul -w "%%{http_code}" http://127.0.0.1:6696 2>nul | findstr "200" >nul
22
+ if %errorlevel% equ 0 (
23
+ echo [3/3] 服务已就绪,正在打开网页...
24
+ timeout /t 1 /nobreak >nul
25
+ start http://127.0.0.1:6696
26
+ echo.
27
+ echo ========================================
28
+ echo KiroProxy 启动完成!
29
+ echo 网页地址: http://127.0.0.1:6696
30
+ echo ========================================
31
+ goto end
32
+ )
33
+
34
+ :: 如果curl不可用,使用PowerShell进行检查
35
+ powershell -Command "try { $response = Invoke-WebRequest -Uri 'http://127.0.0.1:6696' -TimeoutSec 2 -UseBasicParsing; if ($response.StatusCode -eq 200) { exit 0 } else { exit 1 } } catch { exit 1 }" >nul 2>&1
36
+ if %errorlevel% equ 0 (
37
+ echo [3/3] 服务已就绪,正在打开网页...
38
+ timeout /t 1 /nobreak >nul
39
+ start http://127.0.0.1:6696
40
+ echo.
41
+ echo ========================================
42
+ echo KiroProxy 启动完成!
43
+ echo 网页地址: http://127.0.0.1:6696
44
+ echo ========================================
45
+ goto end
46
+ )
47
+
48
+ if %count% lss %max_attempts% (
49
+ timeout /t 2 /nobreak >nul
50
+ goto check_service
51
+ )
52
+
53
+ echo.
54
+ echo ========================================
55
+ echo 警告: 服务启动超时
56
+ echo 请手动访问: http://127.0.0.1:6696
57
+ echo ========================================
58
+
59
+ :end
60
+ pause