Skip to main content
Alye

alye (Python)

Records every LLM call your services make: model, tokens, latency, time to first token, errors and retries. You add two lines at startup; calls through the official OpenAI, Anthropic and Google GenAI clients are recorded automatically.

pip install alye

Set your API key as an environment variable (keep it out of your code):

export ALYE_API_KEY=alye_live_...      # from Setup in Alye
import alye

alye.init(
    service="support-reply",          # one name per microservice
    env="production",
)

That is all most services need. Calls are sent to Alye at https://api.alye.ai/ingest; set ALYE_ENDPOINT only if you run your own server. Without an API key the SDK just prints calls to the console. Python 3.10+, no required dependencies.

LangChain

Nothing extra to set up. langchain-openai and langchain-anthropic call the official clients, so after alye.init() every model call is recorded: invoke, stream, batch, async calls, chains, with_structured_output and embeddings.

import alye
from langchain_openai import ChatOpenAI

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

llm = ChatOpenAI(model="gpt-5-mini")
llm.invoke("Summarise this ticket: ...")   # recorded with model, tokens, latency and cost

What gets recorded

One span per model call, using the OpenTelemetry GenAI attribute names:

Attribute Meaning
gen_ai.provider.name openai, anthropic, gcp.gemini, gcp.vertex_ai, azure.ai.openai, deepseek, ...
gen_ai.operation.name chat, responses, embeddings, generate_content
gen_ai.request.model / gen_ai.response.model Model asked for / model that answered
gen_ai.usage.input_tokens / output_tokens Tokens, including cached input and reasoning output
gen_ai.usage.cache_read.input_tokens, cache_creation.input_tokens, reasoning.output_tokens Token breakdown when the provider reports it
gen_ai.response.finish_reasons, gen_ai.response.id Why the model stopped
gen_ai.response.time_to_first_chunk Seconds to first streamed chunk
error.type rate_limited, timeout, overloaded, server_error, auth, invalid_request, connection_error, ...
alye.retries, alye.retry_statuses Retries the provider SDK made inside one call (e.g. after a 429)
alye.usage_unknown The call returned but the provider sent no usage; it may still have been billed
alye.customer_id, session.id, gen_ai.agent.name, alye.data_source From alye.context()
gen_ai.input.messages / gen_ai.output.messages Prompt and response, only with capture_content=True

Failed calls are recorded even if your code catches the exception. Cost is worked out on the server from a versioned price list, so prices are never stale in your deployed code.

Supported automatically:

  • openai: chat.completions.create and .parse (structured outputs), responses.create and .parse, embeddings.create; sync and async; stream=True; .with_raw_response. Also OpenAI-compatible providers used through the same client.
  • anthropic: messages.create and beta.messages.create (sync, async, stream=True, .with_raw_response) and the messages.stream() helper.
  • google-genai: models.generate_content, generate_content_stream, embed_content; Gemini API and Vertex AI; sync and async.

Tag calls (optional)

with alye.context(customer_id="northwind", session_id=ticket.id, agent="reply-agent"):
    answer = run_agent(ticket)          # every call inside is tagged

Tags flow through await. For new threads, start them with contextvars.copy_context().run(...). In middleware you can use alye.set_tags(...) instead of a with block. Customer tagging is only needed if your app serves several customers; without it, everything is grouped by service.

Group steps into one trace (optional)

@alye.trace()                         # or alye.trace("answer_ticket")
def answer_ticket(ticket): ...

with alye.span("retrieve_context", data_source="support-kb"):
    docs = search(query)

Model calls made inside become children of the span, so one agent run shows as a single trace with its steps.

Providers that are not patched

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

Settings

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

init() argument Environment variable Default
api_key ALYE_API_KEY none
service ALYE_SERVICE or OTEL_SERVICE_NAME unknown_service (with a warning)
env ALYE_ENV production
endpoint ALYE_ENDPOINT https://api.alye.ai/ingest when an API key is set; without a key, calls are printed to the console
region ALYE_REGION none: where calls are processed, for regional pricing (eu, us, or a region like europe-west4); Bedrock and Vertex regions are detected automatically
capture_content ALYE_CAPTURE_CONTENT False
redact_pii ALYE_REDACT_PII True
sample_rate ALYE_SAMPLE_RATE 1.0 (errors are always kept)
exporter ALYE_EXPORTER otlp (console, none, or an exporter object)
instrument every installed provider (e.g. ["openai"])
debug ALYE_DEBUG False
disabled ALYE_DISABLED False
release ALYE_RELEASE none

Other options: redact_patterns=[(regex, replacement)], redact=callable, max_content_chars (8000), inject_stream_usage (True: asks OpenAI streams to include usage), flush_interval (2 s), max_batch (256), max_queue (10,000), timeout (5 s), heartbeat_interval (60 s), resource_attributes.

Privacy

  • Prompts and responses are not sent unless you set capture_content=True.
  • When they are, emails, phone numbers, card numbers (Luhn-checked) and API keys are replaced with [EMAIL], [PHONE], [CARD], [SECRET] before anything leaves the process. Add your own patterns with redact_patterns.
  • Content is truncated to max_content_chars.

How it sends data

  • Spans are queued in memory and sent in batches from a background thread, so your request path never waits on the network. If the queue fills up, new spans are dropped and counted rather than slowing you down.
  • Format: OTLP/HTTP JSON, gzip, to {endpoint}/v1/traces with an x-api-key header. Retries 429 and 5xx with backoff.
  • A heartbeat goes to {endpoint}/v1/heartbeat every 60 s with the SDK version, patched libraries and queue stats, so the backend can tell a quiet service from one that stopped reporting.
  • Works with forking servers (gunicorn, uWSGI): workers restart their own sender after fork.
  • In short-lived scripts, jobs and serverless handlers, call alye.flush() before exiting. atexit also flushes on normal exit.

Local development

python -m alye.devserver           # prints every call it receives
ALYE_ENDPOINT=http://localhost:4318 python your_service.py

Without an API key (and no endpoint) the SDK prints one line per call to stderr, so nothing leaves your laptop.

Tests

python -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/pytest

The tests run the real openai, anthropic and google-genai clients against fake HTTP servers, so no API keys or network are needed.

License

Apache License 2.0. Copyright Onruyl Private Limited. See LICENSE.

Metadata

Release files for alye 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for alye 0.1.1
File Size Uploaded Provenance
alye-0.1.1.tar.gz 38.8 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for alye 0.1.1
File Interpreter ABI Platform
alye-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 79.0 kB

Release files / alye-0.1.1.tar.gz

Download URL alye-0.1.1.tar.gz
Size 38.8 kB
Tags Source
SHA-256 checksum
How to use checksums
b5cec833ebb24607573604d44806356e8651614ba6a536caaf334f7b16fc84ee
BLAKE2b-256 checksum
How to use checksums
6608303e2234b3c0454be0f8d35b9be43e6ebacf8b069fdf676c56efea679544
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.2

Release files / alye-0.1.1-py3-none-any.whl

Download URL alye-0.1.1-py3-none-any.whl
Size 40.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
268f4b4b800cfd454bc629e7c1df7416ec3aaece7aaa85e6bc6947ccc7575aa3
BLAKE2b-256 checksum
How to use checksums
aded4352378d581477d4fef0888e785582340d995fd1edbd1df732c6b1b69d0f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.2

Security for alye 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page