ToolCall.Typespec (ToolCall v0.1.0)

Copy Markdown

Converts a quoted typespec AST (aliases already expanded) into ToolCall.IR.Type.

This module is deliberately total: unknown shapes degrade to :any rather than raising, so that adding a new typespec construct to a codebase never breaks compilation of unrelated tools.

Summary

Functions

True when the tree contains an object whose keys the spec does not fix.

True when the tree contains a tuple, which strict mode cannot express.

True when the tree contains a domain JSON cannot represent at all.

True when the tree has no JSON type the model can constrain against.

Validates a runtime ToolCall.IR.Tool and returns a copy with prepared parameters.

Validates a runtime ToolCall.IR.Type and returns a copy with compiled pattern_regex.

Functions

open_object?(type)

@spec open_object?(ToolCall.IR.Type.t() | nil) :: boolean()

True when the tree contains an object whose keys the spec does not fix.

Strict mode closes every object with additionalProperties: false, so an open one reaches the model as an object that accepts no key at all.

to_type(ast, providers \\ [ToolCall.Providers.Stdlib])

@spec to_type(term(), [module()]) :: ToolCall.IR.Type.t()

Maps a quoted type to a ToolCall.IR.Type.

Aliases must already be expanded (Macro.expand/2), otherwise remote types such as String.t() arrive as {:__aliases__, ...} and degrade to :any.

Built-in providers resolve String, Keyword, Path and the calendar types and are used by default. Passing providers explicitly replaces that default; use ToolCall, types: [...] prepends the built-ins before the user list. A provider must return a well-formed IR node: an unknown kind, a non-string or uncompilable pattern, a malformed transform, enum values, bounds, bound ordering, array minimums, formats, nullability, field metadata, or malformed fields/items/variants/additional raises at compile time.

tuple?(type)

@spec tuple?(ToolCall.IR.Type.t() | nil) :: boolean()

True when the tree contains a tuple, which strict mode cannot express.

OpenAI rejects prefixItems, so the emitter can only keep the arity.

unrepresentable?(arg1)

@spec unrepresentable?(ToolCall.IR.Type.t()) :: boolean()

True when the tree contains a domain JSON cannot represent at all.

:any is not unrepresentable: it means "unknown", and JSON can carry any value for it. Only :unsupported (functions, pids, refs) is.

untyped?(type)

@spec untyped?(ToolCall.IR.Type.t() | nil) :: boolean()

True when the tree has no JSON type the model can constrain against.

This is broader than unrepresentable?/1: :any and open objects do have a JSON encoding, but they cannot be typed in the strict schema subset.

validate_tool!(tool)

@spec validate_tool!(ToolCall.IR.Tool.t()) :: ToolCall.IR.Tool.t()

Validates a runtime ToolCall.IR.Tool and returns a copy with prepared parameters.

Parameter types are validated through validate_type!/1, so hand-built patterns receive a compiled pattern_regex; tool and parameter metadata are checked as well.

validate_type!(type)

@spec validate_type!(ToolCall.IR.Type.t()) :: ToolCall.IR.Type.t()

Validates a runtime ToolCall.IR.Type and returns a copy with compiled pattern_regex.

Use this for hand-built types before passing them to ToolCall.Coerce or rendering them with ToolCall.Emit.