Langertha-Knarr
view release on metacpan or search on metacpan
lib/Langertha/Knarr/Tracing.pm view on Meta::CPAN
return;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Langertha::Knarr::Tracing - Automatic Langfuse tracing per proxy request
=head1 VERSION
version 1.101
=head1 SYNOPSIS
use Langertha::Knarr::Tracing;
my $tracing = Langertha::Knarr::Tracing->new(config => $config);
my $trace_id = $tracing->start_trace(
model => 'gpt-5.6-terra',
engine => 'Langertha::Engine::OpenAI',
messages => \@messages,
params => \%params,
format => 'openai',
);
# ... handle request ...
$tracing->end_trace($trace_id,
output => $response_text,
model => 'gpt-5.6-terra',
usage => { input => 100, output => 50, total => 150 },
);
=head1 DESCRIPTION
Records every proxy request as a Langfuse trace with a nested generation. When
tracing is not configured (no public and secret key), all methods are no-ops.
Langfuse credentials are read from the config file's C<langfuse:> section or
from the C<LANGFUSE_PUBLIC_KEY>, C<LANGFUSE_SECRET_KEY>, and C<LANGFUSE_URL>
environment variables. The module strips surrounding quotes from environment
variable values, which Docker C<--env-file> sometimes adds literally.
=head2 Timing sources
Knarr has two request paths and they do not measure latency the same way.
The generation's C<startTime> always marks the moment L</start_trace> ran;
what differs is where C<endTime> and C<completionStartTime> come from.
=over
=item * B<Routed, non-streaming> â a L<Langertha> engine produced a
L<Langertha::Response>, so L<Langertha::Knarr::Handler::Tracing> hands the
engine-measured C<timing> hash to L</end_trace>. C<endTime> becomes
C<startTime + total_seconds> and C<completionStartTime> becomes
C<startTime + ttft_seconds>, both anchored to the high-resolution
timestamp L</start_trace> recorded. This is the only path with a real
time-to-first-token, and the durations exclude the proxy's own
formatting overhead.
=item * B<Routed, streaming> â the decorator accumulates deltas and never
sees a response object, so there is no C<timing>. C<endTime> is the
wall-clock moment the stream was exhausted and no C<completionStartTime>
is emitted.
=item * B<Raw passthrough> â bytes are piped 1:1 and never parsed, so no
L<Langertha::Response> exists at all. C<endTime> is again the proxy's own
wall clock at L</end_trace>, which includes network time to the upstream
provider.
=back
Callers that pass no C<timing> therefore keep exactly the previous
behaviour: proxy-measured C<endTime>, no C<completionStartTime>.
=head2 config
The L<Langertha::Knarr::Config> object. Required. Provides Langfuse
credentials and C<trace_name>.
=head2 trace_name
The Langfuse trace name applied to all traces. Resolved in priority order from:
C<langfuse.trace_name> in config, C<LANGFUSE_TRACE_NAME> env var,
C<KNARR_TRACE_NAME> env var, or the default C<knarr-proxy>.
=head2 start_trace
my $trace_info = $tracing->start_trace(
model => $model_name,
engine => $engine_class,
messages => \@messages,
params => \%params,
format => 'openai',
);
Creates a new Langfuse trace and generation. Returns a C<$trace_info> hashref
that must be passed to L</end_trace>. Returns C<undef> when tracing is
disabled.
The returned hashref carries C<start_hires>, the C<gettimeofday> pair behind
C<start_time>. L</end_trace> anchors engine-measured durations to it; see
L</Timing sources>.
=head2 end_trace
$tracing->end_trace($trace_info,
output => $response_text,
model => $model,
usage => { input => 100, output => 50, total => 150 },
timing => { ttft_seconds => 0.25, total_seconds => 1.5 },
response_id => 'chatcmpl-123',
);
# On error:
$tracing->end_trace($trace_info, error => "Something went wrong");
Closes the generation and trace started by L</start_trace>, then flushes the
batch to Langfuse. Pass C<error> to record a failed generation at level ERROR.
Does nothing when C<$trace_info> is C<undef> (tracing was disabled at start).
( run in 0.739 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )