@core-ai/core-ai 0.23.0 → 0.25.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/dist/index.d.ts +118 -4
- package/dist/index.js +461 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -143,10 +143,19 @@ type ToolResultMessage = {
|
|
|
143
143
|
*/
|
|
144
144
|
metadata?: Record<string, unknown>;
|
|
145
145
|
};
|
|
146
|
-
type ToolDefinition = {
|
|
146
|
+
type ToolDefinition<TParameters extends z.ZodType = z.ZodType> = {
|
|
147
147
|
name: string;
|
|
148
148
|
description: string;
|
|
149
|
-
parameters:
|
|
149
|
+
parameters: TParameters;
|
|
150
|
+
/**
|
|
151
|
+
* `true` opts this tool into provider-enforced schema adherence. The
|
|
152
|
+
* schema must then satisfy the strict-capable schema contract (closed
|
|
153
|
+
* objects, every key required — use `.nullable()` instead of
|
|
154
|
+
* `.optional()` — and the shared keyword subset). Omitted or `false`
|
|
155
|
+
* means non-strict on every provider. Check
|
|
156
|
+
* `model.capabilities.tools.strictSchemas` before opting in.
|
|
157
|
+
*/
|
|
158
|
+
strict?: boolean;
|
|
150
159
|
};
|
|
151
160
|
type ToolSet = Record<string, ToolDefinition>;
|
|
152
161
|
type ToolChoice = 'auto' | 'none' | 'required' | {
|
|
@@ -169,6 +178,12 @@ type ChatInputModality = 'text' | 'image' | 'file' | 'audio' | 'video';
|
|
|
169
178
|
* from `generate` / `stream`, not those dedicated APIs.
|
|
170
179
|
*/
|
|
171
180
|
type ChatOutputModality = 'text' | 'image' | 'audio' | 'video';
|
|
181
|
+
type ToolSchemaStrictnessCapabilities = {
|
|
182
|
+
supported: false;
|
|
183
|
+
} | {
|
|
184
|
+
supported: true;
|
|
185
|
+
maxStrictTools?: number;
|
|
186
|
+
};
|
|
172
187
|
type ModelCapabilities = {
|
|
173
188
|
reasoning: {
|
|
174
189
|
mode: 'unsupported' | 'optional' | 'always-on';
|
|
@@ -189,6 +204,9 @@ type ModelCapabilities = {
|
|
|
189
204
|
*/
|
|
190
205
|
output: readonly ChatOutputModality[];
|
|
191
206
|
};
|
|
207
|
+
tools: {
|
|
208
|
+
strictSchemas: ToolSchemaStrictnessCapabilities;
|
|
209
|
+
};
|
|
192
210
|
};
|
|
193
211
|
type ChatModel = {
|
|
194
212
|
readonly provider: string;
|
|
@@ -433,6 +451,27 @@ type GeneratedImage = {
|
|
|
433
451
|
revisedPrompt?: string;
|
|
434
452
|
};
|
|
435
453
|
|
|
454
|
+
/**
|
|
455
|
+
* A single violation of the strict-capable schema contract, reported against
|
|
456
|
+
* the JSON Schema derived from the tool's Zod `parameters`.
|
|
457
|
+
*/
|
|
458
|
+
type StrictToolSchemaViolation = {
|
|
459
|
+
toolName: string;
|
|
460
|
+
/** Dot path into the tool's JSON Schema, e.g. `properties.limit`. */
|
|
461
|
+
path: string;
|
|
462
|
+
/** What is wrong and how to fix it. */
|
|
463
|
+
message: string;
|
|
464
|
+
};
|
|
465
|
+
/**
|
|
466
|
+
* Checks the JSON Schema of a strict tool against the strict-capable schema
|
|
467
|
+
* contract: closed objects with every key required, the basic type set, and
|
|
468
|
+
* the keyword/format subset every strict-capable provider accepts.
|
|
469
|
+
*
|
|
470
|
+
* Pure and non-throwing — returns every violation found so callers can report
|
|
471
|
+
* them all at once.
|
|
472
|
+
*/
|
|
473
|
+
declare function getStrictToolSchemaViolations(toolName: string, schema: Record<string, unknown>): StrictToolSchemaViolation[];
|
|
474
|
+
|
|
436
475
|
declare class CoreAIError extends Error {
|
|
437
476
|
readonly cause?: unknown;
|
|
438
477
|
readonly provider?: string;
|
|
@@ -441,6 +480,29 @@ declare class CoreAIError extends Error {
|
|
|
441
480
|
declare class ValidationError extends CoreAIError {
|
|
442
481
|
constructor(message: string, cause?: unknown, provider?: string);
|
|
443
482
|
}
|
|
483
|
+
type ToolSchemaStrictnessErrorReason = 'unsupported' | 'limit-exceeded' | 'invalid-schema';
|
|
484
|
+
type ToolSchemaStrictnessErrorOptions = {
|
|
485
|
+
providerId: string;
|
|
486
|
+
modelId: string;
|
|
487
|
+
toolNames: readonly string[];
|
|
488
|
+
} & ({
|
|
489
|
+
reason: 'unsupported';
|
|
490
|
+
} | {
|
|
491
|
+
reason: 'limit-exceeded';
|
|
492
|
+
maxStrictTools: number;
|
|
493
|
+
} | {
|
|
494
|
+
reason: 'invalid-schema';
|
|
495
|
+
violations: readonly StrictToolSchemaViolation[];
|
|
496
|
+
});
|
|
497
|
+
declare class ToolSchemaStrictnessError extends ValidationError {
|
|
498
|
+
readonly providerId: string;
|
|
499
|
+
readonly modelId: string;
|
|
500
|
+
readonly toolNames: readonly string[];
|
|
501
|
+
readonly reason: ToolSchemaStrictnessErrorReason;
|
|
502
|
+
readonly maxStrictTools?: number;
|
|
503
|
+
readonly violations?: readonly StrictToolSchemaViolation[];
|
|
504
|
+
constructor(options: ToolSchemaStrictnessErrorOptions);
|
|
505
|
+
}
|
|
444
506
|
type UnsupportedInputModalityErrorOptions = {
|
|
445
507
|
modelId: string;
|
|
446
508
|
providerId: string;
|
|
@@ -503,6 +565,15 @@ declare class ContextLengthExceededError extends ProviderError {
|
|
|
503
565
|
readonly actualTokens?: number;
|
|
504
566
|
constructor(message: string, provider: string, options?: ContextLengthExceededErrorOptions);
|
|
505
567
|
}
|
|
568
|
+
type ProviderQuotaExceededErrorOptions = ProviderErrorOptions;
|
|
569
|
+
/**
|
|
570
|
+
* The provider account cannot accept requests because its billing quota or
|
|
571
|
+
* credit balance is exhausted. This is not retryable until the account
|
|
572
|
+
* configuration changes.
|
|
573
|
+
*/
|
|
574
|
+
declare class ProviderQuotaExceededError extends ProviderError {
|
|
575
|
+
constructor(message: string, provider: string, options?: ProviderQuotaExceededErrorOptions);
|
|
576
|
+
}
|
|
506
577
|
type RateLimitErrorOptions = ProviderErrorOptions & {
|
|
507
578
|
retryAfterSeconds?: number;
|
|
508
579
|
};
|
|
@@ -565,16 +636,42 @@ declare function parseRetryAfterSeconds(headers: Headers | Record<string, string
|
|
|
565
636
|
/** Reads `Retry-After` from common SDK error shapes that expose `headers`. */
|
|
566
637
|
declare function getRetryAfterSecondsFromError(error: unknown): number | undefined;
|
|
567
638
|
|
|
568
|
-
declare function defineTool(options: ToolDefinition): ToolDefinition
|
|
639
|
+
declare function defineTool<TParameters extends ToolDefinition['parameters']>(options: ToolDefinition<TParameters>): ToolDefinition<TParameters>;
|
|
569
640
|
|
|
570
641
|
/**
|
|
571
642
|
* Convert a Zod schema to a JSON Schema object using Zod 4's native
|
|
572
643
|
* `z.toJSONSchema()`.
|
|
573
644
|
*/
|
|
574
645
|
declare function zodSchemaToJsonSchema(schema: z.ZodType): Record<string, unknown>;
|
|
646
|
+
/**
|
|
647
|
+
* Normalizes the JSON Schema of a strict tool for providers whose strict mode
|
|
648
|
+
* requires closed objects (OpenAI-style APIs). The transform is semantics
|
|
649
|
+
* preserving with respect to the tool's Zod schema:
|
|
650
|
+
*
|
|
651
|
+
* - drops `$schema` (metadata, not a constraint),
|
|
652
|
+
* - sets `additionalProperties: false` on object nodes where absent, which
|
|
653
|
+
* matches `z.object()` semantics (unknown keys are stripped at parse time),
|
|
654
|
+
* - drops Zod's implicit safe-integer bounds on integer nodes (see
|
|
655
|
+
* {@link isImplicitSafeIntegerBound}),
|
|
656
|
+
* - rewrites `oneOf` to `anyOf`. Zod emits `oneOf` only for
|
|
657
|
+
* `z.discriminatedUnion()`, whose branches are disjoint by construction, so
|
|
658
|
+
* the two keywords accept the same values there — and strict-capable
|
|
659
|
+
* providers accept `anyOf` only.
|
|
660
|
+
*
|
|
661
|
+
* It never widens or narrows what the user's Zod schema accepts; schemas that
|
|
662
|
+
* cannot be expressed in the strict subset are rejected by the contract
|
|
663
|
+
* validator instead of being rewritten.
|
|
664
|
+
*/
|
|
665
|
+
declare function normalizeStrictJsonSchema(schema: Record<string, unknown>): Record<string, unknown>;
|
|
575
666
|
|
|
576
667
|
declare function stripModelDateSuffix(modelId: string): string;
|
|
577
668
|
|
|
669
|
+
declare const UNSUPPORTED_TOOL_SCHEMA_STRICTNESS: Readonly<{
|
|
670
|
+
readonly supported: false;
|
|
671
|
+
}>;
|
|
672
|
+
declare const SUPPORTED_TOOL_SCHEMA_STRICTNESS: Readonly<{
|
|
673
|
+
readonly supported: true;
|
|
674
|
+
}>;
|
|
578
675
|
declare function clampReasoningEffort(effort: ReasoningEffort, supportedEfforts: readonly ReasoningEffort[]): ReasoningEffort;
|
|
579
676
|
/** Text in, text out — the default chat modality profile. */
|
|
580
677
|
declare const TEXT_ONLY_MODALITIES: {
|
|
@@ -607,6 +704,23 @@ type ValidateInputModalitiesOptions = {
|
|
|
607
704
|
*/
|
|
608
705
|
declare function validateInputModalities({ messages, capabilities, modelId, providerId, }: ValidateInputModalitiesOptions): void;
|
|
609
706
|
|
|
707
|
+
type ValidateToolSchemaStrictnessOptions = {
|
|
708
|
+
tools: ToolSet;
|
|
709
|
+
capabilities: ModelCapabilities;
|
|
710
|
+
providerId: string;
|
|
711
|
+
modelId: string;
|
|
712
|
+
};
|
|
713
|
+
/**
|
|
714
|
+
* Validates the strict tools of a request before it reaches the provider.
|
|
715
|
+
*
|
|
716
|
+
* Strictness is per-tool opt-in: only tools with `strict: true` are checked.
|
|
717
|
+
* Throws {@link ToolSchemaStrictnessError} when the model is known not to
|
|
718
|
+
* support strict schemas (`unsupported`), when more tools opt in than the
|
|
719
|
+
* model allows (`limit-exceeded`), or when a strict tool's schema falls
|
|
720
|
+
* outside the strict-capable schema contract (`invalid-schema`).
|
|
721
|
+
*/
|
|
722
|
+
declare function validateToolSchemaStrictness({ tools, capabilities, providerId, modelId, }: ValidateToolSchemaStrictnessOptions): void;
|
|
723
|
+
|
|
610
724
|
declare const UNKNOWN_MODEL: unique symbol;
|
|
611
725
|
type ModelCapabilitiesRegistry<TCapabilities extends ModelCapabilities = ModelCapabilities> = Record<string, TCapabilities> & {
|
|
612
726
|
[UNKNOWN_MODEL]?: TCapabilities;
|
|
@@ -683,4 +797,4 @@ type GenerateImageParams = ImageGenerateOptions & {
|
|
|
683
797
|
};
|
|
684
798
|
declare function generateImage(params: GenerateImageParams): Promise<ImageGenerateResult>;
|
|
685
799
|
|
|
686
|
-
export { AbortedError, type AssistantContentPart, type AssistantMessage, type AssistantTextPart, type AudioPart, type BaseGenerateOptions, type ChatInputModality, type ChatInputTokenDetails, type ChatModel, type ChatModelMiddleware, type ChatOutputModality, type ChatOutputTokenDetails, type ChatStream, type ChatUsage, ContextLengthExceededError, type ContextLengthExceededErrorOptions, CoreAIError, type CreateChatStreamOptions, type EmbedOptions, type EmbedProviderOptions, type EmbedResult, type EmbeddingModel, type EmbeddingModelMiddleware, type EmbeddingUsage, type FilePart, type FinishReason, type GenerateObjectOptions, type GenerateObjectResult, type GenerateOptions, type GenerateProviderOptions, type GenerateResult, type GeneratedImage, type ImageGenerateOptions, type ImageGenerateResult, type ImageModel, type ImageModelMiddleware, type ImagePart, type ImageProviderOptions, MULTIMODAL_INPUT_MODALITIES, type Message, type ModelCapabilities, type ModelCapabilitiesRegistry, ModelOverloadedError, type ModelOverloadedErrorOptions, type ObjectStream, type ObjectStreamEvent, ProviderError, type ProviderErrorOptions, RateLimitError, type RateLimitErrorOptions, type ReasoningConfig, type ReasoningEffort, type ReasoningPart, RetryableProviderError, ServiceUnavailableError, type ServiceUnavailableErrorOptions, StreamAbortedError, type StreamEvent, type StreamObjectOptions, StructuredOutputError, StructuredOutputNoObjectGeneratedError, StructuredOutputParseError, StructuredOutputValidationError, type SystemMessage, TEXT_ONLY_MODALITIES, type TextPart, type ToolCall, type ToolCallPart, type ToolChoice, type ToolDefinition, type ToolResultMessage, type ToolSet, UNKNOWN_MODEL, UnsupportedInputModalityError, type UnsupportedInputModalityErrorOptions, type UserContentPart, type UserMessage, type ValidateInputModalitiesOptions, ValidationError, asObject, asRecord, assistantMessage, clampReasoningEffort, createChatStream, createObjectStream, defineTool, embed, generate, generateImage, generateObject, getErrorMessage, getHttpStatusCode, getProviderMetadata, getRegisteredModelCapabilities, getRetryAfterSecondsFromError, getString, isAbortErrorByName, isRateLimitStatus, isTransientUnavailableStatus, parseRetryAfterSeconds, resultToMessage, safeParseJsonObject, stream, streamObject, stripModelDateSuffix, supportsInputModality, supportsOutputModality, validateInputModalities, wrapChatModel, wrapEmbeddingModel, wrapImageModel, zodSchemaToJsonSchema };
|
|
800
|
+
export { AbortedError, type AssistantContentPart, type AssistantMessage, type AssistantTextPart, type AudioPart, type BaseGenerateOptions, type ChatInputModality, type ChatInputTokenDetails, type ChatModel, type ChatModelMiddleware, type ChatOutputModality, type ChatOutputTokenDetails, type ChatStream, type ChatUsage, ContextLengthExceededError, type ContextLengthExceededErrorOptions, CoreAIError, type CreateChatStreamOptions, type EmbedOptions, type EmbedProviderOptions, type EmbedResult, type EmbeddingModel, type EmbeddingModelMiddleware, type EmbeddingUsage, type FilePart, type FinishReason, type GenerateObjectOptions, type GenerateObjectResult, type GenerateOptions, type GenerateProviderOptions, type GenerateResult, type GeneratedImage, type ImageGenerateOptions, type ImageGenerateResult, type ImageModel, type ImageModelMiddleware, type ImagePart, type ImageProviderOptions, MULTIMODAL_INPUT_MODALITIES, type Message, type ModelCapabilities, type ModelCapabilitiesRegistry, ModelOverloadedError, type ModelOverloadedErrorOptions, type ObjectStream, type ObjectStreamEvent, ProviderError, type ProviderErrorOptions, ProviderQuotaExceededError, type ProviderQuotaExceededErrorOptions, RateLimitError, type RateLimitErrorOptions, type ReasoningConfig, type ReasoningEffort, type ReasoningPart, RetryableProviderError, SUPPORTED_TOOL_SCHEMA_STRICTNESS, ServiceUnavailableError, type ServiceUnavailableErrorOptions, StreamAbortedError, type StreamEvent, type StreamObjectOptions, type StrictToolSchemaViolation, StructuredOutputError, StructuredOutputNoObjectGeneratedError, StructuredOutputParseError, StructuredOutputValidationError, type SystemMessage, TEXT_ONLY_MODALITIES, type TextPart, type ToolCall, type ToolCallPart, type ToolChoice, type ToolDefinition, type ToolResultMessage, type ToolSchemaStrictnessCapabilities, ToolSchemaStrictnessError, type ToolSchemaStrictnessErrorOptions, type ToolSchemaStrictnessErrorReason, type ToolSet, UNKNOWN_MODEL, UNSUPPORTED_TOOL_SCHEMA_STRICTNESS, UnsupportedInputModalityError, type UnsupportedInputModalityErrorOptions, type UserContentPart, type UserMessage, type ValidateInputModalitiesOptions, type ValidateToolSchemaStrictnessOptions, ValidationError, asObject, asRecord, assistantMessage, clampReasoningEffort, createChatStream, createObjectStream, defineTool, embed, generate, generateImage, generateObject, getErrorMessage, getHttpStatusCode, getProviderMetadata, getRegisteredModelCapabilities, getRetryAfterSecondsFromError, getStrictToolSchemaViolations, getString, isAbortErrorByName, isRateLimitStatus, isTransientUnavailableStatus, normalizeStrictJsonSchema, parseRetryAfterSeconds, resultToMessage, safeParseJsonObject, stream, streamObject, stripModelDateSuffix, supportsInputModality, supportsOutputModality, validateInputModalities, validateToolSchemaStrictness, wrapChatModel, wrapEmbeddingModel, wrapImageModel, zodSchemaToJsonSchema };
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,49 @@ var ValidationError = class extends CoreAIError {
|
|
|
15
15
|
this.name = "ValidationError";
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
|
+
var ToolSchemaStrictnessError = class extends ValidationError {
|
|
19
|
+
providerId;
|
|
20
|
+
modelId;
|
|
21
|
+
toolNames;
|
|
22
|
+
reason;
|
|
23
|
+
maxStrictTools;
|
|
24
|
+
violations;
|
|
25
|
+
constructor(options) {
|
|
26
|
+
super(
|
|
27
|
+
getToolSchemaStrictnessErrorMessage(options),
|
|
28
|
+
void 0,
|
|
29
|
+
options.providerId
|
|
30
|
+
);
|
|
31
|
+
this.name = "ToolSchemaStrictnessError";
|
|
32
|
+
this.providerId = options.providerId;
|
|
33
|
+
this.modelId = options.modelId;
|
|
34
|
+
this.toolNames = options.toolNames;
|
|
35
|
+
this.reason = options.reason;
|
|
36
|
+
if (options.reason === "limit-exceeded") {
|
|
37
|
+
this.maxStrictTools = options.maxStrictTools;
|
|
38
|
+
}
|
|
39
|
+
if (options.reason === "invalid-schema") {
|
|
40
|
+
this.violations = options.violations;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
function getToolSchemaStrictnessErrorMessage(options) {
|
|
45
|
+
const tools = options.toolNames.length === 0 ? "(none)" : options.toolNames.map((name) => `"${name}"`).join(", ");
|
|
46
|
+
switch (options.reason) {
|
|
47
|
+
case "limit-exceeded": {
|
|
48
|
+
const toolWord = options.maxStrictTools === 1 ? "tool" : "tools";
|
|
49
|
+
return `${options.providerId} model "${options.modelId}" supports at most ${options.maxStrictTools} strict ${toolWord}, but received ${options.toolNames.length}: ${tools}`;
|
|
50
|
+
}
|
|
51
|
+
case "invalid-schema": {
|
|
52
|
+
const violations = options.violations.map(
|
|
53
|
+
(violation) => `tool "${violation.toolName}"${violation.path === "" ? "" : ` at ${violation.path}`}: ${violation.message}`
|
|
54
|
+
).join("; ");
|
|
55
|
+
return `${options.providerId} model "${options.modelId}" received strict tools whose schemas are outside the strict-capable schema contract: ${violations}`;
|
|
56
|
+
}
|
|
57
|
+
case "unsupported":
|
|
58
|
+
return `${options.providerId} model "${options.modelId}" does not support per-tool strict schemas. Requested by: ${tools}`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
18
61
|
var UnsupportedInputModalityError = class extends ValidationError {
|
|
19
62
|
requestedModalities;
|
|
20
63
|
supportedModalities;
|
|
@@ -82,6 +125,12 @@ var ContextLengthExceededError = class extends ProviderError {
|
|
|
82
125
|
this.actualTokens = options.actualTokens;
|
|
83
126
|
}
|
|
84
127
|
};
|
|
128
|
+
var ProviderQuotaExceededError = class extends ProviderError {
|
|
129
|
+
constructor(message, provider, options = {}) {
|
|
130
|
+
super(message, provider, options);
|
|
131
|
+
this.name = "ProviderQuotaExceededError";
|
|
132
|
+
}
|
|
133
|
+
};
|
|
85
134
|
var RateLimitError = class extends RetryableProviderError {
|
|
86
135
|
retryAfterSeconds;
|
|
87
136
|
constructor(message, provider, options = {}) {
|
|
@@ -250,6 +299,348 @@ function zodSchemaToJsonSchema(schema) {
|
|
|
250
299
|
io: "input"
|
|
251
300
|
});
|
|
252
301
|
}
|
|
302
|
+
var MAX_SAFE_INTEGER_BOUND = 9007199254740991;
|
|
303
|
+
function isPlainObject(value) {
|
|
304
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
305
|
+
}
|
|
306
|
+
function isObjectSchemaNode(node) {
|
|
307
|
+
return node.type === "object" || isPlainObject(node.properties);
|
|
308
|
+
}
|
|
309
|
+
function isImplicitSafeIntegerBound(node, key) {
|
|
310
|
+
if (node.type !== "integer") {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
return key === "minimum" ? node.minimum === -MAX_SAFE_INTEGER_BOUND : node.maximum === MAX_SAFE_INTEGER_BOUND;
|
|
314
|
+
}
|
|
315
|
+
var NESTED_MAP_KEYWORDS = ["properties"];
|
|
316
|
+
var NESTED_LIST_KEYWORDS = ["anyOf", "oneOf", "allOf"];
|
|
317
|
+
var NESTED_SINGLE_KEYWORDS = ["items"];
|
|
318
|
+
var DEFINITION_KEYWORDS = ["$defs", "definitions"];
|
|
319
|
+
function getChildSchemaNodeEntries(node) {
|
|
320
|
+
const entries = getNamedChildEntries(node, NESTED_MAP_KEYWORDS);
|
|
321
|
+
for (const keyword of NESTED_SINGLE_KEYWORDS) {
|
|
322
|
+
if (node[keyword] !== void 0) {
|
|
323
|
+
entries.push({ segment: keyword, child: node[keyword] });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
for (const keyword of NESTED_LIST_KEYWORDS) {
|
|
327
|
+
const value = node[keyword];
|
|
328
|
+
if (Array.isArray(value)) {
|
|
329
|
+
value.forEach((child, index) => {
|
|
330
|
+
entries.push({ segment: `${keyword}.${index}`, child });
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return entries;
|
|
335
|
+
}
|
|
336
|
+
function getSchemaDefinitionEntries(node) {
|
|
337
|
+
return getNamedChildEntries(node, DEFINITION_KEYWORDS);
|
|
338
|
+
}
|
|
339
|
+
function getNamedChildEntries(node, keywords) {
|
|
340
|
+
const entries = [];
|
|
341
|
+
for (const keyword of keywords) {
|
|
342
|
+
const value = node[keyword];
|
|
343
|
+
if (isPlainObject(value)) {
|
|
344
|
+
for (const [key, child] of Object.entries(value)) {
|
|
345
|
+
entries.push({ segment: `${keyword}.${key}`, child });
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return entries;
|
|
350
|
+
}
|
|
351
|
+
function mapChildSchemaNodes(node, map) {
|
|
352
|
+
const result = { ...node };
|
|
353
|
+
for (const keyword of [...NESTED_MAP_KEYWORDS, ...DEFINITION_KEYWORDS]) {
|
|
354
|
+
const value = result[keyword];
|
|
355
|
+
if (isPlainObject(value)) {
|
|
356
|
+
result[keyword] = Object.fromEntries(
|
|
357
|
+
Object.entries(value).map(([key, child]) => [key, map(child)])
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
for (const keyword of NESTED_SINGLE_KEYWORDS) {
|
|
362
|
+
if (result[keyword] !== void 0) {
|
|
363
|
+
result[keyword] = map(result[keyword]);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
for (const keyword of NESTED_LIST_KEYWORDS) {
|
|
367
|
+
const value = result[keyword];
|
|
368
|
+
if (Array.isArray(value)) {
|
|
369
|
+
result[keyword] = value.map((child) => map(child));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
374
|
+
function normalizeStrictJsonSchema(schema) {
|
|
375
|
+
return normalizeObjectNode(schema);
|
|
376
|
+
}
|
|
377
|
+
function normalizeNode(node) {
|
|
378
|
+
return isPlainObject(node) ? normalizeObjectNode(node) : node;
|
|
379
|
+
}
|
|
380
|
+
function normalizeObjectNode(node) {
|
|
381
|
+
const stripped = {};
|
|
382
|
+
for (const [key, value] of Object.entries(node)) {
|
|
383
|
+
if (key === "$schema") {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
if ((key === "minimum" || key === "maximum") && isImplicitSafeIntegerBound(node, key)) {
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
stripped[key] = value;
|
|
390
|
+
}
|
|
391
|
+
const result = mapChildSchemaNodes(stripped, normalizeNode);
|
|
392
|
+
if (Array.isArray(result.oneOf) && result.anyOf === void 0) {
|
|
393
|
+
result.anyOf = result.oneOf;
|
|
394
|
+
delete result.oneOf;
|
|
395
|
+
}
|
|
396
|
+
if (isObjectSchemaNode(result) && result.additionalProperties === void 0) {
|
|
397
|
+
result.additionalProperties = false;
|
|
398
|
+
}
|
|
399
|
+
return result;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// src/strict-tool-schema-contract.ts
|
|
403
|
+
var MAX_SCHEMA_DEPTH = 64;
|
|
404
|
+
var ALLOWED_KEYWORDS = /* @__PURE__ */ new Set([
|
|
405
|
+
"$schema",
|
|
406
|
+
"$ref",
|
|
407
|
+
"$defs",
|
|
408
|
+
"definitions",
|
|
409
|
+
"type",
|
|
410
|
+
"properties",
|
|
411
|
+
"required",
|
|
412
|
+
"additionalProperties",
|
|
413
|
+
"items",
|
|
414
|
+
"enum",
|
|
415
|
+
"const",
|
|
416
|
+
"anyOf",
|
|
417
|
+
"oneOf",
|
|
418
|
+
"format",
|
|
419
|
+
"pattern",
|
|
420
|
+
"title",
|
|
421
|
+
"description",
|
|
422
|
+
"default",
|
|
423
|
+
"examples",
|
|
424
|
+
"minimum",
|
|
425
|
+
"maximum"
|
|
426
|
+
]);
|
|
427
|
+
var ALLOWED_TYPES = /* @__PURE__ */ new Set([
|
|
428
|
+
"object",
|
|
429
|
+
"array",
|
|
430
|
+
"string",
|
|
431
|
+
"number",
|
|
432
|
+
"integer",
|
|
433
|
+
"boolean",
|
|
434
|
+
"null"
|
|
435
|
+
]);
|
|
436
|
+
var ALLOWED_STRING_FORMATS = /* @__PURE__ */ new Set([
|
|
437
|
+
"date-time",
|
|
438
|
+
"time",
|
|
439
|
+
"date",
|
|
440
|
+
"duration",
|
|
441
|
+
"email",
|
|
442
|
+
"hostname",
|
|
443
|
+
"ipv4",
|
|
444
|
+
"ipv6",
|
|
445
|
+
"uuid"
|
|
446
|
+
]);
|
|
447
|
+
var BOUND_HINTS = {
|
|
448
|
+
minimum: "remove numeric bounds (.min()/.gt()/.gte()) \u2014 numeric range constraints are outside the strict-capable subset; validate ranges after parsing instead",
|
|
449
|
+
maximum: "remove numeric bounds (.max()/.lt()/.lte()) \u2014 numeric range constraints are outside the strict-capable subset; validate ranges after parsing instead"
|
|
450
|
+
};
|
|
451
|
+
var KEYWORD_HINTS = {
|
|
452
|
+
...BOUND_HINTS,
|
|
453
|
+
minLength: "remove .min()/.length() \u2014 string length constraints are outside the strict-capable subset; validate lengths after parsing instead",
|
|
454
|
+
maxLength: "remove .max()/.length() \u2014 string length constraints are outside the strict-capable subset; validate lengths after parsing instead",
|
|
455
|
+
exclusiveMinimum: "remove .gt() \u2014 numeric range constraints are outside the strict-capable subset; validate ranges after parsing instead",
|
|
456
|
+
exclusiveMaximum: "remove .lt() \u2014 numeric range constraints are outside the strict-capable subset; validate ranges after parsing instead",
|
|
457
|
+
multipleOf: "remove .multipleOf()/.step() \u2014 numeric constraints are outside the strict-capable subset; validate after parsing instead",
|
|
458
|
+
minItems: "remove .min()/.nonempty() on arrays \u2014 array length constraints are outside the strict-capable subset; validate after parsing instead",
|
|
459
|
+
maxItems: "remove .max() on arrays \u2014 array length constraints are outside the strict-capable subset; validate after parsing instead",
|
|
460
|
+
uniqueItems: "array uniqueness constraints are outside the strict-capable subset; validate after parsing instead",
|
|
461
|
+
prefixItems: "tuples (z.tuple) are not strict-capable; use a uniform z.array() or an object instead",
|
|
462
|
+
allOf: "intersections (z.intersection()/.and(), serialized as allOf) are not strict-capable; flatten the members into a single z.object() instead",
|
|
463
|
+
not: "negated schemas (not) are not strict-capable",
|
|
464
|
+
patternProperties: "pattern properties are not strict-capable; declare explicit keys with z.object()",
|
|
465
|
+
propertyNames: "property-name constraints are not strict-capable; declare explicit keys with z.object()"
|
|
466
|
+
};
|
|
467
|
+
function getStrictToolSchemaViolations(toolName, schema) {
|
|
468
|
+
const violations = [];
|
|
469
|
+
const refEdges = /* @__PURE__ */ new Map();
|
|
470
|
+
if (schema.type !== "object") {
|
|
471
|
+
violations.push({
|
|
472
|
+
toolName,
|
|
473
|
+
path: "type",
|
|
474
|
+
message: "tool parameters must be an object at the root; wrap the schema in z.object()"
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
walkNode(schema, "", "root", 0, toolName, violations, refEdges);
|
|
478
|
+
for (const { segment, child } of getSchemaDefinitionEntries(schema)) {
|
|
479
|
+
walkNode(child, segment, segment, 0, toolName, violations, refEdges);
|
|
480
|
+
}
|
|
481
|
+
for (const region of findCyclicRegions(refEdges)) {
|
|
482
|
+
violations.push({
|
|
483
|
+
toolName,
|
|
484
|
+
path: region === "root" ? "" : region,
|
|
485
|
+
message: "recursive schemas (z.lazy or self-referencing types) are not strict-capable"
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
return violations;
|
|
489
|
+
}
|
|
490
|
+
function walkNode(node, path, region, depth, toolName, violations, refEdges) {
|
|
491
|
+
if (!isPlainObject(node)) {
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
if (depth > MAX_SCHEMA_DEPTH) {
|
|
495
|
+
violations.push({
|
|
496
|
+
toolName,
|
|
497
|
+
path,
|
|
498
|
+
message: `schema nesting exceeds the supported depth of ${MAX_SCHEMA_DEPTH}`
|
|
499
|
+
});
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
for (const key of Object.keys(node)) {
|
|
503
|
+
if (ALLOWED_KEYWORDS.has(key)) {
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
const hint = KEYWORD_HINTS[key];
|
|
507
|
+
violations.push({
|
|
508
|
+
toolName,
|
|
509
|
+
path: joinPath(path, key),
|
|
510
|
+
message: hint ?? `keyword "${key}" is outside the strict-capable subset; remove it and validate after parsing instead`
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
validateBounds(node, path, toolName, violations);
|
|
514
|
+
validateType(node, path, toolName, violations);
|
|
515
|
+
validateFormat(node, path, toolName, violations);
|
|
516
|
+
validateObjectShape(node, path, toolName, violations);
|
|
517
|
+
validateRef(node, path, region, toolName, violations, refEdges);
|
|
518
|
+
for (const { segment, child } of getChildSchemaNodeEntries(node)) {
|
|
519
|
+
walkNode(
|
|
520
|
+
child,
|
|
521
|
+
joinPath(path, segment),
|
|
522
|
+
region,
|
|
523
|
+
depth + 1,
|
|
524
|
+
toolName,
|
|
525
|
+
violations,
|
|
526
|
+
refEdges
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
function validateBounds(node, path, toolName, violations) {
|
|
531
|
+
for (const key of ["minimum", "maximum"]) {
|
|
532
|
+
if (key in node && !isImplicitSafeIntegerBound(node, key)) {
|
|
533
|
+
violations.push({
|
|
534
|
+
toolName,
|
|
535
|
+
path: joinPath(path, key),
|
|
536
|
+
message: BOUND_HINTS[key]
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
function validateType(node, path, toolName, violations) {
|
|
542
|
+
if (!("type" in node)) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
const types = Array.isArray(node.type) ? node.type : [node.type];
|
|
546
|
+
for (const type of types) {
|
|
547
|
+
if (typeof type !== "string" || !ALLOWED_TYPES.has(type)) {
|
|
548
|
+
violations.push({
|
|
549
|
+
toolName,
|
|
550
|
+
path: joinPath(path, "type"),
|
|
551
|
+
message: `type "${String(type)}" is outside the strict-capable subset (object, array, string, number, integer, boolean, null)`
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
function validateFormat(node, path, toolName, violations) {
|
|
557
|
+
if (typeof node.format !== "string") {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (!ALLOWED_STRING_FORMATS.has(node.format)) {
|
|
561
|
+
violations.push({
|
|
562
|
+
toolName,
|
|
563
|
+
path: joinPath(path, "format"),
|
|
564
|
+
message: `format "${node.format}" is outside the strict-capable subset (${[...ALLOWED_STRING_FORMATS].join(", ")}); remove the format refinement (for URLs, use z.string() instead of z.url()) and validate after parsing instead`
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
function validateObjectShape(node, path, toolName, violations) {
|
|
569
|
+
if (!isObjectSchemaNode(node)) {
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
const properties = isPlainObject(node.properties) ? node.properties : {};
|
|
573
|
+
const required = new Set(
|
|
574
|
+
Array.isArray(node.required) ? node.required.filter((key) => typeof key === "string") : []
|
|
575
|
+
);
|
|
576
|
+
for (const key of Object.keys(properties)) {
|
|
577
|
+
if (!required.has(key)) {
|
|
578
|
+
violations.push({
|
|
579
|
+
toolName,
|
|
580
|
+
path: joinPath(path, `properties.${key}`),
|
|
581
|
+
message: `"${key}" is optional; strict schemas require every key \u2014 use .nullable() instead of .optional() (note that .default() also makes a key optional)`
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if ("additionalProperties" in node && node.additionalProperties !== false) {
|
|
586
|
+
violations.push({
|
|
587
|
+
toolName,
|
|
588
|
+
path: joinPath(path, "additionalProperties"),
|
|
589
|
+
message: "open objects are not strict-capable; use z.object() with explicit keys instead of z.record(), .catchall(), .passthrough(), or z.looseObject()"
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
function validateRef(node, path, region, toolName, violations, refEdges) {
|
|
594
|
+
if (typeof node.$ref !== "string") {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
if (node.$ref === "#") {
|
|
598
|
+
addRefEdge(refEdges, region, "root");
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
const match = /^#\/(\$defs|definitions)\/([^/]+)$/.exec(node.$ref);
|
|
602
|
+
if (!match) {
|
|
603
|
+
violations.push({
|
|
604
|
+
toolName,
|
|
605
|
+
path: joinPath(path, "$ref"),
|
|
606
|
+
message: `only local references into $defs are strict-capable; found "${node.$ref}"`
|
|
607
|
+
});
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
addRefEdge(refEdges, region, `${match[1]}.${match[2]}`);
|
|
611
|
+
}
|
|
612
|
+
function addRefEdge(refEdges, from, to) {
|
|
613
|
+
const edges = refEdges.get(from) ?? /* @__PURE__ */ new Set();
|
|
614
|
+
edges.add(to);
|
|
615
|
+
refEdges.set(from, edges);
|
|
616
|
+
}
|
|
617
|
+
function findCyclicRegions(refEdges) {
|
|
618
|
+
const cyclic = /* @__PURE__ */ new Set();
|
|
619
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
620
|
+
const done = /* @__PURE__ */ new Set();
|
|
621
|
+
function visit(region) {
|
|
622
|
+
if (done.has(region)) {
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if (visiting.has(region)) {
|
|
626
|
+
cyclic.add(region);
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
visiting.add(region);
|
|
630
|
+
for (const target of refEdges.get(region) ?? []) {
|
|
631
|
+
visit(target);
|
|
632
|
+
}
|
|
633
|
+
visiting.delete(region);
|
|
634
|
+
done.add(region);
|
|
635
|
+
}
|
|
636
|
+
for (const region of refEdges.keys()) {
|
|
637
|
+
visit(region);
|
|
638
|
+
}
|
|
639
|
+
return [...cyclic];
|
|
640
|
+
}
|
|
641
|
+
function joinPath(path, segment) {
|
|
642
|
+
return path === "" ? segment : `${path}.${segment}`;
|
|
643
|
+
}
|
|
253
644
|
|
|
254
645
|
// src/model-id.ts
|
|
255
646
|
var MODEL_DATE_SUFFIX_PATTERN = /[-@](?:\d{8}|\d{4}-\d{2}-\d{2})$/;
|
|
@@ -265,6 +656,12 @@ var EFFORT_RANK = {
|
|
|
265
656
|
high: 3,
|
|
266
657
|
max: 4
|
|
267
658
|
};
|
|
659
|
+
var UNSUPPORTED_TOOL_SCHEMA_STRICTNESS = Object.freeze({
|
|
660
|
+
supported: false
|
|
661
|
+
});
|
|
662
|
+
var SUPPORTED_TOOL_SCHEMA_STRICTNESS = Object.freeze({
|
|
663
|
+
supported: true
|
|
664
|
+
});
|
|
268
665
|
function clampReasoningEffort(effort, supportedEfforts) {
|
|
269
666
|
if (supportedEfforts.includes(effort)) {
|
|
270
667
|
return effort;
|
|
@@ -343,6 +740,63 @@ function collectRequestedInputModalities(messages) {
|
|
|
343
740
|
return [...requested];
|
|
344
741
|
}
|
|
345
742
|
|
|
743
|
+
// src/validate-tool-schema-strictness.ts
|
|
744
|
+
var contractViolationCache = /* @__PURE__ */ new WeakMap();
|
|
745
|
+
function getContractViolations(tool) {
|
|
746
|
+
let cached = contractViolationCache.get(tool.parameters);
|
|
747
|
+
if (cached === void 0) {
|
|
748
|
+
cached = getStrictToolSchemaViolations(
|
|
749
|
+
tool.name,
|
|
750
|
+
zodSchemaToJsonSchema(tool.parameters)
|
|
751
|
+
).map(({ path, message }) => ({ path, message }));
|
|
752
|
+
contractViolationCache.set(tool.parameters, cached);
|
|
753
|
+
}
|
|
754
|
+
return cached.map((violation) => ({ toolName: tool.name, ...violation }));
|
|
755
|
+
}
|
|
756
|
+
function validateToolSchemaStrictness({
|
|
757
|
+
tools,
|
|
758
|
+
capabilities,
|
|
759
|
+
providerId,
|
|
760
|
+
modelId
|
|
761
|
+
}) {
|
|
762
|
+
const strictTools = Object.values(tools).filter(
|
|
763
|
+
(tool) => tool.strict === true
|
|
764
|
+
);
|
|
765
|
+
if (strictTools.length === 0) {
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
const strictToolNames = strictTools.map((tool) => tool.name);
|
|
769
|
+
const strictCapabilities = capabilities.tools.strictSchemas;
|
|
770
|
+
if (!strictCapabilities.supported) {
|
|
771
|
+
throw new ToolSchemaStrictnessError({
|
|
772
|
+
providerId,
|
|
773
|
+
modelId,
|
|
774
|
+
toolNames: strictToolNames,
|
|
775
|
+
reason: "unsupported"
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
const maxStrictTools = strictCapabilities.maxStrictTools;
|
|
779
|
+
if (maxStrictTools !== void 0 && strictTools.length > maxStrictTools) {
|
|
780
|
+
throw new ToolSchemaStrictnessError({
|
|
781
|
+
providerId,
|
|
782
|
+
modelId,
|
|
783
|
+
toolNames: strictToolNames,
|
|
784
|
+
reason: "limit-exceeded",
|
|
785
|
+
maxStrictTools
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
const violations = strictTools.flatMap(getContractViolations);
|
|
789
|
+
if (violations.length > 0) {
|
|
790
|
+
throw new ToolSchemaStrictnessError({
|
|
791
|
+
providerId,
|
|
792
|
+
modelId,
|
|
793
|
+
toolNames: [...new Set(violations.map((v) => v.toolName))],
|
|
794
|
+
reason: "invalid-schema",
|
|
795
|
+
violations
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
346
800
|
// src/model-capabilities-registry.ts
|
|
347
801
|
var UNKNOWN_MODEL = /* @__PURE__ */ Symbol("unknown-model");
|
|
348
802
|
function getRegisteredModelCapabilities(registry, modelId) {
|
|
@@ -991,8 +1445,10 @@ export {
|
|
|
991
1445
|
MULTIMODAL_INPUT_MODALITIES,
|
|
992
1446
|
ModelOverloadedError,
|
|
993
1447
|
ProviderError,
|
|
1448
|
+
ProviderQuotaExceededError,
|
|
994
1449
|
RateLimitError,
|
|
995
1450
|
RetryableProviderError,
|
|
1451
|
+
SUPPORTED_TOOL_SCHEMA_STRICTNESS,
|
|
996
1452
|
ServiceUnavailableError,
|
|
997
1453
|
StreamAbortedError,
|
|
998
1454
|
StructuredOutputError,
|
|
@@ -1000,7 +1456,9 @@ export {
|
|
|
1000
1456
|
StructuredOutputParseError,
|
|
1001
1457
|
StructuredOutputValidationError,
|
|
1002
1458
|
TEXT_ONLY_MODALITIES,
|
|
1459
|
+
ToolSchemaStrictnessError,
|
|
1003
1460
|
UNKNOWN_MODEL,
|
|
1461
|
+
UNSUPPORTED_TOOL_SCHEMA_STRICTNESS,
|
|
1004
1462
|
UnsupportedInputModalityError,
|
|
1005
1463
|
ValidationError,
|
|
1006
1464
|
asObject,
|
|
@@ -1019,10 +1477,12 @@ export {
|
|
|
1019
1477
|
getProviderMetadata,
|
|
1020
1478
|
getRegisteredModelCapabilities,
|
|
1021
1479
|
getRetryAfterSecondsFromError,
|
|
1480
|
+
getStrictToolSchemaViolations,
|
|
1022
1481
|
getString,
|
|
1023
1482
|
isAbortErrorByName,
|
|
1024
1483
|
isRateLimitStatus,
|
|
1025
1484
|
isTransientUnavailableStatus,
|
|
1485
|
+
normalizeStrictJsonSchema,
|
|
1026
1486
|
parseRetryAfterSeconds,
|
|
1027
1487
|
resultToMessage,
|
|
1028
1488
|
safeParseJsonObject,
|
|
@@ -1032,6 +1492,7 @@ export {
|
|
|
1032
1492
|
supportsInputModality,
|
|
1033
1493
|
supportsOutputModality,
|
|
1034
1494
|
validateInputModalities,
|
|
1495
|
+
validateToolSchemaStrictness,
|
|
1035
1496
|
wrapChatModel,
|
|
1036
1497
|
wrapEmbeddingModel,
|
|
1037
1498
|
wrapImageModel,
|