@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
JavaScript
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