Slingshot v0.2.9 — tool calling in slingshot-ai
@lastshotlabs/slingshot-ai 0.4.0 — tool calling
AiClient could only generate, generateStructured or stream. A consumer needing
retrieval-then-decide had to fake it with a single generateStructured "plan" call:
one guess, no feedback loop. The package had already reserved the ground — AiStopReason
included 'tool_use' and ProviderCapabilities.toolUse was declared per provider — and
nothing consumed either. Now something does.
execute lives on the tool, so the framework runs the loop. That is the only shape in
which the iteration cap, argument validation and spend re-entry are the orchestrator's job
rather than every app's. ProviderResult.toolCalls and the new content parts ship as public
types, so a surface-the-calls API can still be built on top later; the reverse would not hold.
Message model. AiContentPart gains tool_call and tool_result variants rather than
new roles, leaving messageContentUnits and the image rules untouched. A tool_result part
rides a user turn — where OpenAI's role: 'tool' and Anthropic's tool_result block both
normalize to.
Streaming. Two events, deliberately: tool_call_delta is raw, live and advisory so a
trace UI can name the call the moment the model does, and tool_call is validated — the one
an app may act on. The streaming invariant holds: concatenated text deltas still equal
finalResult().text, with tool events interleaved.
Transports. One openaiCompatible translation covers DeepSeek, OpenAI, Grok, Ollama,
vLLM, LM Studio, OpenRouter, Groq and Together; Anthropic and Gemini are one adapter each.
A provider declaring toolUse: false that is handed tools throws rather than silently
dropping them — dropping would look successful while changing the request's meaning.
Invariants held, not re-invented. Tool arguments are safeParsed in the orchestrator and
never trusted from the provider. Every iteration re-enters the pre-flight spend guard and the
request-scoped reservation, so N iterations produce N reservations and N settlements. Hitting
maxToolIterations is an AiDegradation, not a silent truncation. Usage accumulates
additively across iterations and stays disjoint within each. A tool that throws becomes an
isError tool_result the model can react to; an unknown tool name is an isError result,
never an exception at the app.
Prompt caching. The routing key and system blocks are byte-identical across iterations,
so iterations 2..n are structurally cache-eligible. Verified in the per-call usage ledger
rather than the blended rate — over a tool loop the blend is not merely uninformative but
structurally misleading, since iteration 1 is a cold read by construction and always drags
the average down.
Breaking
Additive for anything that only reads results. An exhaustive switch with a never check
over AiContentPart, AiStreamEvent or ProviderStreamEvent will stop compiling — which
is why this is a minor rather than a patch.
Also in this release
The auth family carries security work that had been sitting unpublished: HTTPS redirect
enforcement, secure cookies, fail-closed auth configuration and auth runtime hardening.
slingshot-auth 0.2.4, slingshot-oauth 0.2.3, slingshot-m2m, slingshot-oidc and
slingshot-scim 0.2.2.