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

@llmeval/sdk

v1.0.3

Published

Zero-dependency SDK for logging and tracking LLM interactions with LLMeval

Readme

📊 LLMeval SDK - Documentation Complète

npm version npm downloads

🚀 Maintenant disponible sur npm ! Installez simplement avec npm install @llmeval/sdk

Table des Matières

Vue d'ensemble

Le SDK LLMeval permet de logger et tracer les interactions avec les modèles de langage (LLM) en production. Il offre :

  • 📝 Logging Structuré - Enregistrement des requêtes, réponses et métadonnées LLM
  • 🔗 Traçage Distribué - Suivi des workflows complexes avec spans hiérarchiques
  • 📊 Dashboard Intégré - Visualisation des logs et traces dans une interface web
  • 🔐 Authentification Sécurisée - Accès sécurisé avec clés API et rate limiting
  • ⚡ Monitoring Performance - Suivi des tokens, latence et coûts

Démarrage Rapide

# 1. Installer le SDK via npm
npm install @llmeval/sdk

# 2. Configurer les variables d'environnement
# Pour Next.js (.env.local)
echo "NEXT_PUBLIC_LLM_LOGGER_API_KEY=votre_cle_api" >> .env.local
echo "NEXT_PUBLIC_LLM_LOGGER_API_ENDPOINT=https://llmeval-production.up.railway.app/v1" >> .env.local

# Pour autres projets (.env)
echo "LLM_LOGGER_API_KEY=votre_cle_api" >> .env
echo "LLM_LOGGER_API_ENDPOINT=https://llmeval-production.up.railway.app/v1" >> .env

# 3. Commencer à logger
import { logLLMRun } from '@llmeval/sdk'

Installation

# Installation via npm
npm install @llmeval/sdk

# Ou avec yarn
yarn add @llmeval/sdk

# Ou avec pnpm
pnpm add @llmeval/sdk

Caractéristiques

Le SDK utilise uniquement les APIs natives du navigateur :

  • Fetch API pour les requêtes HTTP
  • Crypto API pour la génération d'UUID
  • JSON pour la sérialisation

Zéro dépendance - Le SDK fonctionne directement !

Structure du Projet

votre-projet/
├── node_modules/
│   └── @llmeval/sdk/     # SDK installé via npm
├── .env                  # Variables d'environnement (ou .env.local pour Next.js)
└── src/
    └── votre-app.ts      # Code de votre application

Configuration API

Obtenir une Clé API

  1. Contactez l'Administrateur - Demandez l'accès à votre administrateur LLMeval
  2. Dashboard Web - Visitez https://llmeval-seven.vercel.app/api-keys
  3. Support - Contactez le support pour obtenir une clé

Configuration des Variables d'Environnement

Pour Next.js (.env.local) :

NEXT_PUBLIC_LLM_LOGGER_API_KEY="faf4b808-4e39-427d-8f9c-4607f91929d5"
NEXT_PUBLIC_LLM_LOGGER_API_ENDPOINT="https://llmeval-production.up.railway.app/v1"

Pour Node.js, React (Vite), ou autres (.env) :

LLM_LOGGER_API_KEY="faf4b808-4e39-427d-8f9c-4607f91929d5"
LLM_LOGGER_API_ENDPOINT="https://llmeval-production.up.railway.app/v1"

Pour les variables d'environnement système :

export LLM_LOGGER_API_KEY="faf4b808-4e39-427d-8f9c-4607f91929d5"
export LLM_LOGGER_API_ENDPOINT="https://llmeval-production.up.railway.app/v1"

Vérifier la Connexion

import { verifyApiKey } from '@llmeval/sdk'

async function checkConnection() {
  try {
    await verifyApiKey()
    console.log("✅ Clé API valide")
  } catch (error) {
    console.error("❌ Échec de vérification:", error)
  }
}

Utilisation de Base

Logging Simple d'un LLM

import { logLLMRun } from '@llmeval/sdk'

