mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
fix(lmstudio): cap model fetch timeout delays
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { createSubsystemLogger } from "openclaw/plugin-sdk/logging-core";
|
||||
import { clampTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
|
||||
import { readProviderJsonArrayFieldResponse } from "openclaw/plugin-sdk/provider-http";
|
||||
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { SELF_HOSTED_DEFAULT_COST } from "openclaw/plugin-sdk/provider-setup";
|
||||
@@ -44,11 +45,12 @@ async function fetchLmstudioEndpoint(params: {
|
||||
ssrfPolicy?: SsrFPolicy;
|
||||
auditContext: string;
|
||||
}): Promise<{ response: Response; release: () => Promise<void> }> {
|
||||
const timeoutMs = clampTimerTimeoutMs(params.timeoutMs) ?? 1;
|
||||
if (params.ssrfPolicy) {
|
||||
return await fetchWithSsrFGuard({
|
||||
url: params.url,
|
||||
init: params.init,
|
||||
timeoutMs: params.timeoutMs,
|
||||
timeoutMs,
|
||||
fetchImpl: params.fetchImpl,
|
||||
policy: params.ssrfPolicy,
|
||||
auditContext: params.auditContext,
|
||||
@@ -58,7 +60,7 @@ async function fetchLmstudioEndpoint(params: {
|
||||
return {
|
||||
response: await fetchFn(params.url, {
|
||||
...params.init,
|
||||
signal: AbortSignal.timeout(params.timeoutMs),
|
||||
signal: AbortSignal.timeout(timeoutMs),
|
||||
}),
|
||||
release: async () => {},
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { MAX_TIMER_TIMEOUT_MS } from "openclaw/plugin-sdk/number-runtime";
|
||||
import {
|
||||
SELF_HOSTED_DEFAULT_CONTEXT_WINDOW,
|
||||
SELF_HOSTED_DEFAULT_MAX_TOKENS,
|
||||
@@ -90,6 +91,7 @@ describe("lmstudio-models", () => {
|
||||
|
||||
afterEach(() => {
|
||||
fetchWithSsrFGuardMock.mockReset();
|
||||
vi.restoreAllMocks();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
@@ -379,6 +381,45 @@ describe("lmstudio-models", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("caps oversized direct fetch timeouts before discovering models", async () => {
|
||||
const timeoutController = new AbortController();
|
||||
const timeoutSpy = vi.spyOn(AbortSignal, "timeout").mockReturnValue(timeoutController.signal);
|
||||
const fetchMock = vi.fn(async (_url: string | URL, init?: RequestInit) => ({
|
||||
ok: true,
|
||||
status: 200,
|
||||
requestInit: init,
|
||||
json: async () => ({ models: [] }),
|
||||
}));
|
||||
|
||||
const result = await fetchLmstudioModels({
|
||||
baseUrl: "http://localhost:1234/v1",
|
||||
timeoutMs: Number.MAX_SAFE_INTEGER,
|
||||
fetchImpl: asFetch(fetchMock),
|
||||
});
|
||||
|
||||
expect(result.reachable).toBe(true);
|
||||
expect(timeoutSpy).toHaveBeenCalledWith(MAX_TIMER_TIMEOUT_MS);
|
||||
expect(fetchMock.mock.calls[0]?.[1]?.signal).toBe(timeoutController.signal);
|
||||
});
|
||||
|
||||
it("caps oversized guarded-fetch timeouts before discovering models", async () => {
|
||||
fetchWithSsrFGuardMock.mockResolvedValue({
|
||||
response: new Response(JSON.stringify({ models: [] }), { status: 200 }),
|
||||
release: vi.fn(async () => undefined),
|
||||
});
|
||||
|
||||
const result = await fetchLmstudioModels({
|
||||
baseUrl: "http://localhost:1234/v1",
|
||||
timeoutMs: Number.MAX_SAFE_INTEGER,
|
||||
ssrfPolicy: {},
|
||||
});
|
||||
|
||||
expect(result.reachable).toBe(true);
|
||||
expect(fetchWithSsrFGuardMock.mock.calls[0]?.[0]).toMatchObject({
|
||||
timeoutMs: MAX_TIMER_TIMEOUT_MS,
|
||||
});
|
||||
});
|
||||
|
||||
it("skips model load when already loaded", async () => {
|
||||
const fetchMock = createModelLoadFetchMock({ loadedContextLength: 64000 });
|
||||
vi.stubGlobal("fetch", asFetch(fetchMock));
|
||||
|
||||
Reference in New Issue
Block a user