| /** | |
| * Definitions and helpers for tools that AI models can request during a | |
| * workflow. | |
| * | |
| * A tool names an operation, describes the parameters it accepts, declares | |
| * successful and failed results, and can require approval before execution. | |
| * This module supports tools defined by the application, tools built into a | |
| * provider, and dynamic tools whose schema is known only at runtime. It also | |
| * includes the shared types and conversion helpers needed by language-model | |
| * requests, tool handlers, and provider integrations. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| import * as Context from "../../Context.js"; | |
| import { constFalse, constTrue, identity } from "../../Function.js"; | |
| import { pipeArguments } from "../../Pipeable.js"; | |
| import * as Predicate from "../../Predicate.js"; | |
| import * as Schema from "../../Schema.js"; | |
| import * as SchemaAST from "../../SchemaAST.js"; | |
| // ============================================================================= | |
| // Type Ids | |
| // ============================================================================= | |
| /** | |
| * Runtime type identifier carried by Effect AI tool values. | |
| * | |
| * **Details** | |
| * | |
| * The tool type guards use this marker, together with more specific markers, | |
| * to distinguish user-defined, provider-defined, and dynamic tools. | |
| * | |
| * @category type IDs | |
| * @since 4.0.0 | |
| */ | |
| export const TypeId = "~effect/ai/Tool"; | |
| /** | |
| * Runtime type identifier carried by provider-defined tools. | |
| * | |
| * **Details** | |
| * | |
| * `isProviderDefined` uses this marker to distinguish tools that are built into | |
| * an AI provider from user-defined and dynamic tools. | |
| * | |
| * @category type IDs | |
| * @since 4.0.0 | |
| */ | |
| export const ProviderDefinedTypeId = "~effect/ai/Tool/ProviderDefined"; | |
| /** | |
| * Runtime type identifier carried by dynamic tools. | |
| * | |
| * **Details** | |
| * | |
| * `isDynamic` uses this marker to distinguish tools whose schema may be | |
| * provided at runtime from user-defined and provider-defined tools. | |
| * | |
| * @category type IDs | |
| * @since 4.0.0 | |
| */ | |
| export const DynamicTypeId = "~effect/ai/Tool/Dynamic"; | |
| // ============================================================================= | |
| // Type Guards | |
| // ============================================================================= | |
| /** | |
| * Type guard to check if a value is a user-defined tool. | |
| * | |
| * **Example** (Checking for user-defined tools) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * import { Tool } from "effect/unstable/ai" | |
| * | |
| * const UserDefinedTool = Tool.make("Calculator", { | |
| * description: "Performs basic arithmetic operations", | |
| * parameters: Schema.Struct({ | |
| * operation: Schema.Literals(["add", "subtract", "multiply", "divide"]), | |
| * a: Schema.Number, | |
| * b: Schema.Number | |
| * }), | |
| * success: Schema.Number | |
| * }) | |
| * | |
| * const ProviderDefinedTool = Tool.providerDefined({ | |
| * id: "openai.web_search", | |
| * customName: "OpenAiWebSearch", | |
| * providerName: "web_search", | |
| * args: Schema.Struct({ | |
| * query: Schema.String | |
| * }), | |
| * success: Schema.Struct({ | |
| * results: Schema.Array(Schema.Struct({ | |
| * title: Schema.String, | |
| * url: Schema.String, | |
| * snippet: Schema.String | |
| * })) | |
| * }) | |
| * }) | |
| * | |
| * console.log(Tool.isUserDefined(UserDefinedTool)) // true | |
| * console.log(Tool.isUserDefined(ProviderDefinedTool)) // false | |
| * ``` | |
| * | |
| * @category guards | |
| * @since 4.0.0 | |
| */ | |
| export const isUserDefined = u => Predicate.hasProperty(u, TypeId) && !isProviderDefined(u) && !isDynamic(u); | |
| /** | |
| * Type guard to check if a value is a provider-defined tool. | |
| * | |
| * **Example** (Checking for provider-defined tools) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * import { Tool } from "effect/unstable/ai" | |
| * | |
| * const UserDefinedTool = Tool.make("Calculator", { | |
| * description: "Performs basic arithmetic operations", | |
| * parameters: Schema.Struct({ | |
| * operation: Schema.Literals(["add", "subtract", "multiply", "divide"]), | |
| * a: Schema.Number, | |
| * b: Schema.Number | |
| * }), | |
| * success: Schema.Number | |
| * }) | |
| * | |
| * const ProviderDefinedTool = Tool.providerDefined({ | |
| * id: "openai.web_search", | |
| * customName: "OpenAiWebSearch", | |
| * providerName: "web_search", | |
| * args: Schema.Struct({ | |
| * query: Schema.String | |
| * }), | |
| * success: Schema.Struct({ | |
| * results: Schema.Array(Schema.Struct({ | |
| * title: Schema.String, | |
| * url: Schema.String, | |
| * snippet: Schema.String | |
| * })) | |
| * }) | |
| * }) | |
| * | |
| * console.log(Tool.isProviderDefined(UserDefinedTool)) // false | |
| * console.log(Tool.isProviderDefined(ProviderDefinedTool)) // true | |
| * ``` | |
| * | |
| * @category guards | |
| * @since 4.0.0 | |
| */ | |
| export const isProviderDefined = u => Predicate.hasProperty(u, ProviderDefinedTypeId); | |
| /** | |
| * Type guard to check if a value is a dynamic tool. | |
| * | |
| * **Example** (Checking for dynamic tools) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * import { Tool } from "effect/unstable/ai" | |
| * | |
| * const DynamicTool = Tool.dynamic("DynamicTool", { | |
| * parameters: { type: "object", properties: {} } | |
| * }) | |
| * | |
| * const UserDefinedTool = Tool.make("Calculator", { | |
| * parameters: Schema.Struct({ a: Schema.Number, b: Schema.Number }), | |
| * success: Schema.Number | |
| * }) | |
| * | |
| * console.log(Tool.isDynamic(DynamicTool)) // true | |
| * console.log(Tool.isDynamic(UserDefinedTool)) // false | |
| * ``` | |
| * | |
| * @category guards | |
| * @since 4.0.0 | |
| */ | |
| export const isDynamic = u => Predicate.hasProperty(u, DynamicTypeId); | |
| // ============================================================================= | |
| // Constructors | |
| // ============================================================================= | |
| const Proto = { | |
| [TypeId]: { | |
| _Requirements: identity | |
| }, | |
| pipe() { | |
| return pipeArguments(this, arguments); | |
| }, | |
| addDependency() { | |
| return userDefinedProto({ | |
| ...this | |
| }); | |
| }, | |
| setParameters(parametersSchema) { | |
| return userDefinedProto({ | |
| ...this, | |
| parametersSchema | |
| }); | |
| }, | |
| setSuccess(successSchema) { | |
| return userDefinedProto({ | |
| ...this, | |
| successSchema | |
| }); | |
| }, | |
| setFailure(failureSchema) { | |
| return userDefinedProto({ | |
| ...this, | |
| failureSchema | |
| }); | |
| }, | |
| annotate(tag, value) { | |
| return userDefinedProto({ | |
| ...this, | |
| annotations: Context.add(this.annotations, tag, value) | |
| }); | |
| }, | |
| annotateMerge(context) { | |
| return userDefinedProto({ | |
| ...this, | |
| annotations: Context.merge(this.annotations, context) | |
| }); | |
| } | |
| }; | |
| const ProviderDefinedProto = { | |
| ...Proto, | |
| [ProviderDefinedTypeId]: ProviderDefinedTypeId | |
| }; | |
| const DynamicProto = { | |
| ...Proto, | |
| [DynamicTypeId]: DynamicTypeId | |
| }; | |
| const userDefinedProto = options => { | |
| const self = Object.assign(Object.create(Proto), options); | |
| self.id = `effect/ai/Tool/${options.name}`; | |
| return self; | |
| }; | |
| const providerDefinedProto = options => Object.assign(Object.create(ProviderDefinedProto), { | |
| ...options | |
| }); | |
| const dynamicProto = options => { | |
| const self = Object.assign(Object.create(DynamicProto), options); | |
| self.id = `effect/ai/Tool/${options.name}`; | |
| return self; | |
| }; | |
| /** | |
| * Creates a user-defined tool with the specified name and configuration. | |
| * | |
| * **Details** | |
| * | |
| * This is the primary constructor for creating custom tools that AI models | |
| * can call. The tool definition includes parameter validation, success/failure | |
| * schemas, and optional service dependencies. | |
| * | |
| * If a tool accepts no parameters but still needs an explicit empty object | |
| * schema, use {@link EmptyParams}. | |
| * | |
| * **Example** (Creating a tool without parameters) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * import { Tool } from "effect/unstable/ai" | |
| * | |
| * // Simple tool with no parameters | |
| * const GetCurrentTime = Tool.make("GetCurrentTime", { | |
| * description: "Returns the current timestamp", | |
| * success: Schema.Number | |
| * }) | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export const make = (name, options) => { | |
| const successSchema = options?.success ?? Schema.Void; | |
| const failureSchema = options?.failure ?? Schema.Never; | |
| return userDefinedProto({ | |
| name, | |
| description: options?.description, | |
| parametersSchema: options?.parameters ?? EmptyParams, | |
| successSchema, | |
| failureSchema, | |
| failureMode: options?.failureMode ?? "error", | |
| annotations: Context.empty(), | |
| needsApproval: options?.needsApproval | |
| }); | |
| }; | |
| /** | |
| * Creates a dynamic tool that can accept either an Effect Schema or a raw | |
| * JSON Schema for its parameters. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you do not know a tool schema at compile time, such as MCP tools | |
| * discovered at runtime or tools from external configurations. | |
| * | |
| * **Details** | |
| * | |
| * - When `parameters` is an Effect Schema: full type safety with validation | |
| * - When `parameters` is a JSON Schema: handler receives `unknown`, no validation | |
| * | |
| * **Example** (Creating a dynamic tool) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * import { Tool } from "effect/unstable/ai" | |
| * | |
| * // With Effect Schema (typed parameters) | |
| * const Calculator = Tool.dynamic("Calculator", { | |
| * parameters: Schema.Struct({ | |
| * operation: Schema.Literals(["add", "subtract"]), | |
| * a: Schema.Number, | |
| * b: Schema.Number | |
| * }), | |
| * success: Schema.Number | |
| * }) | |
| * | |
| * // With JSON Schema (untyped parameters) | |
| * const McpTool = Tool.dynamic("McpTool", { | |
| * description: "Tool from MCP server", | |
| * parameters: { | |
| * type: "object", | |
| * properties: { query: { type: "string" } }, | |
| * required: ["query"] | |
| * } | |
| * }) | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export const dynamic = (name, options) => { | |
| const successSchema = options?.success ?? Schema.Unknown; | |
| const failureSchema = options?.failure ?? Schema.Never; | |
| const rawParameters = options?.parameters ?? Schema.Unknown; | |
| const isEffectSchema = Schema.isSchema(rawParameters); | |
| const parametersSchema = isEffectSchema ? rawParameters : Schema.Unknown; | |
| const jsonSchema = isEffectSchema ? undefined : rawParameters; | |
| return dynamicProto({ | |
| name, | |
| description: options?.description, | |
| parametersSchema, | |
| successSchema, | |
| failureSchema, | |
| failureMode: options?.failureMode ?? "error", | |
| annotations: Context.empty(), | |
| needsApproval: options?.needsApproval, | |
| jsonSchema | |
| }); | |
| }; | |
| /** | |
| * Creates a provider-defined tool which leverages functionality built into a | |
| * large language model provider (e.g. web search, code execution). | |
| * | |
| * **Details** | |
| * | |
| * These tools are executed by the large language model provider rather than | |
| * by your application. However, they can optionally require custom handlers | |
| * implemented in your application to process provider generated results. | |
| * | |
| * **Example** (Creating a provider-defined tool) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * import { Tool } from "effect/unstable/ai" | |
| * | |
| * // Web search tool provided by OpenAI | |
| * const WebSearch = Tool.providerDefined({ | |
| * id: "openai.web_search", | |
| * customName: "OpenAiWebSearch", | |
| * providerName: "web_search", | |
| * args: Schema.Struct({ | |
| * query: Schema.String | |
| * }), | |
| * success: Schema.Struct({ | |
| * results: Schema.Array(Schema.Struct({ | |
| * title: Schema.String, | |
| * url: Schema.String, | |
| * content: Schema.String | |
| * })) | |
| * }) | |
| * }) | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export const providerDefined = options => args => { | |
| const failureMode = Predicate.isNotUndefined(args) && "failureMode" in args ? args.failureMode : undefined; | |
| const successSchema = options?.success ?? Schema.Void; | |
| const failureSchema = options?.failure ?? Schema.Never; | |
| return providerDefinedProto({ | |
| id: options.id, | |
| name: options.customName, | |
| providerName: options.providerName, | |
| args: args, | |
| argsSchema: options?.args ?? Schema.Void, | |
| requiresHandler: options.requiresHandler ?? false, | |
| parametersSchema: options?.parameters ?? Schema.Void, | |
| successSchema, | |
| failureSchema, | |
| failureMode: failureMode ?? "error" | |
| }); | |
| }; | |
| // ============================================================================= | |
| // Utilities | |
| // ============================================================================= | |
| /** | |
| * Maps between a provider-defined tool name and the name given to the tool by | |
| * the Effect AI SDK. | |
| * | |
| * **Details** | |
| * | |
| * The custom names used by the Effect AI SDK are to allow for toolkits which | |
| * contain tools from multiple different providers that would otherwise have | |
| * naming conflicts (i.e. `"web_search"`) to instead use custom names (i.e. | |
| * `"OpenAiWebSearch"`). | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export class NameMapper { | |
| #customToProvider = /*#__PURE__*/new Map(); | |
| #providerToCustom = /*#__PURE__*/new Map(); | |
| constructor(tools) { | |
| for (const tool of tools) { | |
| if (isProviderDefined(tool)) { | |
| this.#customToProvider.set(tool.name, tool.providerName); | |
| this.#providerToCustom.set(tool.providerName, tool.name); | |
| } | |
| } | |
| } | |
| /** | |
| * Returns a list of the user-specified tool names in the name mapper. | |
| */ | |
| get customNames() { | |
| return Array.from(this.#customToProvider.keys()); | |
| } | |
| /** | |
| * Returns a list of the provider-specified tool names in the name mapper. | |
| */ | |
| get providerNames() { | |
| return Array.from(this.#providerToCustom.keys()); | |
| } | |
| /** | |
| * Returns the user-specified tool name that corresponds with the provided | |
| * provider-specified tool name. | |
| * | |
| * **Details** | |
| * | |
| * If the provider-specified tool name was not registered with the name mapper, | |
| * then the provider-specified tool name is returned. | |
| */ | |
| getCustomName(providerName) { | |
| return this.#providerToCustom.get(providerName) ?? providerName; | |
| } | |
| /** | |
| * Returns the provider-specified tool name that corresponds with the provided | |
| * user-specified tool name. | |
| * | |
| * **Details** | |
| * | |
| * If the user-specified tool name was not registered with the name mapper, | |
| * then the user-specified tool name is returned. | |
| */ | |
| getProviderName(customName) { | |
| return this.#customToProvider.get(customName) ?? customName; | |
| } | |
| } | |
| /** | |
| * Extracts the description from a tool's metadata. | |
| * | |
| * **Details** | |
| * | |
| * Returns the tool's description if explicitly set, otherwise attempts to | |
| * extract it from the parameter schema's AST annotations. | |
| * | |
| * **Example** (Reading a tool description) | |
| * | |
| * ```ts | |
| * import { Tool } from "effect/unstable/ai" | |
| * | |
| * const myTool = Tool.make("example", { | |
| * description: "This is an example tool" | |
| * }) | |
| * | |
| * const description = Tool.getDescription(myTool) | |
| * console.log(description) // "This is an example tool" | |
| * ``` | |
| * | |
| * @category getters | |
| * @since 4.0.0 | |
| */ | |
| export const getDescription = tool => { | |
| if (tool.description !== undefined) { | |
| return tool.description; | |
| } | |
| if (Schema.isSchema(tool.parametersSchema)) { | |
| return SchemaAST.resolveDescription(tool.parametersSchema.ast); | |
| } | |
| return undefined; | |
| }; | |
| /** | |
| * Generates a JSON Schema for a tool. | |
| * | |
| * **Details** | |
| * | |
| * This function creates a JSON Schema representation that can be used by | |
| * large language models to indicate the structure and type of the parameters | |
| * that a given tool call should receive. | |
| * | |
| * May accept an optional `CodecTransformer` which can be used to transform the | |
| * tool parameter schema so that the resultant JSON schema for the tool call | |
| * parameters are in a format that conforms to any provider-specific constraints. | |
| * | |
| * **Example** (Generating a tool JSON schema) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * import { Tool } from "effect/unstable/ai" | |
| * | |
| * const weatherTool = Tool.make("get_weather", { | |
| * parameters: Schema.Struct({ | |
| * location: Schema.String, | |
| * units: Schema.Literals(["celsius", "fahrenheit"]) | |
| * }) | |
| * }) | |
| * | |
| * const jsonSchema = Tool.getJsonSchema(weatherTool) | |
| * console.log(jsonSchema) | |
| * // { | |
| * // type: "object", | |
| * // properties: { | |
| * // location: { type: "string" }, | |
| * // units: { type: "string", enum: ["celsius", "fahrenheit"] } | |
| * // }, | |
| * // required: ["location", "units"] | |
| * // } | |
| * ``` | |
| * | |
| * @category getters | |
| * @since 4.0.0 | |
| */ | |
| export const getJsonSchema = (tool, options) => { | |
| if (isDynamic(tool) && tool.jsonSchema !== undefined) { | |
| return tool.jsonSchema; | |
| } | |
| return getJsonSchemaFromSchema(tool.parametersSchema, options); | |
| }; | |
| /** | |
| * Generates a JSON Schema from an Effect `Schema`. | |
| * | |
| * **Details** | |
| * | |
| * If a `CodecTransformer` is supplied, the transformed schema's JSON Schema is | |
| * returned. Otherwise, the schema is converted with | |
| * `Schema.toJsonSchemaDocument` and any generated definitions are attached as | |
| * `$defs`. | |
| * | |
| * @category converting | |
| * @since 4.0.0 | |
| */ | |
| export const getJsonSchemaFromSchema = (schema, options) => { | |
| if (Predicate.isNotUndefined(options?.transformer)) { | |
| return options.transformer(schema).jsonSchema; | |
| } | |
| const document = Schema.toJsonSchemaDocument(schema); | |
| if (Object.keys(document.definitions).length > 0) { | |
| document.schema.$defs = document.definitions; | |
| } | |
| return document.schema; | |
| }; | |
| // ============================================================================= | |
| // Annotations | |
| // ============================================================================= | |
| /** | |
| * Annotation for providing a human-readable title for tools. | |
| * | |
| * **Example** (Annotating a tool title) | |
| * | |
| * ```ts | |
| * import { Tool } from "effect/unstable/ai" | |
| * | |
| * const myTool = Tool.make("calculate_tip") | |
| * .annotate(Tool.Title, "Tip Calculator") | |
| * ``` | |
| * | |
| * @category annotations | |
| * @since 4.0.0 | |
| */ | |
| export class Title extends /*#__PURE__*/Context.Service()("effect/ai/Tool/Title") {} | |
| /** | |
| * Annotation for providing tool metadata for MCP. | |
| * | |
| * **Example** (Annotating MCP metadata) | |
| * | |
| * ```ts | |
| * import { Tool } from "effect/unstable/ai" | |
| * | |
| * const myCalculatorUi = Tool.make("calculator_ui", {}) | |
| * .annotate(Tool.Meta, { ui: { resourceUri: "ui://example/calculator-ui" } }) | |
| * ``` | |
| * | |
| * @category annotations | |
| * @since 4.0.0 | |
| */ | |
| export class Meta extends /*#__PURE__*/Context.Service()("effect/ai/Tool/Meta") {} | |
| /** | |
| * Annotation indicating whether a tool only reads data without making changes. | |
| * | |
| * **Details** | |
| * | |
| * This is emitted as the MCP `readOnlyHint`; unannotated tools default to | |
| * `false`. | |
| * | |
| * **Example** (Marking a tool as read-only) | |
| * | |
| * ```ts | |
| * import { Tool } from "effect/unstable/ai" | |
| * | |
| * const readOnlyTool = Tool.make("get_user_info") | |
| * .annotate(Tool.Readonly, true) | |
| * ``` | |
| * | |
| * @category annotations | |
| * @since 4.0.0 | |
| */ | |
| export const Readonly = /*#__PURE__*/Context.Reference("effect/ai/Tool/Readonly", { | |
| defaultValue: constFalse | |
| }); | |
| /** | |
| * Annotation indicating whether a tool may perform destructive operations. | |
| * | |
| * **Details** | |
| * | |
| * This is emitted as the MCP `destructiveHint`; unannotated tools default to | |
| * `true`, so annotate safe tools with `false`. | |
| * | |
| * **Example** (Marking a tool as non-destructive) | |
| * | |
| * ```ts | |
| * import { Tool } from "effect/unstable/ai" | |
| * | |
| * const safeTool = Tool.make("search_database") | |
| * .annotate(Tool.Destructive, false) | |
| * ``` | |
| * | |
| * @category annotations | |
| * @since 4.0.0 | |
| */ | |
| export const Destructive = /*#__PURE__*/Context.Reference("effect/ai/Tool/Destructive", { | |
| defaultValue: constTrue | |
| }); | |
| /** | |
| * Annotation indicating whether a tool can be called repeatedly with the same | |
| * parameters without changing the result beyond the first call. | |
| * | |
| * **Details** | |
| * | |
| * This is emitted as the MCP `idempotentHint`; unannotated tools default to | |
| * `false`. | |
| * | |
| * **Example** (Marking a tool as idempotent) | |
| * | |
| * ```ts | |
| * import { Tool } from "effect/unstable/ai" | |
| * | |
| * const idempotentTool = Tool.make("get_current_time") | |
| * .annotate(Tool.Idempotent, true) | |
| * ``` | |
| * | |
| * @category annotations | |
| * @since 4.0.0 | |
| */ | |
| export const Idempotent = /*#__PURE__*/Context.Reference("effect/ai/Tool/Idempotent", { | |
| defaultValue: constFalse | |
| }); | |
| /** | |
| * Annotation indicating whether a tool may interact with arbitrary external | |
| * data or systems. | |
| * | |
| * **Details** | |
| * | |
| * This is emitted as the MCP `openWorldHint`; unannotated tools default to | |
| * `true`. | |
| * | |
| * **Example** (Disabling open-world access) | |
| * | |
| * ```ts | |
| * import { Tool } from "effect/unstable/ai" | |
| * | |
| * const restrictedTool = Tool.make("internal_operation") | |
| * .annotate(Tool.OpenWorld, false) | |
| * ``` | |
| * | |
| * @category annotations | |
| * @since 4.0.0 | |
| */ | |
| export const OpenWorld = /*#__PURE__*/Context.Reference("effect/ai/Tool/OpenWorld", { | |
| defaultValue: constTrue | |
| }); | |
| /** | |
| * Annotation controlling whether strict JSON schema mode is enabled for a tool. | |
| * | |
| * **Details** | |
| * | |
| * When `true`, providers that support strict mode will send `strict: true` to | |
| * the model API (e.g. OpenAI's Structured Outputs). | |
| * | |
| * When `false`, strict mode is disabled and `strict: false` is sent. | |
| * | |
| * When `undefined` (default), the provider's global configuration determines | |
| * the behavior (e.g. `Config.strictJsonSchema` for OpenAI). | |
| * | |
| * **Example** (Disabling strict JSON schema mode) | |
| * | |
| * ```ts | |
| * import { Tool } from "effect/unstable/ai" | |
| * | |
| * const flexibleTool = Tool.make("search") | |
| * .annotate(Tool.Strict, false) | |
| * ``` | |
| * | |
| * @category annotations | |
| * @since 4.0.0 | |
| */ | |
| export const Strict = /*#__PURE__*/Context.Reference("effect/ai/Tool/Strict", { | |
| defaultValue: () => undefined | |
| }); | |
| /** | |
| * Returns the strict mode setting for a tool, or `undefined` if not set. | |
| * | |
| * **When to use** | |
| * | |
| * Use to inspect the per-tool strict JSON Schema override attached through | |
| * `Tool.Strict`. | |
| * | |
| * **Gotchas** | |
| * | |
| * `undefined` means no per-tool override is set. It is distinct from `false`; | |
| * provider or global configuration determines the final behavior. | |
| * | |
| * @see {@link Strict} for the annotation read by this helper | |
| * | |
| * @category getters | |
| * @since 4.0.0 | |
| */ | |
| export const getStrictMode = tool => Context.get(tool.annotations, Strict); | |
| // Licensed under BSD-3-Clause (below code only) | |
| // Code adapted from https://github.com/fastify/secure-json-parse/blob/783fcb1b5434709466759847cec974381939673a/index.js | |
| // | |
| // Copyright (c) Effectful Technologies, Inc (https://effectful.co) | |
| // Copyright (c) 2019 The Fastify Team | |
| // Copyright (c) 2019, Sideway Inc, and project contributors | |
| // All rights reserved. | |
| // | |
| // The complete list of contributors can be found at: | |
| // - https://github.com/hapijs/bourne/graphs/contributors | |
| // - https://github.com/fastify/secure-json-parse/graphs/contributors | |
| // - https://github.com/Effect-TS/effect/commits/main/packages/ai/ai/src/Tool.ts | |
| // | |
| // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: | |
| // | |
| // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. | |
| // | |
| // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. | |
| // | |
| // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| const suspectProtoRx = /"__proto__"\s*:/; | |
| const suspectConstructorRx = /"constructor"\s*:/; | |
| function _parse(text) { | |
| // Parse normally | |
| const obj = JSON.parse(text); | |
| // Ignore null and non-objects | |
| if (obj === null || typeof obj !== "object") { | |
| return obj; | |
| } | |
| if (suspectProtoRx.test(text) === false && suspectConstructorRx.test(text) === false) { | |
| return obj; | |
| } | |
| // Scan result for proto keys | |
| return filter(obj); | |
| } | |
| function filter(obj) { | |
| let next = [obj]; | |
| while (next.length) { | |
| const nodes = next; | |
| next = []; | |
| for (const node of nodes) { | |
| if (Object.prototype.hasOwnProperty.call(node, "__proto__")) { | |
| throw new SyntaxError("Object contains forbidden prototype property"); | |
| } | |
| if (Object.prototype.hasOwnProperty.call(node, "constructor") && Object.prototype.hasOwnProperty.call(node.constructor, "prototype")) { | |
| throw new SyntaxError("Object contains forbidden prototype property"); | |
| } | |
| for (const key in node) { | |
| const value = node[key]; | |
| if (value && typeof value === "object") { | |
| next.push(value); | |
| } | |
| } | |
| } | |
| } | |
| return obj; | |
| } | |
| /** | |
| * Parses JSON text while rejecting prototype-pollution keys. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need a JSON parser that throws for invalid JSON or unsafe | |
| * object shapes. | |
| * | |
| * **Gotchas** | |
| * | |
| * Invalid JSON throws through `JSON.parse`. Parsed objects containing an own | |
| * `__proto__` property or a dangerous `constructor.prototype` shape throw a | |
| * `SyntaxError`. | |
| * | |
| * @category unsafe | |
| * @since 4.0.0 | |
| */ | |
| export const unsafeSecureJsonParse = text => { | |
| // Performance optimization, see https://github.com/fastify/secure-json-parse/pull/90 | |
| const { | |
| stackTraceLimit | |
| } = Error; | |
| Error.stackTraceLimit = 0; | |
| try { | |
| return _parse(text); | |
| } finally { | |
| Error.stackTraceLimit = stackTraceLimit; | |
| } | |
| }; | |
| /** | |
| * Schema for tools that accept no parameters. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need an explicit no-parameter `parameters` schema for a tool. | |
| * | |
| * **Details** | |
| * | |
| * This is `Schema.Record(Schema.String, Schema.Never)`, representing an empty | |
| * object parameter shape with no additional properties. | |
| * | |
| * @see {@link make} for the tool constructor that defaults omitted parameters to this schema | |
| * | |
| * @category schemas | |
| * @since 4.0.0 | |
| */ | |
| export const EmptyParams = /*#__PURE__*/Schema.Record(Schema.String, Schema.Never); | |
| /** @internal */ | |
| export function isEmptyParamsRecord(indexSignature) { | |
| return indexSignature.parameter === SchemaAST.string && SchemaAST.isNever(indexSignature.type); | |
| } | |
| //# sourceMappingURL=Tool.js.map |
Xet Storage Details
- Size:
- 26.3 kB
- Xet hash:
- 200385599148817708c5a4a5daf359ea3f5dd9ef811f600b94716e1c7b315e96
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.