139 lines
5.7 KiB
TypeScript
139 lines
5.7 KiB
TypeScript
/**
|
|
* Copyright 2026 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/**
|
|
* Pure helpers behind the run info popover (the Flash / Pro chip in the
|
|
* task stream): elapsed time, compact token counts, context-window share
|
|
* and the plain-language labels of the Pro tuning a run was started with.
|
|
*/
|
|
|
|
import {
|
|
EXPLORER_MODES,
|
|
VERIFICATION_LEVELS,
|
|
levelIndex,
|
|
DEFAULT_EXPLORER_MODE,
|
|
DEFAULT_VERIFICATION_LEVEL
|
|
} from '../core/models/pro-tuning.model';
|
|
import { ModelInfo, Session, SessionRunTuning } from '../core/models/session.model';
|
|
|
|
/** Denominator for "context used" when the backend does not report one. */
|
|
export const OPERATOR_CONTEXT_WINDOW_TOKENS = 300_000;
|
|
|
|
/** Execution mode is independent of the API model name (e.g. MiMo Pro). */
|
|
export function runMode(session?: Partial<Session> | null, model?: ModelInfo | null): 'flash' | 'pro' | 'explore' | 'unknown' {
|
|
if (session?.exploration_kind === 'exploration') return 'explore';
|
|
const recorded = session?.profile || session?.model_info?.architecture || model?.architecture;
|
|
const mode = (recorded || session?.model_info?.name || model?.name || '').toLowerCase().replace(/^artemis\s+/, '');
|
|
if (mode === '探索' || mode === 'explore') return 'explore';
|
|
return mode === 'flash' || mode === 'pro' ? mode : 'unknown';
|
|
}
|
|
|
|
export function contextWindow(windowTokens?: number | null): number {
|
|
const value = Number(windowTokens);
|
|
return value > 0 && Number.isFinite(value) ? Math.min(value, OPERATOR_CONTEXT_WINDOW_TOKENS) : OPERATOR_CONTEXT_WINDOW_TOKENS;
|
|
}
|
|
|
|
/** `42s`, `12m 05s`, `1h 02m 05s`. Negative or invalid input reads as `0s`. */
|
|
export function formatElapsed(totalSeconds: number | null | undefined): string {
|
|
const secs = Math.max(0, Math.floor(Number(totalSeconds) || 0));
|
|
const hours = Math.floor(secs / 3600);
|
|
const minutes = Math.floor((secs % 3600) / 60);
|
|
const seconds = secs % 60;
|
|
const pad = (n: number) => String(n).padStart(2, '0');
|
|
if (hours > 0) return `${hours}h ${pad(minutes)}m ${pad(seconds)}s`;
|
|
if (minutes > 0) return `${minutes}m ${pad(seconds)}s`;
|
|
return `${seconds}s`;
|
|
}
|
|
|
|
/** `842`, `76.4k`, `1.2M` (no unit suffix; the caller labels the row). */
|
|
export function formatCompactTokens(tokens: number | null | undefined): string {
|
|
const n = Math.max(0, Math.floor(Number(tokens) || 0));
|
|
if (n < 1000) return String(n);
|
|
if (n < 1_000_000) return `${(n / 1000).toFixed(1).replace(/\.0$/, '')}k`;
|
|
return `${(n / 1_000_000).toFixed(2).replace(/\.?0+$/, '')}M`;
|
|
}
|
|
|
|
/** Share of the context window in percent (one decimal), or null when unknown. */
|
|
export function contextPercent(
|
|
usedTokens: number | null | undefined,
|
|
windowTokens: number | null | undefined
|
|
): number | null {
|
|
const used = Number(usedTokens);
|
|
const window = contextWindow(windowTokens);
|
|
if (!Number.isFinite(used) || used < 0 || usedTokens === null || usedTokens === undefined) {
|
|
return null;
|
|
}
|
|
const pct = (used / window) * 100;
|
|
return Math.min(100, Math.round(pct * 10) / 10);
|
|
}
|
|
|
|
/** Session-wide token-weighted cache share; cached input is part of total input. */
|
|
export function cachePercent(cachedTokens: number | null | undefined, promptTokens: number | null | undefined): number | null {
|
|
if (typeof cachedTokens !== 'number' || typeof promptTokens !== 'number' ||
|
|
!Number.isFinite(cachedTokens) || !Number.isFinite(promptTokens) ||
|
|
promptTokens <= 0 || cachedTokens < 0 || cachedTokens > promptTokens) return null;
|
|
return Math.round(cachedTokens / promptTokens * 1000) / 10;
|
|
}
|
|
|
|
/**
|
|
* Seconds a session has been (or was) running.
|
|
*
|
|
* Live sessions count from `start_time` to `nowMs`; finished sessions use the
|
|
* recorded `end_time`. A session with no `start_time` reads as 0.
|
|
*/
|
|
export function sessionElapsedSeconds(
|
|
session: { start_time?: number; end_time?: number | null } | null | undefined,
|
|
isRunning: boolean,
|
|
nowMs: number
|
|
): number {
|
|
if (!session?.start_time) return 0;
|
|
const startMs = session.start_time * 1000;
|
|
const endMs = !isRunning && session.end_time ? session.end_time * 1000 : nowMs;
|
|
return Math.max(0, Math.round((endMs - startMs) / 1000));
|
|
}
|
|
|
|
/** Plain-language label for a tuning id, falling back to the ladder default. */
|
|
export function tuningLabel(kind: 'verify' | 'explore', id: string | null | undefined): string {
|
|
if (kind === 'verify') {
|
|
return VERIFICATION_LEVELS[levelIndex(VERIFICATION_LEVELS, id, DEFAULT_VERIFICATION_LEVEL)].label;
|
|
}
|
|
return EXPLORER_MODES[levelIndex(EXPLORER_MODES, id, DEFAULT_EXPLORER_MODE)].label;
|
|
}
|
|
|
|
/**
|
|
* Reads `run_tuning` out of a session's `device_info` (which the sessions API
|
|
* returns either as a JSON string or an already-parsed object).
|
|
*/
|
|
export function parseRunTuning(deviceInfo: unknown): SessionRunTuning | null {
|
|
let info: unknown = deviceInfo;
|
|
if (typeof info === 'string') {
|
|
try {
|
|
info = JSON.parse(info);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
if (!info || typeof info !== 'object') return null;
|
|
const tuning = (info as { run_tuning?: unknown }).run_tuning;
|
|
if (!tuning || typeof tuning !== 'object') return null;
|
|
const t = tuning as SessionRunTuning;
|
|
if (!t.verification_level && !t.explorer_mode) return null;
|
|
return {
|
|
verification_level: t.verification_level ?? null,
|
|
explorer_mode: t.explorer_mode ?? null
|
|
};
|
|
}
|