AI-Anthropic
view release on metacpan or search on metacpan
lib/AI/Anthropic.pm view on Meta::CPAN
$body->{temperature} = $args{temperature} if defined $args{temperature};
$body->{tools} = $args{tools} if defined $args{tools};
$body->{tool_choice} = $args{tool_choice} if defined $args{tool_choice};
# Streaming or regular request
if (my $stream_cb = $args{stream}) {
return $self->_stream_request($body, $stream_cb);
} else {
return $self->_request($body);
}
}
=head2 models
Returns list of available models:
my @models = $claude->models;
=cut
sub models {
return (
'claude-opus-4-20250514',
'claude-sonnet-4-20250514',
'claude-sonnet-4-5-20250929',
'claude-haiku-4-5-20251001',
'claude-3-5-sonnet-20241022',
'claude-3-5-haiku-20241022',
'claude-3-opus-20240229',
'claude-3-sonnet-20240229',
'claude-3-haiku-20240307',
);
}
# ============================================
# Private methods
# ============================================
sub _normalize_messages {
my ($self, $messages) = @_;
my @normalized;
for my $msg (@$messages) {
my $content = $msg->{content};
# Handle image content
if (ref $content eq 'ARRAY') {
my @parts;
for my $part (@$content) {
if ($part->{type} eq 'image' && $part->{path}) {
# Load image from file
push @parts, $self->_image_from_file($part->{path});
} elsif ($part->{type} eq 'image' && $part->{url}) {
# Load image from URL
push @parts, $self->_image_from_url($part->{url});
} elsif ($part->{type} eq 'image' && $part->{base64}) {
push @parts, {
type => 'image',
source => {
type => 'base64',
media_type => $part->{media_type} // 'image/png',
data => $part->{base64},
},
};
} else {
push @parts, $part;
}
}
push @normalized, { role => $msg->{role}, content => \@parts };
} else {
push @normalized, $msg;
}
}
return \@normalized;
}
sub _image_from_file {
my ($self, $path) = @_;
open my $fh, '<:raw', $path
or croak "Cannot open image file '$path': $!";
local $/;
my $data = <$fh>;
close $fh;
# Detect media type
my $media_type = 'image/png';
if ($path =~ /\.jpe?g$/i) {
$media_type = 'image/jpeg';
} elsif ($path =~ /\.gif$/i) {
$media_type = 'image/gif';
} elsif ($path =~ /\.webp$/i) {
$media_type = 'image/webp';
}
return {
type => 'image',
source => {
type => 'base64',
media_type => $media_type,
data => encode_base64($data, ''),
},
};
}
sub _image_from_url {
my ($self, $url) = @_;
return {
type => 'image',
source => {
type => 'url',
url => $url,
},
};
}
sub _request {
my ($self, $body) = @_;
my $response = $self->{_http}->post(
$self->{api_base} . '/v1/messages',
{
headers => $self->_headers,
content => $self->{_json}->encode($body),
}
);
return $self->_handle_response($response);
}
sub _stream_request {
my ($self, $body, $callback) = @_;
$body->{stream} = \1; # JSON true
my $full_text = '';
my $response_data;
# HTTP::Tiny doesn't support streaming well, so we use a data callback
my $response = $self->{_http}->post(
$self->{api_base} . '/v1/messages',
{
headers => $self->_headers,
content => $self->{_json}->encode($body),
data_callback => sub {
my ($chunk, $res) = @_;
( run in 1.665 second using v1.01-cache-2.11-cpan-df04353d9ac )