File Coverage

blib/lib/App/Greple/xlate/gpt4o.pm
Criterion Covered Total %
statement 26 78 33.3
branch 0 24 0.0
condition 0 20 0.0
subroutine 9 14 64.2
pod 0 4 0.0
total 35 140 25.0


line stmt bran cond sub pod time code
1             package App::Greple::xlate::gpt4o;
2              
3             our $VERSION = "1.01";
4              
5 1     1   1669 use v5.14;
  1         5  
6 1     1   7 use warnings;
  1         2  
  1         71  
7 1     1   7 use utf8;
  1         2  
  1         7  
8 1     1   33 use Encode;
  1         3  
  1         108  
9 1     1   8 use Data::Dumper;
  1         1  
  1         64  
10              
11 1     1   7 use List::Util qw(sum);
  1         2  
  1         69  
12 1     1   6 use Command::Run;
  1         3  
  1         32  
13              
14 1     1   5 use App::Greple::xlate qw(opt);
  1         2  
  1         61  
15 1     1   24 use App::Greple::xlate::Lang qw(%LANGNAME);
  1         3  
  1         1458  
16              
17             our $lang_from //= 'ORIGINAL';
18             our $lang_to //= 'JA';
19             our $auth_key;
20             our $method = __PACKAGE__ =~ s/.*://r;
21              
22             my %param = (
23             gpt4o => { engine => 'gpt-4o-mini', temp => '0.0', max => 10000, sub => \&gpty,
24             prompt => <
25             Translate the following text into %s, preserving the line structure.
26             For each input line, output only the corresponding translated line in the same line position.
27             Leave blank lines and any XML-style tags (e.g., , , ) unchanged and do not translate them.
28             Do not output the original (pre-translation) text under any circumstances.
29             The number and order of output lines must always match the input exactly: output line n must correspond to input line n.
30             Output only the translated lines or unchanged tags/blank lines.
31             **Before finishing, carefully check that there are absolutely no omissions or duplicate content of any kind in your output.**
32             END
33             },
34             );
35              
36             sub initialize {
37 0     0 0   my($mod, $argv) = @_;
38 0           $mod->setopt(default => "-Mxlate --xlate-engine=$method");
39             }
40              
41             sub gpty {
42 0     0 0   state $gpty = Command::Run->new;
43 0           my $text = shift;
44 0           my $param = $param{$method};
45 0   0       my $prompt = opt('prompt') || $param->{prompt};
46 0           my @vars = do {
47 0 0         if ($prompt =~ /%s/) {
48 0   0       $LANGNAME{$lang_to} // die "$lang_to: unknown lang.\n"
49             } else {
50 0           ();
51             }
52             };
53 0           my $system = sprintf($prompt, @vars);
54             my @command = (
55             'gpty',
56             -e => $param->{engine},
57             -t => $param->{temp},
58 0           -s => $system,
59             '-',
60             );
61 0 0         warn Dumper \@command if opt('debug');
62 0           $gpty->command(@command)->with(stdin => $text)->update->data;
63             }
64              
65             sub _progress {
66 0 0   0     print STDERR @_ if opt('progress');
67             }
68              
69             sub xlate_each {
70 0   0 0 0   my $call = $param{$method}->{sub} // die;
71 0           my @count = map { int tr/\n/\n/ } @_;
  0            
72 0           my $lines = sum @count;
73 0           _progress("From:\n", map s/^/\t< /mgr, @_);
74 0           my $to = $call->(join '', @_);
75 0 0         if ((my @to = split /(?<=\n)\n+/, $to) > 1) {
76 0 0         $to = join '', map { /(.+\n)\z/ ? $1 : die } @to;
  0            
77             }
78 0           my @out = $to =~ /^.+\n/mg;
79 0           _progress("To:\n", map s/^/\t> /mgr, @out);
80 0 0 0       if ($lines == 1 and @out > 1) {
81 0           @out = ( join "", splice @out );
82             }
83 0 0         if (@out != $lines) {
84 0           die sprintf("\nUnexpected response: [ %d != %d ]\n\n%s\n",
85             @out+0, $lines, $to);
86             }
87 0           map { join '', splice @out, 0, $_ } @count;
  0            
88             }
89              
90             sub xlate {
91 0 0   0 0   my @from = map { /\n\z/ ? $_ : "$_\n" } @_;
  0            
92 0           my @to;
93 0   0       my $maxsize = $App::Greple::xlate::max_length || $param{$method}->{max} // die;
      0        
94 0   0       my $maxline = $App::Greple::xlate::max_line || 1;
95 0 0         if (my @len = grep { $_ > $maxsize } map length, @from) {
  0            
96 0           die "Contain lines longer than max length (@len > $maxsize).\n";
97             }
98 0           while (@from) {
99 0           my @tmp;
100 0           my $len = 0;
101 0           while (@from) {
102 0           my $next = length $from[0];
103 0 0         last if $len + $next > $maxsize;
104 0           $len += $next;
105 0           push @tmp, shift @from;
106 0 0 0       last if $maxline > 0 and @tmp >= $maxline;
107             }
108 0 0         @tmp > 0 or die "Probably text is longer than max length ($maxsize).\n";
109 0           push @to, xlate_each @tmp;
110             }
111 0           @to;
112             }
113              
114             1;
115              
116             __DATA__