PtcRunner.SubAgent.Signature (PtcRunner v0.9.0)

Copy Markdown View Source

Signature parsing and validation for SubAgents.

Signatures define the contract between agents and tools:

  • Input parameters - What the caller must provide
  • Output type - What the callee will return

Signature Format

Full format: (params) -> output Shorthand: output (equivalent to () -> output)

Types

  • Primitives: :string, :int, :float, :bool, :keyword, :any
  • Collections: [:type] (list), {field :type} (map), :map (untyped map)
  • Optional: :type? (nullable field or parameter)

Examples

iex> {:ok, sig} = Signature.parse("(name :string) -> {greeting :string}")
iex> sig
{:signature, [{"name", :string}], {:map, [{"greeting", :string}]}}

iex> {:ok, sig} = Signature.parse("{count :int}")
iex> sig
{:signature, [], {:map, [{"count", :int}]}}

Summary

Functions

Parse a signature string into internal format.

Format a signature back to string representation.

Check if signature returns a list type.

Convert a signature to JSON Schema format.

Validate data against a signature's return type.

Validate input parameters against a signature.

Types

field()

@type field() :: {String.t(), type()}

param()

@type param() :: {String.t(), type()}

return_type()

@type return_type() :: type()

signature()

@type signature() :: {:signature, [param()], return_type()}

type()

@type type() ::
  :string
  | :int
  | :float
  | :bool
  | :keyword
  | :any
  | :map
  | {:optional, type()}
  | {:list, type()}
  | {:map, [field()]}

validation_error()

@type validation_error() :: %{
  path: [String.t() | non_neg_integer()],
  message: String.t()
}

Functions

parse(input)

@spec parse(String.t()) :: {:ok, signature()} | {:error, String.t()}

Parse a signature string into internal format.

Returns {:ok, signature()} or {:error, reason}.

Examples

iex> Signature.parse("(id :int) -> {name :string}")
{:ok, {:signature, [{"id", :int}], {:map, [{"name", :string}]}}}

iex> Signature.parse("() -> :string")
{:ok, {:signature, [], :string}}

iex> Signature.parse("{count :int}")
{:ok, {:signature, [], {:map, [{"count", :int}]}}}

iex> Signature.parse("invalid")
{:error, "..."}

render(signature)

@spec render(signature()) :: String.t()

Format a signature back to string representation.

Used for rendering in prompts or debugging.

returns_list?(arg1)

@spec returns_list?(signature()) :: boolean()

Check if signature returns a list type.

Used to determine if text mode response needs unwrapping.

to_json_schema(arg)

@spec to_json_schema(signature()) :: map()

Convert a signature to JSON Schema format.

Extracts the return type and converts it to a JSON Schema that can be passed to LLM providers for structured output.

Note: Array return types are wrapped in an object with an "items" property because most LLM providers require an object at the root level. Use returns_list?/1 to check if unwrapping is needed.

Examples

iex> {:ok, sig} = PtcRunner.SubAgent.Signature.parse("() -> {sentiment :string, score :float}")
iex> PtcRunner.SubAgent.Signature.to_json_schema(sig)
%{
  "type" => "object",
  "properties" => %{
    "sentiment" => %{"type" => "string"},
    "score" => %{"type" => "number"}
  },
  "required" => ["sentiment", "score"],
  "additionalProperties" => false
}

iex> {:ok, sig} = PtcRunner.SubAgent.Signature.parse("() -> [:int]")
iex> PtcRunner.SubAgent.Signature.to_json_schema(sig)
%{
  "type" => "object",
  "properties" => %{
    "items" => %{"type" => "array", "items" => %{"type" => "integer"}}
  },
  "required" => ["items"],
  "additionalProperties" => false
}

validate(signature, data)

@spec validate(signature(), term()) :: :ok | {:error, [validation_error()]}

Validate data against a signature's return type.

Returns :ok or {:error, [validation_error()]}.

Examples

iex> {:ok, sig} = Signature.parse("() -> {count :int, items [:string]}")
iex> Signature.validate(sig, %{count: 5, items: ["a", "b"]})
:ok

iex> {:ok, sig} = Signature.parse("() -> :int")
iex> Signature.validate(sig, "not an int")
{:error, [%{path: [], message: "expected int, got string"}]}

validate_input(signature, input)

@spec validate_input(signature(), map()) :: :ok | {:error, [validation_error()]}

Validate input parameters against a signature.

Returns :ok or {:error, [validation_error()]}.