Getting started
Alye records every LLM call your services make: model, tokens, cost, latency, time to first token, errors and retries. Setup takes about five minutes per service.
- Create an account and open Setup.
- Create an SDK key. Store it as the environment variable
ALYE_API_KEY, never in code. - Add the SDK to each service and give it a name, as below.
- Run the service. Calls appear on the Overview within a few seconds.
Python
Python 3.10 or newer. Calls through the official OpenAI, Anthropic and Google GenAI clients are recorded automatically, sync and async, streaming included.
pip install alye export ALYE_API_KEY=alye_live_... import alye alye.init(service="support-reply", env="production")
Node.js and TypeScript
Node 20 or newer, ESM or CommonJS, types included. Wrap each client once where you create it.
npm install alye
import * as alye from "alye";
import OpenAI from "openai";
alye.init({ service: "support-reply", env: "production" });
const openai = alye.wrap(new OpenAI());alye.wrap() works with openai (including Azure and OpenAI-compatible providers), @anthropic-ai/sdk and @google/genai.
Express
Each request becomes one trace with its model calls inside. Requests that made no model calls aren't sent.
import { alyeExpress } from "alye/express";
app.use(alyeExpress({ tags: (req) => ({ customerId: req.header("x-customer-id") }) }));NestJS
The module starts Alye and adds an interceptor for HTTP, GraphQL and microservice handlers. Call app.enableShutdownHooks() so queued calls are sent on shutdown.
import { AlyeModule } from "alye/nestjs";
@Module({ imports: [AlyeModule.forRoot({ service: "orders-api" })] })
export class AppModule {}Tag customers
Optional. If your app serves many customers, tag each request with your own customer ID (an account or tenant ID, not a name or email). Tags carry through every await.
# Python with alye.context(customer_id="northwind", session_id=ticket.id): answer = run_agent(ticket) // Node.js await alye.context({ customerId: "northwind" }, () => runAgent(ticket));
Group steps into traces
Wrap an agent run or a job in a span, and the model calls inside become its children, so one run shows as a single trace with its steps.
@alye.trace()
def answer_ticket(ticket): ...
with alye.span("retrieve_context", data_source="support-kb"):
docs = search(query)Alerts
Alerts are set in the app and checked every minute. Watch spend, the monthly forecast, error rate, p95 latency, number of calls, or a service that stops reporting, for all services, each service, one model or one customer. Owners and admins add email, Slack or Microsoft Teams channels under Settings › Alert channels.
OpenTelemetry
Already sending GenAI spans? Point your exporter at Alye. Use the JSON protocol.
OTEL_EXPORTER_OTLP_ENDPOINT=https://llm-api.onruyl.com/ingest OTEL_EXPORTER_OTLP_PROTOCOL=http/json OTEL_EXPORTER_OTLP_HEADERS="x-api-key=alye_live_..."
Settings
Every option can be passed to init() or set as an environment variable.
| Environment variable | What it does | Default |
|---|---|---|
| ALYE_API_KEY | Your SDK key | None: calls print to the console |
| ALYE_SERVICE | One name per microservice | unknown_service |
| ALYE_ENV | Environment | production |
| ALYE_CAPTURE_CONTENT | Send prompts and responses (masked) | Off |
| ALYE_REDACT_PII | Mask emails, phones, cards and keys | On |
| ALYE_SAMPLE_RATE | Share of traces to keep (failures always kept) | 1 |
| ALYE_REGION | Where calls run, for regional prices | Detected for Bedrock and Vertex AI |
| ALYE_DISABLED | Turn the SDK off | Off |
Privacy
- Prompts and responses are not sent unless you turn on content capture.
- When they are, emails, phone numbers, card numbers and API keys are masked inside your service before anything is sent. Add your own patterns if you need more.
- Content is cut to a maximum length, 8,000 characters by default.