Argosvix
Docs menuSDK reference

SDK Reference

API reference for @argosvix/sdk. Wrap your AI client once with wrap() and every call is recorded automatically.


Install

npm install @argosvix/sdk <provider-sdk>

Use openai, @anthropic-ai/sdk, @google/genai, or @mistralai/mistralai as <provider-sdk> (you can mix multiple providers).


wrap(client, options)

Returns a wrapped instance of your AI client. For typical usage (calling create, consuming streams with for-await) you can drop it in without touching the rest of your code.

OpenAI's advanced APIs mostly work as-is after wrapping: .withResponse(), and streaming .tee() / .toReadableStream() / .controller (abort) all keep working. To use them on streams, set stream_options: { include_usage: true } explicitly (without it, the SDK injects include_usage: true for usage recording and returns a compatibility wrapper, so these methods are absent). Two caveats: .asResponse() (reading the raw Response directly) is not supported because it cannot coexist with recording — use the unwrapped client for those specific calls. And since the full stream is read for recording, slow consumption of a received stream temporarily holds the whole response in memory.

import { wrap } from "@argosvix/sdk";
import OpenAI from "openai";

const openai = wrap(new OpenAI(), {
  apiKey: process.env.ARGOSVIX_API_KEY,
  tags: { service: "my-app", env: "prod" },
});

options.apiKey (nothing is recorded without it)

The argk_... token issued on the API keys page. Pass it from an environment variable. The field is optional at the type level, but when unset the SDK does not raise — it simply skips sending records (your LLM calls still pass through). If you wrapped a client and see no records, check this first.

options.tags (optional)

Arbitrary key/value pairs you can use as filter and aggregation axes on the dashboard. Common examples:

tags: {
  service: "my-app",     // split multiple services in one dashboard
  env: "prod",           // separate staging vs prod counts
  feature: "summarize",  // compare cost / latency per feature
  userId: "u_xxx",       // per-user usage measurement (PII caution — hash recommended)
}

⚠️ Do not put end-user PII (email, name, etc.) into tags (see Terms § 4, paragraph 2).

options.sessionId (optional)

An ID that groups calls belonging to the same session (conversation). When set, it is stored on each record as sessionId and can be used to filter by session in the query API. In the Python SDK, pass ArgosvixConfig(session_id="...").

const openai = wrap(new OpenAI(), {
  apiKey: process.env.ARGOSVIX_API_KEY,
  sessionId: "sess_2026_07_08_abc",
});

options.captureContent (optional, default false)

When true, prompt and completion bodies are included in the records. Non-streaming calls are supported across all 4 providers; streaming calls also capture bodies on all observed paths (OpenAI Chat / OpenAI Responses / Anthropic / Gemini / Mistral, in both TypeScript and Python), accumulating up to 256KB per call — anything beyond is dropped and a truncated marker is appended, and if a stream is interrupted the body captured so far is still recorded.

ℹ️ Server-side ingestion applies a separate 64KB limit to each of the prompt body and completion body. The SDK automatically truncates each body to fit before sending (the excess is dropped and a truncated marker is appended), so a record with an oversized body is still delivered — it is never lost.

When a response contains tool calls (function calling), the tool names and arguments are also recorded as toolCalls (OpenAI Chat Completions / Responses calls only; PII inside arguments is masked under the same rules as bodies).

const openai = wrap(new OpenAI(), {
  apiKey: process.env.ARGOSVIX_API_KEY,
  captureContent: true,
});

In the Python SDK, pass capture_content=True.

from argosvix import wrap, ArgosvixConfig

client = wrap(OpenAI(), ArgosvixConfig(
    api_key=os.environ["ARGOSVIX_API_KEY"],
    capture_content=True,
))

How it works and how it stays safe:

  • PII masking always runs before sending (emails, card numbers, phone numbers, national IDs, and IP addresses are replaced with [REDACTED_*]).
  • On the server, bodies are stored (AES-256-GCM encrypted) only when the account is on Pro or above and has explicitly opted in to plaintext storage in the dashboard. Without that consent, submitted bodies are discarded, not stored.
  • Viewing, deleting, and access logs for stored bodies are managed under Privacy & Data in the dashboard settings. See Terms Article 4-2 for details.

Deployed prompts and version tagging (resolvePrompt / withPrompt)

Fetch the currently deployed version of a managed prompt from the SDK, and automatically tag calls made with it as prompt ({name}@v{version}) — the foundation for per-version quality and cost comparison.

import { resolvePrompt, withPrompt } from "@argosvix/sdk";

