Skip to main content

Agent economics SDK — track ROI, KPIs, and cost for AI agents in 2 lines of code

Project description

VelenAI SDK

Agent economics in 2 lines of code. Track ROI, KPIs, and cost for your AI agents.

Langfuse is the debugger. VelenAI is the CFO.

Install

pip install velenai

With provider wrappers and framework adapters:

pip install velenai[anthropic]  # Anthropic auto-capture
pip install velenai[openai]     # OpenAI auto-capture
pip install velenai[crewai]     # CrewAI adapter
pip install velenai[langgraph]  # LangGraph adapter
pip install velenai[autogen]    # AutoGen adapter

Quick Start — Agent Capture

Three lines to instrument your entire application:

import velenai

velenai.install(api_key="vai_...", api_secret="vas_...")

@velenai.agent(id="research", description="Research agent")
def run_research(query: str):
    client = OpenAI()  # automatically captured
    return client.chat.completions.create(model="gpt-4o", messages=[...])

install() patches provider classes process-wide. Every OpenAI() or Anthropic() client created after install() is automatically tracked — no wrapping needed.

Async works too

@velenai.agent(id="writer", description="Content writer", tier="production")
async def write_content(brief: str):
    client = AsyncOpenAI()
    return await client.chat.completions.create(model="gpt-4o", messages=[...])

Imperative registration

For agents created from config or at runtime:

import velenai

for agent_def in load_agents_from_yaml():
    velenai.register_agent(
        id=agent_def["name"],
        description=agent_def["role"],
        division=agent_def["division"],
        tier="production",
        owner="team@example.com",
        tags={"llm_server": agent_def["llm_server"]},
    )

Context manager

For code that can't use decorators (callbacks, event handlers):

from velenai import agent_context

def on_message(event):
    with agent_context("chat-handler"):
        # LLM calls inside this block are attributed to "chat-handler"
        response = client.chat.completions.create(...)

agent_context() sets identity but does not register the agent. Call register_agent() first if you need metadata on the backend.

Identity resolution

When an LLM call happens, the SDK resolves agent identity in this order:

  1. Explicit agent_id on wrap_openai(client, vai, agent_id="...") — highest priority
  2. ContextVar from @agent() decorator or agent_context()
  3. default_agent_id from install(default_agent_id="fallback")
  4. Module inference — derives an ID from the calling module's __name__

Undecorated calls still get tracked; they just use the fallback identity.

Coexistence with explicit wrap

If you have existing wrap_openai() calls, they continue working alongside install(). The explicit agent_id on a wrapped client always wins over the ContextVar:

import velenai
from velenai.providers import wrap_openai

velenai.install(api_key="vai_...", api_secret="vas_...")

# This client uses agent_id from the decorator/context
auto_client = OpenAI()

# This client always reports as "legacy-agent" regardless of context
legacy_client = wrap_openai(OpenAI(), vai, agent_id="legacy-agent")

Healthcheck with status()

import velenai

info = velenai.status()
# {
#   "installed": True,
#   "enabled": True,
#   "patched_providers": ["openai", "anthropic"],
#   "registered_agents": ["research", "writer"],
#   "pending_telemetry_count": 3,
#   "last_flush_at": "2026-04-09T12:00:00Z",
#   ...
# }

Debugging with is_wrapped()

from velenai import is_wrapped

client = OpenAI()
assert is_wrapped(client)  # True after install()

ThreadPoolExecutor (Python < 3.12)

On Python < 3.12, ThreadPoolExecutor.submit() does not propagate contextvars. Use the helper:

from concurrent.futures import ThreadPoolExecutor
from velenai import submit_with_context

executor = ThreadPoolExecutor(max_workers=4)

@velenai.agent(id="parallel-worker")
def do_work(item):
    return client.chat.completions.create(...)

# Propagates agent context to the worker thread
future = submit_with_context(executor, do_work, item)

Known limitation: module-level clients

install() patches provider classes. Clients constructed before install() runs are not patched:

from openai import OpenAI

client = OpenAI()  # created at import time — NOT captured

import velenai
velenai.install(...)  # too late for the client above

Workaround: Call install() before any provider imports, or wrap pre-existing clients explicitly with wrap_openai(client, vai).

Environment variables

Instead of passing credentials to install():

export VELENAI_API_KEY=vai_...
export VELENAI_API_SECRET=vas_...
export VELENAI_HOST=http://localhost:8000
import velenai
velenai.install()  # picks up from env

