diff --git a/web/src/features/monitor/LogDetailDrawer.vue b/web/src/features/monitor/LogDetailDrawer.vue
index 66f49ba8..58101d07 100644
--- a/web/src/features/monitor/LogDetailDrawer.vue
+++ b/web/src/features/monitor/LogDetailDrawer.vue
@@ -25,9 +25,8 @@ import { formatCacheHitRate } from '@/lib/cache-rate'
import {
formatLogDuration,
formatLogOutputRate,
- formatLogReasoningBudget,
formatLogTokenCount,
- reasoningBudgetSemantic,
+ formatRequestLogReasoning,
requestLogCostDisplayState,
requestLogUsageDisplayState,
} from './log-format'
@@ -195,26 +194,16 @@ function upstreamAPILabel(upstreamAPI: RequestLogAttemptDto['upstream_api']): st
return upstreamAPI
}
-function appliedReasoningLabel(reasoning: RequestLogAttemptDto['reasoning']): string {
- if (reasoning === null) return t('monitor.logs.drawer.reasoningNotSpecified')
- const details: string[] = []
- if (reasoning.mode !== null) details.push(reasoning.mode)
- if (reasoning.effort !== null) details.push(reasoning.effort)
- if (reasoning.budget_tokens !== null) {
- const semantic = reasoningBudgetSemantic(reasoning.budget_tokens)
- if (semantic === 'dynamic') {
- details.push(t('monitor.logs.drawer.reasoningBudgetDynamic'))
- } else if (semantic === 'disabled') {
- details.push(t('monitor.logs.drawer.reasoningBudgetDisabled'))
- } else {
- details.push(
- t('monitor.logs.drawer.reasoningBudgetValue', {
- value: formatLogReasoningBudget(reasoning.budget_tokens, locale.value),
- }),
- )
- }
- }
- return details.join(' · ')
+function reasoningLabel(reasoning: RequestLogAttemptDto['reasoning']): string {
+ const value = formatRequestLogReasoning(reasoning, locale.value)
+ return value ? `[${value}]` : ''
+}
+
+function upstreamReasoningLabel(
+ reasoning: RequestLogAttemptDto['reasoning'],
+ routeMode: RequestLogAttemptDto['route_mode'],
+): string {
+ return reasoningLabel(routeMode === 'converted' ? reasoning : (log.value?.reasoning ?? null))
}
function isFinalAttempt(attempt: RequestLogAttemptDto): boolean {
@@ -433,44 +422,13 @@ function toggleAttemptErrorMessage(sequence: number): void {
{{ t('monitor.logs.drawer.clientModel') }}
-
- {{ log.client_model ?? '—' }}
+
+ {{ log.client_model ?? '—' }}{{
+ reasoningLabel(log.reasoning)
+ }}
-
-
-
{{ t('monitor.logs.drawer.reasoningMode') }}
-
- {{ log.reasoning.mode }}
-
-
-
-
{{ t('monitor.logs.drawer.reasoningEffort') }}
-
- {{ log.reasoning.effort }}
-
-
-
-
{{ t('monitor.logs.drawer.reasoningBudget') }}
-
- auto
-
-
- disabled
-
-
- {{
- t('monitor.logs.drawer.reasoningBudgetValue', {
- value: formatLogReasoningBudget(log.reasoning.budget_tokens, locale),
- })
- }}
-
-
-
-
-
{{ t('monitor.logs.drawer.reasoningConfig') }}
- {{ t('monitor.logs.drawer.reasoningNotSpecified') }}
-
@@ -495,14 +453,25 @@ function toggleAttemptErrorMessage(sequence: number): void {
{{ t('monitor.logs.drawer.upstreamModel') }}
-
- {{ log.upstream_model ?? '—' }}
+
+ {{ log.upstream_model ?? '—' }}{{
+ upstreamReasoningLabel(
+ finalAttempt?.reasoning ?? null,
+ finalAttempt?.route_mode ?? null,
+ )
+ }}
-
-
{{ t('monitor.logs.drawer.appliedReasoning') }}
- {{ appliedReasoningLabel(finalAttempt.reasoning) }}
-
-
+
@@ -639,14 +608,15 @@ function toggleAttemptErrorMessage(sequence: number): void {
{{ t('monitor.logs.drawer.upstreamModel') }}
-
- {{ attempt.upstream_model ?? '—' }}
+
+ {{ attempt.upstream_model ?? '—' }}{{ upstreamReasoningLabel(attempt.reasoning, attempt.route_mode) }}
-
-
{{ t('monitor.logs.drawer.appliedReasoning') }}
- {{ appliedReasoningLabel(attempt.reasoning) }}
-
{{ t('monitor.logs.drawer.operation') }}
@@ -790,6 +760,10 @@ function toggleAttemptErrorMessage(sequence: number): void {
padding: 16px 0;
}
+.log-detail__attempt-section {
+ padding: 8px 0;
+}
+
.log-detail__section h3 {
margin: 0 0 12px;
font-size: var(--text-sm);
@@ -819,6 +793,12 @@ function toggleAttemptErrorMessage(sequence: number): void {
overflow-wrap: anywhere;
}
+.log-detail__reasoning {
+ color: var(--color-text-faint);
+ font-size: var(--text-label-xs);
+ font-weight: 400;
+}
+
.log-detail__protocol {
display: flex;
min-width: 0;
@@ -926,7 +906,7 @@ function toggleAttemptErrorMessage(sequence: number): void {
.log-attempt-chain summary {
display: flex;
- min-height: 28px;
+ min-height: 24px;
align-items: center;
gap: 6px;
color: var(--color-text);
@@ -957,7 +937,7 @@ function toggleAttemptErrorMessage(sequence: number): void {
}
.log-attempt-chain[open] .log-attempt:first-of-type {
- margin-top: 10px;
+ margin-top: 4px;
}
.log-attempt + .log-attempt {
@@ -965,7 +945,7 @@ function toggleAttemptErrorMessage(sequence: number): void {
}
.log-attempt {
- padding: 13px 0;
+ padding: 8px 0;
}
.log-error-message--attempt {
@@ -984,7 +964,7 @@ function toggleAttemptErrorMessage(sequence: number): void {
align-items: center;
justify-content: space-between;
gap: 10px;
- margin-bottom: 12px;
+ margin-bottom: 8px;
color: var(--color-text);
font-size: var(--text-sm);
}
diff --git a/web/src/features/monitor/log-format.ts b/web/src/features/monitor/log-format.ts
index 044ff528..161aca8b 100644
--- a/web/src/features/monitor/log-format.ts
+++ b/web/src/features/monitor/log-format.ts
@@ -1,4 +1,4 @@
-import type { RequestLogItemDto } from '@/app/resources/request-logs'
+import type { RequestLogItemDto, RequestLogReasoningDto } from '@/app/resources/request-logs'
export type RequestLogUsageDisplayState = 'reported' | 'missing' | 'not_applicable'
export type RequestLogCostDisplayState = 'complete' | 'unpriced' | 'not_applicable'
@@ -42,7 +42,13 @@ export function formatLogTokenCount(value: string, locale: string): string {
}
export function formatLogReasoning(log: RequestLogItemDto, locale: string): string {
- const value = log.reasoning
+ return formatRequestLogReasoning(log.reasoning, locale)
+}
+
+export function formatRequestLogReasoning(
+ value: RequestLogReasoningDto | null,
+ locale: string,
+): string {
if (value === null) return ''
const details: string[] = []
if (