const p = await resolvePrompt("support-bot", {
  apiKey: process.env.ARGOSVIX_API_KEY!,
  // label: "production" (default)
});

await withPrompt(p, async () => {
  // Calls inside get tags.prompt = "support-bot@v3" automatically
  await openai.chat.completions.create({
    model: "gpt-5.5",
    messages: [{ role: "system", content: p.template }],
  });
});
  • resolvePrompt keeps a 60-second TTL cache and falls back to a stale entry on network or server failures, so your hot path never blocks on prompt resolution (a 404 for a missing deployment still throws).
  • An explicit tags.prompt on a call takes precedence over the ambient tag.
  • The Python SDK mirrors this with resolve_prompt(name, api_key=...) and the with_prompt(p): context manager.

flushClient(client)

In short-lived runtimes (Cloudflare Workers, AWS Lambda, Vercel Edge, and similar) you must await flushClient() inside the handler's finally block. Without it, records will not reach the dashboard because the runtime terminates before the SDK finishes sending.

import { wrap, flushClient } from "@argosvix/sdk";

export default {
  async fetch(req, env) {
    // In Workers, read configuration from the fetch handler's env argument, not process.env
    const client = wrap(new OpenAI({ apiKey: env.OPENAI_API_KEY }), {
      apiKey: env.ARGOSVIX_API_KEY,
    });
    try {
      return await handler(client, req, env);
    } finally {
      await flushClient(client);
    }
  },
};

Long-lived processes (Node.js server, Bun, Deno deploy) don't need it. The TypeScript SDK sends automatically once its buffer reaches 100 records, and also runs an idle auto-flush roughly every 5 seconds while records sit in the buffer (flushIntervalMs, default 5000; call flushClient() any time to send earlier); the Python SDK flushes in the background roughly every 5 seconds. wrap() is best-effort by design: a failed send to Argosvix never breaks your application's LLM call.


Framework integrations

If you call models through a framework instead of a provider client directly, use the matching integration. All of them record the same core fields as wrap() (provider, model, tokens, cost, latency, cache tokens; the two TypeScript integrations additionally record TTFT and reasoning tokens), auto-detect the provider, and honour withTrace grouping. Auto-detection works when you use each vendor's official provider package (@ai-sdk/xai, @langchain/deepseek, and so on). If you point an OpenAI-compatible client at another baseURL, the framework still calls itself "openai", so Argosvix records the call as "openai" (the cost is computed from the same pricing table). To record the real provider, pass provider in the config or wrap the provider client directly — that path detects the provider from the destination URL. Calls made through the Vercel AI Gateway are not detected and are not recorded. Pre-call budget/policy gate checks are enforced only by the Vercel AI SDK middleware — the LangChain.js and LiteLLM callbacks record only and do not evaluate gates (wrap the provider client directly if you need gates there). Recording is best-effort — it never breaks your model call.

Vercel AI SDK (ai package)

argosvixMiddleware() returns a wrapLanguageModel-compatible middleware (LanguageModelV2 / V4). Every call through generateText / streamText / generateObject — including calls the AI SDK issues internally for tool loops and multi-step agents — is recorded.

import { openai } from "@ai-sdk/openai";
import { wrapLanguageModel, generateText } from "ai";
import { argosvixMiddleware, flushClient } from "@argosvix/sdk";

const observed = argosvixMiddleware({ apiKey: process.env.ARGOSVIX_API_KEY });
const model = wrapLanguageModel({ model: openai("gpt-5.5"), middleware: observed });

try {
  await generateText({ model, prompt: "hi" });
} finally {
  await flushClient(observed); // or: await observed.flush()
}

Other providers run normally but are not recorded — pass provider in the config to force a mapping.

LangChain.js

argosvixLangChainHandler() returns a callback handler. Pass it in callbacks (per call or at model construction) and every LLM call through LangChain — including chains and agents — is recorded.

import { ChatOpenAI } from "@langchain/openai";
import { argosvixLangChainHandler, flushClient } from "@argosvix/sdk";

const handler = argosvixLangChainHandler({ apiKey: process.env.ARGOSVIX_API_KEY });
const model = new ChatOpenAI({ model: "gpt-5.5" });

try {
  await model.invoke("hi", { callbacks: [handler] });
} finally {
  await flushClient(handler); // or: await handler.flush()
}

LiteLLM (Python)

ArgosvixLogger is a LiteLLM CustomLogger. Register it once and every LiteLLM completion — sync, async, streaming, and failures — is recorded. It works with both the LiteLLM Python SDK and the LiteLLM proxy.

import litellm
from argosvix.litellm_callback import ArgosvixLogger

