File Coverage

blib/lib/OpenAIAsync/Types/Requests.pm
Criterion Covered Total %
statement 17 173 9.8
branch 0 18 0.0
condition n/a
subroutine 6 75 8.0
pod n/a
total 23 266 8.6


line stmt bran cond sub pod time code
1             package OpenAIAsync::Types::Requests;
2 2     2   36 use v5.36.0;
  2         9  
3 2     2   15 use Object::Pad;
  2         5  
  2         20  
4              
5 2     2   385 use Object::PadX::Role::AutoMarshal;
  2         4  
  2         22  
6 2     2   108 use Object::PadX::Role::AutoJSON;
  2         4  
  2         18  
7 2     2   109 use Object::Pad::ClassAttr::Struct;
  2         5  
  2         17  
8 2     2   105 use OpenAIAsync::Types;
  2         4  
  2         571  
9              
10             role OpenAIAsync::Types::Requests::Base :does(OpenAIAsync::Types::Base) :Struct {
11             method _endpoint(); # How the client finds where to send the request
12             }
13              
14             #### Base Request Types
15              
16             class OpenAIAsync::Types::Requests::ChatCompletion :does(OpenAIAsync::Types::Requests::Base) :Struct {
17 0     0     method _endpoint() {"/chat/completions"}
  0            
  0            
  0            
18 0     0     field $messages :MarshalTo([OpenAIAsync::Types::Requests::ChatCompletion::Messages::Union]);
  0            
19 0     0     field $model :JSONStr = "gpt-3.5-turbo";
20 0           field $frequency_penalty :JSONNum = undef;
21 0     0     field $presence_penalty :JSONNum = undef;
  0            
22 0     0     field $logit_bias = undef; # TODO wtf is this?
  0            
23 0     0     field $max_tokens :JSONNum = undef;
  0            
24 0     0     field $response_format :JSONStr :JSONExclude = undef; # I'm not supporting this this version yet
  0            
25              
26 0     0     field $seed :JSONNum = undef;
  0            
27 0     0     field $stop = undef; # String, array or null, todo handle
  0            
28 0     0     field $stream :JSONBool = undef; # TODO handle
  0            
29 0     0     field $temperature :JSONNum = undef;
  0            
30 0     0     field $top_p :JSONNum = undef;
  0            
31 0     0     field $tools :JSONExclude = undef; # TODO handle this
  0            
32 0     0     field $tool_choice :JSONExclude = undef; # TODO handle this
  0            
33              
34 0     0     field $function_call :JSONExclude = undef;
  0            
35 0     0     field $functions :JSONExclude = undef;
  0            
36 0     0     }
  0            
37              
38             class OpenAIAsync::Types::Requests::Completion :does(OpenAIAsync::Types::Requests::Base) :Struct {
39 0     0     method _endpoint() {"/completions"}
  0            
  0            
  0            
40            
41 0     0     field $model :JSONStr = "gpt-3.5-turbo"; # This is how 99% of everyone else seems to default this
42 0     0     field $prompt :JSONStr;
  0            
  0            
43            
44             field $max_tokens :JSONNum = undef; # use the platform default usually
45 0     0     field $temperature :JSONNum = undef;
  0            
46 0     0     field $top_p :JSONNum = undef;
  0            
47 0     0     field $seed :JSONNum = undef;
  0            
48 0     0     field $echo :JSONBool = undef; # true or false only
  0            
49 0     0     field $suffix :JSONStr = undef;
  0            
50 0     0     field $stop :JSONStr = undef; # array of stop tokens
  0            
51 0     0     field $user :JSONStr = undef; # used for tracking purposes later
  0            
52              
53 0     0     field $frequency_penalty :JSONNum = undef;
  0            
54 0     0     field $presence_penalty :JSONNum = undef;
  0            
55            
56 0     0     field $logit_bias = undef; # TODO make this work
  0            
57 0     0     field $log_probs = undef; # TODO
  0            
58            
59 0     0     field $n :JSONNum = undef; # Danger will robinson! easy to cause $$$$$$$ costs
  0            
60 0     0     field $best_of :JSONNum = undef;
  0            
61              
62 0     0     field $stream :JSONBool = undef; # TODO FALSE ALWAYS RIGHT NOW
  0            
63              
64 0     0     ADJUST {
  0            
65             # Type assertions here
66             die "Streaming unsupported" if $self->stream;
67             }
68             }
69              
70             class OpenAIAsync::Types::Requests::Embedding :does(OpenAIAsync::Types::Requests::Base) :Struct {
71 0     0     method _endpoint() {"/embeddings"}
  0            
  0            
  0            
72 0     0     field $input :JSONStr;
  0            
73 0     0     field $model :JSONStr;
  0            
74             field $encoding_format :JSONStr = undef;
75 0     0     field $user :JSONStr = undef;
  0            
76 0     0     }
  0            
