@juspay/neurolink
Version:
The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002
46 lines (45 loc) • 1.92 kB
JavaScript
export function resolveToolChoice(options, tools, shouldUseTools) {
if (!shouldUseTools || !tools || Object.keys(tools).length === 0) {
return "none";
}
return options.toolChoice ?? "auto";
}
/**
* Normalise an already-resolved tool choice into the shape
* `toolChoiceToAnthropic` (and its peers) accept.
*
* A provider's model handle sees a value that has *already* been through
* `resolveToolChoice` upstream, so it must translate rather than re-resolve —
* re-resolving there would apply the `auto`/`none` defaulting twice, and the
* `tools`/`shouldUseTools` inputs it needs are not in scope at that layer
* anyway.
*
* What does vary is the shape. The value arrives either as a bare string
* (`"auto"` / `"none"` / `"required"`), as `{ type: "tool", toolName }`, or —
* on the native loop's tool-free re-ask — as an injected `{ type: "none" }`
* object. Reading `.type` off the string forms yields `undefined`, which is
* how string choices were silently dropped from the Anthropic generate
* request. This exists so that normalisation lives in one place instead of
* being re-derived at each call site.
*/
export function normalizeResolvedToolChoice(value) {
if (typeof value === "string") {
return value;
}
if (value && typeof value === "object") {
const candidate = value;
if (candidate.type === "tool") {
// A tool choice that names no tool cannot be honoured. Fall through to
// the provider default rather than emitting the bare string "tool",
// which is not one of the valid auto/none/required forms and would be
// rejected downstream.
return candidate.toolName
? { type: "tool", toolName: candidate.toolName }
: undefined;
}
if (candidate.type) {
return candidate.type;
}
}
return undefined;
}