diff --git a/lib/OpenAIAsync/Server/API/v1/Audio.pm b/lib/OpenAIAsync/Server/API/v1/Audio.pm index 0b19e39..57f52e7 100644 --- a/lib/OpenAIAsync/Server/API/v1/Audio.pm +++ b/lib/OpenAIAsync/Server/API/v1/Audio.pm @@ -39,7 +39,7 @@ role OpenAIAsync::Server::API::v1::AudioTTS :strict(params) { ); } - async method audio_create_speech($obj, $http_req, $ctx) {...} + async method audio_create_speech($obj, $http_req, $ctx); } role OpenAIAsync::Server::API::v1::AudioSTT :strict(params) { @@ -53,7 +53,7 @@ role OpenAIAsync::Server::API::v1::AudioSTT :strict(params) { ); } - async method audio_create_transcript($obj, $http_req, $ctx) {...} + async method audio_create_transcript($obj, $http_req, $ctx); } role OpenAIAsync::Server::API::v1::AudioTranslate :strict(params) { @@ -67,7 +67,7 @@ role OpenAIAsync::Server::API::v1::AudioTranslate :strict(params) { ); } - async method audio_create_translation($obj, $http_req, $ctx) {...} + async method audio_create_translation($obj, $http_req, $ctx); } role OpenAIAsync::Server::API::v1::Audio :strict(params) { diff --git a/lib/OpenAIAsync/Server/API/v1/ChatCompletion.pm b/lib/OpenAIAsync/Server/API/v1/ChatCompletion.pm index af3d2fa..4df4b5b 100644 --- a/lib/OpenAIAsync/Server/API/v1/ChatCompletion.pm +++ b/lib/OpenAIAsync/Server/API/v1/ChatCompletion.pm @@ -39,5 +39,5 @@ role OpenAIAsync::Server::API::v1::ChatCompletion :strict(params) { ); } - method chat($obj, $http_req, $ctx); + async method chat($obj, $http_req, $ctx); } \ No newline at end of file diff --git a/lib/OpenAIAsync/Server/API/v1/Completions.pm b/lib/OpenAIAsync/Server/API/v1/Completions.pm index f8eab7c..8c2f066 100644 --- a/lib/OpenAIAsync/Server/API/v1/Completions.pm +++ b/lib/OpenAIAsync/Server/API/v1/Completions.pm @@ -37,5 +37,5 @@ role OpenAIAsync::Server::API::v1::Completions :strict(params) { ); } - async method completion($obj, $http_req, $ctx) {...} + async method completion($obj, $http_req, $ctx); } \ No newline at end of file diff --git a/lib/OpenAIAsync/Server/API/v1/Embeddings.pm b/lib/OpenAIAsync/Server/API/v1/Embeddings.pm index c32a535..9511a0f 100644 --- a/lib/OpenAIAsync/Server/API/v1/Embeddings.pm +++ b/lib/OpenAIAsync/Server/API/v1/Embeddings.pm @@ -37,5 +37,5 @@ role OpenAIAsync::Server::API::v1::Embeddings :strict(params) { ); } - async method embeddings($obj, $http_req, $ctx) {...} + async method embeddings($obj, $http_req, $ctx); } \ No newline at end of file diff --git a/lib/OpenAIAsync/Server/API/v1/File.pm b/lib/OpenAIAsync/Server/API/v1/File.pm index a0c849f..22be369 100644 --- a/lib/OpenAIAsync/Server/API/v1/File.pm +++ b/lib/OpenAIAsync/Server/API/v1/File.pm @@ -66,9 +66,9 @@ role OpenAIAsync::Server::API::v1::File :strict(params) { ); } - async method file_list($http_req, $ctx) {...} - async method file_info($http_req, $ctx, $params) {...} - async method file_delete($http_req, $ctx, $params) {...} - async method file_upload($http_req, $ctx, $params) {...} - async method file_download($http_req, $ctx, $params) {...} + async method file_list($http_req, $ctx); + async method file_info($http_req, $ctx, $params); + async method file_delete($http_req, $ctx, $params); + async method file_upload($http_req, $ctx, $params); + async method file_download($http_req, $ctx, $params); } \ No newline at end of file diff --git a/lib/OpenAIAsync/Server/API/v1/Image.pm b/lib/OpenAIAsync/Server/API/v1/Image.pm index 4e013f2..65fa93d 100644 --- a/lib/OpenAIAsync/Server/API/v1/Image.pm +++ b/lib/OpenAIAsync/Server/API/v1/Image.pm @@ -36,5 +36,5 @@ role OpenAIAsync::Server::API::v1::Image :strict(params) { ); } - async method create_image($http_req, $ctx) {...} + async method create_image($http_req, $ctx); } \ No newline at end of file diff --git a/lib/OpenAIAsync/Server/API/v1/ModelList.pm b/lib/OpenAIAsync/Server/API/v1/ModelList.pm index 224620b..c96ec44 100644 --- a/lib/OpenAIAsync/Server/API/v1/ModelList.pm +++ b/lib/OpenAIAsync/Server/API/v1/ModelList.pm @@ -36,5 +36,5 @@ role OpenAIAsync::Server::API::v1::ModelList :strict(params) { ); } - async method model_list($obj, $http_req, $ctx) {...} + async method model_list($obj, $http_req, $ctx); } \ No newline at end of file diff --git a/lib/OpenAIAsync/Server/API/v1/Moderations.pm b/lib/OpenAIAsync/Server/API/v1/Moderations.pm index 6af1e37..8a23c6f 100644 --- a/lib/OpenAIAsync/Server/API/v1/Moderations.pm +++ b/lib/OpenAIAsync/Server/API/v1/Moderations.pm @@ -36,5 +36,5 @@ role OpenAIAsync::Server::API::v1::Moderations :strict(params) { ); } - async method moderations($obj, $http_req, $ctx) {...} + async method moderations($obj, $http_req, $ctx); } \ No newline at end of file diff --git a/t/03-create-server.t b/t/03-create-server.t index af528a4..3a30161 100644 --- a/t/03-create-server.t +++ b/t/03-create-server.t @@ -17,6 +17,13 @@ BEGIN { class TestServer { inherit OpenAIAsync::Server; apply OpenAIAsync::Server::API::Test::ChatCompletion; + apply OpenAIAsync::Server::API::Test::Audio; + apply OpenAIAsync::Server::API::Test::Completions; + apply OpenAIAsync::Server::API::Test::Embeddings; + apply OpenAIAsync::Server::API::Test::File; + apply OpenAIAsync::Server::API::Test::Image; + apply OpenAIAsync::Server::API::Test::ModelList; + apply OpenAIAsync::Server::API::Test::Moderations; } my $server = TestServer->new(listen => '127.0.0.1', port => 12345); diff --git a/t/lib/OpenAIAsync/Server/API/Test/Audio.pm b/t/lib/OpenAIAsync/Server/API/Test/Audio.pm new file mode 100644 index 0000000..2e949c0 --- /dev/null +++ b/t/lib/OpenAIAsync/Server/API/Test/Audio.pm @@ -0,0 +1,52 @@ +package OpenAIAsync::Server::API::Test::Audio; + +use v5.36.0; +use Object::Pad; +use IO::Async::SSL; # We're not directly using it but I want to enforce that we pull it in when detecting dependencies, since openai itself is always https +use Future::AsyncAwait; +use IO::Async; + +use OpenAIAsync::Types::Results; +use OpenAIAsync::Types::Requests; + +our $VERSION = '0.02'; + +# ABSTRACT: Async server for OpenAI style REST API for various AI systems (LLMs, Images, Video, etc.) + +=pod + +=head1 NAME + +OpenAIAsync::Server::API::Audio - Basic audio api role, consumed to implement the OpenAI audio api. Does not provide an implementation, you are expected to override them in your class + +TODO document the subroles here, split up because TTS is much simpler to implement than the others and will be more valuable to support alone if someone chooses + +=head1 SYNOPSIS + +... + +=cut + + +role OpenAIAsync::Server::API::Test::AudioTTS :strict(params) { + apply OpenAIAsync::Server::API::v1::AudioTTS; + async method audio_create_speech($obj, $http_req, $ctx) {...} +} + +role OpenAIAsync::Server::API::Test::AudioSTT :strict(params) { + apply OpenAIAsync::Server::API::v1::AudioSTT; + async method audio_create_transcript($obj, $http_req, $ctx) {...} +} + +role OpenAIAsync::Server::API::Test::AudioTranslate :strict(params) { + apply OpenAIAsync::Server::API::v1::AudioTranslate; + async method audio_create_translation($obj, $http_req, $ctx) {...} +} + +role OpenAIAsync::Server::API::Test::Audio :strict(params) { + apply OpenAIAsync::Server::API::Test::AudioTTS; + apply OpenAIAsync::Server::API::Test::AudioSTT; + apply OpenAIAsync::Server::API::Test::AudioTranslate; +} + +1; \ No newline at end of file diff --git a/t/lib/OpenAIAsync/Server/API/Test/Completions.pm b/t/lib/OpenAIAsync/Server/API/Test/Completions.pm new file mode 100644 index 0000000..b873081 --- /dev/null +++ b/t/lib/OpenAIAsync/Server/API/Test/Completions.pm @@ -0,0 +1,32 @@ +package OpenAIAsync::Server::API::Test::Completions; + +use v5.36.0; +use Object::Pad; +use IO::Async::SSL; # We're not directly using it but I want to enforce that we pull it in when detecting dependencies, since openai itself is always https +use Future::AsyncAwait; +use IO::Async; + +use OpenAIAsync::Types::Results; +use OpenAIAsync::Types::Requests; + +our $VERSION = '0.02'; + +# ABSTRACT: Async server for OpenAI style REST API for various AI systems (LLMs, Images, Video, etc.) + +=pod + +=head1 NAME + +OpenAIAsync::Server::API::Completions - Basic completion api role, consumed to implement the OpenAI chat completion api. Does not provide an implementation, you are expected to override them in your class + +=head1 SYNOPSIS + +... + +=cut + +role OpenAIAsync::Server::API::Test::Completions :strict(params) { + apply OpenAIAsync::Server::API::v1::Completions; + + async method completion($obj, $http_req, $ctx) { ... } +} \ No newline at end of file diff --git a/t/lib/OpenAIAsync/Server/API/Test/Embeddings.pm b/t/lib/OpenAIAsync/Server/API/Test/Embeddings.pm new file mode 100644 index 0000000..7ec3b3a --- /dev/null +++ b/t/lib/OpenAIAsync/Server/API/Test/Embeddings.pm @@ -0,0 +1,32 @@ +package OpenAIAsync::Server::API::Test::Embeddings; + +use v5.36.0; +use Object::Pad; +use IO::Async::SSL; # We're not directly using it but I want to enforce that we pull it in when detecting dependencies, since openai itself is always https +use Future::AsyncAwait; +use IO::Async; + +use OpenAIAsync::Types::Results; +use OpenAIAsync::Types::Requests; + +our $VERSION = '0.02'; + +# ABSTRACT: Async server for OpenAI style REST API for various AI systems (LLMs, Images, Video, etc.) + +=pod + +=head1 NAME + +OpenAIAsync::Server::API::Embeddings - Basic embeddings api role, consumed to implement the OpenAI embeddings api. Does not provide an implementation, you are expected to override them in your class + +=head1 SYNOPSIS + +... + +=cut + +role OpenAIAsync::Server::API::Test::Embeddings { + apply OpenAIAsync::Server::API::v1::Embeddings; + + async method embeddings($obj, $http_req, $ctx) { ... } +} \ No newline at end of file diff --git a/t/lib/OpenAIAsync/Server/API/Test/File.pm b/t/lib/OpenAIAsync/Server/API/Test/File.pm new file mode 100644 index 0000000..9020ffd --- /dev/null +++ b/t/lib/OpenAIAsync/Server/API/Test/File.pm @@ -0,0 +1,74 @@ +package OpenAIAsync::Server::API::v1::File; + +use v5.36.0; +use Object::Pad; +use IO::Async::SSL; # We're not directly using it but I want to enforce that we pull it in when detecting dependencies, since openai itself is always https +use Future::AsyncAwait; +use IO::Async; + +use OpenAIAsync::Types::Results; +use OpenAIAsync::Types::Requests; + +our $VERSION = '0.02'; + +# ABSTRACT: Async server for OpenAI style REST API for various AI systems (LLMs, Images, Video, etc.) + +=pod + +=head1 NAME + +OpenAIAsync::Server::API::File - Basic file api role, consumed to implement the OpenAI file server. Does not provide an implementation, you are expected to override them in your class + +=head1 SYNOPSIS + +... + +=cut + +role OpenAIAsync::Server::API::v1::File :strict(params) { + ADJUST { + $self->register_url( + method => 'POST', + url => qr{^/v1/files$}, + handle => async sub($req, $ctx, $obj, $params) {await $self->file_upload($obj, $req, $ctx)}, + request_class => "OpenAIAsync::Type::Request::FileUpload", + result_class => "OpenAIAsync::Type::Shared::File", + decoder => 'www-form-urlencoded', # default is json, we need this for this api + ); + $self->register_url( + method => 'GET', + url => qr{^/v1/files/(?[^/]+)/content$}, + handle => async sub($req, $ctx, $obj, $params) {await $self->file_download($obj, $req, $ctx, $params)}, + request_class => "", # No req type here + result_class => "", # TODO this should be special, it's raw content, make it undef? leave it off? + ); + $self->register_url( + method => 'GET', + url => qr{^/v1/files/(?[^/]+)$}, + handle => async sub($req, $ctx, $obj, $params) {await $self->file_info($obj, $req, $ctx, $params)}, + request_class => "", # No req type here + result_class => "OpenAIAsync::Type::Shared::File", + ); + $self->register_url( + method => 'DELETE', + url => qr{^/v1/files/(?[^/]+)$}, + handle => async sub($req, $ctx, $obj, $params) {await $self->file_delete($obj, $req, $ctx, $params)}, + request_class => "", # No req type here + result_class => "OpenAIAsync::Type::Results::FileDeletion", + ); + $self->register_url( + method => 'GET', + url => qr{^/v1/files$}, + handle => async sub($req, $ctx, $obj, $params) {await $self->file_list($obj, $req, $ctx)}, + request_class => "OpenAIAsync::Type::Request::FileList", + result_class => "OpenAIAsync::Type::Results::FileList", + decoder => 'optional_json', # this API input is OPTIONAL, if it's not present then we create a blank object to use. + ); + } + + method file_list($http_req, $ctx); + method file_info($http_req, $ctx, $params); + method file_delete($http_req, $ctx, $params); + method file_upload($http_req, $ctx, $params); + method file_download($http_req, $ctx, $params); +} \ No newline at end of file diff --git a/t/lib/OpenAIAsync/Server/API/Test/Image.pm b/t/lib/OpenAIAsync/Server/API/Test/Image.pm new file mode 100644 index 0000000..c94882f --- /dev/null +++ b/t/lib/OpenAIAsync/Server/API/Test/Image.pm @@ -0,0 +1,40 @@ +package OpenAIAsync::Server::API::v1::Image; + +use v5.36.0; +use Object::Pad; +use IO::Async::SSL; # We're not directly using it but I want to enforce that we pull it in when detecting dependencies, since openai itself is always https +use Future::AsyncAwait; +use IO::Async; + +use OpenAIAsync::Types::Results; +use OpenAIAsync::Types::Requests; + +our $VERSION = '0.02'; + +# ABSTRACT: Async server for OpenAI style REST API for various AI systems (LLMs, Images, Video, etc.) + +=pod + +=head1 NAME + +OpenAIAsync::Server::API::Image - Basic image role, consumed to implement the OpenAI image api. Does not provide an implementation, you are expected to override them in your class + +=head1 SYNOPSIS + +... + +=cut + +role OpenAIAsync::Server::API::v1::Image :strict(params) { + ADJUST { + $self->register_url( + method => 'GET', + url => qr{^/v1/files$}, + handle => async sub($req, $ctx, $obj, $params) {await $self->create_image($obj, $req, $ctx)}, + request_class => "OpenAIAsync::Type::Requests::GenerateImage", + result_class => "", + ); + } + + method create_image($http_req, $ctx); +} \ No newline at end of file diff --git a/t/lib/OpenAIAsync/Server/API/Test/ModelList.pm b/t/lib/OpenAIAsync/Server/API/Test/ModelList.pm new file mode 100644 index 0000000..1306e14 --- /dev/null +++ b/t/lib/OpenAIAsync/Server/API/Test/ModelList.pm @@ -0,0 +1,40 @@ +package OpenAIAsync::Server::API::v1::ModelList; + +use v5.36.0; +use Object::Pad; +use IO::Async::SSL; # We're not directly using it but I want to enforce that we pull it in when detecting dependencies, since openai itself is always https +use Future::AsyncAwait; +use IO::Async; + +use OpenAIAsync::Types::Results; +use OpenAIAsync::Types::Requests; + +our $VERSION = '0.02'; + +# ABSTRACT: Async server for OpenAI style REST API for various AI systems (LLMs, Images, Video, etc.) + +=pod + +=head1 NAME + +OpenAIAsync::Server::API::ModelList - Basic model list api role, consumed to implement the OpenAI model list api. Does not provide an implementation, you are expected to override them in your class + +=head1 SYNOPSIS + +... + +=cut + +role OpenAIAsync::Server::API::v1::ModelList :strict(params) { + ADJUST { + $self->register_url( + method => 'POST', + url => qr{^/v1/models$}, + handle => async sub($req, $ctx, $obj, $params) {await $self->model_list($obj, $req, $ctx)}, + request_class => "", + result_class => "OpenAIAsync::Type::Result::ModelList", + ); + } + + method model_list($obj, $http_req, $ctx); +} \ No newline at end of file diff --git a/t/lib/OpenAIAsync/Server/API/Test/Moderations.pm b/t/lib/OpenAIAsync/Server/API/Test/Moderations.pm new file mode 100644 index 0000000..8f83867 --- /dev/null +++ b/t/lib/OpenAIAsync/Server/API/Test/Moderations.pm @@ -0,0 +1,40 @@ +package OpenAIAsync::Server::API::v1::Moderations; + +use v5.36.0; +use Object::Pad; +use IO::Async::SSL; # We're not directly using it but I want to enforce that we pull it in when detecting dependencies, since openai itself is always https +use Future::AsyncAwait; +use IO::Async; + +use OpenAIAsync::Types::Results; +use OpenAIAsync::Types::Requests; + +our $VERSION = '0.02'; + +# ABSTRACT: Async server for OpenAI style REST API for various AI systems (LLMs, Images, Video, etc.) + +=pod + +=head1 NAME + +OpenAIAsync::Server::API::Moderations - Basic moderation api role, consumed to implement the OpenAI moderation api. Does not provide an implementation, you are expected to override them in your class + +=head1 SYNOPSIS + +... + +=cut + +role OpenAIAsync::Server::API::v1::Moderations :strict(params) { + ADJUST { + $self->register_url( + method => 'POST', + url => qr{^/v1/moderations$}, + handle => async sub($req, $ctx, $obj, $params) {await $self->moderations($obj, $req, $ctx)}, + request_class => "OpenAIAsync::Type::Requests::CreateModeration", + result_class => "OpenAIAsync::Type::Results::Moderations", + ); + } + + method moderations($obj, $http_req, $ctx); +} \ No newline at end of file