npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

logicgaze

v1.6.0

Published

LogicGaze SDK — AI-native observability for LLM applications

Readme

logicgaze

Node.js / TypeScript SDK for LogicGaze — AI-native observability for LLM applications.

Zero runtime dependencies. Works with OpenAI, Anthropic, and any provider.

Installation

npm install logicgaze
# or
pnpm add logicgaze
# or
yarn add logicgaze

Requires Node.js ≥ 18. Current version: 1.6.0


Quick Start

import { init, traceable, wrapOpenAI } from 'logicgaze'
import OpenAI from 'openai'

// 1. Initialise once at app startup
init({
  apiKey: 'lgz-...',                              // or set LOGICGAZE_API_KEY env var
  baseUrl: 'https://your-app.com/api/v1',         // or set LOGICGAZE_BASE_URL
})

// 2. Wrap your AI client
const openai = wrapOpenAI(new OpenAI())

// 3. Wrap your function — every call is automatically traced
const answer = traceable(async (question: string) => {
  const res = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [{ role: 'user', content: question }],
  })
  return res.choices[0].message.content
}, { serviceName: 'qa-service' })

// 4. Call it normally — tracing is invisible
const result = await answer('What is the capital of France?')

Environment Variables

| Variable | Default | Description | |---|---|---| | LOGICGAZE_API_KEY | — | API key (required) | | LOGICGAZE_BASE_URL | http://localhost:8000/api/v1 | Backend URL | | LOGICGAZE_SAMPLE_RATE | 1.0 | Sampling rate 0.0–1.0 | | LOGICGAZE_MAX_RETRIES | 3 | Retry attempts on 429/5xx | | LOGICGAZE_BATCH_SIZE | 50 | Events per ingestion batch | | LOGICGAZE_FLUSH_INTERVAL | 5000 | Flush interval (ms) | | LOGICGAZE_DEBUG | — | Set to 1 for verbose logs |


API

init(options)

Initialise the global client. Call once at startup.

import { init } from 'logicgaze'

init({
  apiKey: 'lgz-...',
  baseUrl: 'https://your-app.com/api/v1',
  sampleRate: 0.5,     // trace 50% of calls
  maxRetries: 3,
  debug: true,

  // Masking — control what LogicGaze stores for span inputs/outputs
  hideInputs: false,               // true → store [REDACTED] instead of input messages
  hideOutputs: false,              // true → store [REDACTED] instead of output content
  mask: (data) => scrubPII(data),  // custom transform; if it throws, [MASKING_ERROR] is stored
})

traceable(fn, options?)

Wrap any async function. Creates a trace for each invocation, propagates context to all wrapped LLM calls inside.

import { traceable } from 'logicgaze'

const myPipeline = traceable(async (input: string) => {
  // All wrapOpenAI / wrapAnthropic calls here auto-attach spans
  return processInput(input)
}, {
  serviceName: 'my-pipeline',
  sessionId: 'session-abc',
  userId: 'user-123',
  tags: { env: 'production' },
})

wrapOpenAI(client, options?)

Patch an OpenAI client to record LLM spans inside active traces.

import OpenAI from 'openai'
import { wrapOpenAI } from 'logicgaze'

const openai = wrapOpenAI(new OpenAI(), { serviceName: 'gpt-4-service' })
// openai.chat.completions.create() now auto-records spans

wrapAnthropic(client, options?)

import Anthropic from '@anthropic-ai/sdk'
import { wrapAnthropic } from 'logicgaze'

const anthropic = wrapAnthropic(new Anthropic())
// anthropic.messages.create() now auto-records spans

Streaming

stream: true calls through wrapped clients are fully instrumented — the wrapped async iterable is a drop-in replacement:

const stream = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [{ role: 'user', content: 'Write a haiku' }],
  stream: true,
})
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? '')
}
  • Content deltas are accumulated into the span output
  • Token usage is captured — for OpenAI, stream_options: { include_usage: true } is auto-injected when absent; for Anthropic, usage is read from message_start / message_delta events
  • time_to_first_token_ms is recorded in span metadata
  • The span is recorded when the stream completes, errors, or is terminated early — bookkeeping can never break your stream loop

withTrace(fn, context)

