has
'config'
=> (
is
=>
'ro'
,
default
=>
sub
{ OpenAI::API::Config->new() },
isa
=>
sub
{
die
"config must be an instance of OpenAI::API::Config"
unless
ref
$_
[0] eq
'OpenAI::API::Config'
;
},
coerce
=>
sub
{
return
$_
[0]
if
ref
$_
[0] eq
'OpenAI::API::Config'
;
return
OpenAI::API::Config->new( %{
$_
[0] } );
},
);
has
'user_agent'
=> (
is
=>
'ro'
,
lazy
=> 1,
builder
=>
'_build_user_agent'
,
);
has
'event_loop'
=> (
is
=>
'ro'
,
lazy
=> 1,
builder
=>
'_build_event_loop'
,
);
sub
_build_user_agent {
my
(
$self
) =
@_
;
$self
->{user_agent} = LWP::UserAgent->new(
timeout
=>
$self
->config->timeout );
}
sub
_build_event_loop {
my
(
$self
) =
@_
;
my
$class
=
$self
->config->event_loop_class;
eval
"require $class"
or
die
"Failed to load event loop class $class: $@"
;
return
$class
->new();
}
sub
endpoint {
die
"Must be implemented"
;
}
sub
method {
die
"Must be implemented"
;
}
sub
_parse_response {
my
(
$self
,
$res
) =
@_
;
my
$class
=
ref
$self
||
$self
;
(
my
$response_module
=
$class
) =~ s/Request/Response/;
eval
"require $response_module"
or
die
$@;
my
$decoded_res
= decode_json(
$res
->decoded_content );
return
$response_module
->new(
$decoded_res
);
}
sub
request_params {
my
(
$self
) =
@_
;
my
%request_params
= %{
$self
};
delete
$request_params
{config};
delete
$request_params
{user_agent};
delete
$request_params
{event_loop};
return
\
%request_params
;
}
sub
send
{
my
$self
=
shift
;
if
(
@_
== 1 ) {
warn
"Sending config via send is deprecated. More info: perldoc OpenAI::API::Config\n"
;
}
my
%args
=
@_
;
my
$res
=
$self
->method eq
'POST'
?
$self
->_post()
:
$self
->method eq
'GET'
?
$self
->_get()
:
die
"Invalid method"
;
if
(
$args
{http_response} ) {
return
$res
;
}
return
$self
->_parse_response(
$res
);
}
sub
_get {
my
(
$self
) =
@_
;
my
$req
=
$self
->_create_request(
'GET'
);
return
$self
->_send_request(
$req
);
}
sub
_post {
my
(
$self
) =
@_
;
my
$req
=
$self
->_create_request(
'POST'
, encode_json(
$self
->request_params() ) );
return
$self
->_send_request(
$req
);
}
sub
send_async {
my
(
$self
,
%args
) =
@_
;
my
$res_future
=
$self
->method eq
'POST'
?
$self
->_post_async()
:
$self
->method eq
'GET'
?
$self
->_get_async()
:
die
"Invalid method"
;
if
(
$args
{http_response} ) {
return
$res_future
;
}
my
$decoded_content_future
=
$res_future
->then(
sub
{
my
$res
=
shift
;
return
$self
->_parse_response(
$res
);
}
);
return
$decoded_content_future
;
}
sub
_get_async {
my
(
$self
) =
@_
;
my
$req
=
$self
->_create_request(
'GET'
);
return
$self
->_send_request_async(
$req
);
}
sub
_post_async {
my
(
$self
,
$config
) =
@_
;
my
$req
=
$self
->_create_request(
'POST'
, encode_json(
$self
->request_params() ) );
return
$self
->_send_request_async(
$req
);
}
sub
_create_request {
my
(
$self
,
$method
,
$content
) =
@_
;
my
$req
= HTTP::Request->new(
$method
=>
$self
->config->api_base .
"/"
.
$self
->endpoint,
$self
->_request_headers(),
$content
,
);
return
$req
;
}
sub
_request_headers {
my
(
$self
) =
@_
;
return
[
'Content-Type'
=>
'application/json'
,
'Authorization'
=>
'Bearer '
.
$self
->config->api_key,
];
}
sub
_send_request {
my
(
$self
,
$req
) =
@_
;
my
$loop
= IO::Async::Loop->new();
my
$future
=
$self
->_async_http_send_request(
$req
);
$loop
->await(
$future
);
my
$res
=
$future
->get;
if
( !
$res
->is_success ) {
OpenAI::API::Error->throw(
message
=>
"Error: '@{[ $res->status_line ]}'"
,
request
=>
$req
,
response
=>
$res
,
);
}
return
$res
;
}
sub
_send_request_async {
my
(
$self
,
$req
) =
@_
;
return
$self
->_async_http_send_request(
$req
)->then(
sub
{
my
$res
=
shift
;
if
( !
$res
->is_success ) {
OpenAI::API::Error->throw(
message
=>
"Error: '@{[ $res->status_line ]}'"
,
request
=>
$req
,
response
=>
$res
,
);
}
return
$res
;
}
)->
catch
(
sub
{
my
$err
=
shift
;
die
$err
;
}
);
}
sub
_http_send_request {
my
(
$self
,
$req
) =
@_
;
for
my
$attempt
( 1 ..
$self
->config->retry ) {
my
$res
=
$self
->user_agent->request(
$req
);
if
(
$res
->is_success ) {
return
$res
;
}
elsif
(
$res
->code =~ /^(?:500|503|504|599)$/ &&
$attempt
<
$self
->config->retry ) {
sleep
(
$self
->config->
sleep
);
}
else
{
return
$res
;
}
}
}
sub
_async_http_send_request {
my
(
$self
,
$req
) =
@_
;
my
$future
= IO::Async::Future->new;
$self
->event_loop->later(
sub
{
eval
{
my
$res
=
$self
->_http_send_request(
$req
);
$future
->done(
$res
);
1;
} or
do
{
my
$err
= $@;
$future
->fail(
$err
);
};
}
);
return
$future
;
}
1;