@xynogen/pix-9router 0.3.9 → 0.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/http.ts +21 -26
- package/src/transcribe.ts +23 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xynogen/pix-9router",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10",
|
|
4
4
|
"description": "Pi extension — 9Router provider + fetch/search tools via router API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -46,6 +46,6 @@
|
|
|
46
46
|
"@xynogen/pix-data": "^0.4.0",
|
|
47
47
|
"@xynogen/pix-pretty": "^1.7.21",
|
|
48
48
|
"typebox": "^1.1.38",
|
|
49
|
-
"@xynogen/pix-runtime": "^0.
|
|
49
|
+
"@xynogen/pix-runtime": "^0.4.0"
|
|
50
50
|
}
|
|
51
51
|
}
|
package/src/http.ts
CHANGED
|
@@ -8,11 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { type ExecFileException, execFile } from "node:child_process";
|
|
11
|
+
import { ioTimeoutMs, ioTimeoutSignal } from "@xynogen/pix-runtime/io";
|
|
11
12
|
import { routerBaseUrl } from "./data.js";
|
|
12
13
|
|
|
13
|
-
/** Default request timeout — audio transcription overrides with a longer one. */
|
|
14
|
-
export const REQUEST_TIMEOUT_MS = 30_000;
|
|
15
|
-
|
|
16
14
|
export function auth(): string | undefined {
|
|
17
15
|
return process.env.ROUTER_API_KEY;
|
|
18
16
|
}
|
|
@@ -22,32 +20,27 @@ export async function apiPost(
|
|
|
22
20
|
path: string,
|
|
23
21
|
body: Record<string, unknown>,
|
|
24
22
|
signal?: AbortSignal,
|
|
25
|
-
timeoutMs: number =
|
|
23
|
+
timeoutMs: number = ioTimeoutMs(),
|
|
26
24
|
): Promise<string> {
|
|
27
25
|
const url = `${routerBaseUrl()}${path}`;
|
|
28
26
|
const key = auth();
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const errText = await res.text().catch(() => "");
|
|
45
|
-
throw new Error(`API ${res.status}: ${errText.slice(0, 500)}`);
|
|
46
|
-
}
|
|
47
|
-
return await res.text();
|
|
48
|
-
} finally {
|
|
49
|
-
clearTimeout(timeout);
|
|
27
|
+
const timeoutSignal =
|
|
28
|
+
timeoutMs === ioTimeoutMs() ? ioTimeoutSignal() : AbortSignal.timeout(timeoutMs);
|
|
29
|
+
const requestSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
30
|
+
const res = await fetch(url, {
|
|
31
|
+
method: "POST",
|
|
32
|
+
headers: {
|
|
33
|
+
"Content-Type": "application/json",
|
|
34
|
+
...(key ? { Authorization: `Bearer ${key}` } : {}),
|
|
35
|
+
},
|
|
36
|
+
body: JSON.stringify(body),
|
|
37
|
+
signal: requestSignal,
|
|
38
|
+
});
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
const errText = await res.text().catch(() => "");
|
|
41
|
+
throw new Error(`API ${res.status}: ${errText.slice(0, 500)}`);
|
|
50
42
|
}
|
|
43
|
+
return res.text();
|
|
51
44
|
}
|
|
52
45
|
|
|
53
46
|
export interface CurlOptions {
|
|
@@ -61,7 +54,9 @@ export interface CurlOptions {
|
|
|
61
54
|
|
|
62
55
|
/** Run curl with sane defaults, rejecting on non-zero exit or timeout. */
|
|
63
56
|
export function curl(args: string[], opts: CurlOptions = {}): Promise<string> {
|
|
64
|
-
const
|
|
57
|
+
const timeoutMs = opts.timeoutMs ?? ioTimeoutMs();
|
|
58
|
+
const maxTime = opts.maxTime ?? timeoutMs / 1000;
|
|
59
|
+
const { stdin } = opts;
|
|
65
60
|
return new Promise((resolve, reject) => {
|
|
66
61
|
const child = execFile(
|
|
67
62
|
"curl",
|
package/src/transcribe.ts
CHANGED
|
@@ -16,12 +16,12 @@ import { access, lstat, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
16
16
|
import { homedir } from "node:os";
|
|
17
17
|
import { basename, dirname, extname, isAbsolute, resolve } from "node:path";
|
|
18
18
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
19
|
+
import { ioTimeoutSignal } from "@xynogen/pix-runtime/io";
|
|
19
20
|
import { Type } from "typebox";
|
|
20
21
|
import { routerBaseUrl } from "./data.js";
|
|
21
22
|
import { auth, curl } from "./http.js";
|
|
22
23
|
import { makeRenderCall, makeRenderResult } from "./render.js";
|
|
23
24
|
|
|
24
|
-
const REQUEST_TIMEOUT_MS = 120_000; // audio transcription can take longer
|
|
25
25
|
const CHAT_TRUNCATE_LIMIT = 50_000; // only when no output_file is provided
|
|
26
26
|
const DEFAULT_MODEL = "dg/nova-3";
|
|
27
27
|
|
|
@@ -71,35 +71,28 @@ async function apiMultipart(
|
|
|
71
71
|
): Promise<string> {
|
|
72
72
|
const url = `${routerBaseUrl()}${path}`;
|
|
73
73
|
const key = auth();
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
});
|
|
95
|
-
if (!res.ok) {
|
|
96
|
-
const errText = await res.text().catch(() => "");
|
|
97
|
-
throw new Error(`API ${res.status}: ${errText.slice(0, 500)}`);
|
|
98
|
-
}
|
|
99
|
-
return await res.text();
|
|
100
|
-
} finally {
|
|
101
|
-
clearTimeout(timeout);
|
|
74
|
+
const requestSignal = ioTimeoutSignal(signal);
|
|
75
|
+
const fileData = await readFile(filePath, { signal: requestSignal });
|
|
76
|
+
const blob = new Blob([fileData], { type: mimeType(filePath) });
|
|
77
|
+
|
|
78
|
+
const form = new FormData();
|
|
79
|
+
form.append("file", blob, basename(filePath));
|
|
80
|
+
form.append("model", model);
|
|
81
|
+
if (language) form.append("language", language);
|
|
82
|
+
|
|
83
|
+
const res = await fetch(url, {
|
|
84
|
+
method: "POST",
|
|
85
|
+
headers: {
|
|
86
|
+
...(key ? { Authorization: `Bearer ${key}` } : {}),
|
|
87
|
+
},
|
|
88
|
+
body: form,
|
|
89
|
+
signal: requestSignal,
|
|
90
|
+
});
|
|
91
|
+
if (!res.ok) {
|
|
92
|
+
const errText = await res.text().catch(() => "");
|
|
93
|
+
throw new Error(`API ${res.status}: ${errText.slice(0, 500)}`);
|
|
102
94
|
}
|
|
95
|
+
return res.text();
|
|
103
96
|
}
|
|
104
97
|
|
|
105
98
|
/** Extract the `text` field from a JSON envelope, or return the raw string. */
|
|
@@ -448,7 +441,7 @@ export default function registerTranscribe(pi: ExtensionAPI): void {
|
|
|
448
441
|
`${routerBaseUrl()}/audio/transcriptions`,
|
|
449
442
|
];
|
|
450
443
|
|
|
451
|
-
const raw = await curl(curlArgs
|
|
444
|
+
const raw = await curl(curlArgs);
|
|
452
445
|
return await run("curl-fallback", raw);
|
|
453
446
|
} catch (curlErr: unknown) {
|
|
454
447
|
const curlMsg = curlErr instanceof Error ? curlErr.message : String(curlErr);
|