@earendil-works/pi-ai 1.0.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -3
- package/dist/api/anthropic-messages.d.ts.map +1 -1
- package/dist/api/anthropic-messages.js +63 -22
- package/dist/api/anthropic-messages.js.map +1 -1
- package/dist/api/bedrock-converse-stream.js +33 -2
- package/dist/api/bedrock-converse-stream.js.map +1 -1
- package/dist/api/classifier-shared.d.ts +21 -0
- package/dist/api/classifier-shared.d.ts.map +1 -0
- package/dist/api/classifier-shared.js +98 -0
- package/dist/api/classifier-shared.js.map +1 -0
- package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -1
- package/dist/api/cloudflare-workers-ai-system-one.js +2 -1
- package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -1
- package/dist/api/lazy.d.ts.map +1 -1
- package/dist/api/lazy.js +4 -3
- package/dist/api/lazy.js.map +1 -1
- package/dist/api/llama-cpp-classify.d.ts.map +1 -1
- package/dist/api/llama-cpp-classify.js +2 -0
- package/dist/api/llama-cpp-classify.js.map +1 -1
- package/dist/api/mistral-conversations.d.ts.map +1 -1
- package/dist/api/mistral-conversations.js +31 -14
- package/dist/api/mistral-conversations.js.map +1 -1
- package/dist/api/openai-codex-responses.d.ts.map +1 -1
- package/dist/api/openai-codex-responses.js +7 -4
- package/dist/api/openai-codex-responses.js.map +1 -1
- package/dist/api/openai-decisions.d.ts +4 -0
- package/dist/api/openai-decisions.d.ts.map +1 -0
- package/dist/api/openai-decisions.js +162 -0
- package/dist/api/openai-decisions.js.map +1 -0
- package/dist/api/openai-decisions.lazy.d.ts +3 -0
- package/dist/api/openai-decisions.lazy.d.ts.map +1 -0
- package/dist/api/openai-decisions.lazy.js +4 -0
- package/dist/api/openai-decisions.lazy.js.map +1 -0
- package/dist/api/system-one-shared.d.ts +0 -1
- package/dist/api/system-one-shared.d.ts.map +1 -1
- package/dist/api/system-one-shared.js +5 -89
- package/dist/api/system-one-shared.js.map +1 -1
- package/dist/api/typesafe-system-one.d.ts.map +1 -1
- package/dist/api/typesafe-system-one.js +2 -1
- package/dist/api/typesafe-system-one.js.map +1 -1
- package/dist/auth/oauth/anthropic.d.ts.map +1 -1
- package/dist/auth/oauth/anthropic.js +14 -6
- package/dist/auth/oauth/anthropic.js.map +1 -1
- package/dist/auth/oauth/openai-chatgpt.js +1 -1
- package/dist/auth/oauth/openai-chatgpt.js.map +1 -1
- package/dist/auth/oauth/openai-codex.d.ts.map +1 -1
- package/dist/auth/oauth/openai-codex.js +4 -4
- package/dist/auth/oauth/openai-codex.js.map +1 -1
- package/dist/auth/types.d.ts +5 -0
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/auth/types.js.map +1 -1
- package/dist/models.d.ts +5 -0
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +3 -1
- package/dist/models.js.map +1 -1
- package/dist/providers/compat-schema.d.ts +338 -0
- package/dist/providers/compat-schema.d.ts.map +1 -0
- package/dist/providers/compat-schema.js +375 -0
- package/dist/providers/compat-schema.js.map +1 -0
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/amazon-bedrock.json +1 -1
- package/dist/providers/data/anthropic.json +1 -1
- package/dist/providers/data/baseten.json +1 -1
- package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/dist/providers/data/cloudflare-workers-ai.json +1 -1
- package/dist/providers/data/github-copilot.json +1 -1
- package/dist/providers/data/google-vertex.json +1 -1
- package/dist/providers/data/google.json +1 -1
- package/dist/providers/data/huggingface.json +1 -1
- package/dist/providers/data/meta.json +1 -1
- package/dist/providers/data/minimax-cn.json +1 -1
- package/dist/providers/data/minimax.json +1 -1
- package/dist/providers/data/mistral.json +1 -1
- package/dist/providers/data/openai.json +1 -1
- package/dist/providers/data/opencode-go.json +1 -1
- package/dist/providers/data/opencode.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/data/radius.json +1 -1
- package/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/dist/providers/faux.d.ts.map +1 -1
- package/dist/providers/faux.js +25 -8
- package/dist/providers/faux.js.map +1 -1
- package/dist/providers/model-schema.d.ts +91 -0
- package/dist/providers/model-schema.d.ts.map +1 -0
- package/dist/providers/model-schema.js +55 -0
- package/dist/providers/model-schema.js.map +1 -0
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +12 -3
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter-api.d.ts +40 -0
- package/dist/providers/openrouter-api.d.ts.map +1 -0
- package/dist/providers/openrouter-api.js +41 -0
- package/dist/providers/openrouter-api.js.map +1 -0
- package/dist/providers/openrouter.d.ts +11 -1
- package/dist/providers/openrouter.d.ts.map +1 -1
- package/dist/providers/openrouter.js +52 -1
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/radius.d.ts.map +1 -1
- package/dist/providers/radius.js +4 -12
- package/dist/providers/radius.js.map +1 -1
- package/dist/types.d.ts +21 -304
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/estimate.js +1 -1
- package/dist/utils/estimate.js.map +1 -1
- package/dist/utils/event-stream.d.ts +10 -1
- package/dist/utils/event-stream.d.ts.map +1 -1
- package/dist/utils/event-stream.js +25 -0
- package/dist/utils/event-stream.js.map +1 -1
- package/dist/utils/model-operations.d.ts +3 -1
- package/dist/utils/model-operations.d.ts.map +1 -1
- package/dist/utils/model-operations.js +6 -0
- package/dist/utils/model-operations.js.map +1 -1
- package/dist/utils/provider-retry.d.ts +2 -0
- package/dist/utils/provider-retry.d.ts.map +1 -1
- package/dist/utils/provider-retry.js +2 -0
- package/dist/utils/provider-retry.js.map +1 -1
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +5 -2
- package/dist/utils/retry.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ Unified LLM API with provider collections, automatic auth resolution, token and
|
|
|
60
60
|
|
|
61
61
|
## Supported Providers
|
|
62
62
|
|
|
63
|
-
- **OpenAI**
|
|
63
|
+
- **OpenAI** (including the Decisions classifier API)
|
|
64
64
|
- **Ant Ling**
|
|
65
65
|
- **Azure OpenAI (Responses)**
|
|
66
66
|
- **OpenAI Codex (legacy)** (ChatGPT Plus/Pro subscription, requires OAuth, see below)
|
|
@@ -890,7 +890,7 @@ console.log(model.output); // ['image'] or ['image', 'text']
|
|
|
890
890
|
|
|
891
891
|
## Classification
|
|
892
892
|
|
|
893
|
-
Classifier models consume structured JSON state and answer one or more typed questions. They do not use chat or image-generation APIs. TypeSafe's Jev model
|
|
893
|
+
Classifier models consume structured JSON state, and optionally images, and answer one or more typed questions. They do not use chat or image-generation APIs. TypeSafe's Jev model, Cloudflare's Clef models, and OpenAI's GPT-6 Luna are available from these built-in providers:
|
|
894
894
|
|
|
895
895
|
| Provider | Model IDs | Auth |
|
|
896
896
|
| --- | --- | --- |
|
|
@@ -899,6 +899,7 @@ Classifier models consume structured JSON state and answer one or more typed que
|
|
|
899
899
|
| `cloudflare-workers-ai` | `typesafe/jev`, `@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash` | `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ACCOUNT_ID` |
|
|
900
900
|
| `vercel-ai-gateway` | `typesafe-ai/jev` | `AI_GATEWAY_API_KEY` |
|
|
901
901
|
| `opencode` | `jev-1.13`, `jev-1.13-free` | `OPENCODE_API_KEY` |
|
|
902
|
+
| `openai` | `gpt-6-luna` | `OPENAI_API_KEY` |
|
|
902
903
|
|
|
903
904
|
```typescript
|
|
904
905
|
import { builtinModels } from '@earendil-works/pi-ai/providers/all';
|
|
@@ -936,10 +937,20 @@ The public contract uses `bool` questions and `{ type: "bool", probability }` an
|
|
|
936
937
|
|
|
937
938
|
When the service reports token counts, `result.usage` carries them with their cost at the model's catalog price, the same `Usage` shape as chat messages. All System One services report token counts; a request that was answered with malformed answers keeps its usage. Local classifiers such as `llama-cpp-classify` report no usage.
|
|
938
939
|
|
|
939
|
-
`ClassifierOptions.temperature` divides the answer logits by the given value before they are normalized; values above 1 soften the distribution. APIs that cannot apply it, such as System One, ignore it.
|
|
940
|
+
`ClassifierOptions.temperature` divides the answer logits by the given value before they are normalized; values above 1 soften the distribution. APIs that cannot apply it, such as System One and OpenAI Decisions, ignore it.
|
|
941
|
+
|
|
942
|
+
`ClassifierContext.images` holds image blocks (`{ type: "image", data, mimeType }`) judged together with the state. Only models whose `input` includes `"image"` accept them; `classify()` returns an error result for other models.
|
|
943
|
+
|
|
944
|
+
### OpenAI Decisions
|
|
945
|
+
|
|
946
|
+
The `openai-decisions` API calls OpenAI's [Decisions API](https://developers.openai.com/api/docs/guides/decisions) (`POST /v1/decisions`). GPT-6 Luna is its only model. The state is sent as JSON text; with images, the input is one user message with the state followed by the images as data URLs (at most 128). `choice` and `score` questions map to the Decisions types of the same name. `bool` questions become `predicate` questions; predicates have no criteria field, so the meanings of true and false are appended to the instructions. If OpenAI refuses to answer a question, the whole result is an error. Usage reports input tokens only, priced at the catalog input rate with OpenAI's long-context multiplier.
|
|
947
|
+
|
|
948
|
+
The endpoint needs an OpenAI API key; Sign in with ChatGPT credentials are rejected, so the `openai` provider hides `gpt-6-luna` from `getAvailableOfType('classifier')` while it uses OAuth. The API rejects inputs above 922K tokens, but requests that run longer than about five seconds, currently above roughly 600K input tokens, fail with a gateway timeout (504).
|
|
940
949
|
|
|
941
950
|
### Chat models on llama.cpp
|
|
942
951
|
|
|
952
|
+
llama.cpp also serves decision models such as Julia-1 or Kev natively through `/v1/systemone`. Since llama.cpp 0.6.0, `GET /models` lists `decisions` in a model's `architecture.output_modalities` for these models. They use the `typesafe-system-one` API with the server's `/v1` URL as `baseUrl` (for example `http://127.0.0.1:8080/v1`); it needs an `apiKey`, which llama.cpp ignores unless it was started with `--api-key`. The `llama-cpp-classify` API is the fallback for chat models.
|
|
953
|
+
|
|
943
954
|
The `llama-cpp-classify` API turns a chat model served by llama.cpp's `llama-server` into a classifier. Each question becomes one chat prompt: the state, every question of the request, the state again, and the question with its answers under single-token labels (letters for a choice, `Yes`/`No` for a bool, digits for a score). The prompt up to the final question is shared by all questions of a request, so the server's prompt cache evaluates the state once per request. The server returns the log-probabilities of the next token, and the answer is the softmax over the label tokens. Choices support up to 62 options and scores up to 10 levels. The model's `baseUrl` is the server URL; a trailing `/v1` is ignored. In router mode, the model ID selects the model.
|
|
944
955
|
|
|
945
956
|
```typescript
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAiC,MAAM,mBAAmB,CAAC;AAoBlE,OAAO,KAAK,EASX,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAMb,MAAM,aAAa,CAAC;AAmJrB,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AA2ChE,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB;AAwRD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAiC,MAAM,mBAAmB,CAAC;AAoBlE,OAAO,KAAK,EASX,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAMb,MAAM,aAAa,CAAC;AAmJrB,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AA2ChE,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB;AAwRD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CA2WzE,CAAC;AA2BF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CAgDlF,CAAC"}
|
|
@@ -425,21 +425,53 @@ export const stream = (model, context, options) => {
|
|
|
425
425
|
client = created.client;
|
|
426
426
|
isOAuth = created.isOAuthToken;
|
|
427
427
|
}
|
|
428
|
-
let params = buildParams(model, normalizedContext, isOAuth, options);
|
|
429
|
-
const nextParams = await options?.onPayload?.(params, model);
|
|
430
|
-
if (nextParams !== undefined) {
|
|
431
|
-
params = { ...nextParams, stream: true };
|
|
432
|
-
}
|
|
433
428
|
const requestOptions = {
|
|
434
429
|
...(options?.signal ? { signal: options.signal } : {}),
|
|
435
430
|
...(options?.timeoutMs !== undefined ? { timeout: options.timeoutMs } : {}),
|
|
436
431
|
maxRetries: 0,
|
|
437
432
|
};
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
433
|
+
let disablePreferredStrictTools = false;
|
|
434
|
+
let response;
|
|
435
|
+
for (;;) {
|
|
436
|
+
options?.signal?.throwIfAborted();
|
|
437
|
+
const built = buildParams(model, normalizedContext, isOAuth, options, disablePreferredStrictTools);
|
|
438
|
+
let params = built.params;
|
|
439
|
+
const nextParams = await options?.onPayload?.(params, model);
|
|
440
|
+
if (nextParams !== undefined) {
|
|
441
|
+
params = { ...nextParams, stream: true };
|
|
442
|
+
}
|
|
443
|
+
try {
|
|
444
|
+
response = await retryProviderRequest(() => client.beta.messages.create(params, requestOptions).asResponse(), {
|
|
445
|
+
maxRetries: options?.maxRetries,
|
|
446
|
+
maxRetryDelayMs: options?.maxRetryDelayMs,
|
|
447
|
+
signal: options?.signal,
|
|
448
|
+
});
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
catch (error) {
|
|
452
|
+
// Only downgrade a rejected request, never an already-started stream.
|
|
453
|
+
if (options?.signal?.aborted ||
|
|
454
|
+
disablePreferredStrictTools ||
|
|
455
|
+
!built.hasPreferredStrictTools ||
|
|
456
|
+
!(error instanceof Anthropic.APIError) ||
|
|
457
|
+
error.status !== 400 ||
|
|
458
|
+
error.type !== "invalid_request_error" ||
|
|
459
|
+
![
|
|
460
|
+
"The compiled grammar is too large",
|
|
461
|
+
"Too many strict tools (",
|
|
462
|
+
"Schemas contains too many parameters with union types (",
|
|
463
|
+
].some((message) => error.message.includes(message))) {
|
|
464
|
+
throw error;
|
|
465
|
+
}
|
|
466
|
+
disablePreferredStrictTools = true;
|
|
467
|
+
appendAssistantMessageDiagnostic(output, {
|
|
468
|
+
type: "anthropic_strict_tools_fallback",
|
|
469
|
+
timestamp: Date.now(),
|
|
470
|
+
error: { message: error.message },
|
|
471
|
+
details: { reason: "strict_tool_limits_exceeded" },
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
}
|
|
443
475
|
await options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
|
|
444
476
|
stream.push({ type: "start", partial: output });
|
|
445
477
|
const blocks = output.content;
|
|
@@ -855,7 +887,7 @@ function getBetaFeatures(model, context, isOAuthToken, nativeToolChanges, option
|
|
|
855
887
|
features.push(INLINE_TOOLS_BETA);
|
|
856
888
|
return [...new Set(features)];
|
|
857
889
|
}
|
|
858
|
-
function buildParams(model, context, isOAuthToken, options) {
|
|
890
|
+
function buildParams(model, context, isOAuthToken, options, disablePreferredStrictTools = false) {
|
|
859
891
|
const { cacheControl } = getCacheControl(model, options?.cacheRetention, options?.env);
|
|
860
892
|
const compat = getAnthropicCompat(model);
|
|
861
893
|
const initialSystemMessage = getInitialSystemMessage(context.messages);
|
|
@@ -866,11 +898,20 @@ function buildParams(model, context, isOAuthToken, options) {
|
|
|
866
898
|
// by value in a `tool_addition` block, which also expresses same-name redefinitions.
|
|
867
899
|
// Anthropic rejects a tool list where every tool is deferred, so there must be an initial
|
|
868
900
|
// active tool to anchor the placeholder. Otherwise the current tool list is sent.
|
|
901
|
+
let hasPreferredStrictTools = false;
|
|
902
|
+
const convertToolDefinitions = (tools, toolCacheControl) => {
|
|
903
|
+
const definitions = convertTools(tools, isOAuthToken, compat.supportsEagerToolInputStreaming, compat.supportsStrictTools, toolCacheControl, disablePreferredStrictTools);
|
|
904
|
+
if (definitions.some((definition, index) => {
|
|
905
|
+
const config = tools[index].constrainedSampling;
|
|
906
|
+
return definition.strict === true && config && config.type === "json_schema" && config.strict === "prefer";
|
|
907
|
+
})) {
|
|
908
|
+
hasPreferredStrictTools = true;
|
|
909
|
+
}
|
|
910
|
+
return definitions;
|
|
911
|
+
};
|
|
869
912
|
const initialTools = initialSystemMessage?.toolsAdded ?? [];
|
|
870
913
|
const nativeToolChanges = compat.supportsMidConvoSystemMessages && compat.supportsMidConvoToolChanges && initialTools.length > 0;
|
|
871
|
-
const converted = convertMessages(conversationMessages, isOAuthToken, cacheControl, compat.allowEmptySignature, model.compat?.supportsMidConvoEffort === true ? model.provider : undefined, nativeToolChanges
|
|
872
|
-
? (tools) => convertTools(tools, isOAuthToken, compat.supportsEagerToolInputStreaming, compat.supportsStrictTools)
|
|
873
|
-
: undefined);
|
|
914
|
+
const converted = convertMessages(conversationMessages, isOAuthToken, cacheControl, compat.allowEmptySignature, model.compat?.supportsMidConvoEffort === true ? model.provider : undefined, nativeToolChanges ? convertToolDefinitions : undefined);
|
|
874
915
|
const activeEffort = options?.effort ?? "high";
|
|
875
916
|
const betaFeatures = getBetaFeatures(model, context, isOAuthToken, nativeToolChanges, options);
|
|
876
917
|
const params = {
|
|
@@ -922,15 +963,12 @@ function buildParams(model, context, isOAuthToken, options) {
|
|
|
922
963
|
// placeholder. The list never changes afterwards: later tools are defined by value in
|
|
923
964
|
// `tool_addition` blocks and withdrawn by `tool_removal`, so the cached prefix survives
|
|
924
965
|
// every tool change.
|
|
925
|
-
params.tools = [
|
|
926
|
-
...convertTools(initialTools, isOAuthToken, compat.supportsEagerToolInputStreaming, compat.supportsStrictTools, toolCacheControl),
|
|
927
|
-
DEFERRED_TOOL_PLACEHOLDER,
|
|
928
|
-
];
|
|
966
|
+
params.tools = [...convertToolDefinitions(initialTools, toolCacheControl), DEFERRED_TOOL_PLACEHOLDER];
|
|
929
967
|
}
|
|
930
968
|
else {
|
|
931
969
|
const tools = getCurrentTools(context.messages);
|
|
932
970
|
if (tools.length > 0) {
|
|
933
|
-
params.tools =
|
|
971
|
+
params.tools = convertToolDefinitions(tools, toolCacheControl);
|
|
934
972
|
}
|
|
935
973
|
}
|
|
936
974
|
// Managed effort models always use adaptive thinking so prefix mismatches can
|
|
@@ -986,7 +1024,7 @@ function buildParams(model, context, isOAuthToken, options) {
|
|
|
986
1024
|
if (allowedFallbackModels && allowedFallbackModels.length > 0) {
|
|
987
1025
|
params.fallbacks = allowedFallbackModels.map((fallback) => ({ model: fallback.model }));
|
|
988
1026
|
}
|
|
989
|
-
return params;
|
|
1027
|
+
return { params, hasPreferredStrictTools };
|
|
990
1028
|
}
|
|
991
1029
|
// Normalize tool call IDs to match Anthropic's required pattern and length
|
|
992
1030
|
function normalizeToolCallId(id) {
|
|
@@ -1255,11 +1293,14 @@ const isAnthropicStrictUnsupportedKeyword = (key, value) => {
|
|
|
1255
1293
|
return typeof value !== "string" || !ANTHROPIC_STRICT_STRING_FORMATS.has(value);
|
|
1256
1294
|
return false;
|
|
1257
1295
|
};
|
|
1258
|
-
function convertTools(tools, isOAuthToken, supportsEagerToolInputStreaming, supportsStrictTools, cacheControl) {
|
|
1296
|
+
function convertTools(tools, isOAuthToken, supportsEagerToolInputStreaming, supportsStrictTools, cacheControl, disablePreferredStrictTools = false) {
|
|
1259
1297
|
if (!tools)
|
|
1260
1298
|
return [];
|
|
1261
1299
|
return tools.map((tool, index) => {
|
|
1262
|
-
const
|
|
1300
|
+
const config = tool.constrainedSampling;
|
|
1301
|
+
const strict = disablePreferredStrictTools && config && config.type === "json_schema" && config.strict === "prefer"
|
|
1302
|
+
? undefined
|
|
1303
|
+
: resolveJsonSchemaStrictSampling(tool, supportsStrictTools, isAnthropicStrictUnsupportedKeyword);
|
|
1263
1304
|
const parameters = getJsonSchemaToolParameters(tool, strict);
|
|
1264
1305
|
const schema = parameters;
|
|
1265
1306
|
const legacyInputSchema = {
|