logger = ArgosvixLogger()  # reads ARGOSVIX_API_KEY from the environment

litellm.callbacks = [logger]
litellm.completion(model="gpt-4o", messages=[{"role": "user", "content": "hi"}])

logger.flush()  # short-lived processes: send buffered records now

For the LiteLLM proxy, put the logger instance in a custom_callbacks.py next to your config.yaml and reference it as litellm_settings.callbacks: custom_callbacks.argosvix_logger.

Cost uses LiteLLM's own response_cost when available (it prices 100+ providers), falling back to the Argosvix pricing table. Providers are mapped to the ingest API vocabulary (xAI Grok records as "xai" and Moonshot Kimi as "moonshot" since 0.5.5, DeepSeek as "deepseek" since 0.6.0 — their real providers; Azure OpenAI still arrives as "openai"); calls to other providers run normally but are not recorded — a one-time warning per provider is printed instead of dropping them silently. Install the optional dependency with pip install 'argosvix[litellm]'.


Covered methods per provider

ProviderCovered methods
OpenAIchat.completions.create, responses.create (both with streaming)
Anthropicmessages.create (with streaming)
Geminimodels.generateContent, models.generateContentStream
Mistralchat.complete, chat.stream
xAI GrokVia the OpenAI-compatible API: wrap() an OpenAI client configured with xAI's baseURL (streaming supported). Since 0.5.5 the destination URL is detected and calls record with provider "xai"
Moonshot KimiVia the OpenAI-compatible API, same pattern as xAI Grok — records with provider "moonshot". For kimi-k3, cache-hit input is priced separately
DeepSeekVia the OpenAI-compatible API, same pattern as xAI Grok — detected by api.deepseek.com and recorded with provider "deepseek". deepseek-v4-flash / deepseek-v4-pro (and the legacy names deepseek-chat / deepseek-reasoner, deprecated 2026-07-24) price cache-hit input separately

Long-context tiered pricing

Some models charge a higher rate once the prompt reaches a size threshold. The higher rate applies to every token in that request, not just the tokens above the threshold. The SDK follows this rule.

ProviderTier applies whenModels
xAIprompt reaches 200,000 (200,000 included)Grok models
OpenAIprompt is above 272,000 (from 272,001)GPT-5.5 / 5.6 family
Googleprompt is above 200,000 (from 200,001)Gemini Pro family

The boundaries differ by one token between providers because the official wording differs ("reaches" vs "greater than"). Cached-input rates also switch to the tier's cached rate.

If a provider reports more cached tokens than the prompt contains, the SDK clamps the cached count to the prompt size so the cost cannot come out inflated.

Example with xAI Grok (Moonshot Kimi works the same way — just swap the baseURL and API key):

import OpenAI from "openai";
import { wrap } from "@argosvix/sdk";

const client = wrap(
  new OpenAI({ apiKey: process.env.XAI_API_KEY, baseURL: "https://api.x.ai/v1" }),
  { apiKey: process.env.ARGOSVIX_API_KEY },
);
// From here, call grok-4.5 etc. exactly like the OpenAI SDK — calls are recorded automatically

A note on interrupting streams early in the Python SDK: breaking out of a synchronous iteration records immediately, but breaking out of async for does not guarantee immediate cleanup due to Python language semantics. To record reliably, consume the stream with async with, or call await stream.aclose() after breaking.

Any other method passes through (not recorded, but errors are not swallowed either). If you want a method outside this list to be recorded, email [email protected].


Types

import type { ArgosvixConfig } from "@argosvix/sdk";

interface ArgosvixConfig {
  /** argk_... token issued on the dashboard (record sending is a no-op when unset) */
  apiKey?: string;
  /** Arbitrary key/value pairs (filter / aggregation axes) */
  tags?: Record<string, string>;
  /**
   * Full POST target URL including the path (default: https://ingest.argosvix.com/v1/ingest).
   * The SDK POSTs to this URL as-is and does not append a path (e.g. proxy or testing; usually not needed)
   */
  endpoint?: string;
}

Troubleshooting

  • No records appear on the dashboard — check that you didn't forget flushClient (in short-lived runtimes), verify network reachability, and double-check the apiKey for typos.
  • Records take a while to appear — sends are batched (TypeScript: at 100 records, on an ~5-second idle auto-flush while records are buffered (flushIntervalMs, default 5000), or on flushClient(); Python: roughly every 5 seconds). For low-traffic processes, call flushClient() / flush() to see records immediately.
  • Cost shows as zero — the pricing table for that provider or model may not be registered yet. Email [email protected] and we can add it.

Support

[email protected]

Last updated: 2026-07-29