Packages

A Gleam framework for building AI agents — type-safe, multi-provider, with tool calling, streaming, and simulation testing

Current section

Files

Jump to
glean src glean@testing@provider.erl
Raw

src/glean@testing@provider.erl

-module(glean@testing@provider).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/glean/testing/provider.gleam").
-export([text_result/1, tool_call_result/1, test_provider/1]).
-export_type([scripted_response/0, scripted_tool_call/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type scripted_response() :: {text_response, binary()} |
{tool_call_response, list(scripted_tool_call())} |
{error_response, glean@error:glean_error()}.
-type scripted_tool_call() :: {scripted_tool_call, binary(), binary(), binary()}.
-file("src/glean/testing/provider.gleam", 79).
?DOC(" Create a simple text response GenerateResult (for building custom test flows).\n").
-spec text_result(binary()) -> glean@provider:generate_result().
text_result(Text) ->
{generate_result,
[{generated_text, Text}],
stop,
{usage, 10, 5},
<<"test-model"/utf8>>}.
-file("src/glean/testing/provider.gleam", 89).
?DOC(" Create a tool call response GenerateResult.\n").
-spec tool_call_result(list({binary(), binary(), binary()})) -> glean@provider:generate_result().
tool_call_result(Calls) ->
Content = gleam@list:map(
Calls,
fun(C) ->
{generated_tool_call,
erlang:element(1, C),
erlang:element(2, C),
erlang:element(3, C)}
end
),
{generate_result,
Content,
tool_calls,
{usage, 10, 5},
<<"test-model"/utf8>>}.
-file("src/glean/testing/provider.gleam", 110).
-spec count_assistant_messages(list(glean@message:message())) -> integer().
count_assistant_messages(Messages) ->
gleam@list:fold(Messages, 0, fun(Count, Msg) -> case Msg of
{assistant_message, _} ->
Count + 1;
_ ->
Count
end end).
-file("src/glean/testing/provider.gleam", 119).
-spec find_response(list({integer(), scripted_response()}), integer()) -> scripted_response().
find_response(Responses, Step) ->
case gleam@list:find(
Responses,
fun(Entry) -> erlang:element(1, Entry) =:= Step end
) of
{ok, {_, Response}} ->
Response;
{error, nil} ->
Last@1 = case gleam@list:last(Responses) of
{ok, {_, Last}} -> Last;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"glean/testing/provider"/utf8>>,
function => <<"find_response"/utf8>>,
line => 127,
value => _assert_fail,
start => 3781,
'end' => 3829,
pattern_start => 3792,
pattern_end => 3806})
end,
Last@1
end.
-file("src/glean/testing/provider.gleam", 133).
-spec scripted_to_result(scripted_response()) -> {ok,
glean@provider:generate_result()} |
{error, glean@error:glean_error()}.
scripted_to_result(Response) ->
case Response of
{text_response, Text} ->
{ok, text_result(Text)};
{tool_call_response, Calls} ->
Content = gleam@list:map(
Calls,
fun(C) ->
{generated_tool_call,
erlang:element(2, C),
erlang:element(3, C),
erlang:element(4, C)}
end
),
{ok,
{generate_result,
Content,
tool_calls,
{usage, 10, 5},
<<"test-model"/utf8>>}};
{error_response, Err} ->
{error, Err}
end.
-file("src/glean/testing/provider.gleam", 42).
?DOC(
" Create a test provider that returns scripted responses in order.\n"
"\n"
" Each call to `generate` consumes the next response from the list.\n"
" Uses a closure over a mutable index — the one controlled escape hatch\n"
" for stateful test providers, hidden behind the Provider interface.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let provider = test_provider([\n"
" ToolCallResponse([ScriptedToolCall(\"c1\", \"calc\", \"{\\\"a\\\":1}\")]),\n"
" TextResponse(\"The answer is 1\"),\n"
" ])\n"
" ```\n"
).
-spec test_provider(list(scripted_response())) -> glean@provider:provider().
test_provider(Responses) ->
Indexed_responses = begin
_pipe = Responses,
gleam@list:index_map(_pipe, fun(R, I) -> {I, R} end)
end,
Generate_fn = fun(Request) ->
Step = count_assistant_messages(erlang:element(2, Request)),
Response = find_response(Indexed_responses, Step),
scripted_to_result(Response)
end,
Stream_fn = fun(Request@1, _) ->
Step@1 = count_assistant_messages(erlang:element(2, Request@1)),
Response@1 = find_response(Indexed_responses, Step@1),
scripted_to_result(Response@1)
end,
{provider, <<"test"/utf8>>, <<"test-model"/utf8>>, Generate_fn, Stream_fn}.