PtcRunner.SubAgent.Signature.Coercion (PtcRunner v0.9.0)

Copy Markdown View Source

Coerce values to expected types with warning generation.

This module handles lenient input validation for LLMs, which sometimes produce slightly malformed data (e.g., quoted numbers, missing types).

Elixir to Signature Type Mapping

Primitive Types

Elixir TypeSignature TypeNotes
String.t():stringUTF-8 strings
binary():stringSame as String.t()
integer():intWhole numbers
non_neg_integer():intValidation can enforce >= 0
pos_integer():intValidation can enforce > 0
float():floatDecimal numbers
number():floatAccepts int or float
boolean():boolBoolean values
atom():keywordAtoms as keywords
any():anyMatches everything

Collection Types

Elixir TypeSignature TypeNotes
list(t)[:t]Homogeneous lists
map():mapUntyped dictionary
%{key: type}{:key :type}Typed map

Special Types

Elixir TypeSignature TypeRationale
DateTime.t():stringISO 8601 format, LLM-friendly

| t | nil | :t? | Optional via ? suffix |

Coercion Rules

LLMs sometimes produce slightly malformed data. Input coercion handles common cases:

FromToBehaviorWarning
"42":int42Yes
"3.14":float3.14Yes
"-5":int-5Yes
"true":booltrueYes
"false":boolfalseYes
42:float42.0No (silent widening)
42.0:intError-
"hello":intError-
:atom:string"atom"Yes
"atom":keyword:atomYes

Output validation is strict - no coercion applied.

Coercion Modes

ModeInput CoercionOutput ValidationUse Case
:enabled (default)Apply with warningsStrictProduction
:warn_onlyApply with warningsLog warnings onlyDevelopment
:strictNo coercionStrict, reject extra fieldsTesting
:disabledSkipSkipDebugging

Examples

iex> PtcRunner.SubAgent.Signature.Coercion.coerce("42", :int)
{:ok, 42, ["coerced string \"42\" to integer"]}

iex> PtcRunner.SubAgent.Signature.Coercion.coerce(42, :float)
{:ok, 42.0, []}

iex> PtcRunner.SubAgent.Signature.Coercion.coerce("hello", :int)
{:error, "cannot coerce string \"hello\" to integer"}

iex> PtcRunner.SubAgent.Signature.Coercion.coerce("hello", :keyword)
{:ok, :hello, ["coerced string \"hello\" to keyword"]}

Summary

Functions

Coerce a value to the expected type.

Coerce a value to the expected type with options.

Types

coercion_result()

@type coercion_result() :: {:ok, term(), [String.t()]} | {:error, String.t()}

Functions

coerce(value, type)

@spec coerce(term(), atom() | tuple()) :: coercion_result()

Coerce a value to the expected type.

Returns {:ok, coerced_value, warnings} or {:error, reason}.

Examples

iex> PtcRunner.SubAgent.Signature.Coercion.coerce("42", :int)
{:ok, 42, ["coerced string \"42\" to integer"]}

iex> PtcRunner.SubAgent.Signature.Coercion.coerce(42, :float)
{:ok, 42.0, []}

iex> PtcRunner.SubAgent.Signature.Coercion.coerce("hello", :int)
{:error, "cannot coerce string \"hello\" to integer"}

iex> PtcRunner.SubAgent.Signature.Coercion.coerce(%{"id" => "42", "name" => "Alice"}, {:map, [{"id", :int}, {"name", :string}]})
{:ok, %{"id" => 42, "name" => "Alice"}, ["coerced string \"42\" to integer"]}

coerce(value, type, opts)

@spec coerce(term(), atom() | tuple(), keyword()) :: coercion_result()

Coerce a value to the expected type with options.

Options:

  • :nested - whether this is a nested coercion (default: false)

Returns {:ok, coerced_value, warnings} or {:error, reason}.