Skip to content

v0.20.0

Choose a tag to compare

@basepointer basepointer released this 20 Oct 06:31

Changed (breaking): Google ADK agents get their prompt and skills from load_agent()

  • New: decimalai.adk.load_agent(name, fallback=..., on_unavailable="continue") returns
    instruction, an ADK instruction provider for LlmAgent(instruction=...): the agent's saved
    prompt ({{placeholders}} filled from the session's state), then the skill list routed on the
    invocation's message. Its tools is load_skill, limited to the agent's skills: ADK agents
    can fetch a skill's text mid-turn for the first time. Each invocation is routed once, on its
    own message, the way OpenAI Agents runs are. Built only on ADK's public instruction and tool
    APIs.
  • DecimalaiPlugin() goes in App(plugins=[...]), ADK's own place for plugins. An invocation
    whose agent has load_agent()'s instruction is one trace filed under that DecimalAI agent,
    with its prompt version, placeholder names and the skills offered, loaded and delivered.
    load_skill's results are in the trace's model inputs.
  • Removed: decimalai.adk.instrument(), init(adk=True) and the Runner.__init__ patch
    behind both, which added the plugin to every runner in the process; the plugin's skill
    injection (a menu and a body pasted into system_instruction); and its enable_skill_loader
    and enable_load_skill_tool parameters. DECIMAL_AUTO_TRACE=adk now logs a warning that
    says to add the plugin, and traces nothing.
  • An agent that carries ADK's own SkillToolset, or another tool named load_skill, gets no
    DecimalAI skills, with one warning.
  • decimalai init --framework adk writes the new shape.

Migration:

# before
decimalai.init(adk=True, agent_name="support")
agent = LlmAgent(name="support", model=MODEL, instruction=BASE, tools=[lookup_order])
runner = InMemoryRunner(agent=agent)

# after
from decimalai.adk import DecimalaiPlugin, load_agent
support = load_agent("support", fallback=BASE)
agent = LlmAgent(name="support", model=MODEL, instruction=support.instruction,
                 tools=[lookup_order, *support.tools])
runner = InMemoryRunner(app=App(name="support", root_agent=agent, plugins=[DecimalaiPlugin()]))

Fixed: OpenAI Agents traces record OpenAI's cached input tokens

  • Each model call's cache_read_tokens now carries the cached-input count OpenAI reports
    (usage.input_tokens_details.cached_tokens), so the trace's total_cache_read_tokens is no
    longer always null. It is a subset of input_tokens, as OpenAI counts it.
  • On the Responses API (the default model) a reported 0 is recorded as 0, a measured cache miss.
    On chat-completions models, LiteLLM, and streamed responses only the span's usage is available,
    and openai-agents writes 0 there when the provider reported nothing; those record a positive
    count only, and leave 0 as not reported.

Changed: a failed skill route serves the agent's menu instead of no skills

  • When POST /api/v1/skills/route times out or fails with a 500 or 504, the SDK asks once for the
    agent's own menu (GET /api/v1/skills/menu) and the turn gets the agent's skills, unranked,
    instead of none. Before, the agent answered without skills and only the trace showed it. This
    covers every adapter that routes through SkillRouter.build_prompt_fragment, including
    load_agent().
  • The extra request has its own 2 s read budget and does not wait out a Retry-After, so a failed
    route costs a turn at most 2 s more. It is not made after a 429 or 503 (the platform is shedding
    load), a connection failure, or while the circuit breaker is open.
  • After a fallback, routes from the same host and API key are skipped for 30 s and turns go
    straight to the menu, so they do not each wait out the route's timeout again.
  • routing_status().menu_fallbacks counts the turns served this way. They had their skills, so
    they do not make healthy False; a rising count says routing itself is failing.