diff --git a/src/main/java/com/crowdin/client/ai/AIApi.java b/src/main/java/com/crowdin/client/ai/AIApi.java index 6c111d781..ac37ff3dc 100644 --- a/src/main/java/com/crowdin/client/ai/AIApi.java +++ b/src/main/java/com/crowdin/client/ai/AIApi.java @@ -561,6 +561,32 @@ public ResponseObject> createProxyChatCompletion(final Long return ResponseObject.of(response.getData()); } + /** + * @param userId user identifier + * @param limit maximum numbers of items to retrieve (default 25) + * @param offset starting offset in the collection (default 0) + * @param providerType provider (e.g. open_ai) + * @param enabled filter by enabled providers + * @param orderBy + * @return List of supported AI models for a specific AI provider + * @see + */ + public ResponseList listSupportedAiProviderModels(Long userId, Integer limit, Integer offset, String providerType, Boolean enabled, String orderBy) throws HttpException, HttpBadRequestException { + Map> queryParams = HttpRequestConfig.buildUrlParams( + "limit", Optional.ofNullable(limit), + "offset", Optional.ofNullable(offset), + "providerType", Optional.ofNullable(providerType), + "enabled", Optional.ofNullable(enabled), + "orderBy", Optional.ofNullable(orderBy) + ); + String url = getAIPath(userId, "ai/providers/supported-models"); + AiSupportedModelResponseList responseList = this.httpClient.get(url, new HttpRequestConfig(queryParams), AiSupportedModelResponseList.class); + return AiSupportedModelResponseList.to(responseList); + } + private String getAIPath(Long userId, String path) { return userId != null ? String.format("%s/users/%d/%s", this.url, userId, path) : String.format("%s/%s", this.url, path); } diff --git a/src/main/java/com/crowdin/client/ai/model/AiSupportedModel.java b/src/main/java/com/crowdin/client/ai/model/AiSupportedModel.java new file mode 100644 index 000000000..979609eff --- /dev/null +++ b/src/main/java/com/crowdin/client/ai/model/AiSupportedModel.java @@ -0,0 +1,50 @@ +package com.crowdin.client.ai.model; + +import lombok.Data; + +import java.util.Date; + +@Data +public class AiSupportedModel { + private Long providerId; + private String providerType; + private String providerName; + private String id; + private String displayName; + private Boolean supportReasoning; + private Integer intelligence; + private Integer speed; + private Price price; + private Modality modalities; + private Integer contextWindow; + private Integer maxOutputTokens; + private Date knowledgeCutoff; + private Date releaseDate; + private Feature features; + + @Data + public static class Price { + private Double input; + private Double output; + } + + @Data + public static class Modality { + private ModalityConfig input; + private ModalityConfig output; + + @Data + public static class ModalityConfig { + private Boolean text; + private Boolean image; + private Boolean audio; + } + } + + @Data + public static class Feature { + private Boolean streaming; + private Boolean structuredOutput; + private Boolean functionCalling; + } +} diff --git a/src/main/java/com/crowdin/client/ai/model/AiSupportedModelResponseList.java b/src/main/java/com/crowdin/client/ai/model/AiSupportedModelResponseList.java new file mode 100644 index 000000000..0eec732f1 --- /dev/null +++ b/src/main/java/com/crowdin/client/ai/model/AiSupportedModelResponseList.java @@ -0,0 +1,25 @@ +package com.crowdin.client.ai.model; + +import com.crowdin.client.core.model.Pagination; +import com.crowdin.client.core.model.ResponseList; +import com.crowdin.client.core.model.ResponseObject; +import lombok.Data; + +import java.util.List; +import java.util.stream.Collectors; + +@Data +public class AiSupportedModelResponseList { + private List data; + private Pagination pagination; + + public static ResponseList to(AiSupportedModelResponseList responseList) { + return ResponseList.of( + responseList.data.stream() + .map(AiSupportedModelResponseObject::getData) + .map(ResponseObject::of) + .collect(Collectors.toList()), + responseList.getPagination() + ); + } +} diff --git a/src/main/java/com/crowdin/client/ai/model/AiSupportedModelResponseObject.java b/src/main/java/com/crowdin/client/ai/model/AiSupportedModelResponseObject.java new file mode 100644 index 000000000..eb5c80bc3 --- /dev/null +++ b/src/main/java/com/crowdin/client/ai/model/AiSupportedModelResponseObject.java @@ -0,0 +1,8 @@ +package com.crowdin.client.ai.model; + +import lombok.Data; + +@Data +public class AiSupportedModelResponseObject { + private AiSupportedModel data; +} diff --git a/src/test/java/com/crowdin/client/ai/AIApiTest.java b/src/test/java/com/crowdin/client/ai/AIApiTest.java index 2f3c9d6f9..44a9e0265 100644 --- a/src/test/java/com/crowdin/client/ai/AIApiTest.java +++ b/src/test/java/com/crowdin/client/ai/AIApiTest.java @@ -25,6 +25,7 @@ public class AIApiTest extends TestClient { private static final long userId = 2L; private static final long customPlaceholderId = 2L; private static final long aiPromptId = 3L; + private static final long providerId = 0L; private static final long progress = 100L; private static final int year = 119; private static final int month = Calendar.SEPTEMBER; @@ -64,6 +65,7 @@ public class AIApiTest extends TestClient { private static final String AI_PROMPT_COMPLETION = "%s/users/%d/ai/prompts/%d/completions/%s"; private static final String AI_PROMPT_COMPLETION_DOWNLOAD = "%s/users/%d/ai/prompts/%d/completions/%s/download"; private static final String PROXY_CHAT = "%s/users/%d/ai/providers/%d/chat/completions"; + private static final String LIST_SUPPORTED_AI_PROVIDER_MODELS = "%s/users/%d/ai/providers/supported-models"; @Override public List getMocks() { @@ -103,7 +105,8 @@ public List getMocks() { RequestMock.build(String.format(AI_PROMPT, this.url, userId, aiPromptId), HttpGet.METHOD_NAME, "api/ai/promptResponse.json"), RequestMock.build(String.format(AI_PROMPT, this.url, userId, aiPromptId), HttpDelete.METHOD_NAME), RequestMock.build(String.format(AI_PROMPT, this.url, userId, aiPromptId), HttpPatch.METHOD_NAME, "api/ai/editPromptRequest.json", "api/ai/promptResponse.json"), - RequestMock.build(String.format(PROXY_CHAT, this.url, userId, aiPromptId), HttpPost.METHOD_NAME, "api/ai/proxyChatCompletionRequest.json", "api/ai/proxyChatCompletionResponse.json") + RequestMock.build(String.format(PROXY_CHAT, this.url, userId, aiPromptId), HttpPost.METHOD_NAME, "api/ai/proxyChatCompletionRequest.json", "api/ai/proxyChatCompletionResponse.json"), + RequestMock.build(String.format(LIST_SUPPORTED_AI_PROVIDER_MODELS, this.url, userId), HttpGet.METHOD_NAME, "api/ai/listSupportedAiProviderModels.json") ); } @@ -515,4 +518,17 @@ public void createProxyChatCompletionTest() { assertEquals(proxyChatCompletion.getData().size(), 0); } + + @Test + public void listSupportedAiProviderModelsTest() { + ResponseList response = this.getAiApi().listSupportedAiProviderModels(userId, null, null, null, null, null); + + assertEquals(1, response.getData().size()); + assertEquals(providerId, response.getData().get(0).getData().getProviderId()); + assertTrue(response.getData().get(0).getData().getFeatures().getStreaming()); + assertEquals(new Date(year, Calendar.AUGUST, 24, 14, 15, 22), response.getData().get(0).getData().getKnowledgeCutoff()); + assertTrue(response.getData().get(0).getData().getModalities().getInput().getText()); + assertTrue(response.getData().get(0).getData().getModalities().getOutput().getImage()); + assertEquals(0.1, response.getData().get(0).getData().getPrice().getInput()); + } } diff --git a/src/test/resources/api/ai/listSupportedAiProviderModels.json b/src/test/resources/api/ai/listSupportedAiProviderModels.json new file mode 100644 index 000000000..bf6f0deb9 --- /dev/null +++ b/src/test/resources/api/ai/listSupportedAiProviderModels.json @@ -0,0 +1,41 @@ +{ + "data": [ + { + "data": { + "providerId": 0, + "providerType": "string", + "providerName": "string", + "id": "string", + "displayName": "string", + "supportReasoning": true, + "intelligence": 0, + "speed": 0, + "price": { + "input": 0.1, + "output": 0.1 + }, + "modalities": { + "input": { + "text": true, + "image": true, + "audio": true + }, + "output": { + "text": true, + "image": true, + "audio": true + } + }, + "contextWindow": 0, + "maxOutputTokens": 0, + "knowledgeCutoff": "2019-08-24T14:15:22Z", + "releaseDate": "2019-08-24T14:15:22Z", + "features": { + "streaming": true, + "structuredOutput": true, + "functionCalling": true + } + } + } + ] +} \ No newline at end of file