async function generateContent(userQuery: string) {
  // Votre appel LLM
  const response = await openai.chat.completions.create({
    model: "gpt-4",
    messages: [{ role: "user", content: userQuery }],
    temperature: 0.7
  })
  
  const output = response.choices[0]?.message?.content || ""
  
  // Logger dans le système de monitoring
  await logLLMRun({
    // Note: run_id et timestamp sont générés automatiquement par le SDK
    
    // Requis: Informations du prompt
    prompt_template: "Répondez à cette question utilisateur: {user_query}",
    prompt_final: `Répondez à cette question utilisateur: ${userQuery}`,
    prompt_name: "chat-response",
    input_vars: { user_query: userQuery },
    
    // Requis: IDs Feature/Step (obtenus depuis le dashboard)
    feature_id: "112cb182-0a67-4e5b-9d89-51377b1ca23b",
    step_id: "ec3cd05a-a819-4c43-85bb-bb931eafd7fb",
    
    // Requis: Réponse du modèle
    output: output,
    raw_response: response,
    
    // Requis: Informations du modèle
    model: "gpt-4",
    provider: "openai",
    model_params: { temperature: 0.7 },
    
    // Requis: Métriques
    tokens_input: response.usage?.prompt_tokens || 0,
    tokens_output: response.usage?.completion_tokens || 0,
    tokens_total: response.usage?.total_tokens || 0,
    duration_ms: 1250,
    
    // Optionnel: Contexte métier
    metadata: {
      userId: "user-123",
      sessionId: "session-456",
      category: "support-technique"
    }
  })
  
  return output
}

Gestion d'Erreurs

async function safeLogLLMRun(params: any) {
  try {
    await logLLMRun(params)
  } catch (error) {
    // Ne pas laisser les échecs de logging casser votre app
    console.error("Échec du logging:", error)
    
    // Optionnel: Fallback vers logging local
    console.log("Requête LLM:", JSON.stringify(params, null, 2))
  }
}

Traçage Avancé

Créer des Traces pour les Workflows

import { traceManager, withSpan, logLLMRun } from '@llmeval/sdk'

async function workflowComplexe(nomEntreprise: string) {
  // 1. Créer une trace pour tout le workflow
  const traceId = await traceManager.createTrace()
  console.log("📊 Trace créée:", traceId)
  
  // 2. Exécuter le workflow avec des spans imbriqués
  return await withSpan({
    span_name: "Analyse Entreprise Workflow",
    span_type: "function_call",
    trace_id: traceId,
    metadata: { 
      nomEntreprise,
      versionWorkflow: "2.1",
      environnement: "production"
    }
  }, async (workflowSpanId) => {
    
    // Étape 1: Collecte de données
    const donneesEntreprise = await withSpan({
      span_name: "collecte-donnees-entreprise",
      span_type: "function_call", 
      parent_span_id: workflowSpanId,
      metadata: { sourceData: "crunchbase" }
    }, async () => {
      return await fetchCompanyData(nomEntreprise)
    })
    
    // Étape 2: Générer description entreprise
    const description = await logLLMRun({
      run_id: randomUUID(),
      timestamp: new Date().toISOString(),
      prompt_template: "Générez une description professionnelle pour: {nom_entreprise}\n\nDonnées: {donnees_entreprise}",
      prompt_final: `Générez une description professionnelle pour: ${nomEntreprise}\n\nDonnées: ${JSON.stringify(donneesEntreprise)}`,
      prompt_name: "description-entreprise",
      input_vars: { 
        nom_entreprise: nomEntreprise,
        donnees_entreprise: JSON.stringify(donneesEntreprise)
      },
      feature_id: "112cb182-0a67-4e5b-9d89-51377b1ca23b",
      step_id: "ec3cd05a-a819-4c43-85bb-bb931eafd7fb",
      output: "TechCorp Inc est une entreprise technologique leader...",
      raw_response: { /* Réponse OpenAI */ },
      model: "gpt-4",
      provider: "openai",
      model_params: { temperature: 0.7 },
      tokens_input: 150,
      tokens_output: 200,
      tokens_total: 350,
      duration_ms: 2000,
      // 🔗 Lier à la trace
      trace_id: traceId,
      span_name: "generer-description-entreprise",
      parent_span_id: workflowSpanId,
      metadata: {
        etape: "description",
        langue: "français"
      }
    })
    
    return { description, donneesEntreprise }
  })
}

