diff --git a/crowdin_api/api_resources/ai/resource.py b/crowdin_api/api_resources/ai/resource.py index fb9d9b6..8c6c672 100644 --- a/crowdin_api/api_resources/ai/resource.py +++ b/crowdin_api/api_resources/ai/resource.py @@ -1,25 +1,33 @@ from typing import Iterable, Optional, Union from crowdin_api.api_resources.abstract.resources import BaseResource -from crowdin_api.api_resources.ai.enums import AIPromptAction, AiPromptFineTuningJobStatus, AIProviderType +from crowdin_api.api_resources.ai.enums import ( + AIPromptAction, + AiPromptFineTuningJobStatus, + AIProviderType, +) from crowdin_api.api_resources.ai.types import ( + AddAiCustomPlaceholderRequest, AddAIPromptRequestScheme, AddAIProviderReqeustScheme, - EditAIPromptScheme, - EditAIProviderRequestScheme, - GoogleGeminiChatProxy, - OtherChatProxy, - GenerateAIPromptFineTuningDatasetRequest, + AiFileTranslationRequest, + AiTranslateStringsRequest, CreateAIPromptFineTuningJobRequest, - AddAiCustomPlaceholderRequest, EditAiCustomPlaceholderPatch, + EditAIPromptScheme, + EditAIProviderRequestScheme, + EditAiSettingsPatch, GenerateAiPromptCompletionRequest, + GenerateAIPromptFineTuningDatasetRequest, GenerateAiReportRequest, - EditAiSettingsPatch, - AiFileTranslationRequest, + GoogleGeminiChatProxy, + OtherChatProxy, ) from crowdin_api.sorting import Sorting -from crowdin_api.utils import convert_enum_collection_to_string_if_exists, convert_enum_to_string_if_exists +from crowdin_api.utils import ( + convert_enum_collection_to_string_if_exists, + convert_enum_to_string_if_exists, +) class AIResource(BaseResource): @@ -778,6 +786,24 @@ def download_ai_file_translation_strings( path=self.get_ai_file_translations_path(user_id, job_identifier) + "/translations", ) + def translate_ai_strings( + self, + user_id: int, + request_data: AiTranslateStringsRequest, + ): + """ + AI Translate Strings + + Link to documentation: + https://support.crowdin.com/developer/api/v2/#tag/AI/operation/api.users.ai.translate.strings.post + """ + + return self.requester.request( + method="post", + path=f"users/{user_id}/ai/translate", + request_data=request_data, + ) + class EnterpriseAIResource(BaseResource): """ @@ -1497,3 +1523,20 @@ def download_ai_file_translation_strings( method="get", path=self.get_ai_file_translations_path(job_identifier) + "/translations", ) + + def translate_ai_strings( + self, + request_data: AiTranslateStringsRequest, + ): + """ + AI Translate Strings + + Link to documentation: + https://support.crowdin.com/developer/enterprise/api/v2/#tag/AI/operation/api.ai.translate-strings.post + """ + + return self.requester.request( + method="post", + path="ai/translate", + request_data=request_data, + ) diff --git a/crowdin_api/api_resources/ai/tests/test_ai_resources.py b/crowdin_api/api_resources/ai/tests/test_ai_resources.py index f5572ad..3662c91 100644 --- a/crowdin_api/api_resources/ai/tests/test_ai_resources.py +++ b/crowdin_api/api_resources/ai/tests/test_ai_resources.py @@ -2,38 +2,39 @@ from unittest import mock import pytest - from crowdin_api.api_resources.ai.enums import ( AIPromptAction, + AiPromptFineTuningJobStatus, AIProviderType, + AiReportFormat, + AiToolType, DatasetPurpose, - AiPromptFineTuningJobStatus, - ListAiPromptFineTuningJobsOrderBy, EditAiCustomPlaceholderPatchPath, - AiToolType, - AiReportFormat, - EditAiSettingsPatchPath, ListSupportedAiModelsOrderBy + EditAiSettingsPatchPath, + ListAiPromptFineTuningJobsOrderBy, + ListSupportedAiModelsOrderBy, ) from crowdin_api.api_resources.ai.resource import AIResource, EnterpriseAIResource from crowdin_api.api_resources.ai.types import ( + AiFileTranslationRequest, AIPromptOperation, - EditAIPromptPath, - CreateAIPromptFineTuningJobRequest, - HyperParameters, - TrainingOptions, - GenerateAIPromptFineTuningDatasetRequest, - GenerateAiPromptCompletionRequest, - PreTranslateActionAiPromptContextResources, AiTool, - AiToolObject, AiToolFunction, - GenerateAiReportRequest, + AiToolObject, + AiTranslateStringsRequest, + CreateAIPromptFineTuningJobRequest, + EditAIPromptPath, GeneralReportSchema, - AiFileTranslationRequest, + GenerateAiPromptCompletionRequest, + GenerateAIPromptFineTuningDatasetRequest, + GenerateAiReportRequest, + HyperParameters, + PreTranslateActionAiPromptContextResources, + TrainingOptions, ) from crowdin_api.api_resources.enums import PatchOperation from crowdin_api.requester import APIRequester -from crowdin_api.sorting import Sorting, SortingRule, SortingOrder +from crowdin_api.sorting import Sorting, SortingOrder, SortingRule class TestAIResources: @@ -1205,6 +1206,64 @@ def test_download_ai_file_translation_strings(self, m_request, base_absolut_url) path=f"users/{user_id}/ai/file-translations/{job_identifier}/translations", ) + @pytest.mark.parametrize( + "incoming_data, request_data", + ( + ( + AiTranslateStringsRequest( + strings=["Some text to translate!"], + targetLanguageId="uk", + ), + { + "strings": ["Some text to translate!"], + "targetLanguageId": "uk", + }, + ), + ( + AiTranslateStringsRequest( + strings=["Some text to translate!"], + targetLanguageId="uk", + sourceLanguageId="en", + tmIds=[123], + glossaryIds=[456], + styleGuideIds=[654], + aiPromptId=789, + aiProviderId=12, + aiModelId="gpt-4.1", + instructions=["Keep a formal tone"], + attachmentIds=[123], + ), + { + "strings": ["Some text to translate!"], + "targetLanguageId": "uk", + "sourceLanguageId": "en", + "tmIds": [123], + "glossaryIds": [456], + "styleGuideIds": [654], + "aiPromptId": 789, + "aiProviderId": 12, + "aiModelId": "gpt-4.1", + "instructions": ["Keep a formal tone"], + "attachmentIds": [123], + }, + ), + ), + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_translate_ai_strings(self, m_request, incoming_data, request_data, base_absolut_url): + m_request.return_value = "response" + + user_id = 1 + + resource = self.get_resource(base_absolut_url) + assert resource.translate_ai_strings(user_id, incoming_data) == "response" + + m_request.assert_called_once_with( + method="post", + path=f"users/{user_id}/ai/translate", + request_data=request_data, + ) + class TestEnterpriseAIResources: resource_class = EnterpriseAIResource @@ -2301,3 +2360,59 @@ def test_download_ai_file_translation_strings(self, m_request, base_absolut_url) method="get", path=f"ai/file-translations/{job_identifier}/translations", ) + + @pytest.mark.parametrize( + "incoming_data, request_data", + ( + ( + AiTranslateStringsRequest( + strings=["Some text to translate!"], + targetLanguageId="uk", + ), + { + "strings": ["Some text to translate!"], + "targetLanguageId": "uk", + }, + ), + ( + AiTranslateStringsRequest( + strings=["Some text to translate!"], + targetLanguageId="uk", + sourceLanguageId="en", + tmIds=[123], + glossaryIds=[456], + styleGuideIds=[654], + aiPromptId=789, + aiProviderId=12, + aiModelId="gpt-4.1", + instructions=["Keep a formal tone"], + attachmentIds=[123], + ), + { + "strings": ["Some text to translate!"], + "targetLanguageId": "uk", + "sourceLanguageId": "en", + "tmIds": [123], + "glossaryIds": [456], + "styleGuideIds": [654], + "aiPromptId": 789, + "aiProviderId": 12, + "aiModelId": "gpt-4.1", + "instructions": ["Keep a formal tone"], + "attachmentIds": [123], + }, + ), + ), + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_translate_ai_strings(self, m_request, incoming_data, request_data, base_absolut_url): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.translate_ai_strings(incoming_data) == "response" + + m_request.assert_called_once_with( + method="post", + path="ai/translate", + request_data=request_data, + ) diff --git a/crowdin_api/api_resources/ai/types.py b/crowdin_api/api_resources/ai/types.py index 11eda9e..513c04e 100644 --- a/crowdin_api/api_resources/ai/types.py +++ b/crowdin_api/api_resources/ai/types.py @@ -4,10 +4,10 @@ AIPromptAction, AIPromptOperation, AIProviderType, + AiToolType, + EditAiCustomPlaceholderPatchPath, EditAIPromptPath, EditAIProviderPath, - EditAiCustomPlaceholderPatchPath, - AiToolType, EditAiSettingsPatchPath, ) from crowdin_api.api_resources.enums import PatchOperation @@ -302,3 +302,17 @@ class AiFileTranslationRequest(TypedDict): aiModelId: Optional[str] instructions: Optional[Iterable[str]] attachmentIds: Optional[Iterable[int]] + + +class AiTranslateStringsRequest(TypedDict): + strings: Iterable[str] + targetLanguageId: str + sourceLanguageId: Optional[str] + tmIds: Optional[Iterable[int]] + glossaryIds: Optional[Iterable[int]] + styleGuideIds: Optional[Iterable[int]] + aiPromptId: Optional[int] + aiProviderId: Optional[int] + aiModelId: Optional[str] + instructions: Optional[Iterable[str]] + attachmentIds: Optional[Iterable[int]]