Since 4.0.0, sample_rate is applied to traces but not to scores. A run sampled at 0.001 still uploads a score for every sample, all pointing at traces that were never exported.
The reason is that create_score builds the ScoreBody with camelCase kwargs (traceId, observationId, ...). The v4 Fern models expect the snake_case field names and, because extra is allowed, quietly store the camelCase ones as extras. So body.trace_id is None, and add_score_task treats every score as a session score and skips the sampler. The request still looks right on the wire only because model_dump passes the extras through.
Check: sample_rate 0.001, 2000 generations, one create_score per generation. On 3.10.3 that enqueues 2 scores. On 4.0.0 and 4.15.4 it enqueues about 1770.
Since 4.0.0, sample_rate is applied to traces but not to scores. A run sampled at 0.001 still uploads a score for every sample, all pointing at traces that were never exported.
The reason is that
create_scorebuilds the ScoreBody with camelCase kwargs (traceId, observationId, ...). The v4 Fern models expect the snake_case field names and, because extra is allowed, quietly store the camelCase ones as extras. Sobody.trace_idis None, and add_score_task treats every score as a session score and skips the sampler. The request still looks right on the wire only because model_dump passes the extras through.Check: sample_rate 0.001, 2000 generations, one
create_scoreper generation. On 3.10.3 that enqueues 2 scores. On 4.0.0 and 4.15.4 it enqueues about 1770.