line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# |
2
|
|
|
|
|
|
|
# This file is part of App-PythonToPerl |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# This software is Copyright (c) 2023 by Auto-Parallel Technologies, Inc. |
5
|
|
|
|
|
|
|
# |
6
|
|
|
|
|
|
|
# This is free software, licensed under: |
7
|
|
|
|
|
|
|
# |
8
|
|
|
|
|
|
|
# The GNU General Public License, Version 3, June 2007 |
9
|
|
|
|
|
|
|
# |
10
|
|
|
|
|
|
|
# [[[ HEADER ]]] |
11
|
|
|
|
|
|
|
# ABSTRACT: an unknown component |
12
|
|
|
|
|
|
|
#use RPerl; |
13
|
|
|
|
|
|
|
package Python::Unknown; |
14
|
2
|
|
|
2
|
|
255598
|
use strict; |
|
2
|
|
|
|
|
11
|
|
|
2
|
|
|
|
|
65
|
|
15
|
2
|
|
|
2
|
|
14
|
use warnings; |
|
2
|
|
|
|
|
11
|
|
|
2
|
|
|
|
|
90
|
|
16
|
|
|
|
|
|
|
our $VERSION = 0.016_000; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# [[[ OO INHERITANCE ]]] |
19
|
2
|
|
|
2
|
|
19
|
use parent qw(Python::Component); |
|
2
|
|
|
|
|
9
|
|
|
2
|
|
|
|
|
21
|
|
20
|
2
|
|
|
2
|
|
114
|
use Python::Component; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
92
|
|
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# [[[ CRITICS ]]] |
23
|
|
|
|
|
|
|
## no critic qw(ProhibitUselessNoCritic ProhibitMagicNumbers RequireCheckedSyscalls) # USER DEFAULT 1: allow numeric values & print op |
24
|
|
|
|
|
|
|
## no critic qw(RequireInterpolationOfMetachars) # USER DEFAULT 2: allow single-quoted control characters & sigils |
25
|
|
|
|
|
|
|
## no critic qw(ProhibitConstantPragma ProhibitMagicNumbers) # USER DEFAULT 3: allow constants |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# [[[ INCLUDES ]]] |
28
|
2
|
|
|
2
|
|
12
|
use Perl::Types; |
|
2
|
|
|
|
|
44
|
|
|
2
|
|
|
|
|
809
|
|
29
|
2
|
|
|
2
|
|
16
|
use OpenAI::API; |
|
2
|
|
|
|
|
7
|
|
|
2
|
|
|
|
|
2766
|
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# [[[ OO PROPERTIES ]]] |
32
|
|
|
|
|
|
|
our hashref $properties = { |
33
|
|
|
|
|
|
|
component_type => my string $TYPED_component_type = 'Python::Unknown', |
34
|
|
|
|
|
|
|
sleep_seconds => my integer $TYPED_sleep_seconds = 5, |
35
|
|
|
|
|
|
|
sleep_retry_multiplier => my number $TYPED_sleep_retry_multiplier = 1.5, # 1.5 == 150%, which means increase by 50% |
36
|
|
|
|
|
|
|
retries_max => my integer $TYPED_retries_max = 10, |
37
|
|
|
|
|
|
|
# all other properties inherited from Python::Component |
38
|
|
|
|
|
|
|
}; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# [[[ SUBROUTINES & OO METHODS ]]] |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# PYUN01x |
43
|
|
|
|
|
|
|
sub python_preparsed_to_perl_source { |
44
|
|
|
|
|
|
|
# translate a chunk of Python source code into Perl source code |
45
|
0
|
|
|
0
|
0
|
|
{ my string $RETURN_TYPE }; |
|
0
|
|
|
|
|
|
|
46
|
0
|
|
|
|
|
|
( my Python::Unknown $self, my OpenAI::API $openai ) = @ARG; |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# error if no OpenAI API |
49
|
0
|
0
|
|
|
|
|
if (not defined $openai) { |
50
|
0
|
|
|
|
|
|
croak 'ERROR EPYUN010: undefined OpenAI API, croaking'; |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# error or warning if no Python source code |
54
|
0
|
0
|
0
|
|
|
|
if ((not exists $self->{python_source_code}) or |
|
|
|
0
|
|
|
|
|
55
|
|
|
|
|
|
|
(not defined $self->{python_source_code}) or |
56
|
|
|
|
|
|
|
($self->{python_source_code} eq q{})) { |
57
|
0
|
|
|
|
|
|
croak 'ERROR EPYUN011: non-existent or undefined or empty Python source code, croaking'; |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# DEV NOTE, PYUN012: $self->{python_preparsed} not used in this class, no need to error check |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# initialize property that will store de-parsed & translated source code; |
63
|
|
|
|
|
|
|
# save fully translated Perl source code, to avoid repeated translating |
64
|
0
|
|
|
|
|
|
$self->{perl_source_code_full} = ''; |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
# DEV NOTE: OpenAI::API rate limit is 20 requests per minute (RPM) for free tier, sleep to avoid hitting rate limit; |
67
|
|
|
|
|
|
|
# DEV NOTE: sleep before API call instead of after call, to avoid "429 Too Many Requests" if previous call was recent |
68
|
0
|
|
|
|
|
|
print 'in Python::Unknown::python_preparsed_to_perl_source(), about to call sleep(', $self->{sleep_seconds}, ')...', "\n"; |
69
|
|
|
|
|
|
|
# sleep before calling API |
70
|
0
|
|
|
|
|
|
sleep($self->{sleep_seconds}); |
71
|
0
|
|
|
|
|
|
print 'in Python::Unknown::python_preparsed_to_perl_source(), ret from call to sleep(', $self->{sleep_seconds}, ')', "\n"; |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# NEED ANSWER: the stop sequences were needed with Codex completions model 'code-davinci-002', do we still need now? |
74
|
|
|
|
|
|
|
# NEED ANSWER: the stop sequences were needed with Codex completions model 'code-davinci-002', do we still need now? |
75
|
|
|
|
|
|
|
# NEED ANSWER: the stop sequences were needed with Codex completions model 'code-davinci-002', do we still need now? |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# these strings signify an end to the translated Perl code, and the completion must stop when they are found |
78
|
0
|
|
|
|
|
|
my string::arrayref $stop_sequences = ['# Python to', 'Python:']; |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# DEV NOTE: shorten error sequences to speed up matching and allow for new or different wording |
81
|
0
|
|
|
|
|
|
my string::arrayref $error_sequences = [ |
82
|
|
|
|
|
|
|
'Sorry, ', |
83
|
|
|
|
|
|
|
# 'Sorry, as an AI language model, I cannot see any Python source code to translate. Can you please provide the code so I can assist you better?', |
84
|
|
|
|
|
|
|
# 'Sorry, I cannot perform this task as the provided Python source code is incomplete and does not provide enough information to be translated accurately. Please provide the complete code.', |
85
|
|
|
|
|
|
|
]; |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# assemble "Python to Perl" message strings |
88
|
|
|
|
|
|
|
my string::hashref::arrayref $messages = [ |
89
|
|
|
|
|
|
|
# DEV NOTE: save money by transmitting fewer tokens! |
90
|
|
|
|
|
|
|
# { 'role' => 'system', 'content' => 'You are a helpful assistant with detailed knowledge of the Perl and Python computer programming languages, as well as modern software development best practices.' }, |
91
|
|
|
|
|
|
|
# { 'role' => 'user', 'content' => 'Translate the following Python source code to Perl:' . "\n" . $self->{python_source_code} . "\n" }, |
92
|
0
|
|
|
|
|
|
{ 'role' => 'user', 'content' => 'Translate this Python to Perl:' . "\n" . $self->{python_source_code} . "\n" }, |
93
|
|
|
|
|
|
|
]; |
94
|
|
|
|
|
|
|
|
95
|
0
|
|
|
|
|
|
print 'in Python::Unknown::python_preparsed_to_perl_source(), have $self->{python_source_code} =', "\n", $self->{python_source_code}, "\n"; |
96
|
0
|
|
|
|
|
|
print 'in Python::Unknown::python_preparsed_to_perl_source(), about to call python_preparsed_to_perl_source_api_call()...', "\n"; |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# call OpenAI API |
99
|
0
|
|
|
|
|
|
my hashref $response = $self->python_preparsed_to_perl_source_api_call($openai, $stop_sequences, $messages); |
100
|
|
|
|
|
|
|
|
101
|
0
|
|
|
|
|
|
print 'in Python::Unknown::python_preparsed_to_perl_source(), ret from call to python_preparsed_to_perl_source_api_call()', "\n"; |
102
|
|
|
|
|
|
|
#print 'in Python::Unknown::python_preparsed_to_perl_source(), received $response = ', Dumper($response), "\n"; |
103
|
|
|
|
|
|
|
#die 'TMP DEBUG, UNKNOWN'; |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# [[[ BEGIN FAILED API CALLS ]]] |
106
|
|
|
|
|
|
|
# [[[ BEGIN FAILED API CALLS ]]] |
107
|
|
|
|
|
|
|
# [[[ BEGIN FAILED API CALLS ]]] |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# handle HTTP response status codes returned from LWP::UserAgent |
110
|
0
|
0
|
0
|
|
|
|
if ((defined $EVAL_ERROR) and ($EVAL_ERROR ne '')) { |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
# NEED UPGRADE: accept any error code and just retry??? |
113
|
|
|
|
|
|
|
# NEED UPGRADE: accept any error code and just retry??? |
114
|
|
|
|
|
|
|
# NEED UPGRADE: accept any error code and just retry??? |
115
|
|
|
|
|
|
|
|
116
|
0
|
|
|
|
|
|
print 'in Python::Unknown::python_preparsed_to_perl_source(), received $EVAL_ERROR = \'', $EVAL_ERROR, '\'', "\n"; |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# Error retrieving 'completions': 400 Bad Request |
119
|
0
|
0
|
|
|
|
|
if ($EVAL_ERROR =~ '400 Bad Request') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
120
|
0
|
|
|
|
|
|
croak 'ERROR EPYUN013a: received HTTP response status code 400 (bad request) instead of OpenAI::API response, croaking', "\n", $EVAL_ERROR, "\n"; |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
# Error retrieving 'completions': 429 Too Many Requests |
123
|
|
|
|
|
|
|
elsif ($EVAL_ERROR =~ '429 Too Many Requests') { |
124
|
0
|
|
|
|
|
|
carp 'WARNING WPYUN013b: received HTTP response status code 429 (too many requests) instead of OpenAI::API response, carping', "\n", $EVAL_ERROR, "\n"; |
125
|
|
|
|
|
|
|
|
126
|
0
|
|
|
|
|
|
my integer $retries = 0; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# backoff algorithm; increase sleep time and retry |
129
|
|
|
|
|
|
|
# https://help.openai.com/en/articles/5955604-how-can-i-solve-429-too-many-requests-errors |
130
|
|
|
|
|
|
|
# https://platform.openai.com/docs/guides/rate-limits/error-mitigation |
131
|
0
|
|
0
|
|
|
|
while ((defined $EVAL_ERROR) and ($EVAL_ERROR =~ '429 Too Many Requests')) { |
132
|
0
|
|
|
|
|
|
print 'in Python::Unknown::python_preparsed_to_perl_source(), about to retry #', $retries, ' call python_preparsed_to_perl_source_api_call()...', "\n"; |
133
|
0
|
|
|
|
|
|
$retries++; |
134
|
0
|
0
|
|
|
|
|
if ($retries >= $self->{retries_max}) { |
135
|
0
|
|
|
|
|
|
croak 'ERROR EPYUN013b: received HTTP response status code 429 (too many requests) instead of OpenAI::API response, maximum retry limit ', $self->{retries_max}, ' reached, croaking'; |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# increase time to sleep by some percentage of current amount |
139
|
0
|
|
|
|
|
|
$self->{sleep_seconds} *= $self->{sleep_retry_multiplier}; |
140
|
|
|
|
|
|
|
|
141
|
0
|
|
|
|
|
|
print 'in Python::Unknown::python_preparsed_to_perl_source(), about to retry #', $retries, ' call sleep(', $self->{sleep_seconds}, ')...', "\n"; |
142
|
|
|
|
|
|
|
# retry sleep before calling API |
143
|
0
|
|
|
|
|
|
sleep($self->{sleep_seconds}); |
144
|
0
|
|
|
|
|
|
print 'in Python::Unknown::python_preparsed_to_perl_source(), ret from retry #', $retries, ' call to sleep(', $self->{sleep_seconds}, ')', "\n"; |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# retry call to OpenAI API |
147
|
0
|
|
|
|
|
|
$response = $self->python_preparsed_to_perl_source_api_call($openai, $messages); |
148
|
|
|
|
|
|
|
|
149
|
0
|
|
|
|
|
|
print 'in Python::Unknown::python_preparsed_to_perl_source(), ret from retry #', $retries, ' call to python_preparsed_to_perl_source_api_call()', "\n"; |
150
|
|
|
|
|
|
|
#print 'in Python::Unknown::python_preparsed_to_perl_source(), received $response = ', Dumper($response), "\n"; |
151
|
|
|
|
|
|
|
#die 'TMP DEBUG, UNKNOWN 429 TOO MANY REQUESTS'; |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
# Error retrieving 'completions': 500 read timeout |
155
|
|
|
|
|
|
|
elsif ($EVAL_ERROR =~ '500 read timeout') { |
156
|
0
|
|
|
|
|
|
croak 'ERROR EPYUN013c: received HTTP response status code 500 (read timeout) instead of OpenAI::API response, croaking', "\n", $EVAL_ERROR, "\n"; |
157
|
|
|
|
|
|
|
# NEED SLEEP AND RETRY? |
158
|
|
|
|
|
|
|
# NEED SLEEP AND RETRY? |
159
|
|
|
|
|
|
|
# NEED SLEEP AND RETRY? |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
else { |
162
|
0
|
|
|
|
|
|
croak 'ERROR EPYUN013d: received unrecognized HTTP response status code instead of OpenAI::API response, croaking', "\n", $EVAL_ERROR, "\n"; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
0
|
|
|
|
|
|
print 'in Python::Unknown::python_preparsed_to_perl_source(), ret from call to OpenAI completions(), received $response =', "\n", Dumper($response), "\n"; |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# DEV NOTE: sometimes the API returns an undefined or empty finish_reason, but still with valid 'text' field; |
169
|
|
|
|
|
|
|
# carp & return dummy code if translation ends for any other reason than reaching a legitimate 'stop' condition |
170
|
0
|
0
|
0
|
|
|
|
if (not defined $response) { |
|
|
0
|
|
|
|
|
|
171
|
0
|
|
|
|
|
|
croak 'ERROR EPYUN014a: received undefined OpenAI::API response, croaking'; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
elsif ((not exists $response->{choices}) or (not defined $response->{choices}) or |
174
|
|
|
|
|
|
|
(not defined $response->{choices}->[0])) { |
175
|
0
|
|
|
|
|
|
croak 'ERROR EPYUN014b: received non-existent or undefined OpenAI::API response choice, croaking'; |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
else { |
178
|
|
|
|
|
|
|
# response choice is defined, check finish_reason and text |
179
|
0
|
0
|
0
|
|
|
|
if ((not exists $response->{choices}->[0]->{finish_reason}) or (not defined $response->{choices}->[0]->{finish_reason})) { |
|
|
0
|
|
|
|
|
|
180
|
0
|
|
|
|
|
|
carp 'WARNING WPYUN015a: received non-existent or undefined OpenAI::API response finish_reason, carping'; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
elsif ($response->{choices}->[0]->{finish_reason} ne 'stop') { |
183
|
0
|
|
|
|
|
|
carp 'WARNING WPYUN015b: received OpenAI::API response finish_reason \'', $response->{choices}->[0]->{finish_reason}, '\', expected \'stop\', carping'; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
0
|
0
|
0
|
|
|
|
if ((not exists $response->{choices}->[0]->{message}) or (not defined $response->{choices}->[0]->{message})) { |
|
|
0
|
0
|
|
|
|
|
187
|
0
|
|
|
|
|
|
croak 'ERROR EPYUN016a: received non-existent or undefined OpenAI::API response message, croaking'; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
elsif ((not exists $response->{choices}->[0]->{message}->{content}) or (not defined $response->{choices}->[0]->{message}->{content})) { |
190
|
0
|
|
|
|
|
|
croak 'ERROR EPYUN016b: received non-existent or undefined OpenAI::API response message content, croaking'; |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
else { |
193
|
|
|
|
|
|
|
# retrieve Perl source code out of valid response choice |
194
|
0
|
|
|
|
|
|
$self->{perl_source_code} = $response->{choices}->[0]->{message}->{content}; |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
# translated Perl code must not start with any stop sequence, |
199
|
|
|
|
|
|
|
# it should never have been returned from OpenAI::API in the first place |
200
|
0
|
|
|
|
|
|
foreach my string $stop_sequence (@{$stop_sequences}) { |
|
0
|
|
|
|
|
|
|
201
|
0
|
|
|
|
|
|
foreach my string $perl_source_code_line (split /\n/, $self->{perl_source_code}) { |
202
|
0
|
0
|
|
|
|
|
if ($perl_source_code_line =~ m/^(\s*)$stop_sequence/) { |
203
|
0
|
|
|
|
|
|
croak 'ERROR EPYUN017a: stop sequence encountered in OpenAI::API response text, API call malfunction, croaking'; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# translated Perl code must not start with any error sequence, |
209
|
|
|
|
|
|
|
# must ensure transmitted Python code is complete & recognizable, |
210
|
|
|
|
|
|
|
# if Python input code is good then we need to upgrade chunking or other App::PytonToPerl components |
211
|
0
|
|
|
|
|
|
foreach my string $error_sequence (@{$error_sequences}) { |
|
0
|
|
|
|
|
|
|
212
|
0
|
|
|
|
|
|
foreach my string $perl_source_code_line (split /\n/, $self->{perl_source_code}) { |
213
|
0
|
0
|
|
|
|
|
if ($perl_source_code_line =~ m/^(\s*)$error_sequence/) { |
214
|
0
|
|
|
|
|
|
croak 'ERROR EPYUN017b: error sequence encountered in OpenAI::API response text, API call malformation, croaking'; |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
# [[[ END FAILED API CALLS ]]] |
220
|
|
|
|
|
|
|
# [[[ END FAILED API CALLS ]]] |
221
|
|
|
|
|
|
|
# [[[ END FAILED API CALLS ]]] |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
# remove all possibly-extraneous trailing blank lines and newlines returned by OpenAI::API response |
224
|
0
|
|
|
|
|
|
while ( chomp $self->{perl_source_code} ) { |
225
|
0
|
|
|
|
|
|
print 'in Python::Unknown->python_preparsed_to_perl_source(), chomping $self->{perl_source_code} before returning', "\n"; |
226
|
0
|
|
|
|
|
|
1; # dummy no-op, so while() loop body is never empty |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# START HERE: need fix indentation of returned Perl code |
230
|
|
|
|
|
|
|
# START HERE: need fix indentation of returned Perl code |
231
|
|
|
|
|
|
|
# START HERE: need fix indentation of returned Perl code |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# NEED REMOVE DUMMY CODE!!! |
235
|
|
|
|
|
|
|
# NEED REMOVE DUMMY CODE!!! |
236
|
|
|
|
|
|
|
# NEED REMOVE DUMMY CODE!!! |
237
|
0
|
0
|
|
|
|
|
if ($self->{perl_source_code} eq '') { |
238
|
0
|
|
|
|
|
|
$self->{perl_source_code} = '# DUMMY PERL SOURCE CODE, NEED RETRY FAILED API CALL!'; # TEMPORARY DEBUG, NEED DELETE! |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
|
241
|
0
|
|
|
|
|
|
print 'in Python::Unknown::python_preparsed_to_perl_source(), have $self->{perl_source_code} =', "\n", $self->{perl_source_code}, "\n"; |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
# return Perl source code |
244
|
0
|
|
|
|
|
|
return $self->{perl_source_code}; |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# PYUN02x |
249
|
|
|
|
|
|
|
sub python_preparsed_to_perl_source_api_call { |
250
|
|
|
|
|
|
|
# call the OpenAI API |
251
|
0
|
|
|
0
|
0
|
|
{ my hashref $RETURN_TYPE }; |
|
0
|
|
|
|
|
|
|
252
|
0
|
|
|
|
|
|
( my Python::Unknown $self, my OpenAI::API $openai, my string::arrayref $stop_sequences, my string::hashref::arrayref $messages ) = @ARG; |
253
|
0
|
|
|
|
|
|
print 'in Python::Unknown::python_preparsed_to_perl_source_api_call(), received $self = ', Dumper($self), "\n"; |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
# error if no OpenAI API |
256
|
0
|
0
|
|
|
|
|
if (not defined $openai) { |
257
|
0
|
|
|
|
|
|
croak 'ERROR EPYUN020: undefined OpenAI API, croaking'; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# error or warning if no Python source code |
261
|
0
|
0
|
0
|
|
|
|
if ((not exists $self->{python_source_code}) or |
|
|
|
0
|
|
|
|
|
262
|
|
|
|
|
|
|
(not defined $self->{python_source_code}) or |
263
|
|
|
|
|
|
|
($self->{python_source_code} eq q{})) { |
264
|
0
|
|
|
|
|
|
croak 'ERROR EPYUN021: non-existent or undefined or empty Python source code, croaking'; |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
# error if no API messages |
268
|
0
|
0
|
0
|
|
|
|
if ((not defined $messages) or |
269
|
|
|
|
|
|
|
(not defined $messages->[0])) { |
270
|
0
|
|
|
|
|
|
croak 'ERROR EPYUN022: undefined API messages, croaking'; |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
0
|
|
|
|
|
|
print 'in Python::Unknown::python_preparsed_to_perl_source_api_call(), about to call OpenAI completions()...', "\n"; |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
# call OpenAI API, passing in Python source code and configuration options; |
276
|
|
|
|
|
|
|
# receive back response, containing Perl source code and other info; |
277
|
|
|
|
|
|
|
# wrap in eval{} to catch die() and enable checking $EVAL_ERROR below |
278
|
0
|
|
|
|
|
|
my hashref $response; |
279
|
0
|
|
|
|
|
|
eval { $response = $openai->chat( |
|
0
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# model => 'code-davinci-002', # RIP free beta 20230323 ~1615hrs CDT; death to OpenAI! |
281
|
|
|
|
|
|
|
model => 'gpt-3.5-turbo', |
282
|
|
|
|
|
|
|
# model => 'gpt-4', # wait until the price comes down, currently ~20x the price of gpt-3.5-turbo |
283
|
|
|
|
|
|
|
messages => $messages, |
284
|
|
|
|
|
|
|
temperature => 0, |
285
|
|
|
|
|
|
|
max_tokens => 256, |
286
|
|
|
|
|
|
|
top_p => 1, |
287
|
|
|
|
|
|
|
frequency_penalty => 0, |
288
|
|
|
|
|
|
|
presence_penalty => 0, |
289
|
|
|
|
|
|
|
# DEV NOTE: the API spec & OpenAI::API allow the stop sequence to be a "string or array" which may contain "Up to 4 sequences" |
290
|
|
|
|
|
|
|
# https://platform.openai.com/docs/api-reference/completions/create#completions/create-stop |
291
|
|
|
|
|
|
|
# https://metacpan.org/pod/OpenAI::API |
292
|
|
|
|
|
|
|
# DEV NOTE: translating Python `if` conditional block header without following body can result in `Python: ... Perl: ...` loop |
293
|
|
|
|
|
|
|
stop => $stop_sequences, |
294
|
|
|
|
|
|
|
); }; |
295
|
|
|
|
|
|
|
|
296
|
0
|
|
|
|
|
|
print 'in Python::Unknown::python_preparsed_to_perl_source_api_call(), received & about to return $response = ', Dumper($response), "\n"; |
297
|
|
|
|
|
|
|
#die 'TMP DEBUG, UNKNOWN OPENAI API'; |
298
|
|
|
|
|
|
|
|
299
|
0
|
|
|
|
|
|
return $response; |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
1; |