Intégrations

Intégration OpenAI

import OpenAI from 'openai'
import { logLLMRun } from '@llmeval/sdk'

class OpenAILogger {
  private openai: OpenAI
  
  constructor(apiKey: string) {
    this.openai = new OpenAI({ apiKey })
  }
  
  async chat({
    messages,
    model = "gpt-4",
    temperature = 0.7,
    feature_id,
    step_id,
    prompt_name,
    trace_id,
    parent_span_id,
    metadata = {}
  }: {
    messages: any[]
    model?: string
    temperature?: number
    feature_id: string
    step_id: string
    prompt_name: string
    trace_id?: string
    parent_span_id?: string
    metadata?: Record<string, any>
  }) {
    const startTime = Date.now()
    
    try {
      const response = await this.openai.chat.completions.create({
        model,
        messages,
        temperature,
        max_tokens: 1000
      })
      
      const duration = Date.now() - startTime
      const output = response.choices[0]?.message?.content || ""
      
      // Auto-logger l'interaction
      const result = await logLLMRun({
        // Note: run_id et timestamp générés automatiquement
        promptTemplate: this.messagesToTemplate(messages),
        inputVars: this.extractVariables(messages),
        feature_id,
        step_id,
        prompt_name,
        prompt_version: "1.0",
        output,
        raw_response: response,
        model,
        provider: "openai",
        temperature,
        business_metadata: {
          ...metadata,
          duration_ms: duration,
          tokens_input: response.usage?.prompt_tokens || 0,
          tokens_output: response.usage?.completion_tokens || 0,
          tokens_total: response.usage?.total_tokens || 0,
          sdk_version: "1.0.0"
        },
        trace_id,
        parent_span_id
      })
      
      return { content: output, response, run_id: result.run_id }
    } catch (error) {
      console.error("Échec appel OpenAI:", error)
      throw error
    }
  }
  
  private messagesToTemplate(messages: any[]): string {
    return messages.map(m => `${m.role}: {${m.role}_input}`).join('\n')
  }
  
  private messagesToFinal(messages: any[]): string {
    return messages.map(m => `${m.role}: ${m.content}`).join('\n')
  }
  
  private extractVariables(messages: any[]): Record<string, any> {
    const result: Record<string, any> = {}
    messages.forEach(m => {
      result[`${m.role}_input`] = m.content
    })
    return result
  }
}

// Utilisation
const llmClient = new OpenAILogger(process.env.OPENAI_API_KEY!)

const result = await llmClient.chat({
  messages: [{ role: "user", content: "Expliquez l'informatique quantique" }],
  feature_id: "112cb182-0a67-4e5b-9d89-51377b1ca23b",
  step_id: "ec3cd05a-a819-4c43-85bb-bb931eafd7fb",
  prompt_name: "explication-quantique",
  metadata: { sujet: "informatique-quantique", difficulte: "debutant" }
})

Intégration Anthropic Claude

import Anthropic from '@anthropic-ai/sdk'
import { logLLMRun } from '@llmeval/sdk'

class ClaudeLogger {
  private anthropic: Anthropic
  
  constructor(apiKey: string) {
    this.anthropic = new Anthropic({ apiKey })
  }
  
