AI-Ollama-Client

 view release on metacpan or  search on metacpan

lib/AI/Ollama/Client/Impl.pm  view on Meta::CPAN


has 'schema' => (
    is => 'lazy',
    default => sub {
        if( my $fn = $_[0]->schema_file ) {
            YAML::PP->new( boolean => 'JSON::PP' )->load_file($fn);
        }
    },
);

has 'validate_requests' => (
    is => 'rw',
    default => 1,
);

has 'validate_responses' => (
    is => 'rw',
    default => 1,
);

has 'openapi' => (
    is => 'lazy',
    default => sub {
        if( my $schema = $_[0]->schema ) {
            OpenAPI::Modern->new( openapi_schema => $schema, openapi_uri => '' )
        }

lib/AI/Ollama/Client/Impl.pm  view on Meta::CPAN

              'digest' => delete $options{'digest'},
    );
    my $url = Mojo::URL->new( $self->server . $path );

    my $tx = $self->ua->build_tx(
        $method => $url,
        {
        }
    );

    $self->validate_request( $tx );

    return $tx
}


sub checkBlob( $self, %options ) {
    my $tx = $self->build_checkBlob_request(%options);


    my $res = Future::Mojo->new();

    my $r1 = Future::Mojo->new();
    $r1->then( sub( $tx ) {
        my $resp = $tx->res;
        $self->emit(response => $resp);
        # Should we validate using OpenAPI::Modern here?!
        if( $resp->code == 200 ) {
            # Blob exists on the server
            $res->done($resp);
        } elsif( $resp->code == 404 ) {
            # Blob was not found
            $res->done($resp);
        } else {
            # An unknown/unhandled response, likely an error
            $res->fail( sprintf( "unknown_unhandled code %d: %s", $resp->code, $resp->body ), $resp);
        }

lib/AI/Ollama/Client/Impl.pm  view on Meta::CPAN


    my $body = delete $options{ body } // '';
    my $tx = $self->ua->build_tx(
        $method => $url,
        {
            "Content-Type" => 'application/octet-stream',
        }
        => $body,
    );

    $self->validate_request( $tx );

    return $tx
}


sub createBlob( $self, %options ) {
    my $tx = $self->build_createBlob_request(%options);


    my $res = Future::Mojo->new();

    my $r1 = Future::Mojo->new();
    $r1->then( sub( $tx ) {
        my $resp = $tx->res;
        $self->emit(response => $resp);
        # Should we validate using OpenAPI::Modern here?!
        if( $resp->code == 201 ) {
            # Blob was successfully created
            $res->done($resp);
        } else {
            # An unknown/unhandled response, likely an error
            $res->fail( sprintf( "unknown_unhandled code %d: %s", $resp->code, $resp->body ), $resp);
        }
    })->retain;

    # Start our transaction

lib/AI/Ollama/Client/Impl.pm  view on Meta::CPAN

    my $request = AI::Ollama::GenerateChatCompletionRequest->new( \%options )->as_hash;
    my $tx = $self->ua->build_tx(
        $method => $url,
        {
            'Accept' => 'application/x-ndjson',
            "Content-Type" => 'application/json',
        }
        => json => $request,
    );

    $self->validate_request( $tx );

    return $tx
}


sub generateChatCompletion( $self, %options ) {
    my $tx = $self->build_generateChatCompletion_request(%options);


    my $res = Future::Mojo->new();

    my $r1 = Future::Mojo->new();
    our @store; # we should use ->retain() instead
    push @store, $r1->then( sub( $tx ) {
        my $resp = $tx->res;
        $self->emit(response => $resp);
        # Should we validate using OpenAPI::Modern here?!
        if( $resp->code == 200 ) {
            # Successful operation.
            my $queue = Future::Queue->new( prototype => 'Future::Mojo' );
            $res->done( $queue );
            my $ct = $resp->headers->content_type;
            return unless $ct;
            $ct =~ s/;\s+.*//;
            if( $ct eq 'application/x-ndjson' ) {
                # we only handle ndjson currently
                my $handled_offset = 0;
                $resp->on(progress => sub($msg,@) {
                    my $fresh = substr( $msg->body, $handled_offset );
                    my $body = $msg->body;
                    $body =~ s/[^\r\n]+\z//; # Strip any unfinished line
                    $handled_offset = length $body;
                    my @lines = split /\n/, $fresh;
                    for (@lines) {
                        my $payload = decode_json( $_ );
                        $self->validate_response( $payload, $tx );
                        $queue->push(
                            AI::Ollama::GenerateChatCompletionResponse->new($payload),

                        );
                    };
                    if( $msg->{state} eq 'finished' ) {
                        $queue->finish();
                    }
                });
            } else {

lib/AI/Ollama/Client/Impl.pm  view on Meta::CPAN


    my $request = AI::Ollama::CopyModelRequest->new( \%options )->as_hash;
    my $tx = $self->ua->build_tx(
        $method => $url,
        {
            "Content-Type" => 'application/json',
        }
        => json => $request,
    );

    $self->validate_request( $tx );

    return $tx
}


sub copyModel( $self, %options ) {
    my $tx = $self->build_copyModel_request(%options);


    my $res = Future::Mojo->new();

    my $r1 = Future::Mojo->new();
    $r1->then( sub( $tx ) {
        my $resp = $tx->res;
        $self->emit(response => $resp);
        # Should we validate using OpenAPI::Modern here?!
        if( $resp->code == 200 ) {
            # Successful operation.
            $res->done($resp);
        } else {
            # An unknown/unhandled response, likely an error
            $res->fail( sprintf( "unknown_unhandled code %d: %s", $resp->code, $resp->body ), $resp);
        }
    })->retain;

    # Start our transaction

lib/AI/Ollama/Client/Impl.pm  view on Meta::CPAN

    my $request = AI::Ollama::CreateModelRequest->new( \%options )->as_hash;
    my $tx = $self->ua->build_tx(
        $method => $url,
        {
            'Accept' => 'application/x-ndjson',
            "Content-Type" => 'application/json',
        }
        => json => $request,
    );

    $self->validate_request( $tx );

    return $tx
}


sub createModel( $self, %options ) {
    my $tx = $self->build_createModel_request(%options);


    my $res = Future::Mojo->new();

    my $r1 = Future::Mojo->new();
    our @store; # we should use ->retain() instead
    push @store, $r1->then( sub( $tx ) {
        my $resp = $tx->res;
        $self->emit(response => $resp);
        # Should we validate using OpenAPI::Modern here?!
        if( $resp->code == 200 ) {
            # Successful operation.
            my $queue = Future::Queue->new( prototype => 'Future::Mojo' );
            $res->done( $queue );
            my $ct = $resp->headers->content_type;
            return unless $ct;
            $ct =~ s/;\s+.*//;
            if( $ct eq 'application/x-ndjson' ) {
                # we only handle ndjson currently
                my $handled_offset = 0;
                $resp->on(progress => sub($msg,@) {
                    my $fresh = substr( $msg->body, $handled_offset );
                    my $body = $msg->body;
                    $body =~ s/[^\r\n]+\z//; # Strip any unfinished line
                    $handled_offset = length $body;
                    my @lines = split /\n/, $fresh;
                    for (@lines) {
                        my $payload = decode_json( $_ );
                        $self->validate_response( $payload, $tx );
                        $queue->push(
                            AI::Ollama::CreateModelResponse->new($payload),

                        );
                    };
                    if( $msg->{state} eq 'finished' ) {
                        $queue->finish();
                    }
                });
            } else {

lib/AI/Ollama/Client/Impl.pm  view on Meta::CPAN


    my $request = AI::Ollama::DeleteModelRequest->new( \%options )->as_hash;
    my $tx = $self->ua->build_tx(
        $method => $url,
        {
            "Content-Type" => 'application/json',
        }
        => json => $request,
    );

    $self->validate_request( $tx );

    return $tx
}


sub deleteModel( $self, %options ) {
    my $tx = $self->build_deleteModel_request(%options);


    my $res = Future::Mojo->new();

    my $r1 = Future::Mojo->new();
    $r1->then( sub( $tx ) {
        my $resp = $tx->res;
        $self->emit(response => $resp);
        # Should we validate using OpenAPI::Modern here?!
        if( $resp->code == 200 ) {
            # Successful operation.
            $res->done($resp);
        } else {
            # An unknown/unhandled response, likely an error
            $res->fail( sprintf( "unknown_unhandled code %d: %s", $resp->code, $resp->body ), $resp);
        }
    })->retain;

    # Start our transaction

lib/AI/Ollama/Client/Impl.pm  view on Meta::CPAN

    my $request = AI::Ollama::GenerateEmbeddingRequest->new( \%options )->as_hash;
    my $tx = $self->ua->build_tx(
        $method => $url,
        {
            'Accept' => 'application/json',
            "Content-Type" => 'application/json',
        }
        => json => $request,
    );

    $self->validate_request( $tx );

    return $tx
}


sub generateEmbedding( $self, %options ) {
    my $tx = $self->build_generateEmbedding_request(%options);


    my $res = Future::Mojo->new();

    my $r1 = Future::Mojo->new();
    $r1->then( sub( $tx ) {
        my $resp = $tx->res;
        $self->emit(response => $resp);
        # Should we validate using OpenAPI::Modern here?!
        if( $resp->code == 200 ) {
            # Successful operation.
            my $ct = $resp->headers->content_type;
            $ct =~ s/;\s+.*//;
            if( $ct eq 'application/json' ) {
                my $payload = $resp->json();
                $self->validate_response( $payload, $tx );
                $res->done(
                    AI::Ollama::GenerateEmbeddingResponse->new($payload),

                );
            } else {
                # Unknown/unhandled content type
                $res->fail( sprintf("unknown_unhandled content type '%s'", $resp->content_type), $resp );
            }
        } else {
            # An unknown/unhandled response, likely an error

lib/AI/Ollama/Client/Impl.pm  view on Meta::CPAN

    my $request = AI::Ollama::GenerateCompletionRequest->new( \%options )->as_hash;
    my $tx = $self->ua->build_tx(
        $method => $url,
        {
            'Accept' => 'application/x-ndjson',
            "Content-Type" => 'application/json',
        }
        => json => $request,
    );

    $self->validate_request( $tx );

    return $tx
}


sub generateCompletion( $self, %options ) {
    my $tx = $self->build_generateCompletion_request(%options);


    my $res = Future::Mojo->new();

    my $r1 = Future::Mojo->new();
    our @store; # we should use ->retain() instead
    push @store, $r1->then( sub( $tx ) {
        my $resp = $tx->res;
        $self->emit(response => $resp);
        # Should we validate using OpenAPI::Modern here?!
        if( $resp->code == 200 ) {
            # Successful operation.
            my $queue = Future::Queue->new( prototype => 'Future::Mojo' );
            $res->done( $queue );
            my $ct = $resp->headers->content_type;
            return unless $ct;
            $ct =~ s/;\s+.*//;
            if( $ct eq 'application/x-ndjson' ) {
                # we only handle ndjson currently
                my $handled_offset = 0;
                $resp->on(progress => sub($msg,@) {
                    my $fresh = substr( $msg->body, $handled_offset );
                    my $body = $msg->body;
                    $body =~ s/[^\r\n]+\z//; # Strip any unfinished line
                    $handled_offset = length $body;
                    my @lines = split /\n/, $fresh;
                    for (@lines) {
                        my $payload = decode_json( $_ );
                        $self->validate_response( $payload, $tx );
                        $queue->push(
                            AI::Ollama::GenerateCompletionResponse->new($payload),

                        );
                    };
                    if( $msg->{state} eq 'finished' ) {
                        $queue->finish();
                    }
                });
            } else {

lib/AI/Ollama/Client/Impl.pm  view on Meta::CPAN

    my $request = AI::Ollama::PullModelRequest->new( \%options )->as_hash;
    my $tx = $self->ua->build_tx(
        $method => $url,
        {
            'Accept' => 'application/x-ndjson',
            "Content-Type" => 'application/json',
        }
        => json => $request,
    );

    $self->validate_request( $tx );

    return $tx
}


sub pullModel( $self, %options ) {
    my $tx = $self->build_pullModel_request(%options);


    my $res = Future::Mojo->new();

    my $r1 = Future::Mojo->new();
    our @store; # we should use ->retain() instead
    push @store, $r1->then( sub( $tx ) {
        my $resp = $tx->res;
        $self->emit(response => $resp);
        # Should we validate using OpenAPI::Modern here?!
        if( $resp->code == 200 ) {
            # Successful operation.
            my $queue = Future::Queue->new( prototype => 'Future::Mojo' );
            $res->done( $queue );
            my $ct = $resp->headers->content_type;
            return unless $ct;
            $ct =~ s/;\s+.*//;
            if( $ct eq 'application/x-ndjson' ) {
                # we only handle ndjson currently
                my $handled_offset = 0;
                $resp->on(progress => sub($msg,@) {
                    my $fresh = substr( $msg->body, $handled_offset );
                    my $body = $msg->body;
                    $body =~ s/[^\r\n]+\z//; # Strip any unfinished line
                    $handled_offset = length $body;
                    my @lines = split /\n/, $fresh;
                    for (@lines) {
                        my $payload = decode_json( $_ );
                        $self->validate_response( $payload, $tx );
                        $queue->push(
                            AI::Ollama::PullModelResponse->new($payload),

                        );
                    };
                    if( $msg->{state} eq 'finished' ) {
                        $queue->finish();
                    }
                });
            } else {

lib/AI/Ollama/Client/Impl.pm  view on Meta::CPAN

    my $request = AI::Ollama::PushModelRequest->new( \%options )->as_hash;
    my $tx = $self->ua->build_tx(
        $method => $url,
        {
            'Accept' => 'application/json',
            "Content-Type" => 'application/json',
        }
        => json => $request,
    );

    $self->validate_request( $tx );

    return $tx
}


sub pushModel( $self, %options ) {
    my $tx = $self->build_pushModel_request(%options);


    my $res = Future::Mojo->new();

    my $r1 = Future::Mojo->new();
    $r1->then( sub( $tx ) {
        my $resp = $tx->res;
        $self->emit(response => $resp);
        # Should we validate using OpenAPI::Modern here?!
        if( $resp->code == 200 ) {
            # Successful operation.
            my $ct = $resp->headers->content_type;
            $ct =~ s/;\s+.*//;
            if( $ct eq 'application/json' ) {
                my $payload = $resp->json();
                $self->validate_response( $payload, $tx );
                $res->done(
                    AI::Ollama::PushModelResponse->new($payload),

                );
            } else {
                # Unknown/unhandled content type
                $res->fail( sprintf("unknown_unhandled content type '%s'", $resp->content_type), $resp );
            }
        } else {
            # An unknown/unhandled response, likely an error

lib/AI/Ollama/Client/Impl.pm  view on Meta::CPAN

    my $request = AI::Ollama::ModelInfoRequest->new( \%options )->as_hash;
    my $tx = $self->ua->build_tx(
        $method => $url,
        {
            'Accept' => 'application/json',
            "Content-Type" => 'application/json',
        }
        => json => $request,
    );

    $self->validate_request( $tx );

    return $tx
}


sub showModelInfo( $self, %options ) {
    my $tx = $self->build_showModelInfo_request(%options);


    my $res = Future::Mojo->new();

    my $r1 = Future::Mojo->new();
    $r1->then( sub( $tx ) {
        my $resp = $tx->res;
        $self->emit(response => $resp);
        # Should we validate using OpenAPI::Modern here?!
        if( $resp->code == 200 ) {
            # Successful operation.
            my $ct = $resp->headers->content_type;
            $ct =~ s/;\s+.*//;
            if( $ct eq 'application/json' ) {
                my $payload = $resp->json();
                $self->validate_response( $payload, $tx );
                $res->done(
                    AI::Ollama::ModelInfo->new($payload),

                );
            } else {
                # Unknown/unhandled content type
                $res->fail( sprintf("unknown_unhandled content type '%s'", $resp->content_type), $resp );
            }
        } else {
            # An unknown/unhandled response, likely an error

lib/AI/Ollama/Client/Impl.pm  view on Meta::CPAN

    my $path = '/tags';
    my $url = Mojo::URL->new( $self->server . $path );

    my $tx = $self->ua->build_tx(
        $method => $url,
        {
            'Accept' => 'application/json',
        }
    );

    $self->validate_request( $tx );

    return $tx
}


sub listModels( $self, %options ) {
    my $tx = $self->build_listModels_request(%options);


    my $res = Future::Mojo->new();

    my $r1 = Future::Mojo->new();
    $r1->then( sub( $tx ) {
        my $resp = $tx->res;
        $self->emit(response => $resp);
        # Should we validate using OpenAPI::Modern here?!
        if( $resp->code == 200 ) {
            # Successful operation.
            my $ct = $resp->headers->content_type;
            $ct =~ s/;\s+.*//;
            if( $ct eq 'application/json' ) {
                my $payload = $resp->json();
                $self->validate_response( $payload, $tx );
                $res->done(
                    AI::Ollama::ModelsResponse->new($payload),

                );
            } else {
                # Unknown/unhandled content type
                $res->fail( sprintf("unknown_unhandled content type '%s'", $resp->content_type), $resp );
            }
        } else {
            # An unknown/unhandled response, likely an error

lib/AI/Ollama/Client/Impl.pm  view on Meta::CPAN

    })->catch(sub($err) {
        $self->emit(response => $tx, $err);
        $r1->fail( $err => $tx );
        undef $r1;
    });

    return $res
}


sub validate_response( $self, $payload, $tx ) {
    if(     $self->validate_responses
        and my $openapi = $self->openapi ) {
        my $results = $openapi->validate_response($payload, { request => $tx->req });
        if( $results->{error}) {
            say $results;
            say $tx->res->to_string;
        };
    };
}

sub validate_request( $self, $tx ) {
    if(        $self->validate_requests
        and my $openapi = $self->openapi ) {
        my $results = $openapi->validate_request($tx->req);
        if( $results->{error}) {
            say $results;
            say $tx->req->to_string;
        };
    };
}

1;



( run in 0.366 second using v1.01-cache-2.11-cpan-a5abf4f5562 )