Connect your agentView as Markdown

SDK reference

Sessions, named steps, workers, redaction and every option of the Python and Node SDKs.

The SDK records every model call your process makes through a supported client, streamed or not, sync or async, and sends it in the background. It is open source under Apache-2.0: read the source. New here? Start with the Quickstart.

Supported providers

ProviderPythonNode.js
OpenAI, including Azure OpenAI and any OpenAI-compatible base URLopenai (chat completions and responses)openai
Anthropic, including Bedrock, Vertex and beta clientsanthropic@anthropic-ai/sdk
Google Geminigoogle-genai@google/genai
AWS Bedrock Converseboto3 bedrock-runtime (converse, converse_stream)@aws-sdk/client-bedrock-runtime (ConverseCommand, ConverseStreamCommand)
OpenAI Realtime (voice)client.realtime.connect(), sync and async (openai[realtime])OpenAIRealtimeWS and OpenAIRealtimeWebSocket
Anything elseSend OpenTelemetrySend OpenTelemetry

Streams reach your code unchanged and are recorded once, as one assembled reply. Bedrock images and documents are sent as {"omitted": "bytes"}. Providers that are not installed are skipped.

Voice agents

A phone call is one conversation, like a chat. What we record is what was said: the caller's words, the agent's spoken replies, every tool call, and how the call went. Audio itself is never sent, by any of the SDKs: inline audio becomes a marker such as [omitted 48211 bytes sha256:...].

  • OpenAI Realtime. Open the connection inside a session, with the id of the call, and the whole call is recorded as it happens: each reply the agent speaks, what the caller said before it, and tool calls and their results. Turn on input audio transcription in the session, or the caller's side is missing.
  • Audio models over chat completions (gpt-audio and similar). Recorded like any other call. A spoken reply shows its transcript.
  • A voice platform or your own speech-to-text and text-to-speech. The model in the middle is an ordinary client call, so it is recorded already. Pass the call's id as the session so every turn of one call lands together.

On a call, each spoken reply also shows how long the caller waited (from when they stopped speaking to the agent's first word) and whether they cut in. The conversation list calls out replies slower than 2 seconds and calls where the caller cut in.

from openai import AsyncOpenAI
import generalcontext

generalcontext.initialize()
client = AsyncOpenAI()

with generalcontext.session(call.sid, channel="voice"):
    async with client.realtime.connect(model="gpt-realtime") as connection:
        await connection.session.update(session={
            "type": "realtime",
            "audio": {"input": {"transcription": {"model": "gpt-live-transcribe"}}},
        })
        await bridge(call, connection)  # your audio loop, unchanged

Supported versions

PythonNode.js
Runtime3.9 to 3.1318 to 24
OpenAIopenai 1.0.0 and lateropenai 4.0.0 and later
Anthropicanthropic 0.20.0 and later@anthropic-ai/sdk 0.20.0 and later
Google Geminigoogle-genai 1.0.0 and later@google/genai 1.0.0 and later
AWS Bedrock Converseboto3 1.34.116 and later@aws-sdk/client-bedrock-runtime 3.587.0 and later

Each release is tested from these minimums to the latest versions, and the results are in stress/compat/RESULTS.md. A call style a release does not have yet, such as responses in openai 1.0, is simply absent. Each provider package's own Node minimum still applies.

Node.js setup

Pass the provider modules you use in instrumentModules. It is required whenever a bundler is involved, because a bundle can carry its own copy of the client that detection never sees.

ts
import OpenAI from 'openai';
import Anthropic from '@anthropic-ai/sdk';
import * as googleGenAI from '@google/genai';
import * as bedrockRuntime from '@aws-sdk/client-bedrock-runtime';
import { initialize } from 'generalcontext';

initialize({ instrumentModules: { openAI: OpenAI, anthropic: Anthropic, googleGenAI, bedrockRuntime } });

Next.js

Initialize in instrumentation.ts at the project root, keep the provider packages out of the bundle so routes use the patched copy, and use the Node.js runtime. The Edge runtime is not supported.

export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    const { initialize } = await import('generalcontext');
    const { OpenAI } = await import('openai');
    const anthropic = await import('@anthropic-ai/sdk');
    initialize({ appName: 'my-agent', instrumentModules: { openAI: OpenAI, anthropic } });
  }
}

