Langertha-Knarr
view release on metacpan or search on metacpan
t/77_raw_passthrough_tracing.t view on Meta::CPAN
);
my $tracer = MockTracer->new;
my $front = Langertha::Knarr->new(
handler => $handler,
loop => $loop,
port => 0,
router => $router,
raw_passthrough => $passthrough,
tracing => $tracer,
);
$front->start;
my $fport = $front->_server->read_handle->sockport;
ok( $fport, "front on port $fport" );
my $http = Net::Async::HTTP->new;
$loop->add($http);
sub post_json {
my ($url, $body, %headers) = @_;
my $req = HTTP::Request->new( POST => $url );
$req->header( 'Content-Type' => 'application/json' );
for my $h (keys %headers) {
$req->header( $h => $headers{$h} );
}
$req->content( $json->encode($body) );
return $http->do_request( request => $req )->get;
}
# --- 1) Raw passthrough routes unknown models (sync) ---
{
ok( $router->is_passthrough_model('claude-opus-4-6'), 'unknown model is passthrough' );
my $resp = post_json(
"http://127.0.0.1:$fport/v1/chat/completions",
{ model => 'gpt-mystery', messages => [ { role => 'user', content => 'hello' } ] },
);
is( $resp->code, 200, 'raw passthrough sync 200' );
my $d = $json->decode( $resp->decoded_content );
# Raw passthrough forwards the backend's response as-is (OpenAI format)
is( $d->{choices}[0]{message}{content}, 'UPSTREAM: hello', 'raw passthrough content' );
}
# --- 2) Raw passthrough creates Langfuse trace ---
{
my $event_count = scalar @{ $tracer->events };
ok( $event_count >= 2, "tracer has events ($event_count)" );
my $start = $tracer->events->[0];
is( $start->{kind}, 'start', 'first event is start' );
is( $start->{engine}, 'passthrough', 'engine tagged as passthrough' );
is( $start->{model}, 'gpt-mystery', 'model recorded' );
is( $start->{format}, 'openai', 'format recorded' );
my $end = $tracer->events->[1];
is( $end->{kind}, 'end', 'second event is end' );
is( $end->{id}, $start->{id}, 'end matches start id' );
}
# --- 3) Raw passthrough streaming ---
{
$tracer->events->@* = (); # reset
my $resp = post_json(
"http://127.0.0.1:$fport/v1/chat/completions",
{ model => 'gpt-unknown', messages => [ { role => 'user', content => 'stream' } ],
stream => JSON::MaybeXS::true() },
);
is( $resp->code, 200, 'raw passthrough stream 200' );
like( $resp->decoded_content, qr/UP/, 'stream chunk present' );
like( $resp->decoded_content, qr/STREAM/, 'stream chunk 2 present' );
# Trace was created for streaming too
ok( scalar @{ $tracer->events } >= 2, 'tracer has stream events' );
is( $tracer->events->[0]{kind}, 'start', 'stream trace start' );
is( $tracer->events->[0]{model}, 'gpt-unknown', 'stream model recorded' );
is( $tracer->events->[-1]{kind}, 'end', 'stream trace end' );
}
# --- 4) Anthropic raw passthrough with auth headers ---
{
$tracer->events->@* = ();
my $resp = post_json(
"http://127.0.0.1:$fport/v1/messages",
{ model => 'claude-test', messages => [ { role => 'user', content => 'hi' } ] },
'x-api-key' => 'sk-ant-test-key',
'anthropic-version' => '2023-06-01',
);
is( $resp->code, 200, 'anthropic raw passthrough 200' );
is( $tracer->events->[0]{format}, 'anthropic', 'anthropic format traced' );
is( $tracer->events->[0]{model}, 'claude-test', 'anthropic model traced' );
}
# --- 5) Without tracing: still works (no crash) ---
{
my $front_no_trace = Langertha::Knarr->new(
handler => $handler,
loop => $loop,
port => 0,
router => $router,
raw_passthrough => $passthrough,
# no tracing attribute
);
$front_no_trace->start;
my $np = $front_no_trace->_server->read_handle->sockport;
my $resp = post_json(
"http://127.0.0.1:$np/v1/chat/completions",
{ model => 'gpt-test', messages => [ { role => 'user', content => 'no trace' } ] },
);
is( $resp->code, 200, 'passthrough without tracing works' );
my $d = $json->decode( $resp->decoded_content );
is( $d->{choices}[0]{message}{content}, 'UPSTREAM: no trace', 'content correct without tracing' );
}
done_testing;
( run in 1.494 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )