Upgrade from any older release
You don’t step through the releases. Go straight to the current SDK and change each old call once:- Install it on every machine and CI job:
pip install -U "decimalai[langchain]>=0.27", with your framework’s extra in place oflangchain. - Build each agent from
agent_config(), as the table below shows. Your old prompt text becomesfallback=: the prompt saved in the dashboard takes its place, and the fallback runs while none is saved. - Run your code. An old call raises an error, usually naming what replaces it, so a spot you missed shows up the first time that code runs. A few changes raise nothing.
- Write generated files again. A file
decimalai initwrote on an older release can import names that are gone.decimalai init <agent-name> --force --framework <framework>overwrites it, your edits included. - Migrate installed skills, if
.decimal/skills.lockis a version 1 lock or you have an npx lock: rundecimalai skills migrateonce.
In each,
support = agent_config("support-agent", fallback=YOUR_PROMPT). CrewAI has no agent_config(): decimalai.crewai.instrument(enable_skill_loader=True) stays its route. Each framework’s page has the whole program.
The sections below show each release’s own code, so the oldest say load_agent(), which 0.21 renamed agent_config().
Changes that raise no error
- Flags that trace only.
init(openai_agents=True)(from 0.18) andinit(langchain=True)(from 0.25), andDECIMAL_AUTO_TRACEset to either, give an agent no prompt or skills. A LangChain run with noagent_config()middleware says so once, in a warning.DECIMAL_AUTO_TRACE=adk(from 0.20) logs a warning and traces nothing. prompt_source’s values changed in 0.21 (see that section), and 0.24 added"fallback (no key)".- A misspelled agent name makes a new agent (from 0.24). Before,
agent_config()raisedAgentNotFoundError. - With no key,
agent_config()uses only the skills.decimal/skills.tomllists and sends nothing (from 0.25). 0.24 searched the public registry with each message and added what the model loaded, and 0.21 to 0.23 needed a key. - A skill the SDK creates reaches only the agents you add it to (from 0.25.1). Through 0.25.0 a create that said nothing was offered to every agent. Pass
agent_name=(decimalai skills sync --to-agent NAME), oroffer_scope="workspace". decimalai skills sync(from 0.25): when your file and DecimalAI’s copy both changed, DecimalAI’s copy wins and yours is kept in.decimal/backup/. Older releases let the newer edit win and kept no copy.- One agent name reaches every call (from 0.17). Through 0.16,
init(agent_name=...)reached only the adapters a flag on the sameinit()call installed. Aninstrument()you called yourself,start_trace()andSkillRoutereach needed the name again, and a call that left it out filed its traces under another agent and was offered none of the agent’s skills, with no error.SkillRouter()neededapi_key=as well.
Moving to SDK 0.25
0.25 changes one module,decimalai.langchain: a LangChain agent gets its prompt and skills from agent_config(), as one middleware you pass to your own create_agent(...), and nothing in LangChain is patched. Through 0.24, instrument(enable_skill_loader=True), and decimalai.init(langchain=True) with it, patched BaseChatModel.invoke, ainvoke, stream and astream and pasted the routed skills into every chat-model call in the process. Code written for 0.24 fails loudly where it passed the loader’s arguments: instrument() raises a TypeError for enable_skill_loader, enable_load_skill_tool, skill_body_top_k, priority_skills and runtime_policy. The one change with no error is init(langchain=True), which now traces only; the first traced run with no agent_config() middleware says so in a warning, and init(langchain=True, skills=False) silences it. CallbackHandler is unchanged.
Install the extra again,
pip install "decimalai[langchain]", which now brings langchain (where create_agent lives) beside langchain-core. decimalai init <agent-name> --framework langchain writes a file in the new shape. Threads are in Conversations.
Files decimalai init wrote through 0.24.1 open with decimalai.init(agent_name=AGENT_NAME) and raise DecimalConfigError with no key. A file written now calls no init(), so it also runs with no key.
Your own OpenAI or Gemini calls
0.25 addsdecimalai.openai and decimalai.google_genai, with nothing you must move. Through 0.24, code that called the OpenAI SDK itself used the top-level decimalai.agent_config(): it wrapped run.tool_spec in OpenAI’s tool shape itself, answered load_skill with run.load_skill(name), and needed decimalai.init(openai=True), agent_run() and the instrumentor package for a run to reach a trace. That still works. decimalai.openai.agent_config() does the same in OpenAI’s shapes, and traces each run with no other line:
decimalai.google_genai.agent_config().
Two changes that need no code
decimalai.pydantic_ai’s capability traces its own runs, so the decimalai.init(otel=True) and Agent.instrument_all() lines a Pydantic AI file carried can go (they keep working, and nothing is reported twice; Tracing). An OpenAI Agents chat on Claude keeps its first run’s instructions once its runs share a RunConfig(group_id=...) (Claude through LiteLLM).
Moving to SDK 0.23
0.23 changes one module,decimalai.anthropic: Anthropic Messages API agents get their prompt and skills from agent_config(), one run per user message, and nothing in the Anthropic SDK is patched. Through 0.22, instrument(enable_skill_loader=True) patched Messages.create(), .stream() and .parse(), sync and async, for every Anthropic client in the process, putting a skill menu, and sometimes one skill’s text, into each request’s system. Code written for 0.22 fails loudly: instrument() is now a stub that raises a TypeError naming agent_config(), and skill_system() and get_current_routing_id() are removed.
pip install "decimalai[anthropic]", which brings the tracing library agent_config() uses, and set up any OpenTelemetry tracing of your own before the first agent_config(). decimalai init <agent-name> --framework anthropic writes a file in the new shape. Chats are in Conversations.
Moving to SDK 0.21
0.21 renames three things:load_agent() is agent_config(), Google ADK’s DecimalaiPlugin is DecimalAIPlugin, and code that calls a model itself starts a run per message. Code written for 0.20 fails loudly: load_agent() and DecimalaiPlugin() raise a TypeError naming the new function, and support.system, support.tool_spec and support.load_skill raise an AttributeError saying where each went. The one change with no error is the value of prompt_source.
The arguments are the same, and so is what each config holds:
instructions and tools on the OpenAI Agents SDK, the capability on Pydantic AI, instruction and tools on Google ADK.
A run serves its own load_skill calls from any thread or task; in 0.19 and 0.20 a load was served against the latest system() of the thread or task that called it. See Your own model calls. Files decimalai init wrote before 0.21 import load_agent and fail: write them again, or rename the import and the call by hand.
Moving to SDK 0.20
0.20 gives Google ADK its ownload_agent() and removes the process-wide install it replaces. Code written for 0.19 fails loudly, with an ImportError or AttributeError for decimalai.adk.instrument, or a TypeError for init(adk=True) and for DecimalaiPlugin(enable_skill_loader=...) or enable_load_skill_tool=. The one silent case is DECIMAL_AUTO_TRACE=adk, which logs a warning naming the plugin and traces nothing.
Google ADK: load_agent() and the plugin in the App
Through 0.19, decimalai.init(adk=True) and decimalai.adk.instrument(enable_skill_loader=True) patched Runner.__init__ to add the DecimalAI plugin to every runner in the process, and the plugin pasted a skill menu and a skill’s text into each request’s system_instruction. 0.20 removed both. An agent gets its prompt and skills from load_agent(), which you pass into your own LlmAgent(...), and its traces come from DecimalaiPlugin() in its App(plugins=[...]):
- Traces come only from the plugin in the
App. A runner built without it is not traced, and nothing else in the process is touched.DecimalaiPlugin(agent_name=..., parent_trace_id=..., on_trace=...)takes the arguments it took before, without the two skill ones. - Your
instruction=text becomes thefallback=. A saved prompt’s placeholders are{{var}}, filled from the session’s state, where a string instruction’s{var}were ADK’s. - Skills arrive through
load_skill. The model fetches a listed skill’s text when it needs it, and the trace records the skill as loaded. Through 0.19 one body was pasted into the instruction; withDECIMALAI_LOAD_SKILL_TOOL=0that still happens, throughload_agent(). - ADK’s own
SkillToolsetneeds only the plugin in theAppto have its loads recorded as activations.
decimalai init --framework adk wrote before 0.20 import instrument and fail: write them again, or make the change above by hand.
Moving to SDK 0.19
0.19 changes what twoload_agent() calls give you: the Pydantic AI one, which replaces decimalai.pydantic_ai.instrument(), and the top-level decimalai.load_agent(). Code written for 0.18 fails loudly, with an ImportError or AttributeError for instrument and get_current_routing_id, an AttributeError for config.system_prompt and the other removed fields, or a TypeError for version= and cache_ttl_seconds=.
Pydantic AI: load_agent()
Through 0.18, decimalai.pydantic_ai.instrument(enable_skill_loader=True) gave every Agent in the process skills and a load_skill tool by patching Pydantic AI, and opened a run span around every run. 0.19 removed it, and an agent gets its prompt and skills from load_agent(), a capability you pass in Agent(capabilities=[...]):
- Traces no longer come from
instrument(). From 0.19 to 0.24 usedecimalai.init(otel=True)withAgent.instrument_all(), or a provider flag such asdecimalai.init(openai=True): one, not both. From 0.25 the capability traces its own runs. The Pydantic AI harness’sdecimalai.pydantic_ai_harness.skills()needs noinstrument()either. - Your
system_prompt=text becomes thefallback=. - The agent’s name. The capability routes as the name it was loaded with, whatever the
Agentis called. Through 0.18,Agent(name=...)won. - Another tool named
load_skillnow fails the run with Pydantic AI’s own error. Through 0.18, DecimalAI’s was renameddecimalai_load_skill; rename yours instead. get_current_routing_id()was removed withinstrument(). Each run’s routing decision is recorded on its trace.
Your own model calls: decimalai.load_agent()
Through 0.18, decimalai.load_agent() returned the saved prompt alone. In 0.19 it returns the agent’s prompt and skills, for code that calls a model itself: support.system(message, context=...), support.tool_spec and support.load_skill(name), which 0.21 moved onto a run. Code that wants only the prompt text reads support.prompt:
Files
decimalai init wrote before 0.19 read config.system_prompt, and so does a Support project from decimalai init --project. Rename config to support and config.system_prompt to support.prompt: a Support project’s checks read support. An agent file can be written again instead.
Moving to SDK 0.18
0.18 removes what earlier releases kept only for compatibility. A removed name fails loudly, with anImportError, AttributeError or TypeError.
OpenAI Agents SDK: load_agent()
Through 0.17, instrument(enable_skill_loader=True) and init(openai_agents=True) gave every agent in the process skills by patching the Agents SDK. In 0.18 both trace only (enable_skill_loader is no longer an argument of instrument()), and an agent gets its prompt and skills from load_agent():
instructions and tools hold.
Removed names
decimalai init --project <dir>, which writes a Support project, is a different option and is unchanged.
A project SDK 0.14 installed
The version 1 lock fallback is gone as well:router.export() and router.update_skills() refuse a project whose .decimal/skills.lock decimalai 0.14 wrote, as every decimalai skills command already did. Run decimalai skills migrate once, as the next section describes.
Upgrading the SDK: skills on disk
SDK 0.15.0 installs registry skills like packages: a manifest,.decimal/skills.toml, a version 2 lock that records every byte written, and one root, .agents/skills/. A project set up by SDK 0.14 or older has a version 1 .decimal/skills.lock (or the npx installer’s .decimal/skills.lock.json), which says which skills were installed but not which bytes. The SDK refuses to work from it, and every command says so:
install, install --frozen and verify exit 3; status reports it. Nothing converts the lock implicitly.
Run it once
1
Upgrade everyone on the project
decimalai skills export, run after the migration, merges version 1 entries into the new lock: install --frozen then refuses it (exit 3), and skills install drops those entries and leaves their folders as unmanaged.2
Migrate
DECIMAL_API_KEY); public skills migrate without one. If an entry does not resolve, nothing changes and it exits 7, naming it. decimalai skills migrate --yes migrates the others.3
Review and commit
.decimal/migrated-v1/ is gitignored. In CI, replace the old export step with decimalai skills install --frozen.What it does
- Seeds
.decimal/skills.tomlfrom the old lock. Version 1 entries become"latest", because those locks did not record real versions. Npx entries keep the exact version npx installed. If the manifest already exists, it wins: none of its lines changes. - Resolves every entry again, by skill id first and by name when the registry no longer knows the id. An entry whose name now resolves to a different skill than its recorded id is skipped and reported. The trust gate applies without its question: a Blocked skill is refused, a Withheld one too unless your organization owns or uses it, and a Caution or not-yet-reviewed one installs with a warning.
- Installs each skill fresh from the registry. The old hashes are never trusted, so every folder is written again in the new format.
- Moves old folders, and deletes nothing. A folder inside
.agents/skills/goes to.decimal/migrated-v1/<txid>/skills/<folder>, so a stale copy cannot load beside the new one. A folder anywhere else (.claude/skills/,.continue/skills/, …) stays and is listed, since an interactive agent may still read it: delete it yourself once nothing does. A folder for an entry that did not resolve, or was skipped, stays as one the engine never touches. - Keeps the old locks in the same
.decimal/migrated-v1/<txid>/, removes the originals, writes the version 2 lock and.agents/.claude-plugin/plugin.json, and prints each skill, what the old lock claimed, the version installed, and where its old folders went.
After the migration
- Skills from a version 1 lock start at
latest. Pin one withdecimalai skills update <name>@<N>; the old lock’s version numbers were not real. - Point your framework at
.agents/skills. One root serves every runtime:decimalai.skills_dir()for the native loaders,claude --plugin-dir "$(git rev-parse --show-toplevel)/.agents"for Claude Code. The--agentand--scope globalflags are gone. - Replace the old verbs:
skills export <ref>andskills install <ref>areskills add <ref>;SkillRouter.pull_missing()isskills install. 0.18 removed the old verbs; Removed names lists what replaces each. skills pullchanged too. Through 0.14 it wrote./<url_slug>/SKILL.mdwith a frontmatter of its own,nameanddescriptiononly, and overwrote an existing folder without asking.- The adapters no longer write skills to disk. Through 0.14, an adapter could mirror your organization’s skills into the working directory in the background. In 0.15, disk changes only when a
decimalai skillscommand runs.