1 | import createClient, { type Middleware } from "openapi-fetch"; |
2 |
|
3 | type Nextcloud = { |
4 | baseUrl: string, |
5 | userId: string, |
6 | token: string |
7 | }; |
8 |
|
9 | const TASK_TYPE_TRANSLATE = "core:text2text:translate"; |
10 | const POLL_INTERVAL_MS = 1000; |
11 | const DEFAULT_POLL_TIMEOUT_MS = 600_000; |
12 |
|
13 | |
14 | * Creates a translation task via the Nextcloud OCS Task Processing API, |
15 | * polls until the task completes, and returns the translated text. |
16 | */ |
17 | export async function main( |
18 | ncResource: Nextcloud, |
19 | text: string, |
20 | targetLanguage: string, |
21 | originLanguage: string | null = null, |
22 | model: string | null = null, |
23 | timeout_ms: number = DEFAULT_POLL_TIMEOUT_MS, |
24 | interval_ms: number = POLL_INTERVAL_MS, |
25 | ) { |
26 | const client = createClient({ baseUrl: ncResource.baseUrl }) as any; |
27 | const authMiddleware: Middleware = { |
28 | async onRequest({ request, options }) { |
29 | request.headers.set("Authorization", `Basic ${btoa(ncResource.userId + ':' + ncResource.token)}`); |
30 | return request; |
31 | }, |
32 | }; |
33 | client.use(authMiddleware); |
34 |
|
35 | const input: Record<string, string> = { |
36 | input: text, |
37 | target_language: targetLanguage, |
38 | }; |
39 | if (originLanguage !== null && originLanguage !== "") { |
40 | input.origin_language = originLanguage; |
41 | } |
42 | if (model !== null && model !== "") { |
43 | input.model = model |
44 | } |
45 |
|
46 | |
47 | const { data: scheduleData, error: scheduleError } = await client.POST( |
48 | "/ocs/v2.php/taskprocessing/schedule", |
49 | { |
50 | params: { |
51 | header: { |
52 | "OCS-APIRequest": true, |
53 | }, |
54 | query: { |
55 | format: "json", |
56 | }, |
57 | }, |
58 | body: { |
59 | type: TASK_TYPE_TRANSLATE, |
60 | input, |
61 | appId: "windmill", |
62 | }, |
63 | }, |
64 | ); |
65 |
|
66 | if (scheduleError) { |
67 | throw new Error(`Failed to schedule translation task: ${JSON.stringify(scheduleError)}`); |
68 | } |
69 | const taskId = scheduleData?.ocs?.data?.task?.id; |
70 |
|
71 | |
72 | const deadline = Date.now() + timeout_ms; |
73 | while (Date.now() < deadline) { |
74 | console.log("Polling task output") |
75 | const { data: taskData, error: taskError } = await client.GET( |
76 | "/ocs/v2.php/taskprocessing/task/{id}", |
77 | { |
78 | params: { |
79 | header: { |
80 | "OCS-APIRequest": true, |
81 | }, |
82 | query: { |
83 | format: "json", |
84 | }, |
85 | path: { |
86 | id: taskId, |
87 | }, |
88 | }, |
89 | }, |
90 | ); |
91 |
|
92 | if (taskError) { |
93 | throw new Error(`Failed to get task ${taskId}: ${JSON.stringify(taskError)}`); |
94 | } |
95 | const task = taskData.ocs?.data?.task; |
96 | const status = task.status; |
97 |
|
98 | if (status === "STATUS_SUCCESSFUL") { |
99 | const output = task.output; |
100 | if (output && typeof output.output === "string") { |
101 | return { translatedText: output.output, taskId }; |
102 | } |
103 | return { translatedText: output, taskId }; |
104 | } |
105 |
|
106 | if (status === "STATUS_FAILED" || status === "STATUS_CANCELLED") { |
107 | const msg = JSON.stringify(task); |
108 | throw new Error(`Translation task failed: ${msg}`); |
109 | } |
110 | if (!["STATUS_SCHEDULED", "STATUS_RUNNING"].includes(status)) { |
111 | const msg = JSON.stringify(task); |
112 | throw new Error(`Translation task failed with unknown status: ${msg}`) |
113 | } |
114 | await new Promise((r) => setTimeout(r, interval_ms)); |
115 | } |
116 |
|
117 | throw new Error( |
118 | `Translation task did not complete within ${timeout_ms}ms (taskId: ${taskId})`, |
119 | ); |
120 | } |
121 |
|