@juspay/neurolink
Version:
The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002
82 lines (81 loc) • 3 kB
JavaScript
/**
* Message identity and metadata for a stored conversation turn.
*
* Both memory managers store a turn as a user message followed by an assistant
* message. When the caller supplies a `turnId` the two messages get stable ids
* (`${turnId}:user`, `${turnId}:assistant`), which lets a repeated store of the
* same turn be recognised and skipped. When the caller sets neither a
* `turnId` nor a `modality`, nothing here changes what is stored: ids stay
* random and no metadata is added, so typed chat is byte-for-byte as before.
*/
import { randomUUID } from "node:crypto";
import { logger } from "./logger.js";
/** How far back a repeated `turnId` is looked for. A turn is always the newest thing in a session. */
const DUPLICATE_LOOKBACK_MESSAGES = 200;
export function userTurnMessageId(turnId) {
return `${turnId}:user`;
}
export function assistantTurnMessageId(turnId) {
return `${turnId}:assistant`;
}
/** Identity and metadata for the user message of a turn. */
export function userTurnIdentity(options) {
const metadata = {};
if (options.modality) {
metadata.modality = options.modality;
}
if (options.turnId) {
metadata.turnId = options.turnId;
}
return {
id: options.turnId ? userTurnMessageId(options.turnId) : undefined,
metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
};
}
/** Identity and metadata for the assistant message of a turn. */
export function assistantTurnIdentity(options, thoughtSignature) {
const metadata = {};
if (thoughtSignature) {
metadata.thoughtSignature = thoughtSignature;
}
if (options.modality) {
metadata.modality = options.modality;
}
if (options.turnId) {
metadata.turnId = options.turnId;
}
if (options.interrupted) {
metadata.interrupted = true;
}
if (options.callEnded) {
metadata.callEnded = true;
}
return {
id: options.turnId ? assistantTurnMessageId(options.turnId) : undefined,
metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
};
}
/** `id` and optional `metadata` of a stored message, ready to spread into it. */
export function turnMessageFields(identity) {
return {
id: identity.id ?? randomUUID(),
...(identity.metadata ? { metadata: identity.metadata } : {}),
};
}
/** True when a turn with this id is already among the session's recent messages. */
export function isTurnAlreadyStored(messages, turnId) {
if (!turnId) {
return false;
}
const assistantId = assistantTurnMessageId(turnId);
const start = Math.max(0, messages.length - DUPLICATE_LOOKBACK_MESSAGES);
for (let index = messages.length - 1; index >= start; index -= 1) {
if (messages[index].id === assistantId) {
logger.debug("[conversationTurnIdentity] Turn already stored, skipping", {
turnId,
});
return true;
}
}
return false;
}