diff --git a/src/CrowdinApiClient/Api/AiGatewayApi.php b/src/CrowdinApiClient/Api/AiGatewayApi.php new file mode 100644 index 00000000..6bfc0580 --- /dev/null +++ b/src/CrowdinApiClient/Api/AiGatewayApi.php @@ -0,0 +1,93 @@ +_get($url, AiGatewayResponse::class); + } + + /** + * AI Gateway POST + * @link https://developer.crowdin.com/api/v2/#operation/api.ai.providers.gateway.crowdin.post API Documentation + * + * @param int $userId + * @param int $aiProviderId + * @param string $path Raw provider API path after `/gateway/` (e.g. `chat/completions`) + * @param array $data Request body forwarded to the AI provider + * @return AiGatewayResponse|null + */ + public function gatewayPost(int $userId, int $aiProviderId, string $path, array $data): ?AiGatewayResponse + { + $url = sprintf('users/%d/ai/providers/%d/gateway/%s', $userId, $aiProviderId, $path); + return $this->_post($url, AiGatewayResponse::class, $data); + } + + /** + * AI Gateway PUT + * @link https://developer.crowdin.com/api/v2/#operation/api.ai.providers.gateway.crowdin.put API Documentation + * + * @param int $userId + * @param int $aiProviderId + * @param string $path Raw provider API path after `/gateway/` (e.g. `chat/completions`) + * @param array $data Request body forwarded to the AI provider + * @return AiGatewayResponse|null + */ + public function gatewayPut(int $userId, int $aiProviderId, string $path, array $data): ?AiGatewayResponse + { + $url = sprintf('users/%d/ai/providers/%d/gateway/%s', $userId, $aiProviderId, $path); + return $this->_put($url, AiGatewayResponse::class, $data); + } + + /** + * AI Gateway PATCH + * @link https://developer.crowdin.com/api/v2/#operation/api.ai.providers.gateway.crowdin.patch API Documentation + * + * @param int $userId + * @param int $aiProviderId + * @param string $path Raw provider API path after `/gateway/` (e.g. `chat/completions`) + * @param array $data Request body forwarded to the AI provider + * @return AiGatewayResponse|null + */ + public function gatewayPatch(int $userId, int $aiProviderId, string $path, array $data): ?AiGatewayResponse + { + $url = sprintf('users/%d/ai/providers/%d/gateway/%s', $userId, $aiProviderId, $path); + return $this->_patch($url, AiGatewayResponse::class, $data); + } + + /** + * AI Gateway DELETE + * @link https://developer.crowdin.com/api/v2/#operation/api.ai.providers.gateway.crowdin.delete API Documentation + * + * @param int $userId + * @param int $aiProviderId + * @param string $path Raw provider API path after `/gateway/` (e.g. `chat/completions`) + * @return AiGatewayResponse|null + */ + public function gatewayDelete(int $userId, int $aiProviderId, string $path): ?AiGatewayResponse + { + $url = sprintf('users/%d/ai/providers/%d/gateway/%s', $userId, $aiProviderId, $path); + return $this->client->apiRequest( + 'delete', + $url, + new ResponseModelDecorator(AiGatewayResponse::class) + ); + } +} diff --git a/src/CrowdinApiClient/Api/Enterprise/AiGatewayApi.php b/src/CrowdinApiClient/Api/Enterprise/AiGatewayApi.php new file mode 100644 index 00000000..b109a64b --- /dev/null +++ b/src/CrowdinApiClient/Api/Enterprise/AiGatewayApi.php @@ -0,0 +1,89 @@ +_get($url, AiGatewayResponse::class); + } + + /** + * AI Gateway POST + * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.providers.gateway.enterprise.post API Documentation + * + * @param int $aiProviderId + * @param string $path Raw provider API path after `/gateway/` (e.g. `chat/completions`) + * @param array $data Request body forwarded to the AI provider + * @return AiGatewayResponse|null + */ + public function gatewayPost(int $aiProviderId, string $path, array $data): ?AiGatewayResponse + { + $url = sprintf('ai/providers/%d/gateway/%s', $aiProviderId, $path); + return $this->_post($url, AiGatewayResponse::class, $data); + } + + /** + * AI Gateway PUT + * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.providers.gateway.enterprise.put API Documentation + * + * @param int $aiProviderId + * @param string $path Raw provider API path after `/gateway/` (e.g. `chat/completions`) + * @param array $data Request body forwarded to the AI provider + * @return AiGatewayResponse|null + */ + public function gatewayPut(int $aiProviderId, string $path, array $data): ?AiGatewayResponse + { + $url = sprintf('ai/providers/%d/gateway/%s', $aiProviderId, $path); + return $this->_put($url, AiGatewayResponse::class, $data); + } + + /** + * AI Gateway PATCH + * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.providers.gateway.enterprise.patch API Documentation + * + * @param int $aiProviderId + * @param string $path Raw provider API path after `/gateway/` (e.g. `chat/completions`) + * @param array $data Request body forwarded to the AI provider + * @return AiGatewayResponse|null + */ + public function gatewayPatch(int $aiProviderId, string $path, array $data): ?AiGatewayResponse + { + $url = sprintf('ai/providers/%d/gateway/%s', $aiProviderId, $path); + return $this->_patch($url, AiGatewayResponse::class, $data); + } + + /** + * AI Gateway DELETE + * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.providers.gateway.enterprise.delete API Documentation + * + * @param int $aiProviderId + * @param string $path Raw provider API path after `/gateway/` (e.g. `chat/completions`) + * @return AiGatewayResponse|null + */ + public function gatewayDelete(int $aiProviderId, string $path): ?AiGatewayResponse + { + $url = sprintf('ai/providers/%d/gateway/%s', $aiProviderId, $path); + return $this->client->apiRequest( + 'delete', + $url, + new ResponseModelDecorator(AiGatewayResponse::class) + ); + } +} diff --git a/src/CrowdinApiClient/Crowdin.php b/src/CrowdinApiClient/Crowdin.php index 7ea7616e..18772014 100644 --- a/src/CrowdinApiClient/Crowdin.php +++ b/src/CrowdinApiClient/Crowdin.php @@ -52,6 +52,7 @@ * @property \CrowdinApiClient\Api\AiApi|\CrowdinApiClient\Api\Enterprise\AiApi $ai * @property \CrowdinApiClient\Api\Enterprise\FieldApi $field * @property \CrowdinApiClient\Api\StyleGuideApi $styleGuide + * @property \CrowdinApiClient\Api\AiGatewayApi|\CrowdinApiClient\Api\Enterprise\AiGatewayApi $aiGateway */ class Crowdin { @@ -126,6 +127,7 @@ class Crowdin 'securityLog', 'ai', 'styleGuide', + 'aiGateway', ]; protected $servicesEnterprise = [ @@ -170,6 +172,7 @@ class Crowdin 'ai', 'field', 'styleGuide', + 'aiGateway', ]; public function __construct(array $config) diff --git a/src/CrowdinApiClient/Model/AiGatewayResponse.php b/src/CrowdinApiClient/Model/AiGatewayResponse.php new file mode 100644 index 00000000..5964500c --- /dev/null +++ b/src/CrowdinApiClient/Model/AiGatewayResponse.php @@ -0,0 +1,10 @@ +mockRequest([ + 'path' => '/users/1/ai/providers/2/gateway/chat/completions', + 'method' => 'get', + 'response' => '{ + "data": { + "id": "chatcmpl-abc123", + "object": "chat.completion", + "choices": [{"message": {"role": "assistant", "content": "Hello!"}}] + } + }', + ]); + + $response = $this->crowdin->aiGateway->gatewayGet(1, 2, 'chat/completions'); + $this->assertInstanceOf(AiGatewayResponse::class, $response); + $this->assertEquals('chatcmpl-abc123', $response->getData()['id']); + $this->assertEquals('chat.completion', $response->getData()['object']); + } + + public function testGatewayPost(): void + { + $data = [ + 'model' => 'gpt-4o', + 'messages' => [ + [ + 'role' => 'user', + 'content' => 'Hi', + ], + ], + ]; + + $this->mockRequest([ + 'path' => '/users/1/ai/providers/2/gateway/chat/completions', + 'method' => 'post', + 'body' => json_encode($data), + 'response' => '{ + "data": { + "id": "chatcmpl-abc123", + "object": "chat.completion", + "choices": [{"message": {"role": "assistant", "content": "Hello!"}}] + } + }', + ]); + + $response = $this->crowdin->aiGateway->gatewayPost(1, 2, 'chat/completions', $data); + $this->assertInstanceOf(AiGatewayResponse::class, $response); + $this->assertEquals('chatcmpl-abc123', $response->getData()['id']); + } + + public function testGatewayPut(): void + { + $data = [ + 'model' => 'gpt-4o', + 'messages' => [ + [ + 'role' => 'user', + 'content' => 'Hi', + ], + ], + ]; + + $this->mockRequest([ + 'path' => '/users/1/ai/providers/2/gateway/chat/completions', + 'method' => 'put', + 'body' => json_encode($data), + 'response' => '{ + "data": { + "id": "chatcmpl-abc123", + "object": "chat.completion", + "choices": [{"message": {"role": "assistant", "content": "Hello!"}}] + } + }', + ]); + + $response = $this->crowdin->aiGateway->gatewayPut(1, 2, 'chat/completions', $data); + $this->assertInstanceOf(AiGatewayResponse::class, $response); + $this->assertEquals('chatcmpl-abc123', $response->getData()['id']); + } + + public function testGatewayPatch(): void + { + $data = [ + 'model' => 'gpt-4o', + 'messages' => [ + [ + 'role' => 'user', + 'content' => 'Hi', + ], + ], + ]; + + $this->mockRequest([ + 'path' => '/users/1/ai/providers/2/gateway/chat/completions', + 'method' => 'patch', + 'body' => json_encode($data), + 'response' => '{ + "data": { + "id": "chatcmpl-abc123", + "object": "chat.completion", + "choices": [{"message": {"role": "assistant", "content": "Hello!"}}] + } + }', + ]); + + $response = $this->crowdin->aiGateway->gatewayPatch(1, 2, 'chat/completions', $data); + $this->assertInstanceOf(AiGatewayResponse::class, $response); + $this->assertEquals('chatcmpl-abc123', $response->getData()['id']); + } + + public function testGatewayDelete(): void + { + $this->mockRequest([ + 'path' => '/users/1/ai/providers/2/gateway/chat/completions', + 'method' => 'delete', + 'response' => '{ + "data": { + "id": "chatcmpl-abc123", + "object": "chat.completion", + "choices": [{"message": {"role": "assistant", "content": "Hello!"}}] + } + }', + ]); + + $response = $this->crowdin->aiGateway->gatewayDelete(1, 2, 'chat/completions'); + $this->assertInstanceOf(AiGatewayResponse::class, $response); + $this->assertEquals('chatcmpl-abc123', $response->getData()['id']); + } +} diff --git a/tests/CrowdinApiClient/Api/Enterprise/AiGatewayApiTest.php b/tests/CrowdinApiClient/Api/Enterprise/AiGatewayApiTest.php new file mode 100644 index 00000000..4b88c527 --- /dev/null +++ b/tests/CrowdinApiClient/Api/Enterprise/AiGatewayApiTest.php @@ -0,0 +1,137 @@ +mockRequest([ + 'path' => '/ai/providers/2/gateway/chat/completions', + 'method' => 'get', + 'response' => '{ + "data": { + "id": "chatcmpl-abc123", + "object": "chat.completion", + "choices": [{"message": {"role": "assistant", "content": "Hello!"}}] + } + }', + ]); + + $response = $this->crowdin->aiGateway->gatewayGet(2, 'chat/completions'); + $this->assertInstanceOf(AiGatewayResponse::class, $response); + $this->assertEquals('chatcmpl-abc123', $response->getData()['id']); + $this->assertEquals('chat.completion', $response->getData()['object']); + } + + public function testGatewayPost(): void + { + $data = [ + 'model' => 'gpt-4o', + 'messages' => [ + [ + 'role' => 'user', + 'content' => 'Hi', + ], + ], + ]; + + $this->mockRequest([ + 'path' => '/ai/providers/2/gateway/chat/completions', + 'method' => 'post', + 'body' => json_encode($data), + 'response' => '{ + "data": { + "id": "chatcmpl-abc123", + "object": "chat.completion", + "choices": [{"message": {"role": "assistant", "content": "Hello!"}}] + } + }', + ]); + + $response = $this->crowdin->aiGateway->gatewayPost(2, 'chat/completions', $data); + $this->assertInstanceOf(AiGatewayResponse::class, $response); + $this->assertEquals('chatcmpl-abc123', $response->getData()['id']); + } + + public function testGatewayPut(): void + { + $data = [ + 'model' => 'gpt-4o', + 'messages' => [ + [ + 'role' => 'user', + 'content' => 'Hi', + ], + ], + ]; + + $this->mockRequest([ + 'path' => '/ai/providers/2/gateway/chat/completions', + 'method' => 'put', + 'body' => json_encode($data), + 'response' => '{ + "data": { + "id": "chatcmpl-abc123", + "object": "chat.completion", + "choices": [{"message": {"role": "assistant", "content": "Hello!"}}] + } + }', + ]); + + $response = $this->crowdin->aiGateway->gatewayPut(2, 'chat/completions', $data); + $this->assertInstanceOf(AiGatewayResponse::class, $response); + $this->assertEquals('chatcmpl-abc123', $response->getData()['id']); + } + + public function testGatewayPatch(): void + { + $data = [ + 'model' => 'gpt-4o', + 'messages' => [ + [ + 'role' => 'user', + 'content' => 'Hi', + ], + ], + ]; + + $this->mockRequest([ + 'path' => '/ai/providers/2/gateway/chat/completions', + 'method' => 'patch', + 'body' => json_encode($data), + 'response' => '{ + "data": { + "id": "chatcmpl-abc123", + "object": "chat.completion", + "choices": [{"message": {"role": "assistant", "content": "Hello!"}}] + } + }', + ]); + + $response = $this->crowdin->aiGateway->gatewayPatch(2, 'chat/completions', $data); + $this->assertInstanceOf(AiGatewayResponse::class, $response); + $this->assertEquals('chatcmpl-abc123', $response->getData()['id']); + } + + public function testGatewayDelete(): void + { + $this->mockRequest([ + 'path' => '/ai/providers/2/gateway/chat/completions', + 'method' => 'delete', + 'response' => '{ + "data": { + "id": "chatcmpl-abc123", + "object": "chat.completion", + "choices": [{"message": {"role": "assistant", "content": "Hello!"}}] + } + }', + ]); + + $response = $this->crowdin->aiGateway->gatewayDelete(2, 'chat/completions'); + $this->assertInstanceOf(AiGatewayResponse::class, $response); + $this->assertEquals('chatcmpl-abc123', $response->getData()['id']); + } +}