File Coverage

blib/lib/AI/Chat.pm
Criterion Covered Total %
statement 28 53 52.8
branch 8 26 30.7
condition 0 3 0.0
subroutine 8 11 72.7
pod 5 5 100.0
total 49 98 50.0


line stmt bran cond sub pod time code
1             package AI::Chat;
2            
3 3     3   585369 use strict;
  3         7  
  3         134  
4 3     3   18 use warnings;
  3         13  
  3         233  
5            
6 3     3   22 use Carp;
  3         7  
  3         245  
7 3     3   2633 use HTTP::Tiny;
  3         284920  
  3         230  
8 3     3   2793 use JSON::PP;
  3         80670  
  3         2946  
9            
10             our $VERSION = '0.6';
11             $VERSION = eval $VERSION;
12            
13             my $http = HTTP::Tiny->new;
14            
15             # Create Chat object
16             sub new {
17 3     3 1 294727 my $class = shift;
18 3         12 my %attr = @_;
19            
20 3         12 $attr{'error'} = '';
21            
22 3 100       15 $attr{'api'} = 'OpenAI' unless $attr{'api'};
23 3 100       13 $attr{'error'} = 'Invalid API' unless $attr{'api'} eq 'OpenAI';
24 3 100       11 $attr{'error'} = 'API Key missing' unless $attr{'key'};
25            
26 3 50       11 $attr{'model'} = 'gpt-4o-mini' unless $attr{'model'};
27            
28 3         13 return bless \%attr, $class;
29             }
30            
31             # Define endpoints for APIs
32             my %url = (
33             'OpenAI' => 'https://api.openai.com/v1/chat/completions',
34             );
35            
36             # Define HTTP Headers for APIs
37             my %header = (
38             'OpenAI' => &_get_header_openai,
39             );
40            
41             # Returns true if last operation was success
42             sub success {
43 3     3 1 1393 my $self = shift;
44 3         20 return !$self->{'error'};
45             }
46            
47             # Returns error if last operation failed
48             sub error {
49 0     0 1 0 my $self = shift;
50 0         0 return $self->{'error'};
51             }
52            
53             # Header for calling OpenAI
54             sub _get_header_openai {
55 3     3   11 my $self = shift;
56 3 50       25 $self->{'key'} = '' unless defined $self->{'key'};
57             return {
58 3         56 'Authorization' => 'Bearer ' . $self->{'key'},
59             'Content-type' => 'application/json'
60             };
61             }
62            
63             # Get a reply from a single prompt
64             sub prompt {
65 0     0 1   my ($self, $prompt, $temperature) = @_;
66            
67 0           $self->{'error'} = '';
68 0 0         unless ($prompt) {
69 0           $self->{'error'} = "Missing prompt calling 'prompt' method";
70 0           return undef;
71             }
72            
73 0 0         $temperature = 1.0 unless $temperature;
74            
75 0           my @messages;
76             push @messages, {
77             role => 'system',
78             content => $self->{'role'},
79 0 0         } if $self->{'role'};
80 0           push @messages, {
81             role => 'user',
82             content => $prompt,
83             };
84            
85 0           return $self->chat(\@messages, $temperature);
86             }
87            
88             # Get a reply from a full chat
89             sub chat {
90 0     0 1   my ($self, $chat, $temperature) = @_;
91            
92 0 0         if (ref($chat) ne 'ARRAY') {
93 0           $self->{'error'} = 'chat method requires an arrayref';
94 0           return undef;
95             }
96            
97 0 0         $temperature = 1.0 unless $temperature;
98            
99             my $response = $http->post($url{$self->{'api'}}, {
100             'headers' => {
101             'Authorization' => 'Bearer ' . $self->{'key'},
102             'Content-type' => 'application/json'
103             },
104             content => encode_json {
105 0           model => $self->{'model'},
106             messages => [ @$chat ],
107             temperature => $temperature,
108             }
109             });
110 0 0         if ($response->{'content'} =~ 'invalid_api_key') {
111 0           croak 'Incorrect API Key - check your API Key is correct';
112             }
113            
114 0 0 0       if ($self->{'debug'} and !$response->{'success'}) {
115 0 0         croak $response if $self->{'debug'} eq 'verbose';
116 0           croak $response->{'content'};
117             }
118            
119 0           my $reply = decode_json($response->{'content'});
120            
121 0           return $reply->{'choices'}[0]->{'message'}->{'content'};
122             }
123            
124            
125             __END__