@traceprompt/node
Version:
Client-side encrypted, audit-ready logging for LLM applications
67 lines (55 loc) • 2.14 kB
text/typescript
import { performance } from "node:perf_hooks";
import { initTracePrompt as initCfgAsync, ConfigManager } from "./config";
import { encryptBuffer } from "./crypto/encryptor";
import { computeLeaf } from "./crypto/hasher";
import { countTokens } from "./utils/tokenCounter";
import { PersistentBatcher as Batcher } from "./queue/persistentBatcher";
const stringify = require("json-stable-stringify") as (v: any) => string;
import type { TracePromptInit, WrapOpts } from "./types";
import { registry } from "./metrics";
import { Histogram } from "prom-client";
const wrapperLatencyHist = new Histogram({
name: "traceprompt_llm_wrapper_latency_ms",
help: "End‑to‑end latency from prompt send to response receive in the SDK wrapper (ms)",
buckets: [50, 100, 250, 500, 1000, 2000, 5000],
registers: [registry],
});
export async function initTracePrompt(
cfg?: Partial<TracePromptInit>
): Promise<void> {
await initCfgAsync(cfg);
}
export function wrapLLM<P extends Record<string, any>, R>(
originalFn: (prompt: string, params?: P) => Promise<R>,
meta: WrapOpts
): (prompt: string, params?: P) => Promise<R> {
const staticMeta = ConfigManager.cfg.staticMeta;
return async function wrapped(prompt: string, params?: P): Promise<R> {
const t0 = performance.now();
const result = await originalFn(prompt, params);
const t1 = performance.now();
wrapperLatencyHist.observe(t1 - t0);
const plaintextJson = JSON.stringify({
prompt,
response: result,
});
const enc = await encryptBuffer(Buffer.from(plaintextJson, "utf8"));
const payload = {
...staticMeta,
orgId: ConfigManager.cfg.orgId,
modelVendor: meta.modelVendor,
modelName: meta.modelName,
userId: meta.userId,
ts_client: new Date().toISOString(),
latency_ms: +(t1 - t0).toFixed(2),
prompt_tokens: countTokens(prompt),
response_tokens: countTokens(
typeof result === "string" ? result : JSON.stringify(result)
),
enc,
};
const leafHash = computeLeaf(stringify(payload));
Batcher.enqueue({ payload, leafHash });
return result;
};
}