feat: 请求候选记录中追踪代理节点信息并在前端 timeline 展示

This commit is contained in:
fawney19
2026-02-08 01:18:00 +08:00
parent 4a1029961a
commit 254d30d32d
5 changed files with 44 additions and 5 deletions

View File

@@ -198,6 +198,19 @@
</template> </template>
</span> </span>
</div> </div>
<div
v-if="currentAttempt.extra_data?.proxy"
class="info-item"
>
<span class="info-label">代理</span>
<span class="info-value">
<span>{{ currentAttempt.extra_data.proxy.node_name || currentAttempt.extra_data.proxy.url || '未知' }}</span>
<span
v-if="currentAttempt.extra_data.proxy.source === 'system'"
class="text-xs text-muted-foreground ml-1"
>(系统)</span>
</span>
</div>
<div <div
v-if="mergedCapabilities.length > 0" v-if="mergedCapabilities.length > 0"
class="info-item" class="info-item"

View File

@@ -2654,6 +2654,8 @@ class CliMessageHandlerBase(BaseMessageHandler):
"chunk_count": ctx.chunk_count, "chunk_count": ctx.chunk_count,
"data_count": ctx.data_count, "data_count": ctx.data_count,
} }
if ctx.proxy_info:
extra_data["proxy"] = ctx.proxy_info
if candidate_first_byte_time_ms is not None: if candidate_first_byte_time_ms is not None:
extra_data["first_byte_time_ms"] = candidate_first_byte_time_ms extra_data["first_byte_time_ms"] = candidate_first_byte_time_ms
if ctx.is_client_disconnected(): if ctx.is_client_disconnected():
@@ -2680,6 +2682,8 @@ class CliMessageHandlerBase(BaseMessageHandler):
"chunk_count": ctx.chunk_count, "chunk_count": ctx.chunk_count,
"data_count": ctx.data_count, "data_count": ctx.data_count,
} }
if ctx.proxy_info:
extra_data["proxy"] = ctx.proxy_info
if ctx.rectified: if ctx.rectified:
extra_data["rectified"] = True extra_data["rectified"] = True
if candidate_first_byte_time_ms is not None: if candidate_first_byte_time_ms is not None:

View File

@@ -351,6 +351,8 @@ class StreamTelemetryRecorder:
} }
if ctx.rectified: if ctx.rectified:
extra_data["rectified"] = True extra_data["rectified"] = True
if ctx.proxy_info:
extra_data["proxy"] = ctx.proxy_info
if ctx.first_byte_time_ms is not None: if ctx.first_byte_time_ms is not None:
# 计算候选自身的 TTFB # 计算候选自身的 TTFB
first_byte_time_ms = RequestCandidateService.calculate_candidate_ttfb( first_byte_time_ms = RequestCandidateService.calculate_candidate_ttfb(

View File

@@ -180,17 +180,23 @@ class RequestExecutor:
) )
else: else:
# 非流式请求:标记为 success 状态 # 非流式请求:标记为 success 状态
from src.clients.http_client import resolve_proxy_info
_extra: dict[str, Any] = {
"is_cached_user": is_cached_user,
"model_name": model_name,
"api_format": api_format,
}
_pi = resolve_proxy_info(getattr(provider, "proxy", None))
if _pi:
_extra["proxy"] = _pi
RequestCandidateService.mark_candidate_success( RequestCandidateService.mark_candidate_success(
db=self.db, db=self.db,
candidate_id=candidate_id, candidate_id=candidate_id,
status_code=200, status_code=200,
latency_ms=context.elapsed_ms, latency_ms=context.elapsed_ms,
concurrent_requests=key_rpm_count, concurrent_requests=key_rpm_count,
extra_data={ extra_data=_extra,
"is_cached_user": is_cached_user,
"model_name": model_name,
"api_format": api_format,
},
) )
return ExecutionResult(response=response, context=context) return ExecutionResult(response=response, context=context)

View File

