-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, score_value_query/2, list_score_values/2, decode_score_values/1]). -export_type([score_view/0, score_count_row/0, score_count_query/0, score_value_row/0, score_value_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` /\n" " `score_value_query` and pass it to the matching `list_*` function.\n" "\n" " The Langfuse v2 metrics endpoint is BETA. This module exposes score\n" " count + avg-value queries grouped by `(name, dataType, source)`;\n" " broader surface (other measures, views, dimensions) will follow the\n" " same 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()}. -type score_value_row() :: {score_value_row, binary(), binary(), binary(), float()}. -type score_value_query() :: {score_value_query, 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", 169). -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", 164). -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", 148). -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", 140). -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", 157). -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", 129). -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() ). -file("src/langfuse_client/metrics.gleam", 96). ?DOC( " Build a query for avg score values grouped by `(name, data_type,\n" " source)` in the given window. Only numeric and boolean scores are\n" " returned.\n" ). -spec score_value_query(binary(), binary()) -> score_value_query(). score_value_query(From_timestamp, To_timestamp) -> {score_value_query, From_timestamp, To_timestamp}. -file("src/langfuse_client/metrics.gleam", 214). -spec lenient_float() -> gleam@dynamic@decode:decoder(float()). lenient_float() -> gleam@dynamic@decode:one_of( {decoder, fun gleam@dynamic@decode:decode_float/1}, [begin _pipe = {decoder, fun gleam@dynamic@decode:decode_int/1}, gleam@dynamic@decode:map(_pipe, fun erlang:float/1) end] ). -file("src/langfuse_client/metrics.gleam", 204). -spec value_row_decoder() -> gleam@dynamic@decode:decoder(score_value_row()). value_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( <<"avg_value"/utf8>>, lenient_float(), fun(Avg_value) -> gleam@dynamic@decode:success( {score_value_row, Name, Data_type, Source, Avg_value} ) end ) end ) end ) end ). -file("src/langfuse_client/metrics.gleam", 199). -spec value_rows_decoder() -> gleam@dynamic@decode:decoder(list(score_value_row())). value_rows_decoder() -> gleam@dynamic@decode:field( <<"data"/utf8>>, gleam@dynamic@decode:list(value_row_decoder()), fun(Rows) -> gleam@dynamic@decode:success(Rows) end ). -file("src/langfuse_client/metrics.gleam", 190). -spec score_value_metrics() -> list(gleam@json:json()). score_value_metrics() -> [gleam@json:object( [{<<"measure"/utf8>>, gleam@json:string(<<"value"/utf8>>)}, {<<"aggregation"/utf8>>, gleam@json:string(<<"avg"/utf8>>)}] )]. -file("src/langfuse_client/metrics.gleam", 179). -spec value_query_body(score_value_query()) -> binary(). value_query_body(Q) -> _pipe = gleam@json:object( [{<<"view"/utf8>>, gleam@json:string(<<"scores-numeric"/utf8>>)}, {<<"fromTimestamp"/utf8>>, gleam@json:string(erlang:element(2, Q))}, {<<"toTimestamp"/utf8>>, gleam@json:string(erlang:element(3, Q))}, {<<"dimensions"/utf8>>, gleam@json:preprocessed_array(score_count_dimensions())}, {<<"metrics"/utf8>>, gleam@json:preprocessed_array(score_value_metrics())}] ), gleam@json:to_string(_pipe). -file("src/langfuse_client/metrics.gleam", 107). ?DOC( " Aggregate avg score values for the query. One row per `(name,\n" " data_type, source)` combination over the window. Erlang-only — see\n" " `langfuse_client/client` module docs.\n" ). -spec list_score_values(langfuse_client@client:client(), score_value_query()) -> {ok, list(score_value_row())} | {error, langfuse_client@client:error()}. list_score_values(C, Q) -> langfuse_client@client:send_get( C, <<"/api/public/v2/metrics"/utf8>>, [{<<"query"/utf8>>, value_query_body(Q)}], value_rows_decoder() ). -file("src/langfuse_client/metrics.gleam", 121). ?DOC( " Parse a `GET /api/public/v2/metrics` response body for a score-value\n" " query.\n" ). -spec decode_score_values(binary()) -> {ok, list(score_value_row())} | {error, gleam@json:decode_error()}. decode_score_values(Body) -> gleam@json:parse(Body, value_rows_decoder()).