77              
78             ### Request Subtypes
79              
80             class OpenAIAsync::Types::Requests::ChatCompletion::Messages::Assistant::ToolCall :does(OpenAIAsync::Types::Base) :Struct {
81 0     0     field $id :JSONStr;
  0            
82 0     0     field $arguments :JSONStr;
  0            
83 0     0     field $type :JSONStr;
  0            
84 0     0     field $function :MarshalTo(OpenAIAsync::Types::Requests::ChatCompletion::Messages::Assistant::FunctionCall);
  0            
85             }
86              
87             class OpenAIAsync::Types::Requests::ChatCompletion::Messages::Assistant::FunctionCall :does(OpenAIAsync::Types::Base) {
88             field $arguments :JSONStr;
89             field $name :JSONStr;
90             }
91              
92             class OpenAIAsync::Types::Requests::ChatCompletion::Messages::User::Text :does(OpenAIAsync::Types::Base) :Struct {
93 0     0     field $type :JSONStr;
  0            
94 0     0     field $text :JSONStr;
  0            
95             }
96              
97             class OpenAIAsync::Types::Requests::ChatCompletion::Messages::User::ImageUrl :does(OpenAIAsync::Types::Base) :Struct {
98 0     0     field $url :JSONStr;
  0            
99             field $detail :JSONStr = undef;
100 0     0     }
  0            
