-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 => <>, 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}.