  async complete({
    prompt,
    model = "claude-3-opus-20240229",
    max_tokens = 1000,
    temperature = 0.7,
    feature_id,
    step_id,
    prompt_name,
    trace_id,
    parent_span_id,
    metadata = {}
  }: any) {
    const startTime = Date.now()
    
    try {
      const response = await this.anthropic.messages.create({
        model,
        max_tokens,
        temperature, 
        messages: [{ role: "user", content: prompt }]
      })
      
      const duration = Date.now() - startTime
      const output = response.content[0]?.text || ""
      
      const result = await logLLMRun({
        // Note: run_id et timestamp générés automatiquement
        promptTemplate: "{user_prompt}",
        inputVars: { user_prompt: prompt },
        feature_id,
        step_id,
        prompt_name,
        prompt_version: "1.0",
        output,
        raw_response: response,
        model,
        provider: "anthropic",
        temperature,
        business_metadata: {
          ...metadata,
          duration_ms: duration,
          tokens_input: response.usage?.input_tokens || 0,
          tokens_output: response.usage?.output_tokens || 0,
          tokens_total: (response.usage?.input_tokens || 0) + (response.usage?.output_tokens || 0),
          max_tokens,
          sdk_version: "1.0.0"
        },
        trace_id,
        parent_span_id
      })
      
      return { content: output, response, run_id: result.run_id }
    } catch (error) {
      console.error("Échec appel Claude:", error)
      throw error
    }
  }
}

Référence API

logLLMRun(params)

Enregistre une interaction LLM unique.

Paramètres Requis:

  • promptTemplate: string - Template avec placeholders {variable}
  • inputVars: object - Variables utilisées dans le template
  • feature_id: string - Identifiant de feature depuis le dashboard
  • step_id: string - Identifiant de step depuis le dashboard
  • prompt_name: string - Nom lisible du prompt
  • prompt_version: string - Version du prompt template
  • output: string - Réponse textuelle du modèle
  • raw_response: object - Objet de réponse API complet
  • model: string - Nom du modèle (ex: "gpt-4")
  • provider: string - Nom du fournisseur (ex: "openai")
  • temperature: number - Température du modèle

Paramètres Optionnels:

  • business_metadata?: object - Contexte métier (tokens, durée, etc.)
  • trace_id?: string - Lier à une trace
  • span_name?: string - Nom métier de cette opération
  • parent_span_id?: string - ID de l'opération parente
  • span_metadata?: object - Métadonnées du span
  • apiKey?: string - Surcharger la clé API par défaut
  • apiEndpoint?: string - Surcharger l'endpoint par défaut

Champs Générés Automatiquement:

  • run_id - UUID unique généré automatiquement
  • timestamp - Timestamp ISO généré automatiquement
  • prompt_final - Prompt avec variables remplacées

traceManager

Gérer les traces et spans pour les workflows complexes.

Méthodes:

  • createTrace(apiKey?, apiEndpoint?) - Créer nouvelle trace, retourne trace_id
  • startSpan(config) - Démarrer nouveau span, retourne span_id
  • endSpan(span_id, status?, error_message?) - Terminer un span
  • getCurrentTrace() - Obtenir l'ID de trace active
  • getCurrentSpan() - Obtenir l'ID de span actif

withSpan(config, fn)

Exécuter une fonction dans un span (fermeture automatique).

Config:

  • span_name: string - Nom métier du span
  • span_type: SpanType - Type: 'function_call' | 'tool_call' | 'retrieval'
  • trace_id?: string - Trace à attacher
  • parent_span_id?: string - Span parent
  • metadata?: object - Contexte additionnel

Bonnes Pratiques

1. Organisation Features et Steps

// ✅ Bon: Organisé par fonction métier
const FEATURES = {
  SUPPORT_CLIENT: "112cb182-0a67-4e5b-9d89-51377b1ca23b",
  GENERATION_CONTENU: "e5f6g7h8-i9j0-k1l2-m3n4-o5p6q7r8s9t0", 
  ANALYSE_DONNEES: "i9j0k1l2-m3n4-o5p6-q7r8-s9t0u1v2w3x4"
}

const STEPS = {
  SUPPORT_CLIENT: {
    REPONSE_CHAT: "ec3cd05a-a819-4c43-85bb-bb931eafd7fb",
    CLASSIFICATION_TICKET: "e5f6g7h8-i9j0-k1l2-m3n4-o5p6q7r8s9t0"
  },
  GENERATION_CONTENU: {
    REDACTION_BLOG: "i9j0k1l2-m3n4-o5p6-q7r8-s9t0u1v2w3x4",
    GENERATION_EMAIL: "m3n4o5p6-q7r8-s9t0-u1v2-w3x4y5z6a7b8"
  }
}

