Auto-Instrumentation
Mantis SDK can automatically instrument popular AI frameworks to capture LLM calls without manual code changes.
Overview
Auto-instrumentation uses OpenTelemetry instrumentors to automatically trace:
- LLM API calls (tokens, latency, model)
- Tool/function calls
- Framework-specific operations
Enabling Instrumentation
Default Instrumentation
Enable the core set of instrumentors:
import mantisdk.tracing as tracing
tracing.init(instrument_default=True)This enables instrumentation for:
- OpenAI
- Anthropic
- LangChain
- LlamaIndex
- LiteLLM
Selective Instrumentation
Enable specific instrumentors only:
import mantisdk.tracing as tracing
tracing.init(instrument_default=False)
# Enable only what you need
tracing.instrument(names=["anthropic", "dspy"])Skipping Instrumentors
Enable defaults but skip specific ones:
import mantisdk.tracing as tracing
tracing.init(instrument_default=False)
tracing.instrument(skip=["langchain"]) # Enable all except LangChainSupported Frameworks
Core Set (Default)
| Framework | Instrumentor Name | Auto-Captured |
|---|---|---|
| OpenAI | openai | Chat completions, embeddings, tokens |
| Anthropic | anthropic | Messages, tool use, tokens |
| LangChain | langchain | Chains, agents, tools, retrievers |
| LlamaIndex | llama_index | Queries, retrievers, indices |
| LiteLLM | litellm | Multi-provider LLM calls |
Additional Frameworks
| Framework | Instrumentor Name | Auto-Captured |
|---|---|---|
| DSPy | dspy | Modules, signatures, optimizers |
| Google ADK | google_adk | Agents, tools, sessions |
| Mistral | mistral | Chat completions, embeddings |
| Groq | groq | Chat completions |
| Bedrock | bedrock | AWS Bedrock model calls |
Usage Examples
DSPy
import mantisdk.tracing as tracing
# Initialize and instrument DSPy
tracing.init(service_name="dspy-agent", instrument_default=False)
tracing.instrument(names=["dspy"])
# Your DSPy code is now automatically traced
import dspy
class MyModule(dspy.Module):
def forward(self, question):
# LLM calls are auto-traced
return self.chain_of_thought(question=question)Anthropic
import mantisdk.tracing as tracing
tracing.init(service_name="anthropic-agent", instrument_default=False)
tracing.instrument(names=["anthropic"])
# All Anthropic API calls are now traced
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-20250514",
messages=[{"role": "user", "content": "Hello!"}]
)
# ^ This call is automatically traced with:
# - Model name
# - Input/output tokens
# - Latency
# - Response contentGoogle ADK
import mantisdk.tracing as tracing
tracing.init(service_name="adk-agent", instrument_default=False)
tracing.instrument(names=["google_adk"])
# Google ADK operations are traced
from google.adk.agents import Agent
from google.adk.runners import InMemoryRunner
agent = Agent(name="my-agent", model="gemini-2.0-flash", tools=[...])
runner = InMemoryRunner(agent=agent)
# Agent runs are automatically tracedLangChain
import mantisdk.tracing as tracing
tracing.init(instrument_default=False)
tracing.instrument(names=["langchain"])
# LangChain chains and agents are traced
from langchain_openai import ChatOpenAI
from langchain.chains import LLMChain
llm = ChatOpenAI()
chain = LLMChain(llm=llm, prompt=my_prompt)
result = chain.invoke({"input": "Hello"})
# ^ Chain execution and LLM calls are tracedCombining Manual and Auto Tracing
Auto-instrumentation works alongside manual tracing:
import mantisdk.tracing as tracing
tracing.init(service_name="my-agent", instrument_default=False)
tracing.instrument(names=["anthropic"])
import anthropic
@tracing.trace
def process_query(query: str) -> str:
"""Manual trace wraps auto-instrumented calls."""
with tracing.span("prepare") as s:
prepared = prepare_query(query)
s.set_attribute("prepared_length", len(prepared))
# This Anthropic call is auto-traced as a child span
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-20250514",
messages=[{"role": "user", "content": prepared}]
)
return response.content[0].textThe resulting trace hierarchy:
process_query (manual trace)
├── prepare (manual span)
└── anthropic.messages.create (auto-instrumented)
├── model: claude-sonnet-4-20250514
├── input_tokens: 15
└── output_tokens: 42Instrumentation Timing
Always initialize tracing before importing the framework:
# CORRECT ORDER
import mantisdk.tracing as tracing
tracing.init(instrument_default=False)
tracing.instrument(names=["anthropic"])
import anthropic # Import AFTER instrumentation# WRONG ORDER - instrumentation may not work
import anthropic # Imported before instrumentation!
import mantisdk.tracing as tracing
tracing.init(instrument_default=False)
tracing.instrument(names=["anthropic"])Complete Example
#!/usr/bin/env python3
"""Auto-instrumentation with Anthropic."""
from dotenv import load_dotenv
load_dotenv()
# Initialize tracing FIRST
import mantisdk.tracing as tracing
from mantisdk.tracing.exporters.insight import is_insight_configured
exporters = []
if is_insight_configured():
exporters.append(tracing.insight_exporter())
tracing.init(
service_name="anthropic-agent",
exporters=exporters if exporters else None,
instrument_default=False,
)
tracing.instrument(names=["anthropic"])
# NOW import Anthropic
import anthropic
def main():
client = anthropic.Anthropic()
with tracing.trace("chat_session") as root:
root.set_attribute(tracing.semconv.USER_ID, "user-123")
# This call is auto-instrumented
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[
{"role": "user", "content": "What is the capital of France?"}
]
)
answer = response.content[0].text
root.set_attribute(tracing.semconv.TRACE_OUTPUT, answer)
print(answer)
if __name__ == "__main__":
try:
main()
finally:
tracing.shutdown()Next Steps
- Integrations - Full framework examples
- Insight Export - View auto-traced data