Skip to content
Docs

Your first LLM trace in about five minutes.

Add Alye to each service and every model call is recorded with its tokens, cost, latency and errors. Python, Node.js and Java, with LangChain, Express and NestJS.

  1. 1
    Create an SDK keySign up free, open Setup and set the key as ALYE_API_KEY, never in code.
  2. 2
    Add two lines to each serviceGive each service a name. Calls through the OpenAI, Anthropic and Gemini clients are recorded from then on.
  3. 3
    Run itCalls appear on the Overview within a few seconds.
# pip install alye
import alye
from openai import OpenAI

alye.init(service="support-reply", env="production")   # reads ALYE_API_KEY

client = OpenAI()   # official clients are recorded automatically
Works withOpenAIAnthropicGeminiVertex AIBedrockAzure OpenAIDeepSeekMistralGroqOpenRouter
Install

Python#

Python 3.10 or newer. Calls through the official OpenAI, Anthropic and Google GenAI clients are recorded automatically, sync and async, streaming included.

terminal
pip install alye
export ALYE_API_KEY=alye_live_...
app.py
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.

terminal
npm install alye
app.ts
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.

Java#

Java 11 or newer, from Maven Central, with no other dependencies. Sync, async and streaming calls are recorded.

<dependency>
  <groupId>ai.alye</groupId>
  <artifactId>alye</artifactId>
  <version>0.1.0</version>
</dependency>
App.java
Alye.init(AlyeOptions.builder()
        .service("support-reply")
        .env("production")
        .build());

OpenAIClient openai = Alye.wrap(OpenAIOkHttpClient.fromEnv());
AnthropicClient claude = Alye.wrap(AnthropicOkHttpClient.fromEnv());
For Azure OpenAI or an OpenAI-compatible provider, name it so the right prices apply: Alye.wrap(client, "azure.ai.openai").

Frameworks

LangChain#

Every call a LangChain chat model makes is recorded: chains, streaming, batches, tools, agents (LangGraph) and structured output. That covers every provider, including Bedrock, Gemini, Mistral, Groq and Ollama, and a call is never counted twice.

# Nothing extra: alye.init() covers every LangChain chat model.
alye.init(service="support-reply")

llm = ChatOpenAI(model="gpt-5-mini")        # or ChatBedrockConverse, ChatMistralAI, ...

Python and Node.js: SDK 0.1.2 or newer for every provider (0.1.1 covers OpenAI, Azure OpenAI and Anthropic models).

Express#

Each request becomes one trace with its model calls inside. Requests that made no model calls aren't sent.

server.ts
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.

app.module.ts
import { AlyeModule } from "alye/nestjs";

@Module({ imports: [AlyeModule.forRoot({ service: "orders-api" })] })
export class AppModule {}

Use it

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.

with alye.context(customer_id="northwind", session_id=ticket.id):
    answer = run_agent(ticket)

Tags carry through every call made inside, so cost and errors show per customer.

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)

Other providers#

For a provider without a wrapper, record the call yourself with the token counts from its response.

with alye.llm_call("voyage", "voyage-3", operation="embeddings") as call:
    r = vo.embed(texts, model="voyage-3")
    call.set_usage(input_tokens=r.total_tokens, output_tokens=0)

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.

Where alerts go: 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.

.env
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.alye.ai/ingest
OTEL_EXPORTER_OTLP_PROTOCOL=http/json
OTEL_EXPORTER_OTLP_HEADERS="x-api-key=alye_live_..."

Reference

Settings#

Every option can be passed to init() or set as an environment variable.

Environment variableWhat it doesDefault
ALYE_API_KEYYour SDK keyNone: calls print to the console
ALYE_SERVICEOne name per microserviceunknown_service
ALYE_ENVEnvironmentproduction
ALYE_CAPTURE_CONTENTSend prompts and responses, masked (Python and Node.js)Off
ALYE_REDACT_PIIMask emails, phones, cards and keysOn
ALYE_SAMPLE_RATEShare of traces to keep; failures always kept (Python and Node.js)1
ALYE_REGIONWhere calls run, for regional pricesDetected for Bedrock and Vertex AI
ALYE_DISABLEDTurn the SDK offOff

Privacy#

  • Prompts and responses are not sent unless you turn on content capture. The Java SDK never sends them.
  • 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.

Next steps

Using an AI coding assistant? Point it at alye.ai/llms.txt for setup steps in Python, Node.js and Java.