@llmeval/sdk
v1.0.3
Published
Zero-dependency SDK for logging and tracking LLM interactions with LLMeval
Maintainers
Readme
📊 LLMeval SDK - Documentation Complète
🚀 Maintenant disponible sur npm ! Installez simplement avec
npm install @llmeval/sdk
Table des Matières
- Vue d'ensemble
- Démarrage Rapide
- Installation
- Configuration API
- Utilisation de Base
- Traçage Avancé
- Intégrations
- Référence API
- Bonnes Pratiques
- Dépannage
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/sdkCaracté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 applicationConfiguration API
Obtenir une Clé API
- Contactez l'Administrateur - Demandez l'accès à votre administrateur LLMeval
- Dashboard Web - Visitez
https://llmeval-seven.vercel.app/api-keys - 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 templatefeature_id: string- Identifiant de feature depuis le dashboardstep_id: string- Identifiant de step depuis le dashboardprompt_name: string- Nom lisible du promptprompt_version: string- Version du prompt templateoutput: string- Réponse textuelle du modèleraw_response: object- Objet de réponse API completmodel: 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 tracespan_name?: string- Nom métier de cette opérationparent_span_id?: string- ID de l'opération parentespan_metadata?: object- Métadonnées du spanapiKey?: string- Surcharger la clé API par défautapiEndpoint?: string- Surcharger l'endpoint par défaut
Champs Générés Automatiquement:
run_id- UUID unique généré automatiquementtimestamp- Timestamp ISO généré automatiquementprompt_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, retournetrace_idstartSpan(config)- Démarrer nouveau span, retournespan_idendSpan(span_id, status?, error_message?)- Terminer un spangetCurrentTrace()- Obtenir l'ID de trace activegetCurrentSpan()- 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 spanspan_type: SpanType- Type:'function_call' | 'tool_call' | 'retrieval'trace_id?: string- Trace à attacherparent_span_id?: string- Span parentmetadata?: 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 keySolution: Vérifiez que vos variables d'environnement sont correctement définies.
2. Timeouts Réseau
Error: Request timeout after 30000msSolution: 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 foundSolution: 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 constraintSolution: 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
- Dashboard: https://llmeval-seven.vercel.app
- API Gateway: https://llmeval-production.up.railway.app
- Health Check: https://llmeval-production.up.railway.app/v1/health
- Repository: https://github.com/llmeval/llmeval
- Issues: https://github.com/llmeval/llmeval/issues
Dernière mise à jour: Décembre 2024 Version SDK: 1.0.0 Status: Production Ready ✅
