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 )