-module(langfuse_client@metrics). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/langfuse_client/metrics.gleam"). -export([score_count_query/3, decode/1, list_score_counts/2]). -export_type([score_view/0, score_count_row/0, score_count_query/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( " `GET /api/public/v2/metrics` — server-side aggregations over Langfuse\n" " score data. Build a query with `score_count_query` and pass it to\n" " `list_score_counts(client, query)`.\n" "\n" " The Langfuse v2 metrics endpoint is BETA. This module exposes only\n" " score-count queries grouped by `(name, dataType, source)`; broader\n" " surface (other measures, views, dimensions) will follow the same\n" " shape once the endpoint stabilises.\n" ). -type score_view() :: scores_numeric | scores_categorical. -type score_count_row() :: {score_count_row, binary(), binary(), binary(), integer()}. -type score_count_query() :: {score_count_query, score_view(), binary(), binary()}. -file("src/langfuse_client/metrics.gleam", 43). ?DOC( " Build a query for counts of scores grouped by `(name, data_type,\n" " source)` in the given window.\n" ). -spec score_count_query(score_view(), binary(), binary()) -> score_count_query(). score_count_query(View, From_timestamp, To_timestamp) -> {score_count_query, View, From_timestamp, To_timestamp}. -file("src/langfuse_client/metrics.gleam", 70). ?DOC( " Parse a `GET /api/public/v2/metrics` response body for a score-count\n" " query. Useful if you already have the raw body in hand (e.g. from a\n" " cached/recorded response).\n" ). -spec decode(binary()) -> {ok, list(score_count_row())} | {error, gleam@json:decode_error()}. decode(Body) -> gleam@json:parse(Body, rows_decoder()). -file("src/langfuse_client/metrics.gleam", 116). -spec row_decoder() -> gleam@dynamic@decode:decoder(score_count_row()). row_decoder() -> gleam@dynamic@decode:field( <<"name"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Name) -> gleam@dynamic@decode:field( <<"dataType"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Data_type) -> gleam@dynamic@decode:field( <<"source"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Source) -> gleam@dynamic@decode:field( <<"sum_count"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Sum_count_str) -> Count = gleam@result:unwrap( gleam_stdlib:parse_int(Sum_count_str), 0 ), gleam@dynamic@decode:success( {score_count_row, Name, Data_type, Source, Count} ) end ) end ) end ) end ). -file("src/langfuse_client/metrics.gleam", 111). -spec rows_decoder() -> gleam@dynamic@decode:decoder(list(score_count_row())). rows_decoder() -> gleam@dynamic@decode:field( <<"data"/utf8>>, gleam@dynamic@decode:list(row_decoder()), fun(Rows) -> gleam@dynamic@decode:success(Rows) end ). -file("src/langfuse_client/metrics.gleam", 95). -spec score_count_metrics() -> list(gleam@json:json()). score_count_metrics() -> [gleam@json:object( [{<<"measure"/utf8>>, gleam@json:string(<<"count"/utf8>>)}, {<<"aggregation"/utf8>>, gleam@json:string(<<"sum"/utf8>>)}] )]. -file("src/langfuse_client/metrics.gleam", 87). -spec score_count_dimensions() -> list(gleam@json:json()). score_count_dimensions() -> [gleam@json:object([{<<"field"/utf8>>, gleam@json:string(<<"name"/utf8>>)}]), gleam@json:object( [{<<"field"/utf8>>, gleam@json:string(<<"dataType"/utf8>>)}] ), gleam@json:object( [{<<"field"/utf8>>, gleam@json:string(<<"source"/utf8>>)}] )]. -file("src/langfuse_client/metrics.gleam", 104). -spec view_string(score_view()) -> binary(). view_string(V) -> case V of scores_numeric -> <<"scores-numeric"/utf8>>; scores_categorical -> <<"scores-categorical"/utf8>> end. -file("src/langfuse_client/metrics.gleam", 76). -spec query_body(score_count_query()) -> binary(). query_body(Q) -> _pipe = gleam@json:object( [{<<"view"/utf8>>, gleam@json:string(view_string(erlang:element(2, Q)))}, {<<"fromTimestamp"/utf8>>, gleam@json:string(erlang:element(3, Q))}, {<<"toTimestamp"/utf8>>, gleam@json:string(erlang:element(4, Q))}, {<<"dimensions"/utf8>>, gleam@json:preprocessed_array(score_count_dimensions())}, {<<"metrics"/utf8>>, gleam@json:preprocessed_array(score_count_metrics())}] ), gleam@json:to_string(_pipe). -file("src/langfuse_client/metrics.gleam", 55). ?DOC( " Aggregate score counts for the query. Returns one row per distinct\n" " `(name, data_type, source)` combination present in the window.\n" " Erlang-only — see `langfuse_client/client` module docs.\n" ). -spec list_score_counts(langfuse_client@client:client(), score_count_query()) -> {ok, list(score_count_row())} | {error, langfuse_client@client:error()}. list_score_counts(C, Q) -> langfuse_client@client:send_get( C, <<"/api/public/v2/metrics"/utf8>>, [{<<"query"/utf8>>, query_body(Q)}], rows_decoder() ).