
























@@ -66,19 +66,25 @@ describe("lmstudio-models", () => {
6666};
6767};
6868const createModelLoadFetchMock = (params?: {
69+key?: string;
70+variants?: unknown;
71+selectedVariant?: unknown;
6972loadedContextLength?: number;
7073maxContextLength?: number;
7174}) =>
7275vi.fn(async (url: string | URL, init?: RequestInit) => {
76+const key = params?.key ?? "qwen3-8b-instruct";
7377if (String(url).endsWith("/api/v1/models")) {
7478return {
7579ok: true,
7680json: async () => ({
7781models: [
7882{
7983type: "llm",
80-key: "qwen3-8b-instruct",
84+ key,
8185max_context_length: params?.maxContextLength,
86+variants: params?.variants,
87+selected_variant: params?.selectedVariant,
8288loaded_instances: params?.loadedContextLength
8389 ? [{ id: "inst-1", config: { context_length: params.loadedContextLength } }]
8490 : [],
@@ -110,6 +116,18 @@ describe("lmstudio-models", () => {
110116const loadBody = parseJsonRequestBody(loadInit) as { context_length: number };
111117expect(loadBody.context_length).toBe(contextLength);
112118};
119+const expectLoadModelKey = (
120+fetchMock: ReturnType<typeof createModelLoadFetchMock>,
121+modelKey: string,
122+) => {
123+const loadCall = findModelLoadCall(fetchMock);
124+if (!loadCall) {
125+throw new Error("expected LM Studio model load request");
126+}
127+const loadInit = loadCall[1] as RequestInit;
128+const loadBody = parseJsonRequestBody(loadInit) as { model: string };
129+expect(loadBody.model).toBe(modelKey);
130+};
113131114132afterEach(() => {
115133fetchWithSsrFGuardMock.mockReset();
@@ -451,7 +469,7 @@ describe("lmstudio-models", () => {
451469baseUrl: "http://localhost:1234/v1",
452470modelKey: "qwen3-8b-instruct",
453471}),
454-).resolves.toBeUndefined();
472+).resolves.toBe("qwen3-8b-instruct");
455473456474expect(fetchMock).toHaveBeenCalledTimes(1);
457475const calledUrls = fetchMock.mock.calls.map((call) => String(call[0]));
@@ -471,12 +489,89 @@ describe("lmstudio-models", () => {
471489modelKey: "qwen3-8b-instruct",
472490requestedContextLength: 8192,
473491}),
474-).resolves.toBeUndefined();
492+).resolves.toBe("qwen3-8b-instruct");
475493476494expect(fetchMock).toHaveBeenCalledTimes(2);
477495expectLoadContextLength(fetchMock, 8192);
478496});
479497498+it("loads the canonical model key when the requested key is an advertised variant", async () => {
499+const canonicalKey = "gemma-4-e4b-it-ultra-uncensored-heretic";
500+const variantKey = `${canonicalKey}@q4_k_m`;
501+const fetchMock = createModelLoadFetchMock({
502+key: canonicalKey,
503+variants: [variantKey],
504+selectedVariant: variantKey,
505+});
506+vi.stubGlobal("fetch", asFetch(fetchMock));
507+508+await expect(
509+ensureLmstudioModelLoaded({
510+baseUrl: "http://localhost:1234/v1",
511+modelKey: variantKey,
512+}),
513+).resolves.toBe(canonicalKey);
514+515+expect(fetchMock).toHaveBeenCalledTimes(2);
516+expectLoadModelKey(fetchMock, canonicalKey);
517+});
518+519+it("keeps the canonical model key on load failures after variant discovery", async () => {
520+const canonicalKey = "gemma-4-e4b-it-ultra-uncensored-heretic";
521+const variantKey = `${canonicalKey}@q4_k_m`;
522+const fetchMock = vi.fn(async (url: string | URL) => {
523+if (String(url).endsWith("/api/v1/models")) {
524+return {
525+ok: true,
526+json: async () => ({
527+models: [
528+{
529+type: "llm",
530+key: canonicalKey,
531+variants: [variantKey],
532+selected_variant: variantKey,
533+loaded_instances: [],
534+},
535+],
536+}),
537+};
538+}
539+if (String(url).endsWith("/api/v1/models/load")) {
540+return new Response("load failed", { status: 503 });
541+}
542+throw new Error(`Unexpected fetch URL: ${String(url)}`);
543+});
544+vi.stubGlobal("fetch", asFetch(fetchMock));
545+546+const error = await ensureLmstudioModelLoaded({
547+baseUrl: "http://localhost:1234/v1",
548+modelKey: variantKey,
549+}).catch((caught: unknown) => caught);
550+551+expect(error).toBeInstanceOf(Error);
552+expect(error).toMatchObject({ resolvedModelKey: canonicalKey });
553+});
554+555+it("preserves a suffixed key when LM Studio advertises it as the model key", async () => {
556+const suffixedKey = "local/special-model@q4_k_m";
557+const fetchMock = createModelLoadFetchMock({
558+key: suffixedKey,
559+variants: ["local/special-model@q8_0"],
560+selectedVariant: "local/special-model@q8_0",
561+});
562+vi.stubGlobal("fetch", asFetch(fetchMock));
563+564+await expect(
565+ensureLmstudioModelLoaded({
566+baseUrl: "http://localhost:1234/v1",
567+modelKey: suffixedKey,
568+}),
569+).resolves.toBe(suffixedKey);
570+571+expect(fetchMock).toHaveBeenCalledTimes(2);
572+expectLoadModelKey(fetchMock, suffixedKey);
573+});
574+480575it("reports malformed model load JSON with an owned error", async () => {
481576const fetchMock = vi.fn(async (url: string | URL) => {
482577if (String(url).endsWith("/api/v1/models")) {
@@ -552,7 +647,7 @@ describe("lmstudio-models", () => {
552647baseUrl: "http://localhost:1234/v1",
553648modelKey: "qwen3-8b-instruct",
554649}),
555-).resolves.toBeUndefined();
650+).resolves.toBe("qwen3-8b-instruct");
556651557652expect(fetchMock).toHaveBeenCalledTimes(2);
558653expectLoadContextLength(fetchMock, 32768);
@@ -572,7 +667,7 @@ describe("lmstudio-models", () => {
572667},
573668modelKey: " qwen3-8b-instruct ",
574669}),
575-).resolves.toBeUndefined();
670+).resolves.toBe("qwen3-8b-instruct");
576671577672expect(fetchMock).toHaveBeenCalledTimes(2);
578673const loadCall = findModelLoadCall(fetchMock);
@@ -608,7 +703,7 @@ describe("lmstudio-models", () => {
608703modelKey: "qwen3-8b-instruct",
609704requestedContextLength: 8192,
610705}),
611-).resolves.toBeUndefined();
706+).resolves.toBe("qwen3-8b-instruct");
612707613708expectLoadContextLength(fetchMock, 8192);
614709});
@@ -626,7 +721,7 @@ describe("lmstudio-models", () => {
626721modelKey: "qwen3-8b-instruct",
627722requestedContextLength: 8192.5,
628723}),
629-).resolves.toBeUndefined();
724+).resolves.toBe("qwen3-8b-instruct");
630725631726expectLoadContextLength(fetchMock, LMSTUDIO_DEFAULT_LOAD_CONTEXT_LENGTH);
632727});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。