diff --git a/frontend/src/api/admin/ops.ts b/frontend/src/api/admin/ops.ts index 64f6a6d0..ac58eff4 100644 --- a/frontend/src/api/admin/ops.ts +++ b/frontend/src/api/admin/ops.ts @@ -969,6 +969,13 @@ export interface OpsErrorLog { client_ip?: string | null request_path?: string stream?: boolean + + // Error observability context (endpoint + model mapping) + inbound_endpoint?: string + upstream_endpoint?: string + requested_model?: string + upstream_model?: string + request_type?: number | null } export interface OpsErrorDetail extends OpsErrorLog { diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index e5a370c8..b5bba9a2 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -3486,7 +3486,12 @@ export default { typeRequest: 'Request', typeAuth: 'Auth', typeRouting: 'Routing', - typeInternal: 'Internal' + typeInternal: 'Internal', + endpoint: 'Endpoint', + requestType: 'Type', + requestTypeSync: 'Sync', + requestTypeStream: 'Stream', + requestTypeWs: 'WS' }, // Error Details Modal errorDetails: { @@ -3572,6 +3577,16 @@ export default { latency: 'Request Duration', businessLimited: 'Business Limited', requestPath: 'Request Path', + inboundEndpoint: 'Inbound Endpoint', + upstreamEndpoint: 'Upstream Endpoint', + requestedModel: 'Requested Model', + upstreamModel: 'Upstream Model', + requestType: 'Request Type', + requestTypeUnknown: 'Unknown', + requestTypeSync: 'Sync', + requestTypeStream: 'Stream', + requestTypeWs: 'WebSocket', + modelMapping: 'Model Mapping', timings: 'Timings', auth: 'Auth', routing: 'Routing', diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index ac6632be..05c69426 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -3651,7 +3651,12 @@ export default { typeRequest: '请求', typeAuth: '认证', typeRouting: '路由', - typeInternal: '内部' + typeInternal: '内部', + endpoint: '端点', + requestType: '类型', + requestTypeSync: '同步', + requestTypeStream: '流式', + requestTypeWs: 'WS' }, // Error Details Modal errorDetails: { @@ -3737,6 +3742,16 @@ export default { latency: '请求时长', businessLimited: '业务限制', requestPath: '请求路径', + inboundEndpoint: '入站端点', + upstreamEndpoint: '上游端点', + requestedModel: '请求模型', + upstreamModel: '上游模型', + requestType: '请求类型', + requestTypeUnknown: '未知', + requestTypeSync: '同步', + requestTypeStream: '流式', + requestTypeWs: 'WebSocket', + modelMapping: '模型映射', timings: '时序信息', auth: '认证', routing: '路由', diff --git a/frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue b/frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue index a7edff96..4bcd0c41 100644 --- a/frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue +++ b/frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue @@ -59,7 +59,28 @@
{{ t('admin.ops.errorDetail.model') }}
- {{ detail.model || '—' }} + + +
+
+ +
+
{{ t('admin.ops.errorDetail.inboundEndpoint') }}
+
+ {{ detail.inbound_endpoint || '—' }} +
+
+ +
+
{{ t('admin.ops.errorDetail.upstreamEndpoint') }}
+
+ {{ detail.upstream_endpoint || '—' }}
@@ -72,6 +93,13 @@ +
+
{{ t('admin.ops.errorDetail.requestType') }}
+
+ {{ formatRequestTypeLabel(detail.request_type) }} +
+
+
{{ t('admin.ops.errorDetail.message') }}
@@ -213,6 +241,15 @@ function isUpstreamError(d: OpsErrorDetail | null): boolean { return phase === 'upstream' && owner === 'provider' } +function formatRequestTypeLabel(type: number | null | undefined): string { + switch (type) { + case 1: return t('admin.ops.errorDetail.requestTypeSync') + case 2: return t('admin.ops.errorDetail.requestTypeStream') + case 3: return t('admin.ops.errorDetail.requestTypeWs') + default: return t('admin.ops.errorDetail.requestTypeUnknown') + } +} + const correlatedUpstream = ref([]) const correlatedUpstreamLoading = ref(false) diff --git a/frontend/src/views/admin/ops/components/OpsErrorLogTable.vue b/frontend/src/views/admin/ops/components/OpsErrorLogTable.vue index 28868552..23377257 100644 --- a/frontend/src/views/admin/ops/components/OpsErrorLogTable.vue +++ b/frontend/src/views/admin/ops/components/OpsErrorLogTable.vue @@ -17,6 +17,9 @@ {{ t('admin.ops.errorLog.type') }} + + {{ t('admin.ops.errorLog.endpoint') }} + {{ t('admin.ops.errorLog.platform') }} @@ -42,7 +45,7 @@ - + {{ t('admin.ops.errorLog.noErrors') }} @@ -74,6 +77,18 @@ + + +
+ + + {{ log.inbound_endpoint }} + + + - +
+ + @@ -83,11 +98,22 @@ -
- - {{ log.model }} - - - +
+ +
@@ -138,6 +164,12 @@ > {{ log.severity }} + + {{ formatRequestType(log.request_type) }} +
@@ -193,6 +225,26 @@ function isUpstreamRow(log: OpsErrorLog): boolean { return phase === 'upstream' && owner === 'provider' } +function formatEndpointTooltip(log: OpsErrorLog): string { + const parts: string[] = [] + if (log.inbound_endpoint) parts.push(`Inbound: ${log.inbound_endpoint}`) + if (log.upstream_endpoint) parts.push(`Upstream: ${log.upstream_endpoint}`) + return parts.join('\n') || '' +} + +function displayModel(log: OpsErrorLog): string { + return log.requested_model || log.model || '' +} + +function formatRequestType(type: number | null | undefined): string { + switch (type) { + case 1: return t('admin.ops.errorLog.requestTypeSync') + case 2: return t('admin.ops.errorLog.requestTypeStream') + case 3: return t('admin.ops.errorLog.requestTypeWs') + default: return '' + } +} + function getTypeBadge(log: OpsErrorLog): { label: string; className: string } { const phase = String(log.phase || '').toLowerCase() const owner = String(log.error_owner || '').toLowerCase()