























@@ -74,12 +74,16 @@ function createTimeoutError(label, timeoutMs) {
7474async function withRequestTimeout(label, timeoutMs, run) {
7575const controller = new AbortController();
7676const timeoutError = createTimeoutError(label, timeoutMs);
77-const timer = setTimeout(() => {
78-controller.abort(timeoutError);
79-}, timeoutMs);
80-timer.unref?.();
77+let timer;
78+const timeoutPromise = new Promise((_, reject) => {
79+timer = setTimeout(() => {
80+controller.abort(timeoutError);
81+reject(timeoutError);
82+}, timeoutMs);
83+timer.unref?.();
84+});
8185try {
82-return await run(controller.signal);
86+return await Promise.race([run(controller.signal, timeoutPromise), timeoutPromise]);
8387} catch (error) {
8488if (controller.signal.aborted) {
8589throw timeoutError;
@@ -90,12 +94,17 @@ async function withRequestTimeout(label, timeoutMs, run) {
9094}
9195}
929693-async function readBoundedResponseText(response, label, byteLimit = responseBodyMaxBytes) {
94-return await readBoundedResponseTextWithLimit(response, label, byteLimit);
97+async function readBoundedResponseText(response, label, timeoutPromise) {
98+return await readBoundedResponseTextWithLimit(
99+response,
100+label,
101+responseBodyMaxBytes,
102+timeoutPromise,
103+);
95104}
9610597-async function readBoundedResponseJson(response, label) {
98-const body = await readBoundedResponseText(response, label);
106+async function readBoundedResponseJson(response, label, timeoutPromise) {
107+const body = await readBoundedResponseText(response, label, timeoutPromise);
99108try {
100109return JSON.parse(body);
101110} catch (error) {
@@ -134,65 +143,85 @@ function sleep(ms) {
134143}
135144136145async function fetchSignin() {
137-return await withRequestTimeout("Open WebUI signin", controlTimeoutMs, async (signal) => {
138-const response = await fetch(`${baseUrl}/api/v1/auths/signin`, {
139-method: "POST",
140-headers: { "content-type": "application/json" },
141-body: JSON.stringify({ email, password }),
142- signal,
143-});
144-if (!response.ok) {
145-const body = await readBoundedResponseText(response, "Open WebUI signin");
146-throw new Error(`signin failed: HTTP ${response.status} ${body}`);
147-}
148-return {
149-cookie: getCookieHeader(response),
150-json: await readBoundedResponseJson(response, "Open WebUI signin"),
151-};
152-});
146+return await withRequestTimeout(
147+"Open WebUI signin",
148+controlTimeoutMs,
149+async (signal, timeoutPromise) => {
150+const response = await fetch(`${baseUrl}/api/v1/auths/signin`, {
151+method: "POST",
152+headers: { "content-type": "application/json" },
153+body: JSON.stringify({ email, password }),
154+ signal,
155+});
156+if (!response.ok) {
157+const body = await readBoundedResponseText(response, "Open WebUI signin", timeoutPromise);
158+throw new Error(`signin failed: HTTP ${response.status} ${body}`);
159+}
160+return {
161+cookie: getCookieHeader(response),
162+json: await readBoundedResponseJson(response, "Open WebUI signin", timeoutPromise),
163+};
164+},
165+);
153166}
154167155168async function fetchModels(authHeaders, attempt) {
156169return await withRequestTimeout(
157170`Open WebUI models attempt ${attempt}`,
158171controlTimeoutMs,
159-async (signal) => {
172+async (signal, timeoutPromise) => {
160173const response = await fetch(`${baseUrl}/api/models`, { headers: authHeaders, signal });
161174if (!response.ok) {
162175return {
163176ok: false,
164177status: response.status,
165-text: await readBoundedResponseText(response, `Open WebUI models attempt ${attempt}`),
178+text: await readBoundedResponseText(
179+response,
180+`Open WebUI models attempt ${attempt}`,
181+timeoutPromise,
182+),
166183};
167184}
168185return {
169-json: await readBoundedResponseJson(response, `Open WebUI models attempt ${attempt}`),
186+json: await readBoundedResponseJson(
187+response,
188+`Open WebUI models attempt ${attempt}`,
189+timeoutPromise,
190+),
170191ok: true,
171192};
172193},
173194);
174195}
175196176197async function fetchChatCompletion(authHeaders, targetModel) {
177-return await withRequestTimeout("Open WebUI chat completion", chatTimeoutMs, async (signal) => {
178-const response = await fetch(`${baseUrl}/api/chat/completions`, {
179-method: "POST",
180-headers: {
181- ...authHeaders,
182-"content-type": "application/json",
183-},
184-body: JSON.stringify({
185-model: targetModel,
186-messages: [{ role: "user", content: prompt }],
187-}),
188- signal,
189-});
190-if (!response.ok) {
191-const body = await readBoundedResponseText(response, "Open WebUI chat completion");
192-throw new Error(`/api/chat/completions failed: HTTP ${response.status} ${body}`);
193-}
194-return await readBoundedResponseJson(response, "Open WebUI chat completion");
195-});
198+return await withRequestTimeout(
199+"Open WebUI chat completion",
200+chatTimeoutMs,
201+async (signal, timeoutPromise) => {
202+const response = await fetch(`${baseUrl}/api/chat/completions`, {
203+method: "POST",
204+headers: {
205+ ...authHeaders,
206+"content-type": "application/json",
207+},
208+body: JSON.stringify({
209+model: targetModel,
210+messages: [{ role: "user", content: prompt }],
211+}),
212+ signal,
213+});
214+if (!response.ok) {
215+const body = await readBoundedResponseText(
216+response,
217+"Open WebUI chat completion",
218+timeoutPromise,
219+);
220+throw new Error(`/api/chat/completions failed: HTTP ${response.status} ${body}`);
221+}
222+return await readBoundedResponseJson(response, "Open WebUI chat completion", timeoutPromise);
223+},
224+);
196225}
197226198227function extractModelIds(modelsJson) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。