Dottle SDK — Instrument your AI agents in 3 lines of code. See every LLM call, tool, cost, and failure in real time.
Project description
Dottle SDK
Monitor your AI agents in minutes. See every LLM call, tool use, cost, latency, and failure — in real time.
dottle.dev · Dashboard · Docs
Install
pip install dottle-sdk
Quickstart
import dottle
# 1. Configure once at startup
dottle.configure(api_key="dtl_live_...")
# 2. Wrap your agent run in a session
with dottle.session("my-agent", user_id="user_123") as sid:
# 3. Track each LLM call as a span
with dottle.span("llm", "gpt-4o reply") as s:
response = openai_client.chat.completions.create(...)
s.record_tokens(
prompt_tokens=response.usage.prompt_tokens,
completion_tokens=response.usage.completion_tokens,
model="gpt-4o",
)
That's it. Open app.dottle.dev to see your agent's sessions, costs, and errors live.
Get your API key
- Sign up at app.dottle.dev
- Create an organization → create a project
- Copy the
dtl_live_...key from Project Settings
What gets tracked
| Signal | How |
|---|---|
| LLM calls | dottle.span("llm", ...) + s.record_tokens(...) |
| Tool calls | dottle.span("tool", ...) |
| Errors | s.record_error(exc) or automatic on exception |
| Cost | Calculated from token counts + model |
| Latency | Automatic (start/end of each span) |
| User | Pass user_id / user_email to dottle.session() |
Full example with Anthropic
import anthropic
import dottle
dottle.configure(api_key="dtl_live_...")
client = anthropic.Anthropic()
def run_agent(user_message: str, user_email: str):
with dottle.session("support-agent", user_email=user_email) as sid:
with dottle.span("llm", "claude-3-5-sonnet") as s:
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[{"role": "user", "content": user_message}],
)
s.record_tokens(
prompt_tokens=response.usage.input_tokens,
completion_tokens=response.usage.output_tokens,
model="claude-3-5-sonnet-20241022",
)
return response.content[0].text
Prompt management
Version-control your prompts in the Dottle dashboard and fetch the active version at runtime — no redeploy needed when you update a prompt.
import os
import dottle
dottle.configure(
api_key=os.environ["DOTTLE_API_KEY"],
project_id=os.environ["DOTTLE_PROJECT_ID"], # or pass to get_prompt()
)
# Fetch the active version (always up-to-date — changes in the UI are live instantly)
prompt = dottle.get_prompt("summarize-article")
# Compile {{variable}} placeholders into messages
messages = prompt.compile(article=article_text, language="English")
# → [{"role": "system", "content": "..."}, {"role": "user", "content": "..."}]
# Pass to any LLM client
response = openai_client.chat.completions.create(
model=prompt.model, # model stored on the prompt in the dashboard
messages=messages,
**prompt.parameters, # temperature, max_tokens, etc.
)
# Or let Dottle call the model for you (routes to OpenAI / Anthropic / Gemini automatically)
result = prompt.invoke(article=article_text, language="English")
Version pinning
# Pin to a specific version number
prompt = dottle.get_prompt("summarize-article", version=3)
# Pin to a named label (e.g. "production", "staging")
prompt = dottle.get_prompt("summarize-article", label="production")
Tool calls
If you defined tools on the prompt in the dashboard, they're available on prompt.tools in OpenAI format:
prompt = dottle.get_prompt("agent-prompt")
response = openai_client.chat.completions.create(
model=prompt.model,
messages=prompt.compile(task=user_task),
tools=prompt.tools, # OpenAI tool-call format
)
Auto-tracking inside a session
When .invoke() is called inside a dottle.session(), it automatically creates an LLM span with the prompt name, version, token counts, and cost — no extra code needed:
with dottle.session("my-agent") as sid:
result = prompt.invoke(article=text)
# ↑ recorded in the dashboard as span "summarize-article v3"
# with input_tokens, output_tokens, cost, and prompt text
Caching
get_prompt() caches the active version in-process for 60 seconds by default, so hot paths don't hit the API on every request:
# Default: 60-second TTL
prompt = dottle.get_prompt("summarize-article")
# Custom TTL
prompt = dottle.get_prompt("summarize-article", ttl=300) # 5 minutes
# Bypass cache (always fetch fresh)
prompt = dottle.get_prompt("summarize-article", ttl=0)
# Pinned version/label fetches are never cached
prompt = dottle.get_prompt("summarize-article", version=3)
# Clear cache (useful in tests)
dottle.clear_prompt_cache()
PromptHandle attributes
| Attribute | Type | Description |
|---|---|---|
prompt.name |
str |
Prompt slug |
prompt.version |
int |
Version number |
prompt.label |
str | None |
Named label (e.g. "production") |
prompt.model |
str |
Model stored on the prompt |
prompt.parameters |
dict |
LLM parameters (temperature, max_tokens, …) |
prompt.tools |
list |
OpenAI-format tool definitions |
prompt.variables |
list[str] |
Detected {{variable}} names |
prompt.messages |
list |
Uncompiled messages (system + user) |
LangChain integration
Zero-code-change tracking for LangChain chains, agents, and LangGraph — attach one callback handler and every LLM call and tool use is automatically recorded.
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor
from dottle.integrations.langchain import DottleCallbackHandler
import dottle
dottle.configure(api_key="dtl_live_...")
handler = DottleCallbackHandler()
llm = ChatOpenAI(model="gpt-4o", callbacks=[handler])
agent_executor = AgentExecutor(agent=agent, tools=tools, callbacks=[handler])
with dottle.session("my-langchain-agent", user_id="user_123") as sid:
result = agent_executor.invoke({"input": "What is the weather in Tokyo?"})
What gets tracked automatically: model name, input/output text, token counts, cost, latency, tool call inputs/outputs, errors, and loop detection.
Works with: ChatOpenAI, ChatAnthropic, ChatGoogleGenerativeAI, all LangChain tools, LCEL chains, LangGraph nodes.
→ Full LangChain integration guide
Zero performance impact
All calls are fire-and-forget (background thread). Your agent never waits for Dottle. If Dottle is unreachable, your agent keeps running — monitoring failures are silently swallowed.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dottle_sdk-0.1.5.tar.gz.
File metadata
- Download URL: dottle_sdk-0.1.5.tar.gz
- Upload date:
- Size: 23.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1948c3cb7419a6d6fed55c959b1df7239e162e411181cb174987f2904fb67b07
|
|
| MD5 |
c9476f29db64692317b7e06fed48474a
|
|
| BLAKE2b-256 |
d29496b73ae99cef7182037d96abd591eecc72233619c49003ad12b4f2b115a5
|
File details
Details for the file dottle_sdk-0.1.5-py3-none-any.whl.
File metadata
- Download URL: dottle_sdk-0.1.5-py3-none-any.whl
- Upload date:
- Size: 30.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de79cf3e97bd990837b9f5fd7510b857b468d8ad136f9ec09bfabe1ec2c38cd4
|
|
| MD5 |
05bd7fa7077a5ef47c9ff780b5436ebd
|
|
| BLAKE2b-256 |
8c93e788aa92c1f0a2b2af712798e2a02541535f08831604879fc89d7e35d61f
|