Repository navigation
v0.19.0
Changed (breaking): Pydantic AI agents get their prompt and skills from load_agent()
- New:
decimalai.pydantic_ai.load_agent(name, fallback=..., on_unavailable="continue")
returns a Pydantic AI capability forAgent(capabilities=[...]). Its instructions are the
agent's saved prompt ({{placeholders}}filled from the run'sdeps), then the skill list
routed on the run's prompt; its toolset isload_skill, limited to the agent's skills; and its
wrap_runopens one DecimalAI run span per run, so a run's model calls are one trace filed under
the DecimalAI agent, whatever theAgentorinit(agent_name=...)is called: under
Agent.instrument_all(), Pydantic AI's own run span is the trace's root, and it is named for the
DecimalAI agent too. Every run is routed on its own prompt, the way OpenAI Agents runs are.
Built only on Pydantic AI's capability API. - Removed:
decimalai.pydantic_ai.instrument()and the three patches behind it
(Agent.__init__twice,Agent.iter): the skills instructions function andload_skillit
added to every Agent in the process, thedecimalai_load_skillrename, the run span it opened
around every run, andget_current_routing_id(). Tracing is DecimalAI's OpenTelemetry exporter:
decimalai.init(otel=True)withAgent.instrument_all(), or a provider instrumentor
(decimalai.init(openai=True)). - An agent that also has the Pydantic AI harness's
Skillsgets no DecimalAI skills from the
capability, with one warning. An agent with another tool namedload_skillfails its run with
Pydantic AI's own error. - The harness's observed
Skills(decimalai.pydantic_ai_harness.skills()) needs no
instrument()either: withinit(otel=True)andAgent.instrument_all(), each run's skill
catalog and every load go on that run's trace. decimalai init --framework pydantic-aiwrites the new shape.
Migration:
# before
from decimalai.pydantic_ai import instrument
instrument(enable_skill_loader=True)
agent = Agent(model, name="support", system_prompt=BASE, tools=[lookup_order])
# after
from decimalai.pydantic_ai import load_agent
support = load_agent("support", fallback=BASE)
agent = Agent(model, name=support.name, tools=[lookup_order], capabilities=[support])Changed (breaking): decimalai.load_agent() returns the agent's prompt and skills
decimalai.load_agent(name, fallback=..., on_unavailable="continue")returns anAgentConfig
that works likedecimalai.openai_agents.load_agent(), for code that calls a model itself:
system(message, context=...)is this turn's system text (the saved prompt with its
{{placeholders}}filled fromcontext, then the skill list routed onmessage),tool_spec
is theload_skilltool as{name, description, parameters}, andload_skill(name)returns a
skill's text.prompt,prompt_version,prompt_hash,prompt_source,variablesand
skill_namesdescribe what it holds. Inside an OpenTelemetry run, each turn's record (prompt
version, placeholder names, skills offered and loaded) goes on that run's trace.- Removed: the prompt-only
AgentConfigit returned before (system_prompt,
version_number,content_hash,is_fallback, …) andload_agent()'sversion=and
cache_ttl_seconds=.support.promptis whatconfig.system_promptwas, except that it is
thefallback=text when no prompt is saved. decimalai initfiles and the Support project readsupport.prompt.decimalai.MissingPromptVariableis importable from the top level.- The OpenTelemetry exporter keeps DecimalAI's own record attributes on the exported span:
decimal.agentanddecimal.prompt.*(version, hash, placeholder names, never their values).
Migration:
# before
config = decimalai.load_agent("support")
agent = create_agent(model, tools=tools, system_prompt=config.system_prompt)
# after
support = decimalai.load_agent("support")
agent = create_agent(model, tools=tools, system_prompt=support.prompt)Fixed: a refusing platform gets fewer retries, spread out
- A retry after a 429 or a 502/503/504 now waits a random time under a ceiling that doubles
(1 s, 2 s, 4 s, capped at 8 s), on top of anyRetry-After, instead of exactly 1, 2 and 4 s.
Processes refused at the same moment no longer come back at the same moment. - When a manifest registration is refused, the background sender's re-registration makes one
request per step instead of running the client's own retries inside each step, and waits
before its first step. One trace whose registration stays refused now sends at most 9
registrations, down from 24 on most adapters, 32 on the generic tracer and 72 on LangChain.
The steps still span 31-62 s, long enough to bridge a backend restart. - The generic tracer and the LangChain adapter no longer repeat a registration that the client
has already retried (429, 502-504) or that a retry cannot fix (other 4xx, a plan quota).
Added: assign a skill to an agent, and create it restricted, from SkillRouter
SkillRouter.assign_skill(agent_name, skill_id, pin_version_id=None)assigns one of your
skills, or a public registry skill, to one agent (POST /api/v1/agents/{name}/skills/{id}).
Assigning again updates the pin.unassign_skill(agent_name, skill_id)removes it (with a key
that has admin permission). Before, this took a raw REST call.create_skill(..., offer_scope="restricted")creates a skill offered only to the agents it
is assigned to, in one call, so it is never offered org-wide in between. Against an older
backend, which ignores the field, it follows up withupdate_skill(offer_scope=...), the
call you used to make yourself.get_trace()'s type lists the skill fields the API returns.skills_deliveredholds names
only; the delivered versions and where each body sits in the prompts are
skills_delivered_versionsandskills_delivered_sites(older backends mixed them into
skills_delivered).