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