Manually scope a block of code to a trace (useful when you can't use traceable).

import { withTrace } from 'logicgaze'
import { randomUUID } from 'crypto'

await withTrace(async () => {
  // All wrapped LLM calls here attach to this trace
  const res = await openai.chat.completions.create({ ... })
}, {
  traceId: randomUUID(),
  serviceName: 'my-service',
  sessionId: req.session.id,
  userId: req.user.id,
})

Scores & Feedback

import { getClient } from 'logicgaze'

const client = getClient()

// Data type inferred: number → NUMERIC, boolean → BOOLEAN, stringValue → CATEGORICAL
await client.logScore(traceId, 'relevance', 0.92)
await client.logScore(traceId, 'is_correct', true)
await client.logScore(traceId, 'tone', undefined, { stringValue: 'formal' })
await client.logScore(traceId, 'faithfulness', 0.8, { spanId, comment: 'minor gap' })

// End-user feedback: 'thumbs_up' | 'thumbs_down' | 'rating' | 'free_form'
await client.logFeedback(traceId, { feedbackType: 'thumbs_up', score: 1 })
await client.logFeedback(traceId, { feedbackType: 'rating', score: 4, comment: 'Helpful' })

Datasets

const dataset = await client.createDataset('qa-regression', {
  description: 'Golden QA test cases',
  tags: ['qa'],
})

await client.addDatasetItem(dataset.id, { question: 'What is RAG?' }, {
  expectedOutput: { answer: '...' },
  metadata: { source: 'manual' },
})

const items = await client.getDatasetItems(dataset.id, 1, 50)
const all = await client.listDatasets()

Prompt Management

// Create (or version-bump — the server auto-increments versions)
await client.createPrompt('welcome-email', 'Hi {{name}}, welcome to {{product}}!', {
  tags: ['onboarding'],
  config: { model: 'gpt-4o' },
})

// Fetch with a client-side stale-while-revalidate TTL cache (default 60s)
const prompt = await client.getPrompt('welcome-email')
const pinned = await client.getPrompt('welcome-email', { version: 2, cacheTtlSeconds: 300 })

const text = prompt.compile({ name: 'Ada', product: 'LogicGaze' })
// "Hi Ada, welcome to LogicGaze!" — unknown {{placeholders}} are left intact

Fresh cache entries are served with no network call; stale entries are served immediately and revalidated in the background — network errors keep serving the stale prompt.

Manual Tracing

For full control:

import { getClient } from 'logicgaze'

const client = getClient()

// Create trace
const trace = await client.startTrace({ serviceName: 'my-service' })

// Record a span directly
await client.createSpan(trace.traceId, {
  spanType: 'llm',
  name: 'GPT-4 call',
  model: 'gpt-4o',
  provider: 'openai',
  promptTokens: 100,
  completionTokens: 50,
  durationMs: 1234,
})

// Finish trace
await client.endTrace(trace.traceId)

observe (alias)

observe is an alias for traceable for LangSmith / Laminar compatibility:

import { observe } from 'logicgaze'

const myFn = observe(async () => { ... })

Context Accessors

Inside a traceable / withTrace context, you can read the current trace:

import { getCurrentTraceId, getCurrentSessionId } from 'logicgaze'

const traceId = getCurrentTraceId()   // string | null
const sessionId = getCurrentSessionId() // string | undefined

Error Handling

All errors are typed:

import {
  LogicGazeError,
  AuthenticationError,
  RateLimitError,
  ServerError,
  NetworkError,
  TimeoutError,
  ConfigurationError,
} from 'logicgaze'

try {
  await client.startTrace({ ... })
} catch (err) {
  if (err instanceof RateLimitError) {
    console.log(`Rate limited. Retry after: ${err.retryAfter}s`)
  } else if (err instanceof AuthenticationError) {
    console.error('Invalid API key')
  }
}

Shutdown & Flushing

The background queue is unref-ed so it won't prevent Node from exiting, and the client automatically flushes queued spans on process beforeExit — short-lived scripts and serverless functions lose nothing without extra code.

For explicit control (e.g. before process.exit(), which skips beforeExit):

import { getClient } from 'logicgaze'

await getClient().flush()   // send everything queued right now
getClient().shutdown()      // stop the background queue

License

MIT