Integrations
Trace OpenAI, Anthropic, Gemini, Azure, Bedrock, Vertex AI, OpenRouter, and more — in Python and TypeScript.
Neatlogs traces the AI providers and SDKs you already use. In most cases you wrap the client you construct and every call it makes is captured — no other code changes.
import neatlogs
neatlogs.init(api_key="YOUR_PROJECT_KEY", workflow_name="my-app")
from openai import OpenAI
client = neatlogs.wrap(OpenAI()) # every call is now tracedWhere an integration exists for more than one language, the page shows language tabs. The Python compatibility table, TypeScript explicit-helper table, and Go SDK reference define the tested versions and mechanism for each SDK; do not copy an integration mechanism from one language to another.
Cloud & direct providers
OpenAI
The OpenAI SDK — chat, responses, embeddings.
Anthropic
Claude via the Anthropic SDK.
Google Gemini
The google-genai / @google/genai SDK.
Azure OpenAI
Azure-hosted OpenAI deployments.
AWS Bedrock
Bedrock Converse + InvokeModel.
Vertex AI
Gemini on Vertex AI.
OpenRouter
200+ models through one API.
Agent frameworks
LangChain
LangChain & LangGraph (Python + TS).
CrewAI
Multi-agent crews (Python).
Mastra
Mastra agents & workflows (TS).
Vercel AI SDK
generateText / streamText (TS).
Pydantic AI
Pydantic AI agents (Python).
DSPy
DSPy modules (Python).
Agno
Agno agents, teams, workflows (Python).
Strands
Strands agents (Python).
Google ADK
Agent Development Kit runner (Python + Go).
Pi Agent
Pi agents (TS).
OpenAI Agents SDK
OpenAI Agents (Python + TS).
Agent SDKs
Claude Agent SDK
claude-agent-sdk / @anthropic-ai/claude-agent-sdk (Python + TS).
Hermes
NousResearch Hermes agent (Python).
Developer tools
Claude Code
Capture every Claude Code session as a trace.
Codex
Capture Codex turns, tools, and subagents through lifecycle hooks.
opencode
Trace opencode CLI sessions.
Agent Skills
Teach your coding agent to instrument code.
MCP Server
Query traces, detections, and incidents from your agent.
Grouping calls into one trace
A wrapped call just works on its own. A provider LLM call with no parent is itself a valid trace root, and a non-root operation such as an embedding gets one automatic WORKFLOW root (named after your workflow_name), so a single call renders in the dashboard with no extra code.
Because of that, each wrapped call with no surrounding context becomes its own trace. When a run makes several calls — or mixes provider calls with your own functions — group them under one root so they appear together in one trace:
# Decorate your entry point — it becomes the single root for everything inside…
@neatlogs.span(kind="WORKFLOW")
def handle_request(q):
client.chat.completions.create(...) # nests under handle_request
client.chat.completions.create(...) # …same trace
# …or wrap a block when there's no single entry function:
with neatlogs.trace("my-run", kind="WORKFLOW"):
client.chat.completions.create(...)
client.chat.completions.create(...)When you provide your own root, the automatic one steps aside — your calls nest under it. Agent SDKs and frameworks that already emit their own root (like the Claude Agent SDK, Hermes, or the OpenAI Agents SDK) work the same way: no extra wrapper needed. See Span Kinds for the full list of root kinds.
When to add your own WORKFLOW root
A lone wrapped call doesn't need one. Add an explicit WORKFLOW (or AGENT / CHAIN) root when:
- A run makes several provider calls that belong together — e.g. a multi-turn exchange, or a retry loop. One root keeps them in a single trace instead of one trace per call.
- You mix provider calls with your own functions or tools. A current Python or TypeScript SDK gives a standalone
TOOLone automaticWORKFLOWparent, so it is not dropped. Add an explicit root when the tool and several provider calls belong to the same run; otherwise each standalone call becomes its own trace. A tool-calling turn (LLM → tool → LLM) is the classic case. - You want a meaningfully named trace rather than the default
workflow_name.
No double root. Auto-root fires only when there's no active recording parent and the operation is not already root-eligible. The moment you open a WORKFLOW, the wrapper detects it and skips auto-root — so a manual root plus a wrapped call always produces exactly one root, never two.
To instrument your own functions (custom agents, pipelines, tools) rather than a known provider or framework, use the @span decorator — see the Python SDK or TypeScript SDK.
