UNPKG

@adonis-agora/agent

Version:

Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.

73 lines • 3.13 kB
import { useEffect, useRef, useState } from 'react'; /** * Where a reasoning part carries how long the model thought, in ms. Both producers write it here — * the transport on the live `reasoning-end` chunk, `storedMessageToUiMessage` on a replayed part — * so a reader has one place to look whichever way the message arrived. */ export const REASONING_METADATA_KEY = 'agent'; /** The `providerMetadata` a reasoning part is stamped with. */ export function reasoningDurationMetadata(reasoningMs) { return { [REASONING_METADATA_KEY]: { reasoningMs } }; } /** The thinking time stamped on a reasoning part, or `null` when it carries none. */ export function readReasoningMs(part) { const metadata = part.providerMetadata; if (typeof metadata !== 'object' || metadata === null) return null; const scoped = metadata[REASONING_METADATA_KEY]; if (typeof scoped !== 'object' || scoped === null) return null; const value = scoped.reasoningMs; return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : null; } /** * Milliseconds since `running` last turned true — ticking while it stays true, frozen at the final * value once it turns false, `0` before it has ever run. Headless: pair it with * {@link formatElapsed} or any label of your own. * * For a reasoning block, prefer the duration the block already carries (`block.durationMs`, the * server's own measurement) and fall back to this only while the block streams: * `block.durationMs ?? elapsed`. */ export function useElapsed(running, options = {}) { const { intervalMs = 1000 } = options; const nowRef = useRef(options.now ?? Date.now); nowRef.current = options.now ?? Date.now; const startedAt = useRef(null); const [elapsed, setElapsed] = useState(0); useEffect(() => { if (!running) { if (startedAt.current !== null) { setElapsed(nowRef.current() - startedAt.current); startedAt.current = null; } return; } startedAt.current = nowRef.current(); setElapsed(0); const timer = setInterval(() => { if (startedAt.current !== null) setElapsed(nowRef.current() - startedAt.current); }, intervalMs); return () => clearInterval(timer); }, [running, intervalMs]); return elapsed; } /** * `"<1s"`, `"4s"`, `"1m 5s"`, `"1h 2m"` — whole units, the shape a "Thought for …" label wants. * Negative or non-finite input reads as `"<1s"` rather than a nonsense figure. */ export function formatElapsed(ms) { if (!Number.isFinite(ms) || ms < 1000) return '<1s'; const totalSeconds = Math.floor(ms / 1000); const hours = Math.floor(totalSeconds / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const seconds = totalSeconds % 60; if (hours > 0) return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`; if (minutes > 0) return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`; return `${seconds}s`; } //# sourceMappingURL=timing.js.map