SDKs
Envía trazas desde tu aplicación con los SDKs livianos de Verica.
Verica publica tres SDKs de observabilidad — TypeScript, Python y Ruby — que instrumentan tus llamadas a LLMs y las exportan como trazas.
Los tres comparten el mismo diseño:
- Exportan OTLP a
{endpoint}/v1/tracesconAuthorization: Bearer {token}, por lotes. - El token es un token de API de Verica con permiso de ingesta. El botón Conectar aplicación, en Observabilidad, lo crea y arma el snippet por vos.
- Fallan hacia afuera, no hacia adentro: si Verica no responde o el token es inválido, los spans se descartan y tu aplicación no se entera. Los errores de exportación son silenciosos salvo que actives
debug. - Nunca envían el costo: Verica lo calcula al ingerir, a partir del modelo y los tokens. Por eso el modelo es lo que hace falta para que una traza quede costeada.
Instalación e inicialización
npm install @verica-app/observabilityCon CommonJS, inicializa antes de requerir los clientes: OpenAI y Anthropic se instrumentan solos.
const { init, wrapGoogleGenAI } = require('@verica-app/observability');
init({ token: process.env.VERICA_TOKEN });
// Requiere los clientes DESPUÉS de init.
const OpenAI = require('openai');
const Anthropic = require('@anthropic-ai/sdk');
// Gemini no tiene instrumentación comunitaria: se envuelve.
const { GoogleGenAI } = require('@google/genai');
const ai = wrapGoogleGenAI(new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY }));Con ESM los imports no se pueden parchear después, así que se le pasa el módulo:
import { init } from '@verica-app/observability';
import * as openai from 'openai';
init({ token: process.env.VERICA_TOKEN, openaiModule: openai });pip install verica-observabilityimport os
import verica
verica.init(token=os.environ["VERICA_TOKEN"])
# Importa los clientes DESPUÉS de init para que queden instrumentados.
from openai import OpenAI
from anthropic import Anthropic
from google import genaiPython es el único de los tres con instrumentación nativa de Gemini. Cada instrumentador es opcional: si falta uno, la inicialización sigue adelante igual.
gem install verica-observabilityLa gema oficial de openai no tiene instrumentación automática en ningún lado; este SDK la trae.
require 'verica'
Verica.init(token: ENV['VERICA_TOKEN'])
client = Verica.wrap_openai(OpenAI::Client.new)Para la gema comunitaria ruby-openai, un solo envoltorio cubre OpenAI, Gemini y Anthropic a través de sus endpoints compatibles con OpenAI — el proveedor se deduce del modelo:
openai = Verica.wrap_openai_compatible(OpenAI::Client.new(access_token: ENV['OPENAI_API_KEY']))
gemini = Verica.wrap_openai_compatible(OpenAI::Client.new(
access_token: ENV['GEMINI_API_KEY'],
uri_base: 'https://generativelanguage.googleapis.com/v1beta/openai/'
))
anthropic = Verica.wrap_openai_compatible(OpenAI::Client.new(
access_token: ENV['ANTHROPIC_API_KEY'],
uri_base: 'https://api.anthropic.com/v1/'
))Con RubyLLM y su instrumentación de OpenTelemetry, alcanza con Verica.init.
Opciones
Las mismas en los tres, con la convención de nombres de cada lenguaje:
| Opción | Variable de entorno | Por defecto | Para qué |
|---|---|---|---|
token | VERICA_TOKEN | (obligatorio) | Token de API con permiso de ingesta. |
endpoint | VERICA_ENDPOINT | https://ingest.verica.app | Cámbialo solo en instalaciones propias. |
captureContent | VERICA_CAPTURE_CONTENT | true | Enviar el contenido de prompts y respuestas. |
conversationId | — | (ninguno) | Agrupa las trazas en una sesión. |
tags | — | (ninguno) | Tags globales para todas las trazas. |
serviceName | OTEL_SERVICE_NAME | app | El nombre del servicio en la traza. |
debug | VERICA_DEBUG | false | Registrar los errores de exportación. |
Ruby suma stream_usage (VERICA_STREAM_USAGE, false), que inyecta stream_options para obtener tokens en respuestas por streaming.
captureContent, las trazas llegan solo con metadata: modelo, proveedor, tokens, costo y latencia. Siguen siendo evaluables con span_check, pero no con criterios sobre el texto.Árboles de spans: agentes y herramientas
Una llamada suelta se traza sola. Para un agente — varias llamadas y varias herramientas en un mismo turno — envuelve el bucle una vez y cada ejecución de herramienta una vez. Todo cae bajo la misma traza, con su árbol de spans.
const { withSpan, withTool } = require('@verica-app/observability');
const answer = await withSpan('agent.run', async () => {
await client.chat.completions.create({ /* … */ }); // span hijo
const weather = withTool('lookup_weather', () => lookupWeather('Cordoba'));
return client.chat.completions.create({ /* … */ }); // span hijo
});with verica.span("agent.run"):
client.chat.completions.create(...) # span hijo
with verica.tool("lookup_weather"):
weather = lookup_weather("Cordoba")
client.chat.completions.create(...) # span hijoVerica.with_span('agent.run') do
client.chat.completions.create(model: 'gpt-4o-mini', messages: plan_messages)
weather = Verica.with_tool('lookup_weather') { lookup_weather('Cordoba') }
client.chat.completions.create(model: 'gpt-4o-mini', messages: reply_messages(weather))
endDetalles que valen para los tres:
- El nombre del span se usa literal: es lo que van a matchear los patrones de
span_check. - Devuelven lo que devuelve el bloque, sin tocarlo.
- Una excepción marca el span como error y se vuelve a lanzar sin cambios. Esos spans son los que cuenta la comprobación Spans con error.
- Sin haber llamado a
init, la función simplemente ejecuta el bloque y no emite nada.
Como el exportador manda los spans a medida que terminan, un agente largo llega en varios envíos; Verica los fusiona en una sola traza.
Sesiones multi-turno
Para que Verica agrupe los turnos de una conversación, cada llamada tiene que llevar el mismo identificador de sesión.
import { init, withConversation, withTags } from '@verica-app/observability';
init({ token: process.env.VERICA_TOKEN!, tags: ['web', 'prod'] });
const reply = await withTags(['chat', 'premium'], () =>
withConversation(`chat-${chatId}`, () => openai.chat.completions.create({ /* … */ })),
);El alcance usa AsyncLocalStorage, así que sobrevive a los await y nunca se filtra entre peticiones concurrentes.
with verica.conversation(f"chat-{chat_id}"):
response = client.chat.completions.create(...)
with verica.tags(["chat", "premium"]):
client.chat.completions.create(...)El alcance usa contextvars, así que es seguro con async y con hilos.
class MessagesController < ApplicationController
def create
Verica.with_conversation("chat-#{conversation.id}") do
reply = openai.chat(parameters: {
model: 'gpt-4o-mini',
messages: conversation.to_openai_messages
})
# …persistir y renderizar la respuesta…
end
end
endEl alcance es local al hilo y anidable; se restaura al salir aunque el bloque lance una excepción.
Los tags por petición se suman a los globales en lugar de reemplazarlos, y los bloques anidados acumulan. El identificador de conversación, en cambio, reemplaza al global dentro de su bloque; pasarle null (o None, o vacío) suprime el atributo para ese alcance.
Streaming (Ruby)
El envoltorio wrap_openai_compatible captura las respuestas por streaming: acumula los fragmentos mientras fluyen hacia tu bloque y después anota el span con la salida completa. Tu bloque recibe todos los fragmentos, en orden y sin modificar.
Los tokens y el costo solo existen si el proveedor manda un fragmento final de uso:
client.chat(parameters: {
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: '¡Hola!' }],
stream_options: { include_usage: true },
stream: proc { |chunk| print chunk.dig('choices', 0, 'delta', 'content') }
})También puedes usar Verica.init(..., stream_usage: true) y el envoltorio inyecta esa opción en una copia privada de tus parámetros, sin mutar tu hash. Viene apagado por defecto porque ese fragmento extra llega con choices vacío y algunos bloques no lo esperan.
Las llamadas a herramientas se capturan en ambos caminos, incluso cuando el turno no tiene texto.
Entornos serverless
Antes de que se congele el runtime, vacía el búfer:
- TypeScript:
await flush()oawait shutdown() - Python:
verica.flush()overica.shutdown() - Ruby:
Verica.flushoVerica.shutdown
Siguientes pasos
- Trazas y sesiones de producción — qué hacer con lo que llega.
- n8n — la alternativa sin código.