sync SVN r18206
This commit is contained in:
90
.archon/scripts/analyze_claude.pl
Normal file
90
.archon/scripts/analyze_claude.pl
Normal file
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/perl
|
||||
use strict; use warnings;
|
||||
use JSON::PP;
|
||||
my $file = shift @ARGV or die "usage: analyze_claude.pl <file.jsonl>\n";
|
||||
my $json = JSON::PP->new;
|
||||
my %calls; # id -> {name, args, reqts(epoch ms)}
|
||||
my @results;
|
||||
open my $fh, '<:encoding(UTF-8)', $file or die $!;
|
||||
sub to_epoch {
|
||||
my $iso = shift;
|
||||
return undef unless $iso;
|
||||
# 2026-09-24T22:48:53.885Z
|
||||
if ($iso =~ /^(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)(?:\.(\d+))?Z$/) {
|
||||
my ($Y,$Mo,$D,$H,$Mi,$S,$ms) = ($1,$2,$3,$4,$5,$6,$7//0);
|
||||
require Time::Local;
|
||||
my $epoch = Time::Local::timegm($S,$Mi,$H,$D,$Mo-1,$Y);
|
||||
return $epoch*1000 + $ms;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
while (my $line = <$fh>) {
|
||||
chomp $line;
|
||||
next unless length $line;
|
||||
my $obj = eval { $json->decode($line) };
|
||||
next unless $obj;
|
||||
my $ts = to_epoch($obj->{timestamp});
|
||||
my $msg = $obj->{message} or next;
|
||||
my $role = $msg->{role} // '';
|
||||
my $content = $msg->{content};
|
||||
next unless ref($content) eq 'ARRAY';
|
||||
if ($role eq 'assistant') {
|
||||
for my $c (@$content) {
|
||||
next unless ($c->{type}//'') eq 'tool_use';
|
||||
my $args = '';
|
||||
if (ref($c->{input}) eq 'HASH') {
|
||||
$args = $c->{input}{command} // $c->{input}{file_path} // $c->{input}{pattern} // '';
|
||||
$args = substr((eval{$json->encode($c->{input})}//''),0,200) unless $args;
|
||||
}
|
||||
$calls{$c->{id}} = { name => $c->{name}, args => $args, reqts => $ts };
|
||||
}
|
||||
} elsif ($role eq 'user') {
|
||||
for my $c (@$content) {
|
||||
next unless ($c->{type}//'') eq 'tool_result';
|
||||
my $iserror = $c->{is_error} ? 1 : 0;
|
||||
my $snippet = '';
|
||||
if ($iserror) {
|
||||
my $ct = $c->{content};
|
||||
if (ref($ct) eq 'ARRAY') { $snippet = substr($ct->[0]{text}//'',0,300); }
|
||||
elsif (!ref($ct)) { $snippet = substr($ct//'',0,300); }
|
||||
}
|
||||
push @results, { id => $c->{tool_use_id}, rests => $ts, iserror => $iserror, snippet => $snippet };
|
||||
}
|
||||
}
|
||||
}
|
||||
close $fh;
|
||||
|
||||
my @rows;
|
||||
for my $r (@results) {
|
||||
my $c = $calls{$r->{id}} or next;
|
||||
my $dur = ($r->{rests} && $c->{reqts}) ? ($r->{rests} - $c->{reqts}) : undef;
|
||||
push @rows, { name => $c->{name}, args => $c->{args}, dur => $dur, iserror => $r->{iserror}, snippet => $r->{snippet} };
|
||||
}
|
||||
|
||||
my %cnt; $cnt{$_->{name}}++ for @rows;
|
||||
print "=== tool counts ===\n";
|
||||
for my $k (sort { $cnt{$b} <=> $cnt{$a} } keys %cnt) { print "$k: $cnt{$k}\n"; }
|
||||
|
||||
print "\n=== top 10 longest ===\n";
|
||||
my @sorted = sort { ($b->{dur}//0) <=> ($a->{dur}//0) } @rows;
|
||||
for my $i (0..9) {
|
||||
last unless $sorted[$i];
|
||||
my $row = $sorted[$i];
|
||||
my $a = $row->{args}; $a =~ s/\s+/ /g; $a = substr($a,0,100);
|
||||
printf "%8dms %-10s %s\n", ($row->{dur}//-1), $row->{name}, $a;
|
||||
}
|
||||
|
||||
print "\n=== errors ===\n";
|
||||
my $ec = 0;
|
||||
for my $row (@rows) {
|
||||
next unless $row->{iserror};
|
||||
$ec++;
|
||||
my $a = $row->{args}; $a =~ s/\s+/ /g; $a = substr($a,0,150);
|
||||
my $s = $row->{snippet}; $s =~ s/\s+/ /g; $s = substr($s,0,200);
|
||||
print "dur=".($row->{dur}//'?')."ms CMD: $a\n ERR: $s\n";
|
||||
}
|
||||
print "total errors: $ec\n";
|
||||
|
||||
my $totaldur = 0; $totaldur += ($_->{dur}//0) for @rows;
|
||||
print "\n=== total tool time (sum durations, ms) === $totaldur\n";
|
||||
print "=== total tool calls with duration === " . scalar(@rows) . "\n";
|
||||
Reference in New Issue
Block a user