refactor: enforce 800 line source limits
This commit is contained in:
191
web/src/views/scripts/travelReimbursementFlowTiming.js
Normal file
191
web/src/views/scripts/travelReimbursementFlowTiming.js
Normal file
@@ -0,0 +1,191 @@
|
||||
export const FLOW_RUN_DETAIL_REFRESH_TIMEOUT_MS = 3000
|
||||
|
||||
const FLOW_DURATION_MS_FIELDS = [
|
||||
'duration_ms',
|
||||
'elapsed_ms',
|
||||
'latency_ms',
|
||||
'total_duration_ms',
|
||||
'execution_time_ms'
|
||||
]
|
||||
const FLOW_DURATION_SECOND_FIELDS = [
|
||||
'duration_seconds',
|
||||
'elapsed_seconds',
|
||||
'latency_seconds',
|
||||
'execution_time_seconds'
|
||||
]
|
||||
const FLOW_DURATION_AUTO_FIELDS = ['duration', 'elapsed', 'latency', 'execution_time']
|
||||
const FLOW_STARTED_AT_FIELDS = ['started_at', 'start_time', 'created_at', 'queued_at']
|
||||
const FLOW_FINISHED_AT_FIELDS = ['finished_at', 'completed_at', 'ended_at', 'end_time', 'updated_at']
|
||||
|
||||
export function formatFlowDuration(ms) {
|
||||
if (ms === null || ms === undefined || ms === '') {
|
||||
return '--'
|
||||
}
|
||||
const numericValue = Number(ms)
|
||||
if (!Number.isFinite(numericValue) || numericValue <= 0) {
|
||||
return '--'
|
||||
}
|
||||
if (numericValue < 1000) {
|
||||
return `${Math.max(0.1, numericValue / 1000).toFixed(1)}s`
|
||||
}
|
||||
if (numericValue < 10000) {
|
||||
return `${(numericValue / 1000).toFixed(1)}s`
|
||||
}
|
||||
return `${Math.round(numericValue / 1000)}s`
|
||||
}
|
||||
|
||||
function parseFlowTimestamp(value) {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return 0
|
||||
}
|
||||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||
return value > 0 && value < 10000000000 ? Math.round(value * 1000) : Math.round(value)
|
||||
}
|
||||
const timestamp = new Date(value).getTime()
|
||||
return Number.isFinite(timestamp) ? timestamp : 0
|
||||
}
|
||||
|
||||
function normalizeDurationValue(value, unit = 'ms') {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return null
|
||||
}
|
||||
|
||||
let numericValue = Number(value)
|
||||
let normalizedUnit = unit
|
||||
if (typeof value === 'string') {
|
||||
const text = value.trim()
|
||||
const match = text.match(/^(\d+(?:\.\d+)?)\s*(ms|毫秒|s|秒)?$/i)
|
||||
if (match) {
|
||||
numericValue = Number(match[1])
|
||||
if (match[2]) {
|
||||
normalizedUnit = ['s', '秒'].includes(match[2].toLowerCase()) ? 'seconds' : 'ms'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Number.isFinite(numericValue) || numericValue <= 0) {
|
||||
return null
|
||||
}
|
||||
if (normalizedUnit === 'seconds') {
|
||||
return Math.round(numericValue * 1000)
|
||||
}
|
||||
if (normalizedUnit === 'auto') {
|
||||
return Math.round(numericValue <= 300 ? numericValue * 1000 : numericValue)
|
||||
}
|
||||
return Math.round(numericValue)
|
||||
}
|
||||
|
||||
function readFirstDurationField(source, fields, unit) {
|
||||
if (!source || typeof source !== 'object') {
|
||||
return null
|
||||
}
|
||||
for (const field of fields) {
|
||||
if (!Object.prototype.hasOwnProperty.call(source, field)) {
|
||||
continue
|
||||
}
|
||||
const durationMs = normalizeDurationValue(source[field], unit)
|
||||
if (durationMs) {
|
||||
return durationMs
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function resolveDurationFromFields(source) {
|
||||
return (
|
||||
readFirstDurationField(source, FLOW_DURATION_MS_FIELDS, 'ms')
|
||||
|| readFirstDurationField(source, FLOW_DURATION_SECOND_FIELDS, 'seconds')
|
||||
|| readFirstDurationField(source, FLOW_DURATION_AUTO_FIELDS, 'auto')
|
||||
)
|
||||
}
|
||||
|
||||
function readFirstTimestampField(source, fields) {
|
||||
if (!source || typeof source !== 'object') {
|
||||
return 0
|
||||
}
|
||||
for (const field of fields) {
|
||||
const timestamp = parseFlowTimestamp(source[field])
|
||||
if (timestamp) {
|
||||
return timestamp
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
export function resolveStartedTimestamp(source) {
|
||||
return readFirstTimestampField(source, FLOW_STARTED_AT_FIELDS)
|
||||
}
|
||||
|
||||
export function resolveFinishedTimestamp(source) {
|
||||
return readFirstTimestampField(source, FLOW_FINISHED_AT_FIELDS)
|
||||
}
|
||||
|
||||
function resolveTimeRangeDurationMs(source) {
|
||||
const startedAt = resolveStartedTimestamp(source)
|
||||
const finishedAt = resolveFinishedTimestamp(source)
|
||||
return finishedAt > startedAt ? finishedAt - startedAt : null
|
||||
}
|
||||
|
||||
export function resolveSemanticPhaseDurations(run) {
|
||||
const runStart = resolveStartedTimestamp(run)
|
||||
const toolCalls = Array.isArray(run?.tool_calls) ? run.tool_calls : []
|
||||
const firstToolStartedAt = toolCalls
|
||||
.map((item) => resolveStartedTimestamp(item))
|
||||
.filter((value) => value > 0)
|
||||
.sort((left, right) => left - right)[0] || 0
|
||||
const runFinishedAt = resolveFinishedTimestamp(run)
|
||||
const semanticFinishedAt = firstToolStartedAt || runFinishedAt
|
||||
|
||||
if (!runStart || !semanticFinishedAt || semanticFinishedAt <= runStart) {
|
||||
return { intentMs: null, extractionMs: null }
|
||||
}
|
||||
|
||||
const totalMs = semanticFinishedAt - runStart
|
||||
const intentMs = Math.max(120, Math.round(totalMs * 0.35))
|
||||
const extractionMs = Math.max(160, totalMs - intentMs)
|
||||
return {
|
||||
intentMs,
|
||||
extractionMs
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveToolCallDurationMs(toolCall, index, toolCalls, run) {
|
||||
const response = toolCall?.response_json && typeof toolCall.response_json === 'object'
|
||||
? toolCall.response_json
|
||||
: {}
|
||||
const explicitDuration = resolveDurationFromFields(toolCall)
|
||||
|| resolveTimeRangeDurationMs(toolCall)
|
||||
|| resolveDurationFromFields(response)
|
||||
|| resolveTimeRangeDurationMs(response)
|
||||
if (explicitDuration) {
|
||||
return explicitDuration
|
||||
}
|
||||
|
||||
const startedAt = resolveStartedTimestamp(toolCall)
|
||||
if (!startedAt) {
|
||||
return null
|
||||
}
|
||||
|
||||
const nextStartedAt = resolveStartedTimestamp(toolCalls[index + 1])
|
||||
const runFinishedAt = resolveFinishedTimestamp(run)
|
||||
const finishedAt = nextStartedAt > startedAt ? nextStartedAt : (runFinishedAt > startedAt ? runFinishedAt : 0)
|
||||
|
||||
if (!finishedAt || finishedAt <= startedAt) {
|
||||
return null
|
||||
}
|
||||
|
||||
return finishedAt - startedAt
|
||||
}
|
||||
|
||||
export function summarizeVisibleToolText(value) {
|
||||
const text = String(value || '')
|
||||
.replace(/\|[^\n]*\|/g, '')
|
||||
.replace(/\*\*/g, '')
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.find(Boolean) || ''
|
||||
if (!text) {
|
||||
return ''
|
||||
}
|
||||
return text.length > 80 ? `${text.slice(0, 80)}...` : text
|
||||
}
|
||||
Reference in New Issue
Block a user