-module(caffeine_lang@codegen@relay). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/caffeine_lang/codegen/relay.gleam"). -export([generate_langfuse_to_datadog/1]). -export_type([relay_file/0, scorer_data_type/0, scorer/0, langfuse_datadog_relay/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. ?MODULEDOC( " Codegen for the Relay artifact.\n" "\n" " A relay is a GitHub Action that pulls metrics from a source vendor and\n" " pushes them to a destination vendor. The compiler emits a self-contained\n" " set of files that a target repository drops in to start relaying.\n" "\n" " Today the only supported pairing is Langfuse → Datadog, producing four\n" " files:\n" " - `.github/workflows/.yml` — workflow that runs the runner on a\n" " schedule, manages a cursor in a repo Variable, and dispatches manually.\n" " - `relay/gleam.toml` — runner project manifest.\n" " - `relay/src/relay.gleam` — runner that queries Langfuse v2 metrics\n" " for `[from, to)` and submits one Datadog metric per scorer.\n" " - `relay/src/manifest.gleam` — static declaration of which metrics the\n" " relay emits. The runner imports it for its scorer allowlist; later\n" " language tooling will import it to type-check measurement references.\n" "\n" " File contents come from templates under `priv/templates/relay//`.\n" " Each template is a real `.yml` / `.toml` / `.gleam` file with\n" " `{{PLACEHOLDER}}` tokens — so editors and reviewers get the right syntax\n" " highlighting and the diffs read naturally.\n" ). -type relay_file() :: {relay_file, binary(), binary()}. -type scorer_data_type() :: numeric | boolean | categorical. -type scorer() :: {scorer, binary(), scorer_data_type()}. -type langfuse_datadog_relay() :: {langfuse_datadog_relay, binary(), binary(), binary(), list(scorer())}. -file("src/caffeine_lang/codegen/relay.gleam", 146). -spec data_type_string(scorer_data_type()) -> binary(). data_type_string(Dt) -> case Dt of numeric -> <<"numeric"/utf8>>; boolean -> <<"boolean"/utf8>>; categorical -> <<"categorical"/utf8>> end. -file("src/caffeine_lang/codegen/relay.gleam", 131). -spec render_scorer_entry(scorer(), binary()) -> binary(). render_scorer_entry(Scorer, Metric_prefix) -> Data_type_str = data_type_string(erlang:element(3, Scorer)), Value_metric = case erlang:element(3, Scorer) of categorical -> <<"option.None"/utf8>>; numeric -> <<<<"option.Some(\""/utf8, Metric_prefix/binary>>/binary, ".value\")"/utf8>>; boolean -> <<<<"option.Some(\""/utf8, Metric_prefix/binary>>/binary, ".value\")"/utf8>> end, <<<<<<<<<<<<<<<<<<<<<<<<<<" ScorerInfo(\n"/utf8, " name: \""/utf8>>/binary, (erlang:element(2, Scorer))/binary>>/binary, "\",\n"/utf8>>/binary, " data_type: \""/utf8>>/binary, Data_type_str/binary>>/binary, "\",\n"/utf8>>/binary, " count_metric: \""/utf8>>/binary, Metric_prefix/binary>>/binary, ".count\",\n"/utf8>>/binary, " value_metric: "/utf8>>/binary, Value_metric/binary>>/binary, ",\n"/utf8>>/binary, " ),\n"/utf8>>. -file("src/caffeine_lang/codegen/relay.gleam", 125). -spec scorer_entries(langfuse_datadog_relay()) -> binary(). scorer_entries(Relay) -> _pipe = erlang:element(5, Relay), _pipe@1 = gleam@list:map( _pipe, fun(_capture) -> render_scorer_entry(_capture, erlang:element(3, Relay)) end ), erlang:list_to_binary(_pipe@1). -file("src/caffeine_lang/codegen/relay.gleam", 119). -spec cursor_var_name(langfuse_datadog_relay()) -> binary(). cursor_var_name(Relay) -> <<"RELAY_CURSOR_"/utf8, (string:uppercase(erlang:element(2, Relay)))/binary>>. -file("src/caffeine_lang/codegen/relay.gleam", 109). -spec substitutions(langfuse_datadog_relay()) -> list({binary(), binary()}). substitutions(Relay) -> [{<<"{{RELAY_NAME}}"/utf8>>, erlang:element(2, Relay)}, {<<"{{METRIC_PREFIX}}"/utf8>>, erlang:element(3, Relay)}, {<<"{{SCHEDULE_CRON}}"/utf8>>, erlang:element(4, Relay)}, {<<"{{CURSOR_VAR}}"/utf8>>, cursor_var_name(Relay)}, {<<"{{SCORER_ENTRIES}}"/utf8>>, scorer_entries(Relay)}]. -file("src/caffeine_lang/codegen/relay.gleam", 101). ?DOC( " Read a template from `priv/templates/relay/langfuse_to_datadog/` and\n" " substitute the relay's fields into the `{{PLACEHOLDER}}` tokens. A\n" " missing template file means the package was shipped broken — assert so\n" " it fails loudly.\n" ). -spec render(langfuse_datadog_relay(), binary()) -> binary(). render(Relay, Template_name) -> Template@1 = case simplifile:read( <<"priv/templates/relay/langfuse_to_datadog/"/utf8, Template_name/binary>> ) of {ok, Template} -> Template; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"caffeine_lang/codegen/relay"/utf8>>, function => <<"render"/utf8>>, line => 102, value => _assert_fail, start => 4166, 'end' => 4238, pattern_start => 4177, pattern_end => 4189}) end, gleam@list:fold( substitutions(Relay), Template@1, fun(Acc, Sub) -> {Placeholder, Value} = Sub, gleam@string:replace(Acc, Placeholder, Value) end ). -file("src/caffeine_lang/codegen/relay.gleam", 75). ?DOC(" Generate the file set for a Langfuse → Datadog relay.\n"). -spec generate_langfuse_to_datadog(langfuse_datadog_relay()) -> list(relay_file()). generate_langfuse_to_datadog(Relay) -> [{relay_file, <<<<".github/workflows/"/utf8, (erlang:element(2, Relay))/binary>>/binary, ".yml"/utf8>>, render(Relay, <<"workflow.yml"/utf8>>)}, {relay_file, <<"relay/gleam.toml"/utf8>>, render(Relay, <<"gleam.toml"/utf8>>)}, {relay_file, <<"relay/src/relay.gleam"/utf8>>, render(Relay, <<"runner.gleam"/utf8>>)}, {relay_file, <<"relay/src/manifest.gleam"/utf8>>, render(Relay, <<"manifest.gleam"/utf8>>)}].