Skip to content

Commit 13b72f6

Browse files
committed
Merge remote-tracking branch 'origin/feat/ops-error-observability-transfer' into feat/anthropic-openai-endpoint-compat
# Conflicts: # frontend/src/api/admin/ops.ts # frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue # frontend/src/views/admin/ops/components/OpsErrorLogTable.vue
2 parents c5aa96a + ecad083 commit 13b72f6

5 files changed

Lines changed: 105 additions & 5 deletions

File tree

frontend/src/api/admin/ops.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -970,9 +970,12 @@ export interface OpsErrorLog {
970970
request_path?: string
971971
stream?: boolean
972972

973-
// Model mapping context for ops error observability
973+
// Error observability context (endpoint + model mapping)
974+
inbound_endpoint?: string
975+
upstream_endpoint?: string
974976
requested_model?: string
975977
upstream_model?: string
978+
request_type?: number | null
976979
}
977980

978981
export interface OpsErrorDetail extends OpsErrorLog {

frontend/src/i18n/locales/en.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3486,7 +3486,12 @@ export default {
34863486
typeRequest: 'Request',
34873487
typeAuth: 'Auth',
34883488
typeRouting: 'Routing',
3489-
typeInternal: 'Internal'
3489+
typeInternal: 'Internal',
3490+
endpoint: 'Endpoint',
3491+
requestType: 'Type',
3492+
requestTypeSync: 'Sync',
3493+
requestTypeStream: 'Stream',
3494+
requestTypeWs: 'WS'
34903495
},
34913496
// Error Details Modal
34923497
errorDetails: {
@@ -3572,6 +3577,16 @@ export default {
35723577
latency: 'Request Duration',
35733578
businessLimited: 'Business Limited',
35743579
requestPath: 'Request Path',
3580+
inboundEndpoint: 'Inbound Endpoint',
3581+
upstreamEndpoint: 'Upstream Endpoint',
3582+
requestedModel: 'Requested Model',
3583+
upstreamModel: 'Upstream Model',
3584+
requestType: 'Request Type',
3585+
requestTypeUnknown: 'Unknown',
3586+
requestTypeSync: 'Sync',
3587+
requestTypeStream: 'Stream',
3588+
requestTypeWs: 'WebSocket',
3589+
modelMapping: 'Model Mapping',
35753590
timings: 'Timings',
35763591
auth: 'Auth',
35773592
routing: 'Routing',

frontend/src/i18n/locales/zh.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3651,7 +3651,12 @@ export default {
36513651
typeRequest: '请求',
36523652
typeAuth: '认证',
36533653
typeRouting: '路由',
3654-
typeInternal: '内部'
3654+
typeInternal: '内部',
3655+
endpoint: '端点',
3656+
requestType: '类型',
3657+
requestTypeSync: '同步',
3658+
requestTypeStream: '流式',
3659+
requestTypeWs: 'WS'
36553660
},
36563661
// Error Details Modal
36573662
errorDetails: {
@@ -3737,6 +3742,16 @@ export default {
37373742
latency: '请求时长',
37383743
businessLimited: '业务限制',
37393744
requestPath: '请求路径',
3745+
inboundEndpoint: '入站端点',
3746+
upstreamEndpoint: '上游端点',
3747+
requestedModel: '请求模型',
3748+
upstreamModel: '上游模型',
3749+
requestType: '请求类型',
3750+
requestTypeUnknown: '未知',
3751+
requestTypeSync: '同步',
3752+
requestTypeStream: '流式',
3753+
requestTypeWs: 'WebSocket',
3754+
modelMapping: '模型映射',
37403755
timings: '时序信息',
37413756
auth: '认证',
37423757
routing: '路由',

frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,20 @@
7070
</div>
7171
</div>
7272

73+
<div class="rounded-xl bg-gray-50 p-4 dark:bg-dark-900">
74+
<div class="text-xs font-bold uppercase tracking-wider text-gray-400">{{ t('admin.ops.errorDetail.inboundEndpoint') }}</div>
75+
<div class="mt-1 break-all font-mono text-sm font-medium text-gray-900 dark:text-white">
76+
{{ detail.inbound_endpoint || '—' }}
77+
</div>
78+
</div>
79+
80+
<div class="rounded-xl bg-gray-50 p-4 dark:bg-dark-900">
81+
<div class="text-xs font-bold uppercase tracking-wider text-gray-400">{{ t('admin.ops.errorDetail.upstreamEndpoint') }}</div>
82+
<div class="mt-1 break-all font-mono text-sm font-medium text-gray-900 dark:text-white">
83+
{{ detail.upstream_endpoint || '—' }}
84+
</div>
85+
</div>
86+
7387
<div class="rounded-xl bg-gray-50 p-4 dark:bg-dark-900">
7488
<div class="text-xs font-bold uppercase tracking-wider text-gray-400">{{ t('admin.ops.errorDetail.status') }}</div>
7589
<div class="mt-1">
@@ -79,6 +93,13 @@
7993
</div>
8094
</div>
8195

96+
<div class="rounded-xl bg-gray-50 p-4 dark:bg-dark-900">
97+
<div class="text-xs font-bold uppercase tracking-wider text-gray-400">{{ t('admin.ops.errorDetail.requestType') }}</div>
98+
<div class="mt-1 text-sm font-medium text-gray-900 dark:text-white">
99+
{{ formatRequestTypeLabel(detail.request_type) }}
100+
</div>
101+
</div>
102+
82103
<div class="rounded-xl bg-gray-50 p-4 dark:bg-dark-900">
83104
<div class="text-xs font-bold uppercase tracking-wider text-gray-400">{{ t('admin.ops.errorDetail.message') }}</div>
84105
<div class="mt-1 truncate text-sm font-medium text-gray-900 dark:text-white" :title="detail.message">
@@ -220,6 +241,15 @@ function isUpstreamError(d: OpsErrorDetail | null): boolean {
220241
return phase === 'upstream' && owner === 'provider'
221242
}
222243
244+
function formatRequestTypeLabel(type: number | null | undefined): string {
245+
switch (type) {
246+
case 1: return t('admin.ops.errorDetail.requestTypeSync')
247+
case 2: return t('admin.ops.errorDetail.requestTypeStream')
248+
case 3: return t('admin.ops.errorDetail.requestTypeWs')
249+
default: return t('admin.ops.errorDetail.requestTypeUnknown')
250+
}
251+
}
252+
223253
function hasModelMapping(d: OpsErrorDetail | null): boolean {
224254
if (!d) return false
225255
const requested = String(d.requested_model || '').trim()

frontend/src/views/admin/ops/components/OpsErrorLogTable.vue

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
<th class="border-b border-gray-200 px-4 py-2.5 text-left text-[11px] font-bold uppercase tracking-wider text-gray-500 dark:border-dark-700 dark:text-dark-400">
1818
{{ t('admin.ops.errorLog.type') }}
1919
</th>
20+
<th class="border-b border-gray-200 px-4 py-2.5 text-left text-[11px] font-bold uppercase tracking-wider text-gray-500 dark:border-dark-700 dark:text-dark-400">
21+
{{ t('admin.ops.errorLog.endpoint') }}
22+
</th>
2023
<th class="border-b border-gray-200 px-4 py-2.5 text-left text-[11px] font-bold uppercase tracking-wider text-gray-500 dark:border-dark-700 dark:text-dark-400">
2124
{{ t('admin.ops.errorLog.platform') }}
2225
</th>
@@ -42,7 +45,7 @@
4245
</thead>
4346
<tbody class="divide-y divide-gray-100 dark:divide-dark-700">
4447
<tr v-if="rows.length === 0">
45-
<td colspan="9" class="py-12 text-center text-sm text-gray-400 dark:text-dark-500">
48+
<td colspan="10" class="py-12 text-center text-sm text-gray-400 dark:text-dark-500">
4649
{{ t('admin.ops.errorLog.noErrors') }}
4750
</td>
4851
</tr>
@@ -74,6 +77,18 @@
7477
</span>
7578
</td>
7679

80+
<!-- Endpoint -->
81+
<td class="px-4 py-2">
82+
<div class="max-w-[160px]">
83+
<el-tooltip v-if="log.inbound_endpoint" :content="formatEndpointTooltip(log)" placement="top" :show-after="500">
84+
<span class="truncate font-mono text-[11px] text-gray-700 dark:text-gray-300">
85+
{{ log.inbound_endpoint }}
86+
</span>
87+
</el-tooltip>
88+
<span v-else class="text-xs text-gray-400">-</span>
89+
</div>
90+
</td>
91+
7792
<!-- Platform -->
7893
<td class="whitespace-nowrap px-4 py-2">
7994
<span class="inline-flex items-center rounded bg-gray-100 px-1.5 py-0.5 text-[10px] font-bold uppercase text-gray-600 dark:bg-dark-700 dark:text-gray-300">
@@ -83,7 +98,7 @@
8398

8499
<!-- Model -->
85100
<td class="px-4 py-2">
86-
<div class="max-w-[180px]">
101+
<div class="max-w-[160px]">
87102
<template v-if="hasModelMapping(log)">
88103
<el-tooltip :content="modelMappingTooltip(log)" placement="top" :show-after="500">
89104
<span class="flex items-center gap-1 truncate font-mono text-[11px] text-gray-700 dark:text-gray-300">
@@ -149,6 +164,12 @@
149164
>
150165
{{ log.severity }}
151166
</span>
167+
<span
168+
v-if="log.request_type != null && log.request_type > 0"
169+
class="rounded bg-gray-100 px-1.5 py-0.5 text-[10px] font-bold text-gray-600 dark:bg-dark-700 dark:text-gray-300"
170+
>
171+
{{ formatRequestType(log.request_type) }}
172+
</span>
152173
</div>
153174
</td>
154175

@@ -204,6 +225,13 @@ function isUpstreamRow(log: OpsErrorLog): boolean {
204225
return phase === 'upstream' && owner === 'provider'
205226
}
206227
228+
function formatEndpointTooltip(log: OpsErrorLog): string {
229+
const parts: string[] = []
230+
if (log.inbound_endpoint) parts.push(`Inbound: ${log.inbound_endpoint}`)
231+
if (log.upstream_endpoint) parts.push(`Upstream: ${log.upstream_endpoint}`)
232+
return parts.join('\n') || ''
233+
}
234+
207235
function hasModelMapping(log: OpsErrorLog): boolean {
208236
const requested = String(log.requested_model || '').trim()
209237
const upstream = String(log.upstream_model || '').trim()
@@ -226,6 +254,15 @@ function displayModel(log: OpsErrorLog): string {
226254
return String(log.model || '').trim()
227255
}
228256
257+
function formatRequestType(type: number | null | undefined): string {
258+
switch (type) {
259+
case 1: return t('admin.ops.errorLog.requestTypeSync')
260+
case 2: return t('admin.ops.errorLog.requestTypeStream')
261+
case 3: return t('admin.ops.errorLog.requestTypeWs')
262+
default: return ''
263+
}
264+
}
265+
229266
function getTypeBadge(log: OpsErrorLog): { label: string; className: string } {
230267
const phase = String(log.phase || '').toLowerCase()
231268
const owner = String(log.error_owner || '').toLowerCase()

0 commit comments

Comments
 (0)