Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@crowdin/crowdin-api-client",
"version": "1.57.0",
"version": "1.58.0",
"description": "JavaScript library for Crowdin API",
"main": "out/index.js",
"types": "out/index.d.ts",
Expand Down
73 changes: 73 additions & 0 deletions src/ai/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,29 @@ export class Ai extends CrowdinApi {
return this.getList(url, options?.limit, options?.offset);
}

/**
* @param options optional parameters for the request
* @see https://support.crowdin.com/developer/enterprise/api/v2/#tag/AI/operation/api.ai.usage.members.getMany
*/
listAiOrganizationUsageMembers(
options?: AiModel.ListAiUsageMembersOptions,
): Promise<ResponseList<AiModel.AiUsageMember>> {
let url = `${this.url}/ai/usage/members`;
url = this.addQueryParam(url, 'userIds', options?.userIds?.join(','));
url = this.addQueryParam(url, 'orderBy', options?.orderBy);
return this.getList(url, options?.limit, options?.offset);
}

/**
* @param memberId user identifier
* @see https://support.crowdin.com/developer/enterprise/api/v2/#tag/AI/operation/api.ai.usage.members.get
*/
getAiOrganizationUsageMember(memberId: number): Promise<ResponseObject<AiModel.AiUsageMember>> {
const url = `${this.url}/ai/usage/members/${memberId}`;

return this.get(url, this.defaultConfig());
}

/**
* @param aiProviderId ai Provider identifier
* @param path raw provider API path after `/gateway/`
Expand Down Expand Up @@ -1235,6 +1258,32 @@ export class Ai extends CrowdinApi {
return this.getList(url, options?.limit, options?.offset);
}

/**
* @param userId user identifier
* @param options optional parameters for the request
* @see https://support.crowdin.com/developer/api/v2/#tag/AI/operation/api.ai.usage.members.getMany
*/
listAiUserUsageMembers(
userId: number,
options?: AiModel.ListAiUsageMembersOptions,
): Promise<ResponseList<AiModel.AiUsageMember>> {
let url = `${this.url}/users/${userId}/ai/usage/members`;
url = this.addQueryParam(url, 'userIds', options?.userIds?.join(','));
url = this.addQueryParam(url, 'orderBy', options?.orderBy);
return this.getList(url, options?.limit, options?.offset);
}

/**
* @param userId user identifier
* @param memberId user identifier
* @see https://support.crowdin.com/developer/api/v2/#tag/AI/operation/api.ai.usage.members.get
*/
getAiUserUsageMember(userId: number, memberId: number): Promise<ResponseObject<AiModel.AiUsageMember>> {
const url = `${this.url}/users/${userId}/ai/usage/members/${memberId}`;

return this.get(url, this.defaultConfig());
}

/**
* @param userId user identifier
* @param aiProviderId ai Provider identifier
Expand Down Expand Up @@ -1847,6 +1896,30 @@ export namespace AiModel {
}
/* ai Request Logs Section END */

/* ai Usage Members Section START */
export interface AiUsageMember {
user: {
id: number;
username: string;
fullName: string;
avatarUrl: string;
};
/** in USD; `null` means no limit, `0` means AI usage is blocked for the user */
dailyCostLimit: number | null;
dailyCostSpent: number;
dailyResetAt: string;
/** in USD; `null` means no limit, `0` means AI usage is blocked for the user */
monthlyCostLimit: number | null;
monthlyCostSpent: number;
monthlyResetAt: string;
}

export interface ListAiUsageMembersOptions extends PaginationOptions {
userIds?: number[];
orderBy?: string;
}
/* ai Usage Members Section END */

export type Action = 'pre_translate' | 'alignment' | 'qa_check';
export type ProviderType =
| 'open_ai'
Expand Down
59 changes: 59 additions & 0 deletions tests/ai/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ describe('AI API', () => {
const projectId = 123;
const jobId = 'test-job';
const eventId = '12312event';
const memberId = 5;

const name = 'name';
const action = 'pre_translate';
Expand Down Expand Up @@ -1282,6 +1283,40 @@ describe('AI API', () => {
.reply(200, {
data: [{ data: { id: 1 } }],
pagination: { offset: 0, limit },
})
.get('/ai/usage/members', undefined, {
reqheaders: {
Authorization: `Bearer ${api.token}`,
},
})
.reply(200, {
data: [{ data: { user: { id: memberId } } }],
pagination: { offset: 0, limit },
})
.get(`/ai/usage/members/${memberId}`, undefined, {
reqheaders: {
Authorization: `Bearer ${api.token}`,
},
})
.reply(200, {
data: { user: { id: memberId } },
})
.get(`/users/${userId}/ai/usage/members`, undefined, {
reqheaders: {
Authorization: `Bearer ${api.token}`,
},
})
.reply(200, {
data: [{ data: { user: { id: memberId } } }],
pagination: { offset: 0, limit },
})
.get(`/users/${userId}/ai/usage/members/${memberId}`, undefined, {
reqheaders: {
Authorization: `Bearer ${api.token}`,
},
})
.reply(200, {
data: { user: { id: memberId } },
});
});

Expand Down Expand Up @@ -1888,4 +1923,28 @@ describe('AI API', () => {
expect(res.data.length).toBe(1);
expect(res.pagination.limit).toBe(limit);
});

it('List AI Organization Usage Members', async () => {
const res = await api.listAiOrganizationUsageMembers();
expect(res.data.length).toBe(1);
expect(res.data[0].data.user.id).toBe(memberId);
expect(res.pagination.limit).toBe(limit);
});

it('Get AI Organization Usage Member', async () => {
const res = await api.getAiOrganizationUsageMember(memberId);
expect(res.data.user.id).toBe(memberId);
});

it('List AI User Usage Members', async () => {
const res = await api.listAiUserUsageMembers(userId);
expect(res.data.length).toBe(1);
expect(res.data[0].data.user.id).toBe(memberId);
expect(res.pagination.limit).toBe(limit);
});

it('Get AI User Usage Member', async () => {
const res = await api.getAiUserUsageMember(userId, memberId);
expect(res.data.user.id).toBe(memberId);
});
});