Skip to content

Commit 1b17706

Browse files
committed
feat: added yandexgpt support
1 parent 4295e13 commit 1b17706

File tree

8 files changed

+166
-3
lines changed

8 files changed

+166
-3
lines changed

README-RU.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,11 @@ const langs = await client.getLangs();
5050
|| YandexBrowser | Translate<br>Detect<br>GetLangs | 10k chars/req<br>10k chars/req |
5151
|| YandexCloud | Translate<br>Detect<br>GetLangs | 2k chars/req<br>1k chars/req |
5252
|| YandexTranslate | Translate<br>Detect<br>GetLangs | 10k chars/req<br>10k chars/req |
53+
|| YandexGPT\*¹ | Translate<br>Detect<br>GetLangs | 10k chars/req<br>10k chars/req |
5354
|| MSEdge | Translate<br>Detect<br>GetLangs | 50k chars/req<br>50k chars/req |
5455

56+
\*¹ - перевод с помощью YandexGPT работает только для пары en-ru. Для всех остальных случаев используется перевод аналогичный YandexTranslate
57+
5558
## Сборка
5659

5760
Для сборки необходимо наличие:

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,11 @@ You can see more code examples [here](https://github.com/FOSWLY/translate/tree/m
5050
|| YandexBrowser | Translate<br>Detect<br>GetLangs | 10k chars/req<br>10k chars/req |
5151
|| YandexCloud | Translate<br>Detect<br>GetLangs | 2k chars/req<br>1k chars/req |
5252
|| YandexTranslate | Translate<br>Detect<br>GetLangs | 10k chars/req<br>10k chars/req |
53+
|| YandexGPT\*¹ | Translate<br>Detect<br>GetLangs | 10k chars/req<br>10k chars/req |
5354
|| MSEdge | Translate<br>Detect<br>GetLangs | 50k chars/req<br>50k chars/req |
5455

56+
\*¹ - translation using YandexGPT only works for the en-ru pair, For all other cases, a translation similar to YandexTranslate is used.
57+
5558
## Build
5659

5760
To build, you must have:

changelog.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
# 1.x (unreleased)
1+
# 1.0.5
2+
3+
## Lib
4+
5+
- Added support [new YandexGPT translation model](https://habr.com/ru/companies/yandex/articles/884416/)
6+
- Removed sonarjs rule comments
7+
- Rewritted typebox generation logic with `@toil/typebox-genx`
28

39
## Workspace
410

src/providers/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,21 @@ import { TranslationService } from "@/types/client";
44
import YandexBrowserProvider from "./yandexbrowser";
55
import YandexCloudProvider from "./yandexcloud";
66
import YandexTranslateProvider from "./yandextranslate";
7+
import YandexGPTProvider from "./yandexgpt";
78
import MSEdgeTranslateProvider from "./msedge";
89

910
export { default as BaseProvider } from "./base";
1011
export { default as YandexBrowserProvider } from "./yandexbrowser";
1112
export { default as YandexCloudProvider } from "./yandexcloud";
1213
export { default as YandexTranslateProvider } from "./yandextranslate";
14+
export { default as YandexGPTProvider } from "./yandexgpt";
1315
export { default as MSEdgeTranslateProvider } from "./msedge";
1416

1517
export const availableProviders = {
1618
[TranslationService.yandexbrowser]: YandexBrowserProvider,
1719
[TranslationService.yandexcloud]: YandexCloudProvider,
1820
[TranslationService.yandextranslate]: YandexTranslateProvider,
21+
[TranslationService.yandexgpt]: YandexGPTProvider,
1922
[TranslationService.msedge]: MSEdgeTranslateProvider,
2023
};
2124

src/providers/yandexgpt.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { Lang } from "@/types/client";
2+
3+
import {
4+
TranslateDetailSuccessResponse,
5+
TranslateOptions,
6+
} from "@/types/providers/yandextranslate";
7+
import { TranslateError } from "@/errors";
8+
import YandexTranslateProvider from "./yandextranslate";
9+
10+
/**
11+
* The total limit of characters per request is 10k chars
12+
*/
13+
export default class YandexGPTProvider extends YandexTranslateProvider {
14+
YAGPT_ORIGIN = "https://neuro.translate.yandex.ru";
15+
16+
getSid() {
17+
const timestamp = Date.now().toString(16);
18+
19+
return (
20+
timestamp +
21+
Array.from({ length: 16 - timestamp.length }, () =>
22+
Math.floor(Math.random() * 16).toString(16),
23+
).join("")
24+
);
25+
}
26+
27+
isSupportedLLMOptions(options: TranslateOptions) {
28+
return ![
29+
TranslateOptions.detailAlign,
30+
TranslateOptions.detailAlignAndDetectedLang,
31+
].includes(options);
32+
}
33+
34+
async llmRawRequest(
35+
text: string | string[],
36+
lang: Lang = "en-ru",
37+
options: TranslateOptions = TranslateOptions.default,
38+
format: "text" | "html" = "text",
39+
) {
40+
if (lang !== "en-ru") {
41+
throw new TranslateError("LLM service support only en-ru lang");
42+
}
43+
44+
if (!this.isSupportedLLMOptions(options)) {
45+
throw new TranslateError(
46+
"Detail align options not supported by LLM service",
47+
);
48+
}
49+
50+
if (!Array.isArray(text)) {
51+
text = [text];
52+
}
53+
54+
const sid = this.getSid();
55+
const params = new URLSearchParams({
56+
lang,
57+
id: `${sid}-0-0`,
58+
srv: "yabrowser-tr-text-app",
59+
tr_model: "llm_text",
60+
format,
61+
}).toString();
62+
63+
const textArray: readonly [string, string][] = text.map((val) => [
64+
"text",
65+
val,
66+
]);
67+
const body = new URLSearchParams([
68+
["options", options.toString()],
69+
["lang", lang],
70+
...textArray,
71+
]);
72+
73+
const res = await this.request<TranslateDetailSuccessResponse>(
74+
`/translate?${params}`,
75+
body,
76+
{
77+
Origin: this.YAGPT_ORIGIN,
78+
Referer: `${this.YAGPT_ORIGIN}/`,
79+
},
80+
);
81+
82+
if (!this.isSuccessProviderRes<TranslateDetailSuccessResponse>(res)) {
83+
throw new TranslateError(res.data);
84+
}
85+
86+
const { lang: resLang, text: resText = [""], align, detected } = res.data;
87+
88+
return {
89+
lang: resLang,
90+
translations: resText,
91+
align,
92+
detected,
93+
};
94+
}
95+
96+
/**
97+
* You can use this method if you need also get a detected language or align
98+
*/
99+
async rawTranslate(
100+
text: string | string[],
101+
lang: Lang = "en-ru",
102+
options: TranslateOptions = TranslateOptions.default,
103+
format: "text" | "html" = "text",
104+
) {
105+
if (lang === "en-ru" && this.isSupportedLLMOptions(options)) {
106+
return await this.llmRawRequest(text, lang, options, format);
107+
}
108+
109+
return await super.rawTranslate(text, lang, options, format);
110+
}
111+
}

src/types/client.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ export type FetchFunction = (
33
init?: any,
44
) => Promise<Response>;
55

6-
// eslint-disable-next-line sonarjs/redundant-type-aliases
76
export type Lang = string;
87
export type LangPair = `${string}-${string}`;
98

@@ -22,6 +21,10 @@ export enum TranslationService {
2221
* The total limit of characters per request is 10k chars
2322
*/
2423
yandextranslate = "yandextranslate",
24+
/**
25+
* The total limit of characters per request is 10k chars
26+
*/
27+
yandexgpt = "yandexgpt",
2528
/**
2629
* The total limit of characters per request is 50k chars
2730
*/

tests/data.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
export const phrase = "The quick brown fox jumps over the lazy dog";
1+
export const phrase =
2+
"His intemperate youthful idealism, for which he'd done five years of hard time in Siberia, provided the impetus for Crime and Punishment and The Devils; his sensualism and compulsive nature and caustic rationality were the personally destabilizing forces against which he subsequently erected the fortress of The Brother Karamazov and lesser redoubts like The Gambler.";
23
export const secondPhrase = "Hello world!";

tests/yandexgpt.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { describe, expect, test } from "bun:test";
2+
import TranslationClient from "../src";
3+
import { TranslationService } from "../src/types/client";
4+
import { phrase, secondPhrase } from "./data";
5+
6+
const translationClient = new TranslationClient({
7+
service: TranslationService.yandexgpt,
8+
});
9+
10+
describe("translate", () => {
11+
test("translate phrase", async () => {
12+
const res = await translationClient.translate(phrase);
13+
expect(res.lang).toEqual("en-ru");
14+
expect(res.translations.length).toEqual(1);
15+
expect(res.translations[0]).toInclude("Бесов");
16+
});
17+
test("translate multi phrases", async () => {
18+
const res = await translationClient.translate([phrase, secondPhrase]);
19+
expect(res.lang).toEqual("en-ru");
20+
expect(res.translations.length).toEqual(2);
21+
});
22+
});
23+
24+
test("detect language", async () => {
25+
const res = await translationClient.detect(phrase);
26+
expect(res.lang).toEqual("en");
27+
expect(res.score).not.toEqual(null);
28+
});
29+
30+
test("get languages", async () => {
31+
const res = await translationClient.getLangs();
32+
expect(res.length).toBeGreaterThan(0);
33+
});

0 commit comments

Comments
 (0)