El SDK de TypeScript (sdk/typescript, paquete @ledger/sdk) es la implementación de referencia — paridad completa, incluyendo auto-instrumentación para los clientes de Anthropic y OpenAI.
Aún no está publicado en npm. Por ahora, incorpore sdk/typescript a su proyecto — copie el directorio, o referéncielo como una dependencia local/git:
npm install <path-to-repo>/sdk/typescript
import { init } from "@ledger/sdk";
const ledger = init({
endpoint: process.env.LEDGER_ENDPOINT ?? "http://127.0.0.1:4010",
ingestKey: process.env.LEDGER_INGEST_KEY ?? "",
agentId: "urn:agent:research-assistant",
onError: (e) => console.error("[ledger]", e),
});
const t0 = new Date();
// ... haga el trabajo ...
const t1 = new Date();
ledger.record({
action_type: "llm_call",
provider: "anthropic",
model: "claude-fable-5",
started_at: t0,
ended_at: t1,
status: "success",
usage: { tokens_in: 420, tokens_out: 180 },
payloads: [
{ kind: "prompt", content: "Summarize the policy for the client memo." },
{ kind: "response", content: "The policy provides that…" },
],
trigger: { type: "human", subject: "person@example.com" },
});
await ledger.flush();
await ledger.shutdown();
shutdown() hace un flush final con el mejor esfuerzo posible y un plazo máximo (2s por defecto) — llámelo antes de que su proceso termine para que no se pierdan los eventos en buffer.
Para Anthropic u OpenAI, olvídese por completo de las llamadas manuales a record() — envuelva el cliente una vez y cada llamada posterior se captura automáticamente, incluido el streaming:
import { init } from "@ledger/sdk";
import Anthropic from "@anthropic-ai/sdk";
const ledger = init({
endpoint: process.env.LEDGER_ENDPOINT ?? "http://127.0.0.1:4010",
ingestKey: process.env.LEDGER_INGEST_KEY ?? "",
agentId: "urn:agent:research-assistant",
});
ledger.setTrigger({ type: "human", subject: "person@example.com" });
const anthropic = ledger.instrumentAnthropic(new Anthropic());
// código de la aplicación sin modificar a partir de aquí — no hace falta ninguna referencia al ledger
const res = await anthropic.messages.create({
model: "claude-fable-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Draft the client memo." }],
});
instrumentOpenAI(client) funciona igual para el cliente de OpenAI. Ambos son wrappers duck-typed — cero dependencias de SDKs de proveedores dentro del propio @ledger/sdk.
const { spanId } = ledger.record({ action_type: "search", /* ... */ });
ledger.within(spanId, async () => {
// todo lo que se registre aquí dentro toma spanId como parent_ref por defecto
const anthropic = ledger.instrumentAnthropic(client);
await anthropic.messages.create(/* ... */);
});
Pase parentRef explícitamente en una llamada a record() para anular el padre ambiental.
const { blob_id } = await ledger.uploadBlob(fileBuffer, "application/pdf");
ledger.record({
action_type: "data_read",
payloads: [{ kind: "artifact", blob_ref: blob_id }],
// ...
});
Si ya tiene un pipeline de OTel, conecte el ledger como un exportador o procesador de spans en lugar de instrumentar a mano:
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
provider.addSpanProcessor(ledger.otelSpanProcessor());
// o: new BatchSpanProcessor(ledger.otelExporter())
Cada span gen_ai.* se convierte en un evento del ledger; el resto de spans pasan sin modificar.