@farzanhossans/agentlens
v0.2.2
Published
Universal AI agent observability — 1 line to trace every LLM call (OpenAI, Anthropic, Gemini, Cohere, Mistral)
Maintainers
Readme
@farzanhossans/agentlens
Universal AI agent observability — one line to trace every LLM call.
Patches globalThis.fetch and Node's http/https so every call to OpenAI,
Anthropic, Gemini, Cohere, or Mistral is captured automatically. No SDK
wrappers. No code changes inside your call sites.
Install
pnpm add @farzanhossans/agentlens
# or: npm i @farzanhossans/agentlensQuickstart
1. Cloud — zero config (default)
import { AgentLens } from '@farzanhossans/agentlens'
AgentLens.init({ apiKey: 'proj_xxx' })2. Self-hosted — point at your own ingest endpoint
import { AgentLens } from '@farzanhossans/agentlens'
AgentLens.init({
apiKey: 'proj_xxx', // key from your own AgentLens dashboard
endpoint: 'https://agentlens.your-company.com/v1/spans',
})3. Self-hosted + debug mode (logs each captured span to stdout)
AgentLens.init({
apiKey: 'proj_xxx',
endpoint: 'https://agentlens.your-company.com/v1/spans',
debug: true,
})4. Disable PII scrubbing (only for trusted internal data)
AgentLens.init({
apiKey: 'proj_xxx',
pii: false,
})What gets captured
For every matched LLM HTTP call:
providerandmodelinputTokens,outputTokens,totalTokenscostUsd(calculated from a built-in price table per provider)inputTextandoutputText(PII-scrubbed by default)latencyandstatusisStreamflag — streaming responses are tapped viaReadableStream.tee()so the stream you read is byte-identical to the original
Supported providers
All five providers support both streaming and non-streaming responses.
| Provider | Hosts | Endpoints | Streaming |
| --------- | -------------------------------------- | -------------------------------------------------------------- | --------- |
| OpenAI | api.openai.com | /v1/chat/completions, /v1/completions, /v1/embeddings | ✅ |
| Anthropic | api.anthropic.com | /v1/messages | ✅ |
| Gemini | generativelanguage.googleapis.com | /v1beta/models, /v1/models | ✅ |
| Cohere | api.cohere.com | /v1/chat, /v1/generate, /v2/chat (v1 + v2 stream shapes) | ✅ |
| Mistral | api.mistral.ai | /v1/chat/completions | ✅ |
Grouping calls with trace()
Wrap a logical unit of work (an agent step, a multi-call retrieval, etc.) in
AgentLens.trace(name, fn). Every LLM call inside fn — including async ones
and across nested traces — is auto-tagged with the same traceId and gets
parentSpanId set to the trace's span. Built on Node's AsyncLocalStorage,
so it composes with Promise.all, setTimeout, async generators, etc.
import { AgentLens } from '@farzanhossans/agentlens'
AgentLens.init({ apiKey: 'proj_xxx' })
await AgentLens.trace('classify-then-rephrase', async () => {
const classification = await openai.chat.completions.create({ ... })
// ↑ span emitted with parentSpanId = the trace's spanId
const rephrased = await anthropic.messages.create({ ... })
// ↑ same trace, same parentSpanId
})Nested traces nest:
await AgentLens.trace('outer', async () => {
await AgentLens.trace('inner', async () => {
await openai.chat.completions.create({ ... })
// child of `inner`, which is child of `outer`, all sharing one traceId
})
})PII scrubbing
Enabled by default. Strips emails, phone numbers, SSNs, credit cards, IPv4
addresses, and obvious API-key shapes from inputText / outputText before
spans leave your process. Pass pii: false to disable.
Self-hosted flow
- Deploy AgentLens via Docker Compose on your own server
- Open your own dashboard → create a project → copy the API key
AgentLens.init({ apiKey, endpoint })pointing at your own ingest URL- Spans flow: your app → your worker → your DB. Nothing touches AgentLens cloud.
API
AgentLens.init(config: {
apiKey: string
endpoint?: string // default: https://ingest.agentlens.dev/v1/spans
debug?: boolean // default: false
pii?: boolean // default: true
flushIntervalMs?: number // default: 500
maxBatchSize?: number // default: 50
}): void
AgentLens.flush(): Promise<void> // force-flush pending spans
AgentLens.shutdown(): void // stop the flush timer
AgentLens.trace<T>(name: string, fn: () => Promise<T>): Promise<T>
// re-exports from @farzanhossans/agentlens-core:
getCurrentTrace(), getCurrentTraceId(), getCurrentSpanId(), runWithTrace(ctx, fn)Requirements
Node ≥ 18 (the SDK uses AsyncLocalStorage, globalThis.fetch, and crypto.randomUUID).
License
MIT