2. Gestion d'Erreurs

// ✅ Bon: Logging non-bloquant
async function appelLLMSecurise() {
  try {
    const result = await openai.chat.completions.create({...})
    
    // Logger de manière asynchrone sans bloquer
    logLLMRun({...}).catch(err => 
      console.error("Échec logging:", err)
    )
    
    return result
  } catch (error) {
    // Gérer les erreurs LLM séparément des erreurs de logging
    throw error
  }
}

3. Optimisation Performance

// ✅ Bon: Logging par batch pour haut débit
class BatchLogger {
  private batch: any[] = []
  private batchSize = 10
  private flushInterval = 5000
  
  constructor() {
    setInterval(() => this.flush(), this.flushInterval)
  }
  
  async log(params: any) {
    this.batch.push(params)
    if (this.batch.length >= this.batchSize) {
      await this.flush()
    }
  }
  
  private async flush() {
    if (this.batch.length === 0) return
    
    const currentBatch = [...this.batch]
    this.batch = []
    
    try {
      await Promise.all(currentBatch.map(params => logLLMRun(params)))
    } catch (error) {
      console.error("Échec logging par batch:", error)
    }
  }
}

Dépannage

Problèmes Courants

1. Erreurs de Clé API

Error: Missing API key

Solution: Vérifiez que vos variables d'environnement sont correctement définies.

2. Timeouts Réseau

Error: Request timeout after 30000ms

Solution: Vérifiez l'URL de votre endpoint API et la connectivité réseau.

3. Rate Limiting

Error: Too many requests (429)

Solution: Implémentez un backoff exponentiel ou réduisez la fréquence des requêtes.

4. IDs Feature/Step Invalides

Error: Feature ID not found

Solution: Vérifiez que les IDs existent dans votre dashboard et correspondent exactement.

5. Contraintes de Schéma

Error: null value in column "prompt_template" violates not-null constraint

Solution: Assurez-vous que tous les champs requis sont fournis dans votre appel logLLMRun.

Mode Debug

// Activer le logging de debug
process.env.LLM_LOGGER_DEBUG = "true"

// Vérifier ce qui est envoyé
import { logLLMRun } from '@llmeval/sdk'

// Ceci loggera les détails de la requête dans la console
await logLLMRun({...})

Health Check

async function healthCheck() {
  try {
    // Test de l'endpoint de santé
    const response = await fetch('https://llmeval-production.up.railway.app/v1/health')
    const data = await response.json()
    console.log("✅ API Gateway OK:", data)
    
    // Test de la clé API
    await verifyApiKey()
    console.log("✅ Clé API OK")
    
    // Test de création de trace
    const traceId = await traceManager.createTrace()
    console.log("✅ Création trace OK:", traceId)
    
    console.log("🟢 LLMeval SDK est opérationnel")
  } catch (error) {
    console.error("❌ Health check échoué:", error)
  }
}

Exemples de Test

// Test complet du SDK
import { logLLMRun } from '@llmeval/sdk'

async function testSDK() {
  try {
    const result = await logLLMRun({
      promptTemplate: "Test SDK: {input}",
      inputVars: { input: "Hello World" },
      feature_id: "112cb182-0a67-4e5b-9d89-51377b1ca23b",
      step_id: "ec3cd05a-a819-4c43-85bb-bb931eafd7fb",
      prompt_name: "test-sdk",
      prompt_version: "1.0",
      output: "Test réussi !",
      raw_response: { test: true },
      model: "gpt-4",
      provider: "openai",
      temperature: 0.7,
      business_metadata: {
        tokens_input: 10,
        tokens_output: 5,
        tokens_total: 15,
        duration_ms: 1000,
        test: true
      }
    })
    
    console.log("✅ Test SDK réussi:", result)
  } catch (error) {
    console.error("❌ Test SDK échoué:", error)
  }
}

Support


Dernière mise à jour: Décembre 2024 Version SDK: 1.0.0 Status: Production Ready ✅