Other env vars: VELENAI_DISABLED=true, VELENAI_DEFAULT_AGENT_ID=fallback, VELENAI_DETECT_PREEXISTING=false.

Advanced — Explicit Wrap API

For fine-grained control, wrap individual clients manually:

from velenai import VelenAI
from velenai.providers import wrap_openai

vai = VelenAI(api_key="your-key", api_secret="your-secret", host="http://localhost:8000")
client = wrap_openai(OpenAI(), vai, agent_id="my-agent")
response = client.chat.completions.create(model="gpt-4o", ...)

Decorator (explicit)

@vai.track("my-agent")
def run_agent(query: str):
    result = llm.chat(query)
    return result

Context Manager (explicit)

with vai.trace("my-agent") as t:
    result = do_work()
    t.success = True
    t.tokens = 150
    t.cost_usd = 0.003
    t.metadata["model"] = "gpt-4"

Manual emit

vai.emit(
    agent_id="my-agent",
    success=True,
    latency_ms=1200,
    tokens=150,
    cost_usd=0.003,
    metadata={"model": "gpt-4"},
)

Provider wrappers

from anthropic import Anthropic
from velenai.providers import wrap_anthropic

client = wrap_anthropic(Anthropic(), vai=vai)
response = client.messages.create(model="claude-sonnet-4-20250514", ...)
from openai import OpenAI
from velenai.providers import wrap_openai

client = wrap_openai(OpenAI(), vai=vai)
response = client.chat.completions.create(model="gpt-4o", ...)

Framework Adapters

CrewAI

from velenai_sdk import VelenAI
from velenai_sdk.adapters.crewai import VelenAICrewCallback

vai = VelenAI()
crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, write_task],
    callbacks=[VelenAICrewCallback(vai)]
)

LangGraph

from velenai_sdk import VelenAI
from velenai_sdk.adapters.langgraph import instrument_graph

vai = VelenAI()
graph = StateGraph(...).compile()
graph = instrument_graph(vai, graph, agent_id="my-workflow")
result = graph.invoke(input)

Or as a LangChain callback:

from velenai_sdk.adapters.langgraph import VelenAILangGraphCallback

result = graph.invoke(input, config={"callbacks": [VelenAILangGraphCallback(vai)]})

AutoGen

from velenai_sdk import VelenAI
from velenai_sdk.adapters.autogen import instrument_agent

vai = VelenAI()
assistant = AssistantAgent("analyst", llm_config=llm_config)
assistant = instrument_agent(vai, assistant)

Features

  • Sync and async support
  • Background batching (events queued and flushed every 5s)
  • HMAC-signed requests (replay-protected)
  • Fire-and-forget (never blocks your agent)
  • Agent Capture: process-wide auto-instrumentation with install()
  • Framework adapters: CrewAI, LangGraph, AutoGen
  • Zero dependencies beyond httpx
  • Self-hosted

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

velenai-0.8.1.tar.gz (39.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

velenai-0.8.1-py3-none-any.whl (39.1 kB view details)

Uploaded Python 3

File details

Details for the file velenai-0.8.1.tar.gz.

File metadata

  • Download URL: velenai-0.8.1.tar.gz
  • Upload date:
  • Size: 39.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.16.5 cpython/3.10.12 HTTPX/0.28.1

File hashes

Hashes for velenai-0.8.1.tar.gz
Algorithm Hash digest
SHA256 219837dc9c894204d657d95f73a1092357a886592c466646e9256bbcc174e507
MD5 6661150cb8d8260e79a00d5bbf6e5aaa
BLAKE2b-256 8c57d9ba331b392d60b355376786e6cfd837fae47ce0e21322ab6326b751d527

See more details on using hashes here.

File details

Details for the file velenai-0.8.1-py3-none-any.whl.

File metadata

  • Download URL: velenai-0.8.1-py3-none-any.whl
  • Upload date:
  • Size: 39.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.16.5 cpython/3.10.12 HTTPX/0.28.1

File hashes

Hashes for velenai-0.8.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1ce5eff09d3ad8ff0a332dfde8d76b6b2ed30198a40d666748d17ad93d29b937
MD5 be46fad300397689ecfc1bd20ce43123
BLAKE2b-256 6f9792a3a970cf2be999756cf267499fe8b1565fed703c627a84d2c75fe49fd7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page