101              
102             class OpenAIAsync::Types::Requests::ChatCompletion::Messages::User::Image :does(OpenAIAsync::Types::Base) :Struct {
103 0     0     field $type :JSONStr;
  0            
104 0     0     field $image_url :MarshalTo(OpenAIAsync::Types::Requests::ChatCompletion::Messages::User::ImageUrl);
  0            
105             }
106              
107             # TODO, why have two of these? just shove it into the big one below
108              
109             package
110             OpenAIAsync::Types::Requests::ChatCompletion::Messages::User::ContentUnion {
111             # This guy does some additional checks to give us the right type here
112            
113             sub new {
114 0     0     my $class = shift @_;
115 0           my %input = @_;
116            
117 0 0         die "Missing type in creation" unless $input{type};
118              
119 0 0         if ($input{type} eq 'text') {
    0          
120 0           return OpenAIAsync::Types::Requests::ChatCompletion::Messages::User::Text->new(%input);
121             } elsif ($input{type} eq 'image_url') {
122 0           return OpenAIAsync::Types::Requests::ChatCompletion::Messages::User::Image->new(%input);
123             } else {
124 0           die "Unsupported ChatCompletion User Message type: [".$input{type}."]";
125             }
126             }
127             };
128              
129             class OpenAIAsync::Types::Requests::ChatCompletion::Messages::User :does(OpenAIAsync::Types::Base) :Struct {
130             # This particular type is more complicated than AutoMarshal can handle, so we need to
131             # do this in a custom manner.
132 0     0     field $role;
  0            
133             field $name = undef;
134 0     0     field $content;
  0     0      
  0            
  0            
135              
136             ADJUST {
137             my $create_obj = sub {
138             my $cont = shift;
139              
140             if (ref($cont) eq 'HASH') {
141             # We've got a more detailed type here, create the union type here
142             my $obj = OpenAIAsync::Types::Requests::ChatCompletion::Messages::User::ContentUnion->new(%$cont);
143             } elsif (ref($cont) eq '') {
144             return $cont; # Bare string/scalar is fine
145             } else {
146             die "Can't nest other types in \$content of a ChatCompletion user message: ".ref($cont);
147             }
148             };
149              
150             if (ref($content) eq 'ARRAY') {
151             $content = [map {$create_obj->($_)} $content->@*];
152             } else {
153             # TODO check that this is acutally doing the right thing. I think it might not be for user messages that are just text
154             $content = $create_obj->($content);
155             }
156             }
157             }
158              
159             class OpenAIAsync::Types::Requests::ChatCompletion::Messages::Assistant :does(OpenAIAsync::Types::Base) :Struct {
160 0     0     field $role :JSONStr;
  0            
161 0     0     field $content :JSONStr;
  0            
162             field $name = undef;
163 0     0     field $tool_calls :MarshalTo([OpenAIAsync::Types::Requests::ChatCompletion::Messages::Assistant::ToolCall]) = undef;
  0            
164 0     0     field $function_call :MarshalTo(OpenAIAsync::Types::Requests::ChatCompletion::Messages::Assistant::FunctionCall) = undef;
  0            
165 0     0     }
  0            
166              
167             class OpenAIAsync::Types::Requests::ChatCompletion::Messages::Function :does(OpenAIAsync::Types::Base) :Struct {
168 0     0     field $role :JSONStr;
  0            
169 0     0     field $content :JSONStr;
  0            
170 0     0     field $name :JSONStr;
  0            
171             }
172              
173             class OpenAIAsync::Types::Requests::ChatCompletion::Messages::Tool :does(OpenAIAsync::Types::Base) :Struct {
174 0     0     field $role :JSONStr;
  0            
175 0     0     field $content :JSONStr;
  0            
176 0     0     field $tool_call_id :JSONStr;
  0            
177             }
178              
179             class OpenAIAsync::Types::Requests::ChatCompletion::Messages::System :does(OpenAIAsync::Types::Base) :Struct {
180 0     0     field $role :JSONStr;
  0            
181             field $name :JSONStr = undef;
182 0     0     field $content :JSONStr;
  0     0      
  0            
  0            
183             }
184              
185              
186             package
187             OpenAIAsync::Types::Requests::ChatCompletion::Messages::Union {
188             # This guy does some additional checks to give us the right type here
189            
190             sub new {
191 0     0     my ($class, %input) = @_;
192 0 0         die "Missing role in creation" unless $input{role};
193              
194 0 0         if ($input{role} eq 'system') {
    0          
    0          
    0          
    0          
195 0           return OpenAIAsync::Types::Requests::ChatCompletion::Messages::System->new(%input);
196             } elsif ($input{role} eq 'user') {
197 0           return OpenAIAsync::Types::Requests::ChatCompletion::Messages::User->new(%input);
198             } elsif ($input{role} eq 'tool') {
199 0           return OpenAIAsync::Types::Requests::ChatCompletion::Messages::Tool->new(%input);
200             } elsif ($input{role} eq 'function') {
201 0           return OpenAIAsync::Types::Requests::ChatCompletion::Messages::Function->new(%input);
202             } elsif ($input{role} eq 'assistant') {
203 0           return OpenAIAsync::Types::Requests::ChatCompletion::Messages::Assistant->new(%input);
204             } else {
205 0           die "Unsupported ChatCompletion Message role: [".$input{role}."]";
206             }
207             }
208             };
209              
210             1;