Proxies

Node's built-in fetch ignores HTTPS_PROXY. On Node 24 and later, set NODE_USE_ENV_PROXY=1. On older versions, install a global dispatcher, for example undici's setGlobalDispatcher(new EnvHttpProxyAgent()). If the proxy inspects TLS, point NODE_EXTRA_CA_CERTS at its CA certificate. Python honors HTTPS_PROXY and SSL_CERT_FILE on its own.

Sessions

Calls inside a session are grouped into one conversation, with the id you pass. Sessions follow async work, threads (Python) and promises (Node) in the same process.

with generalcontext.session(ticket.id, channel="email", user_id=customer.id, product="billing", account=company.id):
    handle(ticket)

channel and product are free-form labels you can filter by in the console. Without a session, calls are grouped by their first message, which every later call in the same conversation repeats.

Accounts

If your agent works for several companies of its own, as a platform used by many businesses does, pass account with the id you already use for that company. You can then filter Traces by account. It is optional, up to 120 characters, and trimmed. With OpenTelemetry, set generalcontext.account on the span or resource instead.

Name the steps

Name a step to see which part of your agent each call came from. The conversation page shows the main thread as the timeline, and lists every model call under Every model call, with side calls such as a classifier or a summary marked and labeled by their step. (These are steps inside your agent, not benchmark tasks.)

with generalcontext.task("triage"):
    category = classify(message)

Serverless and background jobs

Calls are sent in batches in the background. In a serverless function, queue worker or script, flush before returning.

generalcontext.flush()  # waits up to 10 seconds; True when everything was sent

When work moves to another process (a queue, a cron job), pass the conversation id along with the job and open the same session there. Calls from both processes land in one conversation.

Redaction and filtering

With redact=True (redact: true in Node), phone numbers, emails, card numbers and SSNs are replaced before anything leaves your process. Each becomes a stable placeholder such as <email:3f9a1c> keyed by GENERALCONTEXT_REDACT_SALT, so the same value reads the same across a conversation without being sent. Redaction also covers outcomes and events.

Credentials are always masked, with or without redact: API keys from OpenAI, Anthropic, Google, AWS, Stripe, GitHub, Slack and General Context, bearer tokens, JWTs and private keys become <openai-key>, <jwt> and so on before anything is sent. Per-request headers and query parameters (extra_headers, extra_query, Gemini's http_options, Node's per-request headers) are never recorded.

For anything else, before_send (beforeSend) receives each call before it is sent. Return it, changed or not, or return nothing to drop it.

def before_send(call):
    call["request"].pop("metadata", None)
    return call

generalcontext.initialize(redact=True, before_send=before_send)

One client only

To record a single client instead of every client in the process, wrap it. Prefer initialize() unless you need this.

client = generalcontext.wrap(OpenAI())

What is sent

For each model call: the request (messages, tools, model and settings), the response, timing, token usage, any error, and your session and task tags. Nothing else from your process.

If something goes wrong

  • Your model calls are never slowed or changed. If we are unreachable, calls are dropped and a warning is logged at most once a minute.
  • Memory is bounded: at most 5,000 calls or 64 MB are held. A single call over 4 MB is dropped.
  • Safe with forking servers (gunicorn, Celery). On exit, it flushes for up to 2 seconds. Before os._exit(), which skips that, call flush() yourself.
  • GENERALCONTEXT_DISABLED=1 turns recording off without a code change.

Configuration

Option (Python / Node.js)Environment variableDefault
api_key / apiKeyGENERALCONTEXT_API_KEYrequired
base_url / baseUrlGENERALCONTEXT_BASE_URLGeneral Context
app_name / appNamenone
redact / redactGENERALCONTEXT_REDACT_SALToff
before_send / beforeSendnone
instrument_http / instrumentFetchoff
instrumentModules (Node.js)detected
disable_batch / disableBatchoff: calls are sent in batches
GENERALCONTEXT_DISABLED=1recording on

instrument_http / instrumentFetch also records raw HTTP calls to OpenAI-compatible /chat/completions endpoints made without the OpenAI client: httpx and requests in Python, fetch in Node.js.

Security

  • Sends only over HTTPS to the configured host and never follows redirects, so your key cannot be forwarded.
  • No runtime dependencies. Releases are built in CI with signed provenance. Details: SECURITY.md.