@@ -699,6 +699,7 @@ class TaskService:
""" """
import httpx import httpx
from src.clients.http_client import resolve_proxy_info
from src.core.api_format.conversion.exceptions import FormatConversionError from src.core.api_format.conversion.exceptions import FormatConversionError
from src.core.error_utils import extract_error_message from src.core.error_utils import extract_error_message
from src.core.exceptions import ( from src.core.exceptions import (
@@ -710,12 +711,17 @@ class TaskService:
) )
from src.services.request.executor import ExecutionError from src.services.request.executor import ExecutionError
# 提前解析代理信息,写入候选记录的 extra_data用于链路追踪展示
_proxy_info = resolve_proxy_info(getattr(candidate.provider, "proxy", None))
_proxy_extra: dict[str, Any] | None = {"proxy": _proxy_info} if _proxy_info else None
if not isinstance(exec_err, ExecutionError): if not isinstance(exec_err, ExecutionError):
RequestCandidateService.mark_candidate_failed( RequestCandidateService.mark_candidate_failed(
db=self.db, db=self.db,
candidate_id=candidate_record_id, candidate_id=candidate_record_id,
error_type=type(exec_err).__name__, error_type=type(exec_err).__name__,
error_message=str(exec_err), error_message=str(exec_err),
extra_data=_proxy_extra,
) )
return "raise" return "raise"
@@ -762,6 +768,7 @@ class TaskService:
error_message=extract_error_message(cause), error_message=extract_error_message(cause),
latency_ms=elapsed_ms, latency_ms=elapsed_ms,
concurrent_requests=captured_key_concurrent, concurrent_requests=captured_key_concurrent,
extra_data=_proxy_extra,
) )
return "break" return "break"
@@ -788,6 +795,7 @@ class TaskService:
status_code=embedded_status, status_code=embedded_status,
latency_ms=elapsed_ms, latency_ms=elapsed_ms,
concurrent_requests=captured_key_concurrent, concurrent_requests=captured_key_concurrent,
extra_data=_proxy_extra,
) )
client_error.request_metadata = { client_error.request_metadata = {
"provider": provider.name, "provider": provider.name,
@@ -811,6 +819,7 @@ class TaskService:
status_code=embedded_status, status_code=embedded_status,
latency_ms=elapsed_ms, latency_ms=elapsed_ms,
concurrent_requests=captured_key_concurrent, concurrent_requests=captured_key_concurrent,
extra_data=_proxy_extra,
) )
return "continue" if has_retry_left else "break" return "continue" if has_retry_left else "break"
@@ -835,6 +844,8 @@ class TaskService:
serializable_extra_data = { serializable_extra_data = {
k: v for k, v in extra_data.items() if k != "converted_error" k: v for k, v in extra_data.items() if k != "converted_error"
} }
if _proxy_info:
serializable_extra_data["proxy"] = _proxy_info
if isinstance(converted_error, ThinkingSignatureException): if isinstance(converted_error, ThinkingSignatureException):
action = self._handle_thinking_signature_error( action = self._handle_thinking_signature_error(
@@ -910,6 +921,7 @@ class TaskService:
error_message=extract_error_message(cause), error_message=extract_error_message(cause),
latency_ms=elapsed_ms, latency_ms=elapsed_ms,
concurrent_requests=captured_key_concurrent, concurrent_requests=captured_key_concurrent,
extra_data=_proxy_extra,
) )
return "continue" if has_retry_left else "break" return "continue" if has_retry_left else "break"
@@ -922,6 +934,7 @@ class TaskService:
error_message=str(cause), error_message=str(cause),
latency_ms=elapsed_ms, latency_ms=elapsed_ms,
concurrent_requests=captured_key_concurrent, concurrent_requests=captured_key_concurrent,
extra_data=_proxy_extra,
) )
return "break" return "break"
@@ -932,6 +945,7 @@ class TaskService:
error_message=extract_error_message(cause), error_message=extract_error_message(cause),
latency_ms=elapsed_ms, latency_ms=elapsed_ms,
concurrent_requests=captured_key_concurrent, concurrent_requests=captured_key_concurrent,
extra_data=_proxy_extra,
) )
return "raise" return "raise"