Current section
Files
Jump to
Current section
Files
src/tools/core/adk_tool_call.erl
%% @doc Structural validation for provider-emitted tool calls.
%%
%% Provider output is untrusted. Validate the complete batch before persisting
%% an event, invoking callbacks, or starting any tool so a malformed later
%% entry cannot leave an invocation partially executed.
-module(adk_tool_call).
-export([validate/1, validate_list/1]).
-spec validate(term()) -> ok | {error, term()}.
validate({Name, Args}) ->
validate_fields(Name, Args, undefined, undefined);
validate({Name, Args, ThoughtSignature}) ->
validate_fields(Name, Args, ThoughtSignature, undefined);
validate({Name, Args, ThoughtSignature, CallId}) ->
validate_fields(Name, Args, ThoughtSignature, CallId);
validate(_Call) ->
{error, invalid_shape}.
-spec validate_list(term()) -> ok | {error, term()}.
validate_list(Calls) when is_list(Calls) ->
validate_list(Calls, 0);
validate_list(_Calls) ->
{error, invalid_tool_calls}.
validate_list([], _Index) -> ok;
validate_list([Call | Rest], Index) ->
case validate(Call) of
ok -> validate_list(Rest, Index + 1);
{error, Reason} ->
{error, {invalid_tool_call, Index, Reason}}
end;
validate_list(_Improper, Index) ->
{error, {invalid_tool_call, Index, improper_list}}.
validate_fields(Name, Args, ThoughtSignature, CallId) ->
case valid_name(Name) of
false -> {error, invalid_name};
true when not is_map(Args) -> {error, invalid_arguments};
true ->
case valid_optional_binary(ThoughtSignature) of
false -> {error, invalid_thought_signature};
true ->
case valid_optional_binary(CallId) of
true -> ok;
false -> {error, invalid_call_id}
end
end
end.
valid_name(Name) when is_binary(Name) -> byte_size(Name) > 0;
valid_name(_Name) -> false.
valid_optional_binary(undefined) -> true;
valid_optional_binary(Value) when is_binary(Value) -> true;
valid_optional_binary(_Value) -> false.