diff --git a/src/CrowdinApiClient/Api/AiApi.php b/src/CrowdinApiClient/Api/AiApi.php
index ee40fcae..cdda4ac7 100644
--- a/src/CrowdinApiClient/Api/AiApi.php
+++ b/src/CrowdinApiClient/Api/AiApi.php
@@ -3,11 +3,20 @@
namespace CrowdinApiClient\Api;
use CrowdinApiClient\Model\AiFileTranslation;
+use CrowdinApiClient\Model\AiPrompt;
+use CrowdinApiClient\Model\AiPromptCompletion;
+use CrowdinApiClient\Model\AiProvider;
+use CrowdinApiClient\Model\AiProviderModel;
+use CrowdinApiClient\Model\AiProxyChatCompletion;
+use CrowdinApiClient\Model\AiReport;
+use CrowdinApiClient\Model\AiSettings;
+use CrowdinApiClient\Model\AiSnippet;
use CrowdinApiClient\Model\AiTranslation;
use CrowdinApiClient\Model\DownloadFile;
+use CrowdinApiClient\ModelCollection;
/**
- * Use API to leverage AI-powered translation of strings.
+ * Use API to manage AI providers, prompts, and leverage AI-powered translation.
*
* @package Crowdin\Api
*/
@@ -118,4 +127,425 @@ public function downloadFileTranslationStrings(int $userId, string $jobIdentifie
$path = sprintf('users/%d/ai/file-translations/%s/translations', $userId, $jobIdentifier);
return $this->_get($path, DownloadFile::class);
}
+
+ /**
+ * List AI Prompts
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.prompts.getMany API Documentation
+ *
+ * @param int $userId
+ * @param array $params
+ * integer $params[projectId]
+ * string $params[action]
+ * integer $params[limit]
+ * integer $params[offset]
+ * @return ModelCollection
+ */
+ public function listPrompts(int $userId, array $params = []): ModelCollection
+ {
+ $path = sprintf('users/%d/ai/prompts', $userId);
+ return $this->_list($path, AiPrompt::class, $params);
+ }
+
+ /**
+ * Add AI Prompt
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.prompts.post API Documentation
+ *
+ * @param int $userId
+ * @param array $data
+ * string $data[name] required
+ * string $data[action] required
+ * array $data[config] required
+ * integer $data[aiProviderId]
+ * string $data[aiModelId]
+ * boolean $data[isEnabled]
+ * array $data[enabledProjectIds]
+ * @return AiPrompt|null
+ */
+ public function createPrompt(int $userId, array $data): ?AiPrompt
+ {
+ $path = sprintf('users/%d/ai/prompts', $userId);
+ return $this->_create($path, AiPrompt::class, $data);
+ }
+
+ /**
+ * Get AI Prompt
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.prompts.get API Documentation
+ *
+ * @param int $userId
+ * @param int $aiPromptId
+ * @return AiPrompt|null
+ */
+ public function getPrompt(int $userId, int $aiPromptId): ?AiPrompt
+ {
+ $path = sprintf('users/%d/ai/prompts/%d', $userId, $aiPromptId);
+ return $this->_get($path, AiPrompt::class);
+ }
+
+ /**
+ * Edit AI Prompt
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.prompts.patch API Documentation
+ *
+ * @param int $userId
+ * @param AiPrompt $aiPrompt
+ * @return AiPrompt|null
+ */
+ public function updatePrompt(int $userId, AiPrompt $aiPrompt): ?AiPrompt
+ {
+ $path = sprintf('users/%d/ai/prompts/%d', $userId, $aiPrompt->getId());
+ return $this->_update($path, $aiPrompt);
+ }
+
+ /**
+ * Delete AI Prompt
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.prompts.delete API Documentation
+ *
+ * @param int $userId
+ * @param int $aiPromptId
+ * @return mixed
+ */
+ public function deletePrompt(int $userId, int $aiPromptId)
+ {
+ $path = sprintf('users/%d/ai/prompts/%d', $userId, $aiPromptId);
+ return $this->_delete($path);
+ }
+
+ /**
+ * Clone AI Prompt
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.prompts.clones.post API Documentation
+ *
+ * @param int $userId
+ * @param int $aiPromptId
+ * @param array $data
+ * string $data[name] required
+ * @return AiPrompt|null
+ */
+ public function clonePrompt(int $userId, int $aiPromptId, array $data): ?AiPrompt
+ {
+ $path = sprintf('users/%d/ai/prompts/%d/clones', $userId, $aiPromptId);
+ return $this->_post($path, AiPrompt::class, $data);
+ }
+
+ /**
+ * Create AI Prompt Completion
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.prompts.completions.post API Documentation
+ *
+ * @param int $userId
+ * @param int $aiPromptId
+ * @param array $data
+ * array $data[resources] required
+ * array $data[tools]
+ * mixed $data[tool_choice]
+ * @return AiPromptCompletion|null
+ */
+ public function createPromptCompletion(int $userId, int $aiPromptId, array $data): ?AiPromptCompletion
+ {
+ $path = sprintf('users/%d/ai/prompts/%d/completions', $userId, $aiPromptId);
+ return $this->_post($path, AiPromptCompletion::class, $data);
+ }
+
+ /**
+ * Get AI Prompt Completion Status
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.prompts.completions.get API Documentation
+ *
+ * @param int $userId
+ * @param int $aiPromptId
+ * @param string $completionId
+ * @return AiPromptCompletion|null
+ */
+ public function getPromptCompletion(int $userId, int $aiPromptId, string $completionId): ?AiPromptCompletion
+ {
+ $path = sprintf('users/%d/ai/prompts/%d/completions/%s', $userId, $aiPromptId, $completionId);
+ return $this->_get($path, AiPromptCompletion::class);
+ }
+
+ /**
+ * Cancel AI Prompt Completion
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.prompts.completions.delete API Documentation
+ *
+ * @param int $userId
+ * @param int $aiPromptId
+ * @param string $completionId
+ * @return mixed
+ */
+ public function deletePromptCompletion(int $userId, int $aiPromptId, string $completionId)
+ {
+ $path = sprintf('users/%d/ai/prompts/%d/completions/%s', $userId, $aiPromptId, $completionId);
+ return $this->_delete($path);
+ }
+
+ /**
+ * Download AI Prompt Completion
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.prompts.completions.download.download API Documentation
+ *
+ * @param int $userId
+ * @param int $aiPromptId
+ * @param string $completionId
+ * @return DownloadFile|null
+ */
+ public function downloadPromptCompletion(int $userId, int $aiPromptId, string $completionId): ?DownloadFile
+ {
+ $path = sprintf('users/%d/ai/prompts/%d/completions/%s/download', $userId, $aiPromptId, $completionId);
+ return $this->_get($path, DownloadFile::class);
+ }
+
+ /**
+ * List AI Providers
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.providers.getMany API Documentation
+ *
+ * @param int $userId
+ * @param array $params
+ * integer $params[limit]
+ * integer $params[offset]
+ * @return ModelCollection
+ */
+ public function listProviders(int $userId, array $params = []): ModelCollection
+ {
+ $path = sprintf('users/%d/ai/providers', $userId);
+ return $this->_list($path, AiProvider::class, $params);
+ }
+
+ /**
+ * Add AI Provider
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.providers.post API Documentation
+ *
+ * @param int $userId
+ * @param array $data
+ * string $data[name] required
+ * string $data[type] required
+ * array $data[credentials]
+ * array $data[config]
+ * boolean $data[isEnabled]
+ * boolean $data[useSystemCredentials]
+ * @return AiProvider|null
+ */
+ public function createProvider(int $userId, array $data): ?AiProvider
+ {
+ $path = sprintf('users/%d/ai/providers', $userId);
+ return $this->_create($path, AiProvider::class, $data);
+ }
+
+ /**
+ * Get AI Provider
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.providers.get API Documentation
+ *
+ * @param int $userId
+ * @param int $aiProviderId
+ * @return AiProvider|null
+ */
+ public function getProvider(int $userId, int $aiProviderId): ?AiProvider
+ {
+ $path = sprintf('users/%d/ai/providers/%d', $userId, $aiProviderId);
+ return $this->_get($path, AiProvider::class);
+ }
+
+ /**
+ * Edit AI Provider
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.providers.patch API Documentation
+ *
+ * @param int $userId
+ * @param AiProvider $aiProvider
+ * @return AiProvider|null
+ */
+ public function updateProvider(int $userId, AiProvider $aiProvider): ?AiProvider
+ {
+ $path = sprintf('users/%d/ai/providers/%d', $userId, $aiProvider->getId());
+ return $this->_update($path, $aiProvider);
+ }
+
+ /**
+ * Delete AI Provider
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.providers.delete API Documentation
+ *
+ * @param int $userId
+ * @param int $aiProviderId
+ * @return mixed
+ */
+ public function deleteProvider(int $userId, int $aiProviderId)
+ {
+ $path = sprintf('users/%d/ai/providers/%d', $userId, $aiProviderId);
+ return $this->_delete($path);
+ }
+
+ /**
+ * List AI Provider Models
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.providers.models.getMany API Documentation
+ *
+ * @param int $userId
+ * @param int $aiProviderId
+ * @param array $params
+ * integer $params[limit]
+ * integer $params[offset]
+ * @return ModelCollection
+ */
+ public function listProviderModels(int $userId, int $aiProviderId, array $params = []): ModelCollection
+ {
+ $path = sprintf('users/%d/ai/providers/%d/models', $userId, $aiProviderId);
+ return $this->_list($path, AiProviderModel::class, $params);
+ }
+
+ /**
+ * Create AI Provider Chat Completion
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.providers.chat.completions.post API Documentation
+ *
+ * @param int $userId
+ * @param int $aiProviderId
+ * @param array $data
+ * string $data[model]
+ * boolean $data[stream]
+ * @return AiProxyChatCompletion|null
+ */
+ public function createProviderChatCompletion(
+ int $userId,
+ int $aiProviderId,
+ array $data
+ ): ?AiProxyChatCompletion {
+ $path = sprintf('users/%d/ai/providers/%d/chat/completions', $userId, $aiProviderId);
+ return $this->_post($path, AiProxyChatCompletion::class, $data);
+ }
+
+ /**
+ * Generate AI Report
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.reports.post API Documentation
+ *
+ * @param int $userId
+ * @param array $data
+ * string $data[type] required
+ * array $data[schema] required
+ * @return AiReport|null
+ */
+ public function generateReport(int $userId, array $data): ?AiReport
+ {
+ $path = sprintf('users/%d/ai/reports', $userId);
+ return $this->_post($path, AiReport::class, $data);
+ }
+
+ /**
+ * Check AI Report Generation Status
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.reports.get API Documentation
+ *
+ * @param int $userId
+ * @param string $aiReportId
+ * @return AiReport|null
+ */
+ public function getReport(int $userId, string $aiReportId): ?AiReport
+ {
+ $path = sprintf('users/%d/ai/reports/%s', $userId, $aiReportId);
+ return $this->_get($path, AiReport::class);
+ }
+
+ /**
+ * Download AI Report
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.reports.download.download API Documentation
+ *
+ * @param int $userId
+ * @param string $aiReportId
+ * @return DownloadFile|null
+ */
+ public function downloadReport(int $userId, string $aiReportId): ?DownloadFile
+ {
+ $path = sprintf('users/%d/ai/reports/%s/download', $userId, $aiReportId);
+ return $this->_get($path, DownloadFile::class);
+ }
+
+ /**
+ * Get AI Settings
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.settings.get API Documentation
+ *
+ * @param int $userId
+ * @return AiSettings|null
+ */
+ public function getSettings(int $userId): ?AiSettings
+ {
+ $path = sprintf('users/%d/ai/settings', $userId);
+ return $this->_get($path, AiSettings::class);
+ }
+
+ /**
+ * Edit AI Settings
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.settings.patch API Documentation
+ *
+ * @param int $userId
+ * @param AiSettings $aiSettings
+ * @return AiSettings|null
+ */
+ public function updateSettings(int $userId, AiSettings $aiSettings): ?AiSettings
+ {
+ $path = sprintf('users/%d/ai/settings', $userId);
+ return $this->_update($path, $aiSettings);
+ }
+
+ /**
+ * List AI Snippets
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.settings.snippets.getMany API Documentation
+ *
+ * @param int $userId
+ * @param array $params
+ * integer $params[limit]
+ * integer $params[offset]
+ * @return ModelCollection
+ */
+ public function listSnippets(int $userId, array $params = []): ModelCollection
+ {
+ $path = sprintf('users/%d/ai/settings/snippets', $userId);
+ return $this->_list($path, AiSnippet::class, $params);
+ }
+
+ /**
+ * Add AI Snippet
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.settings.snippets.post API Documentation
+ *
+ * @param int $userId
+ * @param array $data
+ * string $data[description] required
+ * string $data[placeholder] required
+ * string $data[value] required
+ * @return AiSnippet|null
+ */
+ public function createSnippet(int $userId, array $data): ?AiSnippet
+ {
+ $path = sprintf('users/%d/ai/settings/snippets', $userId);
+ return $this->_create($path, AiSnippet::class, $data);
+ }
+
+ /**
+ * Get AI Snippet
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.settings.snippets.get API Documentation
+ *
+ * @param int $userId
+ * @param int $aiSnippetId
+ * @return AiSnippet|null
+ */
+ public function getSnippet(int $userId, int $aiSnippetId): ?AiSnippet
+ {
+ $path = sprintf('users/%d/ai/settings/snippets/%d', $userId, $aiSnippetId);
+ return $this->_get($path, AiSnippet::class);
+ }
+
+ /**
+ * Edit AI Snippet
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.settings.snippets.patch API Documentation
+ *
+ * @param int $userId
+ * @param AiSnippet $aiSnippet
+ * @return AiSnippet|null
+ */
+ public function updateSnippet(int $userId, AiSnippet $aiSnippet): ?AiSnippet
+ {
+ $path = sprintf('users/%d/ai/settings/snippets/%d', $userId, $aiSnippet->getId());
+ return $this->_update($path, $aiSnippet);
+ }
+
+ /**
+ * Delete AI Snippet
+ * @link https://developer.crowdin.com/api/v2/#operation/api.users.ai.settings.snippets.delete API Documentation
+ *
+ * @param int $userId
+ * @param int $aiSnippetId
+ * @return mixed
+ */
+ public function deleteSnippet(int $userId, int $aiSnippetId)
+ {
+ $path = sprintf('users/%d/ai/settings/snippets/%d', $userId, $aiSnippetId);
+ return $this->_delete($path);
+ }
}
diff --git a/src/CrowdinApiClient/Api/Enterprise/AiApi.php b/src/CrowdinApiClient/Api/Enterprise/AiApi.php
index 0eeb0344..a6db555b 100644
--- a/src/CrowdinApiClient/Api/Enterprise/AiApi.php
+++ b/src/CrowdinApiClient/Api/Enterprise/AiApi.php
@@ -4,11 +4,20 @@
use CrowdinApiClient\Api\AbstractApi;
use CrowdinApiClient\Model\AiFileTranslation;
+use CrowdinApiClient\Model\AiPrompt;
+use CrowdinApiClient\Model\AiPromptCompletion;
+use CrowdinApiClient\Model\AiProvider;
+use CrowdinApiClient\Model\AiProviderModel;
+use CrowdinApiClient\Model\AiProxyChatCompletion;
+use CrowdinApiClient\Model\AiReport;
+use CrowdinApiClient\Model\AiSettings;
+use CrowdinApiClient\Model\AiSnippet;
use CrowdinApiClient\Model\AiTranslation;
use CrowdinApiClient\Model\DownloadFile;
+use CrowdinApiClient\ModelCollection;
/**
- * Use API to leverage AI-powered translation of strings.
+ * Use API to manage AI providers, prompts, and leverage AI-powered translation.
*
* @package Crowdin\Api\Enterprise
*/
@@ -111,4 +120,386 @@ public function downloadFileTranslationStrings(string $jobIdentifier): ?Download
$path = sprintf('ai/file-translations/%s/translations', $jobIdentifier);
return $this->_get($path, DownloadFile::class);
}
+
+ /**
+ * List AI Prompts
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.prompts.getMany API Documentation
+ *
+ * @param array $params
+ * integer $params[projectId]
+ * string $params[action]
+ * integer $params[limit]
+ * integer $params[offset]
+ * @return ModelCollection
+ */
+ public function listPrompts(array $params = []): ModelCollection
+ {
+ return $this->_list('ai/prompts', AiPrompt::class, $params);
+ }
+
+ /**
+ * Add AI Prompt
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.prompts.post API Documentation
+ *
+ * @param array $data
+ * string $data[name] required
+ * string $data[action] required
+ * array $data[config] required
+ * integer $data[aiProviderId]
+ * string $data[aiModelId]
+ * boolean $data[isEnabled]
+ * array $data[enabledProjectIds]
+ * @return AiPrompt|null
+ */
+ public function createPrompt(array $data): ?AiPrompt
+ {
+ return $this->_create('ai/prompts', AiPrompt::class, $data);
+ }
+
+ /**
+ * Get AI Prompt
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.prompts.get API Documentation
+ *
+ * @param int $aiPromptId
+ * @return AiPrompt|null
+ */
+ public function getPrompt(int $aiPromptId): ?AiPrompt
+ {
+ $path = sprintf('ai/prompts/%d', $aiPromptId);
+ return $this->_get($path, AiPrompt::class);
+ }
+
+ /**
+ * Edit AI Prompt
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.prompts.patch API Documentation
+ *
+ * @param AiPrompt $aiPrompt
+ * @return AiPrompt|null
+ */
+ public function updatePrompt(AiPrompt $aiPrompt): ?AiPrompt
+ {
+ $path = sprintf('ai/prompts/%d', $aiPrompt->getId());
+ return $this->_update($path, $aiPrompt);
+ }
+
+ /**
+ * Delete AI Prompt
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.prompts.delete API Documentation
+ *
+ * @param int $aiPromptId
+ * @return mixed
+ */
+ public function deletePrompt(int $aiPromptId)
+ {
+ $path = sprintf('ai/prompts/%d', $aiPromptId);
+ return $this->_delete($path);
+ }
+
+ /**
+ * Clone AI Prompt
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.prompts.clones.post API Documentation
+ *
+ * @param int $aiPromptId
+ * @param array $data
+ * string $data[name] required
+ * @return AiPrompt|null
+ */
+ public function clonePrompt(int $aiPromptId, array $data): ?AiPrompt
+ {
+ $path = sprintf('ai/prompts/%d/clones', $aiPromptId);
+ return $this->_post($path, AiPrompt::class, $data);
+ }
+
+ /**
+ * Create AI Prompt Completion
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.prompts.completions.post API Documentation
+ *
+ * @param int $aiPromptId
+ * @param array $data
+ * array $data[resources] required
+ * array $data[tools]
+ * mixed $data[tool_choice]
+ * @return AiPromptCompletion|null
+ */
+ public function createPromptCompletion(int $aiPromptId, array $data): ?AiPromptCompletion
+ {
+ $path = sprintf('ai/prompts/%d/completions', $aiPromptId);
+ return $this->_post($path, AiPromptCompletion::class, $data);
+ }
+
+ /**
+ * Get AI Prompt Completion Status
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.prompts.completions.get API Documentation
+ *
+ * @param int $aiPromptId
+ * @param string $completionId
+ * @return AiPromptCompletion|null
+ */
+ public function getPromptCompletion(int $aiPromptId, string $completionId): ?AiPromptCompletion
+ {
+ $path = sprintf('ai/prompts/%d/completions/%s', $aiPromptId, $completionId);
+ return $this->_get($path, AiPromptCompletion::class);
+ }
+
+ /**
+ * Cancel AI Prompt Completion
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.prompts.completions.delete API Documentation
+ *
+ * @param int $aiPromptId
+ * @param string $completionId
+ * @return mixed
+ */
+ public function deletePromptCompletion(int $aiPromptId, string $completionId)
+ {
+ $path = sprintf('ai/prompts/%d/completions/%s', $aiPromptId, $completionId);
+ return $this->_delete($path);
+ }
+
+ /**
+ * Download AI Prompt Completion
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.prompts.completions.download.download API Documentation
+ *
+ * @param int $aiPromptId
+ * @param string $completionId
+ * @return DownloadFile|null
+ */
+ public function downloadPromptCompletion(int $aiPromptId, string $completionId): ?DownloadFile
+ {
+ $path = sprintf('ai/prompts/%d/completions/%s/download', $aiPromptId, $completionId);
+ return $this->_get($path, DownloadFile::class);
+ }
+
+ /**
+ * List AI Providers
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.providers.getMany API Documentation
+ *
+ * @param array $params
+ * integer $params[limit]
+ * integer $params[offset]
+ * @return ModelCollection
+ */
+ public function listProviders(array $params = []): ModelCollection
+ {
+ return $this->_list('ai/providers', AiProvider::class, $params);
+ }
+
+ /**
+ * Add AI Provider
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.providers.post API Documentation
+ *
+ * @param array $data
+ * string $data[name] required
+ * string $data[type] required
+ * array $data[credentials]
+ * array $data[config]
+ * boolean $data[isEnabled]
+ * boolean $data[useSystemCredentials]
+ * @return AiProvider|null
+ */
+ public function createProvider(array $data): ?AiProvider
+ {
+ return $this->_create('ai/providers', AiProvider::class, $data);
+ }
+
+ /**
+ * Get AI Provider
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.providers.get API Documentation
+ *
+ * @param int $aiProviderId
+ * @return AiProvider|null
+ */
+ public function getProvider(int $aiProviderId): ?AiProvider
+ {
+ $path = sprintf('ai/providers/%d', $aiProviderId);
+ return $this->_get($path, AiProvider::class);
+ }
+
+ /**
+ * Edit AI Provider
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.providers.patch API Documentation
+ *
+ * @param AiProvider $aiProvider
+ * @return AiProvider|null
+ */
+ public function updateProvider(AiProvider $aiProvider): ?AiProvider
+ {
+ $path = sprintf('ai/providers/%d', $aiProvider->getId());
+ return $this->_update($path, $aiProvider);
+ }
+
+ /**
+ * Delete AI Provider
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.providers.delete API Documentation
+ *
+ * @param int $aiProviderId
+ * @return mixed
+ */
+ public function deleteProvider(int $aiProviderId)
+ {
+ $path = sprintf('ai/providers/%d', $aiProviderId);
+ return $this->_delete($path);
+ }
+
+ /**
+ * List AI Provider Models
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.providers.models.getMany API Documentation
+ *
+ * @param int $aiProviderId
+ * @param array $params
+ * integer $params[limit]
+ * integer $params[offset]
+ * @return ModelCollection
+ */
+ public function listProviderModels(int $aiProviderId, array $params = []): ModelCollection
+ {
+ $path = sprintf('ai/providers/%d/models', $aiProviderId);
+ return $this->_list($path, AiProviderModel::class, $params);
+ }
+
+ /**
+ * Create AI Provider Chat Completion
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.providers.chat.completions.post API Documentation
+ *
+ * @param int $aiProviderId
+ * @param array $data
+ * string $data[model]
+ * boolean $data[stream]
+ * @return AiProxyChatCompletion|null
+ */
+ public function createProviderChatCompletion(int $aiProviderId, array $data): ?AiProxyChatCompletion
+ {
+ $path = sprintf('ai/providers/%d/chat/completions', $aiProviderId);
+ return $this->_post($path, AiProxyChatCompletion::class, $data);
+ }
+
+ /**
+ * Generate AI Report
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.reports.post API Documentation
+ *
+ * @param array $data
+ * string $data[type] required
+ * array $data[schema] required
+ * @return AiReport|null
+ */
+ public function generateReport(array $data): ?AiReport
+ {
+ return $this->_post('ai/reports', AiReport::class, $data);
+ }
+
+ /**
+ * Check AI Report Generation Status
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.reports.get API Documentation
+ *
+ * @param string $aiReportId
+ * @return AiReport|null
+ */
+ public function getReport(string $aiReportId): ?AiReport
+ {
+ $path = sprintf('ai/reports/%s', $aiReportId);
+ return $this->_get($path, AiReport::class);
+ }
+
+ /**
+ * Download AI Report
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.reports.download.download API Documentation
+ *
+ * @param string $aiReportId
+ * @return DownloadFile|null
+ */
+ public function downloadReport(string $aiReportId): ?DownloadFile
+ {
+ $path = sprintf('ai/reports/%s/download', $aiReportId);
+ return $this->_get($path, DownloadFile::class);
+ }
+
+ /**
+ * Get AI Settings
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.settings.get API Documentation
+ *
+ * @return AiSettings|null
+ */
+ public function getSettings(): ?AiSettings
+ {
+ return $this->_get('ai/settings', AiSettings::class);
+ }
+
+ /**
+ * Edit AI Settings
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.settings.patch API Documentation
+ *
+ * @param AiSettings $aiSettings
+ * @return AiSettings|null
+ */
+ public function updateSettings(AiSettings $aiSettings): ?AiSettings
+ {
+ return $this->_update('ai/settings', $aiSettings);
+ }
+
+ /**
+ * List AI Snippets
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.settings.snippets.getMany API Documentation
+ *
+ * @param array $params
+ * integer $params[limit]
+ * integer $params[offset]
+ * @return ModelCollection
+ */
+ public function listSnippets(array $params = []): ModelCollection
+ {
+ return $this->_list('ai/settings/snippets', AiSnippet::class, $params);
+ }
+
+ /**
+ * Add AI Snippet
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.settings.snippets.post API Documentation
+ *
+ * @param array $data
+ * string $data[description] required
+ * string $data[placeholder] required
+ * string $data[value] required
+ * @return AiSnippet|null
+ */
+ public function createSnippet(array $data): ?AiSnippet
+ {
+ return $this->_create('ai/settings/snippets', AiSnippet::class, $data);
+ }
+
+ /**
+ * Get AI Snippet
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.settings.snippets.get API Documentation
+ *
+ * @param int $aiSnippetId
+ * @return AiSnippet|null
+ */
+ public function getSnippet(int $aiSnippetId): ?AiSnippet
+ {
+ $path = sprintf('ai/settings/snippets/%d', $aiSnippetId);
+ return $this->_get($path, AiSnippet::class);
+ }
+
+ /**
+ * Edit AI Snippet
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.settings.snippets.patch API Documentation
+ *
+ * @param AiSnippet $aiSnippet
+ * @return AiSnippet|null
+ */
+ public function updateSnippet(AiSnippet $aiSnippet): ?AiSnippet
+ {
+ $path = sprintf('ai/settings/snippets/%d', $aiSnippet->getId());
+ return $this->_update($path, $aiSnippet);
+ }
+
+ /**
+ * Delete AI Snippet
+ * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.ai.settings.snippets.delete API Documentation
+ *
+ * @param int $aiSnippetId
+ * @return mixed
+ */
+ public function deleteSnippet(int $aiSnippetId)
+ {
+ $path = sprintf('ai/settings/snippets/%d', $aiSnippetId);
+ return $this->_delete($path);
+ }
}
diff --git a/src/CrowdinApiClient/Model/AiPrompt.php b/src/CrowdinApiClient/Model/AiPrompt.php
new file mode 100644
index 00000000..ce9fa8d6
--- /dev/null
+++ b/src/CrowdinApiClient/Model/AiPrompt.php
@@ -0,0 +1,197 @@
+id = (int)$this->getDataProperty('id');
+ $this->name = (string)$this->getDataProperty('name');
+ $this->action = (string)$this->getDataProperty('action');
+ $this->aiProviderId = $this->getDataProperty('aiProviderId');
+ $this->aiModelId = $this->getDataProperty('aiModelId');
+ $this->isEnabled = (bool)$this->getDataProperty('isEnabled');
+ $this->enabledProjectIds = (array)$this->getDataProperty('enabledProjectIds');
+ $this->config = (array)$this->getDataProperty('config');
+ $this->promptPreview = $this->getDataProperty('promptPreview');
+ $this->isFineTuningAvailable = (bool)$this->getDataProperty('isFineTuningAvailable');
+ $this->createdBy = $this->getDataProperty('createdBy');
+ $this->updatedBy = $this->getDataProperty('updatedBy');
+ $this->lastUsedBy = $this->getDataProperty('lastUsedBy');
+ $this->lastUsedAt = $this->getDataProperty('lastUsedAt');
+ $this->usageCount = (int)$this->getDataProperty('usageCount');
+ $this->createdAt = (string)$this->getDataProperty('createdAt');
+ $this->updatedAt = (string)$this->getDataProperty('updatedAt');
+ }
+
+ public function getId(): int
+ {
+ return $this->id;
+ }
+
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ public function getAction(): string
+ {
+ return $this->action;
+ }
+
+ public function getAiProviderId(): ?int
+ {
+ return $this->aiProviderId;
+ }
+
+ public function getAiModelId(): ?string
+ {
+ return $this->aiModelId;
+ }
+
+ public function isEnabled(): bool
+ {
+ return $this->isEnabled;
+ }
+
+ public function getEnabledProjectIds(): array
+ {
+ return $this->enabledProjectIds;
+ }
+
+ public function getConfig(): array
+ {
+ return $this->config;
+ }
+
+ public function getPromptPreview(): ?string
+ {
+ return $this->promptPreview;
+ }
+
+ public function isFineTuningAvailable(): bool
+ {
+ return $this->isFineTuningAvailable;
+ }
+
+ public function getCreatedBy(): ?int
+ {
+ return $this->createdBy;
+ }
+
+ public function getUpdatedBy(): ?int
+ {
+ return $this->updatedBy;
+ }
+
+ public function getLastUsedBy(): ?int
+ {
+ return $this->lastUsedBy;
+ }
+
+ public function getLastUsedAt(): ?string
+ {
+ return $this->lastUsedAt;
+ }
+
+ public function getUsageCount(): int
+ {
+ return $this->usageCount;
+ }
+
+ public function getCreatedAt(): string
+ {
+ return $this->createdAt;
+ }
+
+ public function getUpdatedAt(): string
+ {
+ return $this->updatedAt;
+ }
+
+ public function setName(string $name): void
+ {
+ $this->name = $name;
+ }
+
+ public function setAction(string $action): void
+ {
+ $this->action = $action;
+ }
+
+ public function setAiProviderId(?int $aiProviderId): void
+ {
+ $this->aiProviderId = $aiProviderId;
+ }
+
+ public function setAiModelId(?string $aiModelId): void
+ {
+ $this->aiModelId = $aiModelId;
+ }
+
+ public function setEnabledProjectIds(array $enabledProjectIds): void
+ {
+ $this->enabledProjectIds = $enabledProjectIds;
+ }
+
+ public function setConfig(array $config): void
+ {
+ $this->config = $config;
+ }
+}
diff --git a/src/CrowdinApiClient/Model/AiPromptCompletion.php b/src/CrowdinApiClient/Model/AiPromptCompletion.php
new file mode 100644
index 00000000..b676ad42
--- /dev/null
+++ b/src/CrowdinApiClient/Model/AiPromptCompletion.php
@@ -0,0 +1,86 @@
+identifier = (string)$this->getDataProperty('identifier');
+ $this->status = (string)$this->getDataProperty('status');
+ $this->progress = (int)$this->getDataProperty('progress');
+ $this->attributes = (array)$this->getDataProperty('attributes');
+ $this->createdAt = (string)$this->getDataProperty('createdAt');
+ $this->updatedAt = $this->getDataProperty('updatedAt');
+ $this->startedAt = $this->getDataProperty('startedAt');
+ $this->finishedAt = $this->getDataProperty('finishedAt');
+ }
+
+ public function getIdentifier(): string
+ {
+ return $this->identifier;
+ }
+
+ public function getStatus(): string
+ {
+ return $this->status;
+ }
+
+ public function getProgress(): int
+ {
+ return $this->progress;
+ }
+
+ public function getAttributes(): array
+ {
+ return $this->attributes;
+ }
+
+ public function getCreatedAt(): string
+ {
+ return $this->createdAt;
+ }
+
+ public function getUpdatedAt(): ?string
+ {
+ return $this->updatedAt;
+ }
+
+ public function getStartedAt(): ?string
+ {
+ return $this->startedAt;
+ }
+
+ public function getFinishedAt(): ?string
+ {
+ return $this->finishedAt;
+ }
+}
diff --git a/src/CrowdinApiClient/Model/AiProvider.php b/src/CrowdinApiClient/Model/AiProvider.php
new file mode 100644
index 00000000..3bb40220
--- /dev/null
+++ b/src/CrowdinApiClient/Model/AiProvider.php
@@ -0,0 +1,134 @@
+id = (int)$this->getDataProperty('id');
+ $this->name = (string)$this->getDataProperty('name');
+ $this->type = (string)$this->getDataProperty('type');
+ $this->credentials = $this->getDataProperty('credentials');
+ $this->config = (array)$this->getDataProperty('config');
+ $this->isEnabled = (bool)$this->getDataProperty('isEnabled');
+ $this->useSystemCredentials = (bool)$this->getDataProperty('useSystemCredentials');
+ $this->createdAt = (string)$this->getDataProperty('createdAt');
+ $this->updatedAt = (string)$this->getDataProperty('updatedAt');
+ $this->promptsCount = (int)$this->getDataProperty('promptsCount');
+ }
+
+ public function getId(): int
+ {
+ return $this->id;
+ }
+
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ public function getCredentials(): ?array
+ {
+ return $this->credentials;
+ }
+
+ public function getConfig(): array
+ {
+ return $this->config;
+ }
+
+ public function isEnabled(): bool
+ {
+ return $this->isEnabled;
+ }
+
+ public function isUseSystemCredentials(): bool
+ {
+ return $this->useSystemCredentials;
+ }
+
+ public function getCreatedAt(): string
+ {
+ return $this->createdAt;
+ }
+
+ public function getUpdatedAt(): string
+ {
+ return $this->updatedAt;
+ }
+
+ public function getPromptsCount(): int
+ {
+ return $this->promptsCount;
+ }
+
+ public function setName(string $name): void
+ {
+ $this->name = $name;
+ }
+
+ public function setType(string $type): void
+ {
+ $this->type = $type;
+ }
+
+ public function setCredentials(?array $credentials): void
+ {
+ $this->credentials = $credentials;
+ }
+
+ public function setConfig(array $config): void
+ {
+ $this->config = $config;
+ }
+
+ public function setIsEnabled(bool $isEnabled): void
+ {
+ $this->isEnabled = $isEnabled;
+ }
+
+ public function setUseSystemCredentials(bool $useSystemCredentials): void
+ {
+ $this->useSystemCredentials = $useSystemCredentials;
+ }
+}
diff --git a/src/CrowdinApiClient/Model/AiProviderModel.php b/src/CrowdinApiClient/Model/AiProviderModel.php
new file mode 100644
index 00000000..e7365b98
--- /dev/null
+++ b/src/CrowdinApiClient/Model/AiProviderModel.php
@@ -0,0 +1,50 @@
+id = (string)$this->getDataProperty('id');
+ $this->provider = $this->getDataProperty('provider');
+ $this->providerName = $this->getDataProperty('providerName');
+ $this->providerId = $this->getDataProperty('providerId');
+ }
+
+ public function getId(): string
+ {
+ return $this->id;
+ }
+
+ public function getProvider(): ?string
+ {
+ return $this->provider;
+ }
+
+ public function getProviderName(): ?string
+ {
+ return $this->providerName;
+ }
+
+ public function getProviderId(): ?int
+ {
+ return $this->providerId;
+ }
+}
diff --git a/src/CrowdinApiClient/Model/AiProxyChatCompletion.php b/src/CrowdinApiClient/Model/AiProxyChatCompletion.php
new file mode 100644
index 00000000..f156098d
--- /dev/null
+++ b/src/CrowdinApiClient/Model/AiProxyChatCompletion.php
@@ -0,0 +1,13 @@
+identifier = (string)$this->getDataProperty('identifier');
+ $this->status = (string)$this->getDataProperty('status');
+ $this->progress = (int)$this->getDataProperty('progress');
+ $this->attributes = (array)$this->getDataProperty('attributes');
+ $this->createdAt = (string)$this->getDataProperty('createdAt');
+ $this->updatedAt = $this->getDataProperty('updatedAt');
+ $this->startedAt = $this->getDataProperty('startedAt');
+ $this->finishedAt = $this->getDataProperty('finishedAt');
+ }
+
+ public function getIdentifier(): string
+ {
+ return $this->identifier;
+ }
+
+ public function getStatus(): string
+ {
+ return $this->status;
+ }
+
+ public function getProgress(): int
+ {
+ return $this->progress;
+ }
+
+ public function getAttributes(): array
+ {
+ return $this->attributes;
+ }
+
+ public function getCreatedAt(): string
+ {
+ return $this->createdAt;
+ }
+
+ public function getUpdatedAt(): ?string
+ {
+ return $this->updatedAt;
+ }
+
+ public function getStartedAt(): ?string
+ {
+ return $this->startedAt;
+ }
+
+ public function getFinishedAt(): ?string
+ {
+ return $this->finishedAt;
+ }
+}
diff --git a/src/CrowdinApiClient/Model/AiSettings.php b/src/CrowdinApiClient/Model/AiSettings.php
new file mode 100644
index 00000000..f58c2b5d
--- /dev/null
+++ b/src/CrowdinApiClient/Model/AiSettings.php
@@ -0,0 +1,70 @@
+preTranslationAiPromptId = (int)$this->getDataProperty('preTranslationAiPromptId');
+ $this->editorSuggestionAiPromptId = (int)$this->getDataProperty('editorSuggestionAiPromptId');
+ $this->qaCheckActionAiPromptId = $this->getDataProperty('qaCheckActionAiPromptId');
+ $this->contextReviewAiPromptId = $this->getDataProperty('contextReviewAiPromptId');
+ }
+
+ public function getPreTranslationAiPromptId(): int
+ {
+ return $this->preTranslationAiPromptId;
+ }
+
+ public function getEditorSuggestionAiPromptId(): int
+ {
+ return $this->editorSuggestionAiPromptId;
+ }
+
+ public function getQaCheckActionAiPromptId(): ?int
+ {
+ return $this->qaCheckActionAiPromptId;
+ }
+
+ public function getContextReviewAiPromptId(): ?int
+ {
+ return $this->contextReviewAiPromptId;
+ }
+
+ public function setPreTranslationAiPromptId(int $preTranslationAiPromptId): void
+ {
+ $this->preTranslationAiPromptId = $preTranslationAiPromptId;
+ }
+
+ public function setEditorSuggestionAiPromptId(int $editorSuggestionAiPromptId): void
+ {
+ $this->editorSuggestionAiPromptId = $editorSuggestionAiPromptId;
+ }
+
+ public function setQaCheckActionAiPromptId(?int $qaCheckActionAiPromptId): void
+ {
+ $this->qaCheckActionAiPromptId = $qaCheckActionAiPromptId;
+ }
+
+ public function setContextReviewAiPromptId(?int $contextReviewAiPromptId): void
+ {
+ $this->contextReviewAiPromptId = $contextReviewAiPromptId;
+ }
+}
diff --git a/src/CrowdinApiClient/Model/AiSnippet.php b/src/CrowdinApiClient/Model/AiSnippet.php
new file mode 100644
index 00000000..d0d3299a
--- /dev/null
+++ b/src/CrowdinApiClient/Model/AiSnippet.php
@@ -0,0 +1,83 @@
+id = (int)$this->getDataProperty('id');
+ $this->description = (string)$this->getDataProperty('description');
+ $this->placeholder = (string)$this->getDataProperty('placeholder');
+ $this->value = (string)$this->getDataProperty('value');
+ $this->createdAt = (string)$this->getDataProperty('createdAt');
+ $this->updatedAt = (string)$this->getDataProperty('updatedAt');
+ }
+
+ public function getId(): int
+ {
+ return $this->id;
+ }
+
+ public function getDescription(): string
+ {
+ return $this->description;
+ }
+
+ public function getPlaceholder(): string
+ {
+ return $this->placeholder;
+ }
+
+ public function getValue(): string
+ {
+ return $this->value;
+ }
+
+ public function getCreatedAt(): string
+ {
+ return $this->createdAt;
+ }
+
+ public function getUpdatedAt(): string
+ {
+ return $this->updatedAt;
+ }
+
+ public function setDescription(string $description): void
+ {
+ $this->description = $description;
+ }
+
+ public function setPlaceholder(string $placeholder): void
+ {
+ $this->placeholder = $placeholder;
+ }
+
+ public function setValue(string $value): void
+ {
+ $this->value = $value;
+ }
+}
diff --git a/tests/CrowdinApiClient/Api/AiApiTest.php b/tests/CrowdinApiClient/Api/AiApiTest.php
index b8fc4d51..2defe765 100644
--- a/tests/CrowdinApiClient/Api/AiApiTest.php
+++ b/tests/CrowdinApiClient/Api/AiApiTest.php
@@ -3,8 +3,17 @@
namespace CrowdinApiClient\Tests\Api;
use CrowdinApiClient\Model\AiFileTranslation;
+use CrowdinApiClient\Model\AiPrompt;
+use CrowdinApiClient\Model\AiPromptCompletion;
+use CrowdinApiClient\Model\AiProvider;
+use CrowdinApiClient\Model\AiProviderModel;
+use CrowdinApiClient\Model\AiProxyChatCompletion;
+use CrowdinApiClient\Model\AiReport;
+use CrowdinApiClient\Model\AiSettings;
+use CrowdinApiClient\Model\AiSnippet;
use CrowdinApiClient\Model\AiTranslation;
use CrowdinApiClient\Model\DownloadFile;
+use CrowdinApiClient\ModelCollection;
class AiApiTest extends AbstractTestApi
{
@@ -12,6 +21,7 @@ public function testTranslateStrings(): void
{
$params = [
'strings' => ['Some text to translate!'],
+ 'sourceLanguageId' => 'en',
'targetLanguageId' => 'uk',
];
@@ -24,7 +34,8 @@ public function testTranslateStrings(): void
"sourceLanguageId": "en",
"targetLanguageId": "uk",
"translations": [
- "Перекладений текст 1"
+ "Перекладений текст 1",
+ "Перекладений текст 2"
]
}
}',
@@ -34,14 +45,23 @@ public function testTranslateStrings(): void
$this->assertInstanceOf(AiTranslation::class, $aiTranslation);
$this->assertEquals('en', $aiTranslation->getSourceLanguageId());
$this->assertEquals('uk', $aiTranslation->getTargetLanguageId());
- $this->assertEquals(['Перекладений текст 1'], $aiTranslation->getTranslations());
+ $this->assertEquals(['Перекладений текст 1', 'Перекладений текст 2'], $aiTranslation->getTranslations());
}
public function testCreateFileTranslation(): void
{
$params = [
'storageId' => 123,
+ 'sourceLanguageId' => 'en',
'targetLanguageId' => 'uk',
+ 'type' => 'xliff',
+ 'parserVersion' => 1,
+ 'tmIds' => [123],
+ 'glossaryIds' => [456],
+ 'styleGuideIds' => [654],
+ 'aiPromptId' => 789,
+ 'instructions' => ['Keep a formal tone'],
+ 'attachmentIds' => [123],
];
$this->mockRequest([
@@ -54,9 +74,9 @@ public function testCreateFileTranslation(): void
"status": "finished",
"progress": 100,
"attributes": {
- "stage": "done",
+ "stage": "translate",
"error": null,
- "downloadName": "Sample_Chrome.json",
+ "downloadName": "file.pdf",
"sourceLanguageId": "en",
"targetLanguageId": "uk",
"originalFileName": "Sample_Chrome.json",
@@ -87,22 +107,22 @@ public function testGetFileTranslation(): void
'response' => '{
"data": {
"identifier": "50fb3506-4127-4ba8-8296-f97dc7e3e0c3",
- "status": "in_progress",
- "progress": 50,
+ "status": "finished",
+ "progress": 100,
"attributes": {
"stage": "translate",
"error": null,
- "downloadName": null,
+ "downloadName": "file.pdf",
"sourceLanguageId": "en",
"targetLanguageId": "uk",
"originalFileName": "Sample_Chrome.json",
- "detectedType": null,
- "parserVersion": null
+ "detectedType": "chrome",
+ "parserVersion": 2
},
"createdAt": "2026-01-23T11:26:54+00:00",
"updatedAt": "2026-01-23T11:26:54+00:00",
"startedAt": "2026-01-23T11:26:54+00:00",
- "finishedAt": null
+ "finishedAt": "2026-01-23T11:26:54+00:00"
}
}',
]);
@@ -110,8 +130,12 @@ public function testGetFileTranslation(): void
$fileTranslation = $this->crowdin->ai->getFileTranslation(1, '50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
$this->assertInstanceOf(AiFileTranslation::class, $fileTranslation);
$this->assertEquals('50fb3506-4127-4ba8-8296-f97dc7e3e0c3', $fileTranslation->getIdentifier());
- $this->assertEquals('in_progress', $fileTranslation->getStatus());
- $this->assertEquals(50, $fileTranslation->getProgress());
+ $this->assertEquals('finished', $fileTranslation->getStatus());
+ $this->assertEquals(100, $fileTranslation->getProgress());
+ $this->assertEquals('2026-01-23T11:26:54+00:00', $fileTranslation->getCreatedAt());
+ $this->assertEquals('2026-01-23T11:26:54+00:00', $fileTranslation->getUpdatedAt());
+ $this->assertEquals('2026-01-23T11:26:54+00:00', $fileTranslation->getStartedAt());
+ $this->assertEquals('2026-01-23T11:26:54+00:00', $fileTranslation->getFinishedAt());
}
public function testDeleteFileTranslation(): void
@@ -132,16 +156,16 @@ public function testDownloadFileTranslation(): void
'method' => 'get',
'response' => '{
"data": {
- "url": "https://example.com/download/file.json",
- "expireIn": "2026-01-23T12:00:00+00:00"
+ "url": "https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff",
+ "expireIn": "2019-09-20T10:31:21+00:00"
}
}',
]);
$downloadFile = $this->crowdin->ai->downloadFileTranslation(1, '50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
$this->assertInstanceOf(DownloadFile::class, $downloadFile);
- $this->assertEquals('https://example.com/download/file.json', $downloadFile->getUrl());
- $this->assertEquals('2026-01-23T12:00:00+00:00', $downloadFile->getExpireIn());
+ $this->assertEquals('https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff', $downloadFile->getUrl());
+ $this->assertEquals('2019-09-20T10:31:21+00:00', $downloadFile->getExpireIn());
}
public function testDownloadFileTranslationStrings(): void
@@ -151,15 +175,880 @@ public function testDownloadFileTranslationStrings(): void
'method' => 'get',
'response' => '{
"data": {
- "url": "https://example.com/download/strings.json",
- "expireIn": "2026-01-23T12:00:00+00:00"
+ "url": "https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff",
+ "expireIn": "2019-09-20T10:31:21+00:00"
}
}',
]);
$downloadFile = $this->crowdin->ai->downloadFileTranslationStrings(1, '50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
$this->assertInstanceOf(DownloadFile::class, $downloadFile);
- $this->assertEquals('https://example.com/download/strings.json', $downloadFile->getUrl());
- $this->assertEquals('2026-01-23T12:00:00+00:00', $downloadFile->getExpireIn());
+ $this->assertEquals('https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff', $downloadFile->getUrl());
+ $this->assertEquals('2019-09-20T10:31:21+00:00', $downloadFile->getExpireIn());
+ }
+
+ public function testListPrompts(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/prompts',
+ 'method' => 'get',
+ 'response' => '{
+ "data": [
+ {
+ "data": {
+ "id": 2,
+ "name": "Pre-translate prompt",
+ "action": "pre_translate",
+ "aiProviderId": 2,
+ "aiModelId": "gpt-5.4",
+ "isEnabled": true,
+ "enabledProjectIds": [1],
+ "config": {},
+ "promptPreview": null,
+ "isFineTuningAvailable": true,
+ "createdBy": 123,
+ "updatedBy": 456,
+ "lastUsedBy": 789,
+ "lastUsedAt": "2019-09-25T14:30:00+00:00",
+ "usageCount": 42,
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00"
+ }
+ }
+ ],
+ "pagination": {"offset": 0, "limit": 25}
+ }',
+ ]);
+
+ $prompts = $this->crowdin->ai->listPrompts(1);
+ $this->assertInstanceOf(ModelCollection::class, $prompts);
+ $this->assertInstanceOf(AiPrompt::class, $prompts[0]);
+ $this->assertEquals(2, $prompts[0]->getId());
+ $this->assertEquals('Pre-translate prompt', $prompts[0]->getName());
+ $this->assertEquals('pre_translate', $prompts[0]->getAction());
+ }
+
+ public function testCreatePrompt(): void
+ {
+ $params = [
+ 'name' => 'Pre-translate prompt',
+ 'action' => 'pre_translate',
+ 'config' => ['mode' => 'basic'],
+ ];
+
+ $this->mockRequest([
+ 'path' => '/users/1/ai/prompts',
+ 'method' => 'post',
+ 'body' => json_encode($params),
+ 'response' => '{
+ "data": {
+ "id": 2,
+ "name": "Pre-translate prompt",
+ "action": "pre_translate",
+ "aiProviderId": 2,
+ "aiModelId": "gpt-5.4",
+ "isEnabled": true,
+ "enabledProjectIds": [1],
+ "config": {"mode": "basic"},
+ "promptPreview": null,
+ "isFineTuningAvailable": true,
+ "createdBy": 123,
+ "updatedBy": 456,
+ "lastUsedBy": 789,
+ "lastUsedAt": "2019-09-25T14:30:00+00:00",
+ "usageCount": 42,
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00"
+ }
+ }',
+ ]);
+
+ $prompt = $this->crowdin->ai->createPrompt(1, $params);
+ $this->assertInstanceOf(AiPrompt::class, $prompt);
+ $this->assertEquals(2, $prompt->getId());
+ $this->assertEquals('Pre-translate prompt', $prompt->getName());
+ $this->assertTrue($prompt->isEnabled());
+ }
+
+ public function testGetPrompt(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/prompts/2',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "id": 2,
+ "name": "Pre-translate prompt",
+ "action": "pre_translate",
+ "aiProviderId": 2,
+ "aiModelId": "gpt-5.4",
+ "isEnabled": true,
+ "enabledProjectIds": [1],
+ "config": {
+ "mode": "basic",
+ "snippets": ["%custom:companyDescription%"],
+ "otherLanguageTranslations": {
+ "isEnabled": true,
+ "languageIds": ["uk"]
+ },
+ "glossaryTerms": true,
+ "tmSuggestions": true,
+ "fileContext": true,
+ "generateFileSummary": true,
+ "screenshots": true,
+ "projectContext": true,
+ "siblingsStrings": true,
+ "retryOnQaIssues": true
+ },
+ "promptPreview": null,
+ "isFineTuningAvailable": true,
+ "createdBy": 123,
+ "updatedBy": 456,
+ "lastUsedBy": 789,
+ "lastUsedAt": "2019-09-25T14:30:00+00:00",
+ "usageCount": 42,
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00"
+ }
+ }',
+ ]);
+
+ $prompt = $this->crowdin->ai->getPrompt(1, 2);
+ $this->assertInstanceOf(AiPrompt::class, $prompt);
+ $this->assertEquals(2, $prompt->getId());
+ $this->assertEquals(2, $prompt->getAiProviderId());
+ $this->assertEquals('gpt-5.4', $prompt->getAiModelId());
+ $this->assertTrue($prompt->isFineTuningAvailable());
+ $this->assertEquals(42, $prompt->getUsageCount());
+ $this->assertEquals([1], $prompt->getEnabledProjectIds());
+ $this->assertNull($prompt->getPromptPreview());
+ $this->assertEquals(123, $prompt->getCreatedBy());
+ $this->assertEquals(456, $prompt->getUpdatedBy());
+ $this->assertEquals(789, $prompt->getLastUsedBy());
+ $this->assertEquals('2019-09-25T14:30:00+00:00', $prompt->getLastUsedAt());
+ $this->assertEquals('2019-09-20T11:11:05+00:00', $prompt->getCreatedAt());
+ $this->assertEquals('2019-09-20T12:22:20+00:00', $prompt->getUpdatedAt());
+ $this->assertIsArray($prompt->getConfig());
+ }
+
+ public function testDeletePrompt(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/prompts/2',
+ 'method' => 'delete',
+ 'response' => '',
+ ]);
+
+ $this->crowdin->ai->deletePrompt(1, 2);
+ }
+
+ public function testClonePrompt(): void
+ {
+ $params = [
+ 'name' => 'Pre-translate prompt',
+ ];
+
+ $this->mockRequest([
+ 'path' => '/users/1/ai/prompts/2/clones',
+ 'method' => 'post',
+ 'body' => json_encode($params),
+ 'response' => '{
+ "data": {
+ "id": 2,
+ "name": "Pre-translate prompt",
+ "action": "pre_translate",
+ "aiProviderId": 2,
+ "aiModelId": "gpt-5.4",
+ "isEnabled": true,
+ "enabledProjectIds": [1],
+ "config": {},
+ "promptPreview": null,
+ "isFineTuningAvailable": true,
+ "createdBy": 123,
+ "updatedBy": 456,
+ "lastUsedBy": 789,
+ "lastUsedAt": "2019-09-25T14:30:00+00:00",
+ "usageCount": 42,
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00"
+ }
+ }',
+ ]);
+
+ $cloned = $this->crowdin->ai->clonePrompt(1, 2, $params);
+ $this->assertInstanceOf(AiPrompt::class, $cloned);
+ $this->assertEquals(2, $cloned->getId());
+ $this->assertEquals('Pre-translate prompt', $cloned->getName());
+ }
+
+ public function testCreatePromptCompletion(): void
+ {
+ $params = [
+ 'resources' => [
+ 'projectId' => 123,
+ 'targetLanguageId' => 'uk',
+ 'stringIds' => [78253],
+ ],
+ ];
+
+ $this->mockRequest([
+ 'path' => '/users/1/ai/prompts/2/completions',
+ 'method' => 'post',
+ 'body' => json_encode($params),
+ 'response' => '{
+ "data": {
+ "identifier": "50fb3506-4127-4ba8-8296-f97dc7e3e0c3",
+ "status": "finished",
+ "progress": 100,
+ "attributes": {
+ "aiPromptId": 38
+ },
+ "createdAt": "2019-09-23T11:26:54+00:00",
+ "updatedAt": "2019-09-23T11:26:54+00:00",
+ "startedAt": "2019-09-23T11:26:54+00:00",
+ "finishedAt": "2019-09-23T11:26:54+00:00"
+ }
+ }',
+ ]);
+
+ $completion = $this->crowdin->ai->createPromptCompletion(1, 2, $params);
+ $this->assertInstanceOf(AiPromptCompletion::class, $completion);
+ $this->assertEquals('50fb3506-4127-4ba8-8296-f97dc7e3e0c3', $completion->getIdentifier());
+ $this->assertEquals('finished', $completion->getStatus());
+ $this->assertEquals(['aiPromptId' => 38], $completion->getAttributes());
+ }
+
+ public function testGetPromptCompletion(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/prompts/2/completions/50fb3506-4127-4ba8-8296-f97dc7e3e0c3',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "identifier": "50fb3506-4127-4ba8-8296-f97dc7e3e0c3",
+ "status": "finished",
+ "progress": 100,
+ "attributes": {
+ "aiPromptId": 38
+ },
+ "createdAt": "2019-09-23T11:26:54+00:00",
+ "updatedAt": "2019-09-23T11:26:54+00:00",
+ "startedAt": "2019-09-23T11:26:54+00:00",
+ "finishedAt": "2019-09-23T11:26:54+00:00"
+ }
+ }',
+ ]);
+
+ $completion = $this->crowdin->ai->getPromptCompletion(1, 2, '50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
+ $this->assertInstanceOf(AiPromptCompletion::class, $completion);
+ $this->assertEquals('finished', $completion->getStatus());
+ $this->assertEquals(100, $completion->getProgress());
+ $this->assertEquals('2019-09-23T11:26:54+00:00', $completion->getCreatedAt());
+ $this->assertEquals('2019-09-23T11:26:54+00:00', $completion->getUpdatedAt());
+ $this->assertEquals('2019-09-23T11:26:54+00:00', $completion->getStartedAt());
+ $this->assertEquals('2019-09-23T11:26:54+00:00', $completion->getFinishedAt());
+ }
+
+ public function testDeletePromptCompletion(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/prompts/2/completions/50fb3506-4127-4ba8-8296-f97dc7e3e0c3',
+ 'method' => 'delete',
+ 'response' => '',
+ ]);
+
+ $this->crowdin->ai->deletePromptCompletion(1, 2, '50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
+ }
+
+ public function testDownloadPromptCompletion(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/prompts/2/completions/50fb3506-4127-4ba8-8296-f97dc7e3e0c3/download',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "url": "https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff",
+ "expireIn": "2019-09-20T10:31:21+00:00"
+ }
+ }',
+ ]);
+
+ $download = $this->crowdin->ai->downloadPromptCompletion(1, 2, '50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
+ $this->assertInstanceOf(DownloadFile::class, $download);
+ $this->assertEquals('https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff', $download->getUrl());
+ $this->assertEquals('2019-09-20T10:31:21+00:00', $download->getExpireIn());
+ }
+
+ public function testListProviders(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/providers',
+ 'method' => 'get',
+ 'response' => '{
+ "data": [
+ {
+ "data": {
+ "id": 2,
+ "name": "OpenAI",
+ "type": "open_ai",
+ "credentials": {"apiKey": "sk-..."},
+ "config": {
+ "actionRules": [
+ {
+ "action": "pre_translate",
+ "availableAiModelIds": ["gpt-5.4"]
+ }
+ ]
+ },
+ "isEnabled": true,
+ "useSystemCredentials": false,
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00",
+ "promptsCount": 42
+ }
+ }
+ ],
+ "pagination": {"offset": 0, "limit": 25}
+ }',
+ ]);
+
+ $providers = $this->crowdin->ai->listProviders(1);
+ $this->assertInstanceOf(ModelCollection::class, $providers);
+ $this->assertInstanceOf(AiProvider::class, $providers[0]);
+ $this->assertEquals(2, $providers[0]->getId());
+ $this->assertEquals('OpenAI', $providers[0]->getName());
+ $this->assertEquals('open_ai', $providers[0]->getType());
+ }
+
+ public function testCreateProvider(): void
+ {
+ $params = [
+ 'name' => 'OpenAI',
+ 'type' => 'open_ai',
+ 'credentials' => ['apiKey' => 'sk-...'],
+ 'isEnabled' => true,
+ 'useSystemCredentials' => false,
+ ];
+
+ $this->mockRequest([
+ 'path' => '/users/1/ai/providers',
+ 'method' => 'post',
+ 'body' => json_encode($params),
+ 'response' => '{
+ "data": {
+ "id": 2,
+ "name": "OpenAI",
+ "type": "open_ai",
+ "credentials": {"apiKey": "sk-..."},
+ "config": {},
+ "isEnabled": true,
+ "useSystemCredentials": false,
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00",
+ "promptsCount": 42
+ }
+ }',
+ ]);
+
+ $provider = $this->crowdin->ai->createProvider(1, $params);
+ $this->assertInstanceOf(AiProvider::class, $provider);
+ $this->assertEquals(2, $provider->getId());
+ $this->assertEquals('OpenAI', $provider->getName());
+ $this->assertEquals(42, $provider->getPromptsCount());
+ }
+
+ public function testGetProvider(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/providers/2',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "id": 2,
+ "name": "OpenAI",
+ "type": "open_ai",
+ "credentials": {"apiKey": "string"},
+ "config": {
+ "actionRules": [
+ {
+ "action": "pre_translate",
+ "availableAiModelIds": ["gpt-5.4"]
+ }
+ ]
+ },
+ "isEnabled": true,
+ "useSystemCredentials": false,
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00",
+ "promptsCount": 42
+ }
+ }',
+ ]);
+
+ $provider = $this->crowdin->ai->getProvider(1, 2);
+ $this->assertInstanceOf(AiProvider::class, $provider);
+ $this->assertEquals(2, $provider->getId());
+ $this->assertFalse($provider->isUseSystemCredentials());
+ $this->assertEquals(42, $provider->getPromptsCount());
+ $this->assertEquals(['apiKey' => 'string'], $provider->getCredentials());
+ $this->assertIsArray($provider->getConfig());
+ $this->assertTrue($provider->isEnabled());
+ $this->assertEquals('2019-09-20T11:11:05+00:00', $provider->getCreatedAt());
+ $this->assertEquals('2019-09-20T12:22:20+00:00', $provider->getUpdatedAt());
+ }
+
+ public function testDeleteProvider(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/providers/2',
+ 'method' => 'delete',
+ 'response' => '',
+ ]);
+
+ $this->crowdin->ai->deleteProvider(1, 2);
+ }
+
+ public function testListProviderModels(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/providers/2/models',
+ 'method' => 'get',
+ 'response' => '{
+ "data": [
+ {
+ "data": {
+ "id": "gpt-5.4",
+ "provider": "open_ai",
+ "providerName": "OpenAI",
+ "providerId": 1
+ }
+ }
+ ],
+ "pagination": {"offset": 0, "limit": 25}
+ }',
+ ]);
+
+ $models = $this->crowdin->ai->listProviderModels(1, 2);
+ $this->assertInstanceOf(ModelCollection::class, $models);
+ $this->assertInstanceOf(AiProviderModel::class, $models[0]);
+ $this->assertEquals('gpt-5.4', $models[0]->getId());
+ $this->assertEquals('open_ai', $models[0]->getProvider());
+ $this->assertEquals('OpenAI', $models[0]->getProviderName());
+ $this->assertEquals(1, $models[0]->getProviderId());
+ }
+
+ public function testCreateProviderChatCompletion(): void
+ {
+ $params = [
+ 'model' => 'gpt-4o',
+ 'messages' => [
+ ['role' => 'user', 'content' => 'Hello'],
+ ],
+ ];
+
+ $this->mockRequest([
+ 'path' => '/users/1/ai/providers/2/chat/completions',
+ 'method' => 'post',
+ 'body' => json_encode($params),
+ 'response' => '{
+ "data": {
+ "id": "chatcmpl-123",
+ "object": "chat.completion",
+ "model": "gpt-4o",
+ "choices": [{"message": {"role": "assistant", "content": "Hi!"}}]
+ }
+ }',
+ ]);
+
+ $completion = $this->crowdin->ai->createProviderChatCompletion(1, 2, $params);
+ $this->assertInstanceOf(AiProxyChatCompletion::class, $completion);
+ }
+
+ public function testGenerateReport(): void
+ {
+ $params = [
+ 'type' => 'tokens-usage-raw-data',
+ 'schema' => [
+ 'dateFrom' => '2024-01-23T07:00:14+00:00',
+ 'dateTo' => '2024-09-27T07:00:14+00:00',
+ 'format' => 'json',
+ 'projectIds' => [22],
+ 'promptIds' => [18],
+ 'userIds' => [1],
+ ],
+ ];
+
+ $this->mockRequest([
+ 'path' => '/users/1/ai/reports',
+ 'method' => 'post',
+ 'body' => json_encode($params),
+ 'response' => '{
+ "data": {
+ "identifier": "50fb3506-4127-4ba8-8296-f97dc7e3e0c3",
+ "status": "finished",
+ "progress": 100,
+ "attributes": {
+ "format": "json",
+ "reportType": "tokens-usage-raw-data",
+ "schema": {}
+ },
+ "createdAt": "2024-01-23T11:26:54+00:00",
+ "updatedAt": "2024-09-23T11:26:54+00:00",
+ "startedAt": "2024-05-23T11:26:54+00:00",
+ "finishedAt": "2024-05-23T11:26:54+00:00"
+ }
+ }',
+ ]);
+
+ $report = $this->crowdin->ai->generateReport(1, $params);
+ $this->assertInstanceOf(AiReport::class, $report);
+ $this->assertEquals('50fb3506-4127-4ba8-8296-f97dc7e3e0c3', $report->getIdentifier());
+ $this->assertEquals('finished', $report->getStatus());
+ }
+
+ public function testGetReport(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/reports/50fb3506-4127-4ba8-8296-f97dc7e3e0c3',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "identifier": "50fb3506-4127-4ba8-8296-f97dc7e3e0c3",
+ "status": "finished",
+ "progress": 100,
+ "attributes": {
+ "format": "json",
+ "reportType": "tokens-usage-raw-data",
+ "schema": {}
+ },
+ "createdAt": "2024-01-23T11:26:54+00:00",
+ "updatedAt": "2024-09-23T11:26:54+00:00",
+ "startedAt": "2024-05-23T11:26:54+00:00",
+ "finishedAt": "2024-05-23T11:26:54+00:00"
+ }
+ }',
+ ]);
+
+ $report = $this->crowdin->ai->getReport(1, '50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
+ $this->assertInstanceOf(AiReport::class, $report);
+ $this->assertEquals('finished', $report->getStatus());
+ $this->assertEquals(100, $report->getProgress());
+ $this->assertIsArray($report->getAttributes());
+ $this->assertEquals('2024-01-23T11:26:54+00:00', $report->getCreatedAt());
+ $this->assertEquals('2024-09-23T11:26:54+00:00', $report->getUpdatedAt());
+ $this->assertEquals('2024-05-23T11:26:54+00:00', $report->getStartedAt());
+ $this->assertEquals('2024-05-23T11:26:54+00:00', $report->getFinishedAt());
+ }
+
+ public function testDownloadReport(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/reports/50fb3506-4127-4ba8-8296-f97dc7e3e0c3/download',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "url": "https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff",
+ "expireIn": "2019-09-20T10:31:21+00:00"
+ }
+ }',
+ ]);
+
+ $download = $this->crowdin->ai->downloadReport(1, '50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
+ $this->assertInstanceOf(DownloadFile::class, $download);
+ $this->assertEquals('https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff', $download->getUrl());
+ $this->assertEquals('2019-09-20T10:31:21+00:00', $download->getExpireIn());
+ }
+
+ public function testGetSettings(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/settings',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "preTranslationAiPromptId": 2,
+ "editorSuggestionAiPromptId": 5,
+ "qaCheckActionAiPromptId": 8,
+ "contextReviewAiPromptId": 11
+ }
+ }',
+ ]);
+
+ $settings = $this->crowdin->ai->getSettings(1);
+ $this->assertInstanceOf(AiSettings::class, $settings);
+ $this->assertEquals(2, $settings->getPreTranslationAiPromptId());
+ $this->assertEquals(5, $settings->getEditorSuggestionAiPromptId());
+ $this->assertEquals(8, $settings->getQaCheckActionAiPromptId());
+ $this->assertEquals(11, $settings->getContextReviewAiPromptId());
+ }
+
+ public function testListSnippets(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/settings/snippets',
+ 'method' => 'get',
+ 'response' => '{
+ "data": [
+ {
+ "data": {
+ "id": 2,
+ "description": "Product description",
+ "placeholder": "%custom:productDescription%",
+ "value": "The product is the professional consulting service that transform challenges into opportunities.",
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00"
+ }
+ }
+ ],
+ "pagination": {"offset": 0, "limit": 25}
+ }',
+ ]);
+
+ $snippets = $this->crowdin->ai->listSnippets(1);
+ $this->assertInstanceOf(ModelCollection::class, $snippets);
+ $this->assertInstanceOf(AiSnippet::class, $snippets[0]);
+ $this->assertEquals(2, $snippets[0]->getId());
+ $this->assertEquals('%custom:productDescription%', $snippets[0]->getPlaceholder());
+ }
+
+ public function testCreateSnippet(): void
+ {
+ $params = [
+ 'description' => 'Product description',
+ 'placeholder' => '%custom:productDescription%',
+ 'value' => 'The product is the professional consulting service that transform challenges into opportunities.',
+ ];
+
+ $this->mockRequest([
+ 'path' => '/users/1/ai/settings/snippets',
+ 'method' => 'post',
+ 'body' => json_encode($params),
+ 'response' => '{
+ "data": {
+ "id": 2,
+ "description": "Product description",
+ "placeholder": "%custom:productDescription%",
+ "value": "The product is the professional consulting service that transform challenges into opportunities.",
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00"
+ }
+ }',
+ ]);
+
+ $snippet = $this->crowdin->ai->createSnippet(1, $params);
+ $this->assertInstanceOf(AiSnippet::class, $snippet);
+ $this->assertEquals(2, $snippet->getId());
+ $this->assertEquals('Product description', $snippet->getDescription());
+ }
+
+ public function testGetSnippet(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/settings/snippets/2',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "id": 2,
+ "description": "Product description",
+ "placeholder": "%custom:productDescription%",
+ "value": "The product is the professional consulting service that transform challenges into opportunities.",
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00"
+ }
+ }',
+ ]);
+
+ $snippet = $this->crowdin->ai->getSnippet(1, 2);
+ $this->assertInstanceOf(AiSnippet::class, $snippet);
+ $this->assertEquals(
+ 'The product is the professional consulting service that transform challenges into opportunities.',
+ $snippet->getValue()
+ );
+ $this->assertEquals('2019-09-20T11:11:05+00:00', $snippet->getCreatedAt());
+ $this->assertEquals('2019-09-20T12:22:20+00:00', $snippet->getUpdatedAt());
+ }
+
+ public function testDeleteSnippet(): void
+ {
+ $this->mockRequest([
+ 'path' => '/users/1/ai/settings/snippets/2',
+ 'method' => 'delete',
+ 'response' => '',
+ ]);
+
+ $this->crowdin->ai->deleteSnippet(1, 2);
+ }
+
+ public function testUpdatePrompt(): void
+ {
+ $prompt = new AiPrompt([
+ 'id' => 2,
+ 'name' => 'Pre-translate prompt',
+ 'action' => 'pre_translate',
+ 'aiProviderId' => 2,
+ 'aiModelId' => 'gpt-5.4',
+ 'isEnabled' => true,
+ 'enabledProjectIds' => [1],
+ 'config' => [],
+ 'promptPreview' => null,
+ 'isFineTuningAvailable' => true,
+ 'createdBy' => 123,
+ 'updatedBy' => 456,
+ 'lastUsedBy' => 789,
+ 'lastUsedAt' => '2019-09-25T14:30:00+00:00',
+ 'usageCount' => 42,
+ 'createdAt' => '2019-09-20T11:11:05+00:00',
+ 'updatedAt' => '2019-09-20T12:22:20+00:00',
+ ]);
+ $prompt->setName('Updated prompt');
+ $prompt->setAction('translate');
+ $prompt->setAiProviderId(3);
+ $prompt->setAiModelId('gpt-4o');
+ $prompt->setEnabledProjectIds([2, 3]);
+ $prompt->setConfig(['mode' => 'advanced']);
+
+ $this->mockRequestPatch(
+ '/users/1/ai/prompts/2',
+ json_encode([
+ 'data' => [
+ 'id' => 2,
+ 'name' => 'Updated prompt',
+ 'action' => 'translate',
+ 'aiProviderId' => 3,
+ 'aiModelId' => 'gpt-4o',
+ 'isEnabled' => true,
+ 'enabledProjectIds' => [2, 3],
+ 'config' => ['mode' => 'advanced'],
+ 'promptPreview' => null,
+ 'isFineTuningAvailable' => true,
+ 'createdBy' => 123,
+ 'updatedBy' => 456,
+ 'lastUsedBy' => 789,
+ 'lastUsedAt' => '2019-09-25T14:30:00+00:00',
+ 'usageCount' => 42,
+ 'createdAt' => '2019-09-20T11:11:05+00:00',
+ 'updatedAt' => '2019-09-20T12:22:20+00:00',
+ ],
+ ])
+ );
+
+ $updated = $this->crowdin->ai->updatePrompt(1, $prompt);
+ $this->assertInstanceOf(AiPrompt::class, $updated);
+ $this->assertEquals(2, $updated->getId());
+ $this->assertEquals('Updated prompt', $updated->getName());
+ $this->assertEquals('gpt-4o', $updated->getAiModelId());
+ }
+
+ public function testUpdateProvider(): void
+ {
+ $provider = new AiProvider([
+ 'id' => 2,
+ 'name' => 'OpenAI',
+ 'type' => 'open_ai',
+ 'credentials' => ['apiKey' => 'sk-...'],
+ 'config' => [],
+ 'isEnabled' => true,
+ 'useSystemCredentials' => false,
+ 'createdAt' => '2019-09-20T11:11:05+00:00',
+ 'updatedAt' => '2019-09-20T12:22:20+00:00',
+ 'promptsCount' => 42,
+ ]);
+ $provider->setName('Azure OpenAI');
+ $provider->setType('azure_open_ai');
+ $provider->setCredentials(['apiKey' => 'new-key']);
+ $provider->setConfig(['actionRules' => []]);
+ $provider->setIsEnabled(false);
+ $provider->setUseSystemCredentials(true);
+
+ $this->mockRequestPatch(
+ '/users/1/ai/providers/2',
+ json_encode([
+ 'data' => [
+ 'id' => 2,
+ 'name' => 'Azure OpenAI',
+ 'type' => 'azure_open_ai',
+ 'credentials' => ['apiKey' => 'new-key'],
+ 'config' => ['actionRules' => []],
+ 'isEnabled' => false,
+ 'useSystemCredentials' => true,
+ 'createdAt' => '2019-09-20T11:11:05+00:00',
+ 'updatedAt' => '2019-09-20T12:22:20+00:00',
+ 'promptsCount' => 42,
+ ],
+ ])
+ );
+
+ $updated = $this->crowdin->ai->updateProvider(1, $provider);
+ $this->assertInstanceOf(AiProvider::class, $updated);
+ $this->assertEquals(2, $updated->getId());
+ $this->assertEquals('Azure OpenAI', $updated->getName());
+ }
+
+ public function testUpdateSettings(): void
+ {
+ $settings = new AiSettings([
+ 'preTranslationAiPromptId' => 2,
+ 'editorSuggestionAiPromptId' => 5,
+ 'qaCheckActionAiPromptId' => 8,
+ 'contextReviewAiPromptId' => 11,
+ ]);
+ $settings->setPreTranslationAiPromptId(3);
+ $settings->setEditorSuggestionAiPromptId(6);
+ $settings->setQaCheckActionAiPromptId(9);
+ $settings->setContextReviewAiPromptId(12);
+
+ $this->mockRequestPatch(
+ '/users/1/ai/settings',
+ json_encode([
+ 'data' => [
+ 'preTranslationAiPromptId' => 3,
+ 'editorSuggestionAiPromptId' => 6,
+ 'qaCheckActionAiPromptId' => 9,
+ 'contextReviewAiPromptId' => 12,
+ ],
+ ])
+ );
+
+ $updated = $this->crowdin->ai->updateSettings(1, $settings);
+ $this->assertInstanceOf(AiSettings::class, $updated);
+ $this->assertEquals(3, $updated->getPreTranslationAiPromptId());
+ $this->assertEquals(6, $updated->getEditorSuggestionAiPromptId());
+ $this->assertEquals(9, $updated->getQaCheckActionAiPromptId());
+ $this->assertEquals(12, $updated->getContextReviewAiPromptId());
+ }
+
+ public function testUpdateSnippet(): void
+ {
+ $snippet = new AiSnippet([
+ 'id' => 2,
+ 'description' => 'Product description',
+ 'placeholder' => '%custom:productDescription%',
+ 'value' => 'The product is the professional consulting service that transform challenges into opportunities.',
+ 'createdAt' => '2019-09-20T11:11:05+00:00',
+ 'updatedAt' => '2019-09-20T12:22:20+00:00',
+ ]);
+ $snippet->setDescription('Updated description');
+ $snippet->setPlaceholder('%custom:updatedDescription%');
+ $snippet->setValue('Updated value.');
+
+ $this->mockRequestPatch(
+ '/users/1/ai/settings/snippets/2',
+ json_encode([
+ 'data' => [
+ 'id' => 2,
+ 'description' => 'Updated description',
+ 'placeholder' => '%custom:updatedDescription%',
+ 'value' => 'Updated value.',
+ 'createdAt' => '2019-09-20T11:11:05+00:00',
+ 'updatedAt' => '2019-09-20T12:22:20+00:00',
+ ],
+ ])
+ );
+
+ $updated = $this->crowdin->ai->updateSnippet(1, $snippet);
+ $this->assertInstanceOf(AiSnippet::class, $updated);
+ $this->assertEquals('Updated description', $updated->getDescription());
+ $this->assertEquals('%custom:updatedDescription%', $updated->getPlaceholder());
+ $this->assertEquals('Updated value.', $updated->getValue());
}
}
diff --git a/tests/CrowdinApiClient/Api/Enterprise/AiApiTest.php b/tests/CrowdinApiClient/Api/Enterprise/AiApiTest.php
index 526e8fd3..80843165 100644
--- a/tests/CrowdinApiClient/Api/Enterprise/AiApiTest.php
+++ b/tests/CrowdinApiClient/Api/Enterprise/AiApiTest.php
@@ -3,8 +3,17 @@
namespace CrowdinApiClient\Tests\Api\Enterprise;
use CrowdinApiClient\Model\AiFileTranslation;
+use CrowdinApiClient\Model\AiPrompt;
+use CrowdinApiClient\Model\AiPromptCompletion;
+use CrowdinApiClient\Model\AiProvider;
+use CrowdinApiClient\Model\AiProviderModel;
+use CrowdinApiClient\Model\AiProxyChatCompletion;
+use CrowdinApiClient\Model\AiReport;
+use CrowdinApiClient\Model\AiSettings;
+use CrowdinApiClient\Model\AiSnippet;
use CrowdinApiClient\Model\AiTranslation;
use CrowdinApiClient\Model\DownloadFile;
+use CrowdinApiClient\ModelCollection;
class AiApiTest extends AbstractTestApi
{
@@ -12,6 +21,7 @@ public function testTranslateStrings(): void
{
$params = [
'strings' => ['Some text to translate!'],
+ 'sourceLanguageId' => 'en',
'targetLanguageId' => 'uk',
];
@@ -24,7 +34,8 @@ public function testTranslateStrings(): void
"sourceLanguageId": "en",
"targetLanguageId": "uk",
"translations": [
- "Перекладений текст 1"
+ "Перекладений текст 1",
+ "Перекладений текст 2"
]
}
}',
@@ -34,14 +45,23 @@ public function testTranslateStrings(): void
$this->assertInstanceOf(AiTranslation::class, $aiTranslation);
$this->assertEquals('en', $aiTranslation->getSourceLanguageId());
$this->assertEquals('uk', $aiTranslation->getTargetLanguageId());
- $this->assertEquals(['Перекладений текст 1'], $aiTranslation->getTranslations());
+ $this->assertEquals(['Перекладений текст 1', 'Перекладений текст 2'], $aiTranslation->getTranslations());
}
public function testCreateFileTranslation(): void
{
$params = [
'storageId' => 123,
+ 'sourceLanguageId' => 'en',
'targetLanguageId' => 'uk',
+ 'type' => 'xliff',
+ 'parserVersion' => 1,
+ 'tmIds' => [123],
+ 'glossaryIds' => [456],
+ 'styleGuideIds' => [654],
+ 'aiPromptId' => 789,
+ 'instructions' => ['Keep a formal tone'],
+ 'attachmentIds' => [123],
];
$this->mockRequest([
@@ -54,9 +74,9 @@ public function testCreateFileTranslation(): void
"status": "finished",
"progress": 100,
"attributes": {
- "stage": "done",
+ "stage": "translate",
"error": null,
- "downloadName": "Sample_Chrome.json",
+ "downloadName": "file.pdf",
"sourceLanguageId": "en",
"targetLanguageId": "uk",
"originalFileName": "Sample_Chrome.json",
@@ -87,22 +107,22 @@ public function testGetFileTranslation(): void
'response' => '{
"data": {
"identifier": "50fb3506-4127-4ba8-8296-f97dc7e3e0c3",
- "status": "in_progress",
- "progress": 50,
+ "status": "finished",
+ "progress": 100,
"attributes": {
"stage": "translate",
"error": null,
- "downloadName": null,
+ "downloadName": "file.pdf",
"sourceLanguageId": "en",
"targetLanguageId": "uk",
"originalFileName": "Sample_Chrome.json",
- "detectedType": null,
- "parserVersion": null
+ "detectedType": "chrome",
+ "parserVersion": 2
},
"createdAt": "2026-01-23T11:26:54+00:00",
"updatedAt": "2026-01-23T11:26:54+00:00",
"startedAt": "2026-01-23T11:26:54+00:00",
- "finishedAt": null
+ "finishedAt": "2026-01-23T11:26:54+00:00"
}
}',
]);
@@ -110,8 +130,12 @@ public function testGetFileTranslation(): void
$fileTranslation = $this->crowdin->ai->getFileTranslation('50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
$this->assertInstanceOf(AiFileTranslation::class, $fileTranslation);
$this->assertEquals('50fb3506-4127-4ba8-8296-f97dc7e3e0c3', $fileTranslation->getIdentifier());
- $this->assertEquals('in_progress', $fileTranslation->getStatus());
- $this->assertEquals(50, $fileTranslation->getProgress());
+ $this->assertEquals('finished', $fileTranslation->getStatus());
+ $this->assertEquals(100, $fileTranslation->getProgress());
+ $this->assertEquals('2026-01-23T11:26:54+00:00', $fileTranslation->getCreatedAt());
+ $this->assertEquals('2026-01-23T11:26:54+00:00', $fileTranslation->getUpdatedAt());
+ $this->assertEquals('2026-01-23T11:26:54+00:00', $fileTranslation->getStartedAt());
+ $this->assertEquals('2026-01-23T11:26:54+00:00', $fileTranslation->getFinishedAt());
}
public function testDeleteFileTranslation(): void
@@ -132,16 +156,16 @@ public function testDownloadFileTranslation(): void
'method' => 'get',
'response' => '{
"data": {
- "url": "https://example.com/download/file.json",
- "expireIn": "2026-01-23T12:00:00+00:00"
+ "url": "https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff",
+ "expireIn": "2019-09-20T10:31:21+00:00"
}
}',
]);
$downloadFile = $this->crowdin->ai->downloadFileTranslation('50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
$this->assertInstanceOf(DownloadFile::class, $downloadFile);
- $this->assertEquals('https://example.com/download/file.json', $downloadFile->getUrl());
- $this->assertEquals('2026-01-23T12:00:00+00:00', $downloadFile->getExpireIn());
+ $this->assertEquals('https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff', $downloadFile->getUrl());
+ $this->assertEquals('2019-09-20T10:31:21+00:00', $downloadFile->getExpireIn());
}
public function testDownloadFileTranslationStrings(): void
@@ -151,15 +175,863 @@ public function testDownloadFileTranslationStrings(): void
'method' => 'get',
'response' => '{
"data": {
- "url": "https://example.com/download/strings.json",
- "expireIn": "2026-01-23T12:00:00+00:00"
+ "url": "https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff",
+ "expireIn": "2019-09-20T10:31:21+00:00"
}
}',
]);
$downloadFile = $this->crowdin->ai->downloadFileTranslationStrings('50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
$this->assertInstanceOf(DownloadFile::class, $downloadFile);
- $this->assertEquals('https://example.com/download/strings.json', $downloadFile->getUrl());
- $this->assertEquals('2026-01-23T12:00:00+00:00', $downloadFile->getExpireIn());
+ $this->assertEquals('https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff', $downloadFile->getUrl());
+ $this->assertEquals('2019-09-20T10:31:21+00:00', $downloadFile->getExpireIn());
+ }
+
+ public function testListPrompts(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/prompts',
+ 'method' => 'get',
+ 'response' => '{
+ "data": [
+ {
+ "data": {
+ "id": 2,
+ "name": "Pre-translate prompt",
+ "action": "pre_translate",
+ "aiProviderId": 2,
+ "aiModelId": "gpt-5.4",
+ "isEnabled": true,
+ "enabledProjectIds": [1],
+ "config": {},
+ "promptPreview": null,
+ "isFineTuningAvailable": true,
+ "createdBy": 123,
+ "updatedBy": 456,
+ "lastUsedBy": 789,
+ "lastUsedAt": "2019-09-25T14:30:00+00:00",
+ "usageCount": 42,
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00"
+ }
+ }
+ ],
+ "pagination": {"offset": 0, "limit": 25}
+ }',
+ ]);
+
+ $prompts = $this->crowdin->ai->listPrompts();
+ $this->assertInstanceOf(ModelCollection::class, $prompts);
+ $this->assertInstanceOf(AiPrompt::class, $prompts[0]);
+ $this->assertEquals(2, $prompts[0]->getId());
+ $this->assertEquals('pre_translate', $prompts[0]->getAction());
+ }
+
+ public function testCreatePrompt(): void
+ {
+ $params = [
+ 'name' => 'Pre-translate prompt',
+ 'action' => 'pre_translate',
+ 'config' => ['mode' => 'basic'],
+ ];
+
+ $this->mockRequest([
+ 'path' => '/ai/prompts',
+ 'method' => 'post',
+ 'body' => json_encode($params),
+ 'response' => '{
+ "data": {
+ "id": 2,
+ "name": "Pre-translate prompt",
+ "action": "pre_translate",
+ "aiProviderId": 2,
+ "aiModelId": "gpt-5.4",
+ "isEnabled": true,
+ "enabledProjectIds": [1],
+ "config": {"mode": "basic"},
+ "promptPreview": null,
+ "isFineTuningAvailable": true,
+ "createdBy": 123,
+ "updatedBy": 456,
+ "lastUsedBy": 789,
+ "lastUsedAt": "2019-09-25T14:30:00+00:00",
+ "usageCount": 42,
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00"
+ }
+ }',
+ ]);
+
+ $prompt = $this->crowdin->ai->createPrompt($params);
+ $this->assertInstanceOf(AiPrompt::class, $prompt);
+ $this->assertEquals(2, $prompt->getId());
+ $this->assertEquals('Pre-translate prompt', $prompt->getName());
+ }
+
+ public function testGetPrompt(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/prompts/2',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "id": 2,
+ "name": "Pre-translate prompt",
+ "action": "pre_translate",
+ "aiProviderId": 2,
+ "aiModelId": "gpt-5.4",
+ "isEnabled": true,
+ "enabledProjectIds": [1],
+ "config": {
+ "mode": "basic",
+ "snippets": ["%custom:companyDescription%"],
+ "glossaryTerms": true,
+ "tmSuggestions": true,
+ "fileContext": true
+ },
+ "promptPreview": null,
+ "isFineTuningAvailable": true,
+ "createdBy": 123,
+ "updatedBy": 456,
+ "lastUsedBy": 789,
+ "lastUsedAt": "2019-09-25T14:30:00+00:00",
+ "usageCount": 42,
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00"
+ }
+ }',
+ ]);
+
+ $prompt = $this->crowdin->ai->getPrompt(2);
+ $this->assertInstanceOf(AiPrompt::class, $prompt);
+ $this->assertEquals(2, $prompt->getId());
+ $this->assertTrue($prompt->isFineTuningAvailable());
+ $this->assertEquals(42, $prompt->getUsageCount());
+ $this->assertEquals([1], $prompt->getEnabledProjectIds());
+ $this->assertNull($prompt->getPromptPreview());
+ $this->assertEquals(123, $prompt->getCreatedBy());
+ $this->assertEquals(456, $prompt->getUpdatedBy());
+ $this->assertEquals(789, $prompt->getLastUsedBy());
+ $this->assertEquals('2019-09-25T14:30:00+00:00', $prompt->getLastUsedAt());
+ $this->assertEquals('2019-09-20T11:11:05+00:00', $prompt->getCreatedAt());
+ $this->assertEquals('2019-09-20T12:22:20+00:00', $prompt->getUpdatedAt());
+ $this->assertIsArray($prompt->getConfig());
+ }
+
+ public function testDeletePrompt(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/prompts/2',
+ 'method' => 'delete',
+ 'response' => '',
+ ]);
+
+ $this->crowdin->ai->deletePrompt(2);
+ }
+
+ public function testClonePrompt(): void
+ {
+ $params = [
+ 'name' => 'Pre-translate prompt',
+ ];
+
+ $this->mockRequest([
+ 'path' => '/ai/prompts/2/clones',
+ 'method' => 'post',
+ 'body' => json_encode($params),
+ 'response' => '{
+ "data": {
+ "id": 2,
+ "name": "Pre-translate prompt",
+ "action": "pre_translate",
+ "aiProviderId": 2,
+ "aiModelId": "gpt-5.4",
+ "isEnabled": true,
+ "enabledProjectIds": [1],
+ "config": {},
+ "promptPreview": null,
+ "isFineTuningAvailable": true,
+ "createdBy": 123,
+ "updatedBy": 456,
+ "lastUsedBy": 789,
+ "lastUsedAt": "2019-09-25T14:30:00+00:00",
+ "usageCount": 42,
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00"
+ }
+ }',
+ ]);
+
+ $cloned = $this->crowdin->ai->clonePrompt(2, $params);
+ $this->assertInstanceOf(AiPrompt::class, $cloned);
+ $this->assertEquals(2, $cloned->getId());
+ $this->assertEquals('Pre-translate prompt', $cloned->getName());
+ }
+
+ public function testCreatePromptCompletion(): void
+ {
+ $params = [
+ 'resources' => [
+ 'projectId' => 123,
+ 'targetLanguageId' => 'uk',
+ 'stringIds' => [78253],
+ ],
+ ];
+
+ $this->mockRequest([
+ 'path' => '/ai/prompts/2/completions',
+ 'method' => 'post',
+ 'body' => json_encode($params),
+ 'response' => '{
+ "data": {
+ "identifier": "50fb3506-4127-4ba8-8296-f97dc7e3e0c3",
+ "status": "finished",
+ "progress": 100,
+ "attributes": {
+ "aiPromptId": 38
+ },
+ "createdAt": "2019-09-23T11:26:54+00:00",
+ "updatedAt": "2019-09-23T11:26:54+00:00",
+ "startedAt": "2019-09-23T11:26:54+00:00",
+ "finishedAt": "2019-09-23T11:26:54+00:00"
+ }
+ }',
+ ]);
+
+ $completion = $this->crowdin->ai->createPromptCompletion(2, $params);
+ $this->assertInstanceOf(AiPromptCompletion::class, $completion);
+ $this->assertEquals('50fb3506-4127-4ba8-8296-f97dc7e3e0c3', $completion->getIdentifier());
+ $this->assertEquals('finished', $completion->getStatus());
+ }
+
+ public function testGetPromptCompletion(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/prompts/2/completions/50fb3506-4127-4ba8-8296-f97dc7e3e0c3',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "identifier": "50fb3506-4127-4ba8-8296-f97dc7e3e0c3",
+ "status": "finished",
+ "progress": 100,
+ "attributes": {
+ "aiPromptId": 38
+ },
+ "createdAt": "2019-09-23T11:26:54+00:00",
+ "updatedAt": "2019-09-23T11:26:54+00:00",
+ "startedAt": "2019-09-23T11:26:54+00:00",
+ "finishedAt": "2019-09-23T11:26:54+00:00"
+ }
+ }',
+ ]);
+
+ $completion = $this->crowdin->ai->getPromptCompletion(2, '50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
+ $this->assertInstanceOf(AiPromptCompletion::class, $completion);
+ $this->assertEquals('finished', $completion->getStatus());
+ $this->assertEquals(100, $completion->getProgress());
+ $this->assertEquals('2019-09-23T11:26:54+00:00', $completion->getCreatedAt());
+ $this->assertEquals('2019-09-23T11:26:54+00:00', $completion->getUpdatedAt());
+ $this->assertEquals('2019-09-23T11:26:54+00:00', $completion->getStartedAt());
+ $this->assertEquals('2019-09-23T11:26:54+00:00', $completion->getFinishedAt());
+ }
+
+ public function testDeletePromptCompletion(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/prompts/2/completions/50fb3506-4127-4ba8-8296-f97dc7e3e0c3',
+ 'method' => 'delete',
+ 'response' => '',
+ ]);
+
+ $this->crowdin->ai->deletePromptCompletion(2, '50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
+ }
+
+ public function testDownloadPromptCompletion(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/prompts/2/completions/50fb3506-4127-4ba8-8296-f97dc7e3e0c3/download',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "url": "https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff",
+ "expireIn": "2019-09-20T10:31:21+00:00"
+ }
+ }',
+ ]);
+
+ $download = $this->crowdin->ai->downloadPromptCompletion(2, '50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
+ $this->assertInstanceOf(DownloadFile::class, $download);
+ $this->assertEquals('https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff', $download->getUrl());
+ $this->assertEquals('2019-09-20T10:31:21+00:00', $download->getExpireIn());
+ }
+
+ public function testListProviders(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/providers',
+ 'method' => 'get',
+ 'response' => '{
+ "data": [
+ {
+ "data": {
+ "id": 2,
+ "name": "OpenAI",
+ "type": "open_ai",
+ "credentials": {"apiKey": "sk-..."},
+ "config": {
+ "actionRules": [
+ {
+ "action": "pre_translate",
+ "availableAiModelIds": ["gpt-5.4"]
+ }
+ ]
+ },
+ "isEnabled": true,
+ "useSystemCredentials": false,
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00",
+ "promptsCount": 42
+ }
+ }
+ ],
+ "pagination": {"offset": 0, "limit": 25}
+ }',
+ ]);
+
+ $providers = $this->crowdin->ai->listProviders();
+ $this->assertInstanceOf(ModelCollection::class, $providers);
+ $this->assertInstanceOf(AiProvider::class, $providers[0]);
+ $this->assertEquals(2, $providers[0]->getId());
+ $this->assertEquals('OpenAI', $providers[0]->getName());
+ }
+
+ public function testCreateProvider(): void
+ {
+ $params = [
+ 'name' => 'OpenAI',
+ 'type' => 'open_ai',
+ 'credentials' => ['apiKey' => 'sk-...'],
+ 'isEnabled' => true,
+ 'useSystemCredentials' => false,
+ ];
+
+ $this->mockRequest([
+ 'path' => '/ai/providers',
+ 'method' => 'post',
+ 'body' => json_encode($params),
+ 'response' => '{
+ "data": {
+ "id": 2,
+ "name": "OpenAI",
+ "type": "open_ai",
+ "credentials": {"apiKey": "sk-..."},
+ "config": {},
+ "isEnabled": true,
+ "useSystemCredentials": false,
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00",
+ "promptsCount": 42
+ }
+ }',
+ ]);
+
+ $provider = $this->crowdin->ai->createProvider($params);
+ $this->assertInstanceOf(AiProvider::class, $provider);
+ $this->assertEquals(2, $provider->getId());
+ $this->assertEquals('OpenAI', $provider->getName());
+ }
+
+ public function testGetProvider(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/providers/2',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "id": 2,
+ "name": "OpenAI",
+ "type": "open_ai",
+ "credentials": {"apiKey": "string"},
+ "config": {
+ "actionRules": [
+ {
+ "action": "pre_translate",
+ "availableAiModelIds": ["gpt-5.4"]
+ }
+ ]
+ },
+ "isEnabled": true,
+ "useSystemCredentials": false,
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00",
+ "promptsCount": 42
+ }
+ }',
+ ]);
+
+ $provider = $this->crowdin->ai->getProvider(2);
+ $this->assertInstanceOf(AiProvider::class, $provider);
+ $this->assertEquals(2, $provider->getId());
+ $this->assertFalse($provider->isUseSystemCredentials());
+ $this->assertEquals(42, $provider->getPromptsCount());
+ $this->assertEquals(['apiKey' => 'string'], $provider->getCredentials());
+ $this->assertIsArray($provider->getConfig());
+ $this->assertTrue($provider->isEnabled());
+ $this->assertEquals('2019-09-20T11:11:05+00:00', $provider->getCreatedAt());
+ $this->assertEquals('2019-09-20T12:22:20+00:00', $provider->getUpdatedAt());
+ }
+
+ public function testDeleteProvider(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/providers/2',
+ 'method' => 'delete',
+ 'response' => '',
+ ]);
+
+ $this->crowdin->ai->deleteProvider(2);
+ }
+
+ public function testListProviderModels(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/providers/2/models',
+ 'method' => 'get',
+ 'response' => '{
+ "data": [
+ {
+ "data": {
+ "id": "gpt-5.4",
+ "provider": "open_ai",
+ "providerName": "OpenAI",
+ "providerId": 1
+ }
+ }
+ ],
+ "pagination": {"offset": 0, "limit": 25}
+ }',
+ ]);
+
+ $models = $this->crowdin->ai->listProviderModels(2);
+ $this->assertInstanceOf(ModelCollection::class, $models);
+ $this->assertInstanceOf(AiProviderModel::class, $models[0]);
+ $this->assertEquals('gpt-5.4', $models[0]->getId());
+ $this->assertEquals('open_ai', $models[0]->getProvider());
+ $this->assertEquals('OpenAI', $models[0]->getProviderName());
+ }
+
+ public function testCreateProviderChatCompletion(): void
+ {
+ $params = [
+ 'model' => 'gpt-4o',
+ 'messages' => [
+ ['role' => 'user', 'content' => 'Hello'],
+ ],
+ ];
+
+ $this->mockRequest([
+ 'path' => '/ai/providers/2/chat/completions',
+ 'method' => 'post',
+ 'body' => json_encode($params),
+ 'response' => '{
+ "data": {
+ "id": "chatcmpl-123",
+ "object": "chat.completion",
+ "model": "gpt-4o",
+ "choices": [{"message": {"role": "assistant", "content": "Hi!"}}]
+ }
+ }',
+ ]);
+
+ $completion = $this->crowdin->ai->createProviderChatCompletion(2, $params);
+ $this->assertInstanceOf(AiProxyChatCompletion::class, $completion);
+ }
+
+ public function testGenerateReport(): void
+ {
+ $params = [
+ 'type' => 'tokens-usage-raw-data',
+ 'schema' => [
+ 'dateFrom' => '2024-01-23T07:00:14+00:00',
+ 'dateTo' => '2024-09-27T07:00:14+00:00',
+ 'format' => 'json',
+ 'projectIds' => [22],
+ 'promptIds' => [18],
+ 'userIds' => [1],
+ ],
+ ];
+
+ $this->mockRequest([
+ 'path' => '/ai/reports',
+ 'method' => 'post',
+ 'body' => json_encode($params),
+ 'response' => '{
+ "data": {
+ "identifier": "50fb3506-4127-4ba8-8296-f97dc7e3e0c3",
+ "status": "finished",
+ "progress": 100,
+ "attributes": {
+ "format": "json",
+ "reportType": "tokens-usage-raw-data",
+ "schema": {}
+ },
+ "createdAt": "2024-01-23T11:26:54+00:00",
+ "updatedAt": "2024-09-23T11:26:54+00:00",
+ "startedAt": "2024-05-23T11:26:54+00:00",
+ "finishedAt": "2024-05-23T11:26:54+00:00"
+ }
+ }',
+ ]);
+
+ $report = $this->crowdin->ai->generateReport($params);
+ $this->assertInstanceOf(AiReport::class, $report);
+ $this->assertEquals('50fb3506-4127-4ba8-8296-f97dc7e3e0c3', $report->getIdentifier());
+ $this->assertEquals('finished', $report->getStatus());
+ }
+
+ public function testGetReport(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/reports/50fb3506-4127-4ba8-8296-f97dc7e3e0c3',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "identifier": "50fb3506-4127-4ba8-8296-f97dc7e3e0c3",
+ "status": "finished",
+ "progress": 100,
+ "attributes": {
+ "format": "json",
+ "reportType": "tokens-usage-raw-data",
+ "schema": {}
+ },
+ "createdAt": "2024-01-23T11:26:54+00:00",
+ "updatedAt": "2024-09-23T11:26:54+00:00",
+ "startedAt": "2024-05-23T11:26:54+00:00",
+ "finishedAt": "2024-05-23T11:26:54+00:00"
+ }
+ }',
+ ]);
+
+ $report = $this->crowdin->ai->getReport('50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
+ $this->assertInstanceOf(AiReport::class, $report);
+ $this->assertEquals('finished', $report->getStatus());
+ $this->assertEquals(100, $report->getProgress());
+ $this->assertIsArray($report->getAttributes());
+ $this->assertEquals('2024-01-23T11:26:54+00:00', $report->getCreatedAt());
+ $this->assertEquals('2024-09-23T11:26:54+00:00', $report->getUpdatedAt());
+ $this->assertEquals('2024-05-23T11:26:54+00:00', $report->getStartedAt());
+ $this->assertEquals('2024-05-23T11:26:54+00:00', $report->getFinishedAt());
+ }
+
+ public function testDownloadReport(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/reports/50fb3506-4127-4ba8-8296-f97dc7e3e0c3/download',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "url": "https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff",
+ "expireIn": "2019-09-20T10:31:21+00:00"
+ }
+ }',
+ ]);
+
+ $download = $this->crowdin->ai->downloadReport('50fb3506-4127-4ba8-8296-f97dc7e3e0c3');
+ $this->assertInstanceOf(DownloadFile::class, $download);
+ $this->assertEquals('https://production-enterprise-importer.downloads.crowdin.com/992000002/2/14.xliff', $download->getUrl());
+ $this->assertEquals('2019-09-20T10:31:21+00:00', $download->getExpireIn());
+ }
+
+ public function testGetSettings(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/settings',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "preTranslationAiPromptId": 2,
+ "editorSuggestionAiPromptId": 5,
+ "qaCheckActionAiPromptId": 8,
+ "contextReviewAiPromptId": 11
+ }
+ }',
+ ]);
+
+ $settings = $this->crowdin->ai->getSettings();
+ $this->assertInstanceOf(AiSettings::class, $settings);
+ $this->assertEquals(2, $settings->getPreTranslationAiPromptId());
+ $this->assertEquals(5, $settings->getEditorSuggestionAiPromptId());
+ $this->assertEquals(8, $settings->getQaCheckActionAiPromptId());
+ $this->assertEquals(11, $settings->getContextReviewAiPromptId());
+ }
+
+ public function testListSnippets(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/settings/snippets',
+ 'method' => 'get',
+ 'response' => '{
+ "data": [
+ {
+ "data": {
+ "id": 2,
+ "description": "Product description",
+ "placeholder": "%custom:productDescription%",
+ "value": "The product is the professional consulting service that transform challenges into opportunities.",
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00"
+ }
+ }
+ ],
+ "pagination": {"offset": 0, "limit": 25}
+ }',
+ ]);
+
+ $snippets = $this->crowdin->ai->listSnippets();
+ $this->assertInstanceOf(ModelCollection::class, $snippets);
+ $this->assertInstanceOf(AiSnippet::class, $snippets[0]);
+ $this->assertEquals(2, $snippets[0]->getId());
+ $this->assertEquals('%custom:productDescription%', $snippets[0]->getPlaceholder());
+ }
+
+ public function testCreateSnippet(): void
+ {
+ $params = [
+ 'description' => 'Product description',
+ 'placeholder' => '%custom:productDescription%',
+ 'value' => 'The product is the professional consulting service that transform challenges into opportunities.',
+ ];
+
+ $this->mockRequest([
+ 'path' => '/ai/settings/snippets',
+ 'method' => 'post',
+ 'body' => json_encode($params),
+ 'response' => '{
+ "data": {
+ "id": 2,
+ "description": "Product description",
+ "placeholder": "%custom:productDescription%",
+ "value": "The product is the professional consulting service that transform challenges into opportunities.",
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00"
+ }
+ }',
+ ]);
+
+ $snippet = $this->crowdin->ai->createSnippet($params);
+ $this->assertInstanceOf(AiSnippet::class, $snippet);
+ $this->assertEquals(2, $snippet->getId());
+ $this->assertEquals('Product description', $snippet->getDescription());
+ }
+
+ public function testGetSnippet(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/settings/snippets/2',
+ 'method' => 'get',
+ 'response' => '{
+ "data": {
+ "id": 2,
+ "description": "Product description",
+ "placeholder": "%custom:productDescription%",
+ "value": "The product is the professional consulting service that transform challenges into opportunities.",
+ "createdAt": "2019-09-20T11:11:05+00:00",
+ "updatedAt": "2019-09-20T12:22:20+00:00"
+ }
+ }',
+ ]);
+
+ $snippet = $this->crowdin->ai->getSnippet(2);
+ $this->assertInstanceOf(AiSnippet::class, $snippet);
+ $this->assertEquals(
+ 'The product is the professional consulting service that transform challenges into opportunities.',
+ $snippet->getValue()
+ );
+ $this->assertEquals('2019-09-20T11:11:05+00:00', $snippet->getCreatedAt());
+ $this->assertEquals('2019-09-20T12:22:20+00:00', $snippet->getUpdatedAt());
+ }
+
+ public function testDeleteSnippet(): void
+ {
+ $this->mockRequest([
+ 'path' => '/ai/settings/snippets/2',
+ 'method' => 'delete',
+ 'response' => '',
+ ]);
+
+ $this->crowdin->ai->deleteSnippet(2);
+ }
+
+ public function testUpdatePrompt(): void
+ {
+ $prompt = new AiPrompt([
+ 'id' => 2,
+ 'name' => 'Pre-translate prompt',
+ 'action' => 'pre_translate',
+ 'aiProviderId' => 2,
+ 'aiModelId' => 'gpt-5.4',
+ 'isEnabled' => true,
+ 'enabledProjectIds' => [1],
+ 'config' => [],
+ 'promptPreview' => null,
+ 'isFineTuningAvailable' => true,
+ 'createdBy' => 123,
+ 'updatedBy' => 456,
+ 'lastUsedBy' => 789,
+ 'lastUsedAt' => '2019-09-25T14:30:00+00:00',
+ 'usageCount' => 42,
+ 'createdAt' => '2019-09-20T11:11:05+00:00',
+ 'updatedAt' => '2019-09-20T12:22:20+00:00',
+ ]);
+ $prompt->setName('Updated prompt');
+ $prompt->setAction('translate');
+ $prompt->setAiProviderId(3);
+ $prompt->setAiModelId('gpt-4o');
+ $prompt->setEnabledProjectIds([2, 3]);
+ $prompt->setConfig(['mode' => 'advanced']);
+
+ $this->mockRequestPatch(
+ '/ai/prompts/2',
+ json_encode([
+ 'data' => [
+ 'id' => 2,
+ 'name' => 'Updated prompt',
+ 'action' => 'translate',
+ 'aiProviderId' => 3,
+ 'aiModelId' => 'gpt-4o',
+ 'isEnabled' => true,
+ 'enabledProjectIds' => [2, 3],
+ 'config' => ['mode' => 'advanced'],
+ 'promptPreview' => null,
+ 'isFineTuningAvailable' => true,
+ 'createdBy' => 123,
+ 'updatedBy' => 456,
+ 'lastUsedBy' => 789,
+ 'lastUsedAt' => '2019-09-25T14:30:00+00:00',
+ 'usageCount' => 42,
+ 'createdAt' => '2019-09-20T11:11:05+00:00',
+ 'updatedAt' => '2019-09-20T12:22:20+00:00',
+ ],
+ ])
+ );
+
+ $updated = $this->crowdin->ai->updatePrompt($prompt);
+ $this->assertInstanceOf(AiPrompt::class, $updated);
+ $this->assertEquals(2, $updated->getId());
+ $this->assertEquals('Updated prompt', $updated->getName());
+ $this->assertEquals('gpt-4o', $updated->getAiModelId());
+ }
+
+ public function testUpdateProvider(): void
+ {
+ $provider = new AiProvider([
+ 'id' => 2,
+ 'name' => 'OpenAI',
+ 'type' => 'open_ai',
+ 'credentials' => ['apiKey' => 'sk-...'],
+ 'config' => [],
+ 'isEnabled' => true,
+ 'useSystemCredentials' => false,
+ 'createdAt' => '2019-09-20T11:11:05+00:00',
+ 'updatedAt' => '2019-09-20T12:22:20+00:00',
+ 'promptsCount' => 42,
+ ]);
+ $provider->setName('Azure OpenAI');
+ $provider->setType('azure_open_ai');
+ $provider->setCredentials(['apiKey' => 'new-key']);
+ $provider->setConfig(['actionRules' => []]);
+ $provider->setIsEnabled(false);
+ $provider->setUseSystemCredentials(true);
+
+ $this->mockRequestPatch(
+ '/ai/providers/2',
+ json_encode([
+ 'data' => [
+ 'id' => 2,
+ 'name' => 'Azure OpenAI',
+ 'type' => 'azure_open_ai',
+ 'credentials' => ['apiKey' => 'new-key'],
+ 'config' => ['actionRules' => []],
+ 'isEnabled' => false,
+ 'useSystemCredentials' => true,
+ 'createdAt' => '2019-09-20T11:11:05+00:00',
+ 'updatedAt' => '2019-09-20T12:22:20+00:00',
+ 'promptsCount' => 42,
+ ],
+ ])
+ );
+
+ $updated = $this->crowdin->ai->updateProvider($provider);
+ $this->assertInstanceOf(AiProvider::class, $updated);
+ $this->assertEquals(2, $updated->getId());
+ $this->assertEquals('Azure OpenAI', $updated->getName());
+ }
+
+ public function testUpdateSettings(): void
+ {
+ $settings = new AiSettings([
+ 'preTranslationAiPromptId' => 2,
+ 'editorSuggestionAiPromptId' => 5,
+ 'qaCheckActionAiPromptId' => 8,
+ 'contextReviewAiPromptId' => 11,
+ ]);
+ $settings->setPreTranslationAiPromptId(3);
+ $settings->setEditorSuggestionAiPromptId(6);
+ $settings->setQaCheckActionAiPromptId(9);
+ $settings->setContextReviewAiPromptId(12);
+
+ $this->mockRequestPatch(
+ '/ai/settings',
+ json_encode([
+ 'data' => [
+ 'preTranslationAiPromptId' => 3,
+ 'editorSuggestionAiPromptId' => 6,
+ 'qaCheckActionAiPromptId' => 9,
+ 'contextReviewAiPromptId' => 12,
+ ],
+ ])
+ );
+
+ $updated = $this->crowdin->ai->updateSettings($settings);
+ $this->assertInstanceOf(AiSettings::class, $updated);
+ $this->assertEquals(3, $updated->getPreTranslationAiPromptId());
+ $this->assertEquals(6, $updated->getEditorSuggestionAiPromptId());
+ $this->assertEquals(9, $updated->getQaCheckActionAiPromptId());
+ $this->assertEquals(12, $updated->getContextReviewAiPromptId());
+ }
+
+ public function testUpdateSnippet(): void
+ {
+ $snippet = new AiSnippet([
+ 'id' => 2,
+ 'description' => 'Product description',
+ 'placeholder' => '%custom:productDescription%',
+ 'value' => 'The product is the professional consulting service that transform challenges into opportunities.',
+ 'createdAt' => '2019-09-20T11:11:05+00:00',
+ 'updatedAt' => '2019-09-20T12:22:20+00:00',
+ ]);
+ $snippet->setDescription('Updated description');
+ $snippet->setPlaceholder('%custom:updatedDescription%');
+ $snippet->setValue('Updated value.');
+
+ $this->mockRequestPatch(
+ '/ai/settings/snippets/2',
+ json_encode([
+ 'data' => [
+ 'id' => 2,
+ 'description' => 'Updated description',
+ 'placeholder' => '%custom:updatedDescription%',
+ 'value' => 'Updated value.',
+ 'createdAt' => '2019-09-20T11:11:05+00:00',
+ 'updatedAt' => '2019-09-20T12:22:20+00:00',
+ ],
+ ])
+ );
+
+ $updated = $this->crowdin->ai->updateSnippet($snippet);
+ $this->assertInstanceOf(AiSnippet::class, $updated);
+ $this->assertEquals('Updated description', $updated->getDescription());
+ $this->assertEquals('%custom:updatedDescription%', $updated->getPlaceholder());
+ $this->assertEquals('Updated value.', $updated->getValue());
}
}