@@ -5,7 +5,9 @@ import {
|
5 | 5 | buildOllamaProvider, |
6 | 6 | buildOllamaModelDefinition, |
7 | 7 | enrichOllamaModelsWithContext, |
| 8 | +fetchOllamaModels, |
8 | 9 | parseOllamaNumCtxParameter, |
| 10 | +queryOllamaModelShowInfo, |
9 | 11 | resetOllamaModelShowInfoCacheForTest, |
10 | 12 | resolveOllamaApiBase, |
11 | 13 | type OllamaTagModel, |
@@ -380,4 +382,57 @@ describe("ollama provider models", () => {
|
380 | 382 | expect(parseOllamaNumCtxParameter('stop "<|eot_id|>"')).toBeUndefined(); |
381 | 383 | expect(parseOllamaNumCtxParameter({ num_ctx: 8192 })).toBeUndefined(); |
382 | 384 | }); |
| 385 | + |
| 386 | +it("fails soft and stops reading when discovery streams exceed the JSON byte cap", async () => { |
| 387 | +// Larger than the shared 16 MiB readProviderJsonResponse cap so the bounded reader cancels |
| 388 | +// the stream mid-flight; if the cap were removed the reader would buffer the whole payload. |
| 389 | +const ONE_MIB = 1024 * 1024; |
| 390 | +const TOTAL_CHUNKS = 32; // 32 MiB advertised body, double the cap. |
| 391 | +const chunk = new Uint8Array(ONE_MIB); |
| 392 | + |
| 393 | +let bytesPulled = 0; |
| 394 | +let canceled = false; |
| 395 | +const makeOversizedJsonResponse = (): Response => { |
| 396 | +bytesPulled = 0; |
| 397 | +canceled = false; |
| 398 | +let pulled = 0; |
| 399 | +const body = new ReadableStream<Uint8Array>({ |
| 400 | +pull(controller) { |
| 401 | +if (pulled >= TOTAL_CHUNKS) { |
| 402 | +controller.close(); |
| 403 | +return; |
| 404 | +} |
| 405 | +pulled += 1; |
| 406 | +bytesPulled += chunk.length; |
| 407 | +controller.enqueue(chunk); |
| 408 | +}, |
| 409 | +cancel() { |
| 410 | +canceled = true; |
| 411 | +}, |
| 412 | +}); |
| 413 | +return new Response(body, { |
| 414 | +status: 200, |
| 415 | +headers: { "Content-Type": "application/json" }, |
| 416 | +}); |
| 417 | +}; |
| 418 | + |
| 419 | +vi.stubGlobal( |
| 420 | +"fetch", |
| 421 | +vi.fn(async () => makeOversizedJsonResponse()), |
| 422 | +); |
| 423 | +const tags = await fetchOllamaModels("http://127.0.0.1:11434"); |
| 424 | +expect(tags).toEqual({ reachable: false, models: [] }); |
| 425 | +expect(canceled).toBe(true); |
| 426 | +// Only the bounded prefix is pulled, never the full advertised 32 MiB stream. |
| 427 | +expect(bytesPulled).toBeLessThan(TOTAL_CHUNKS * ONE_MIB); |
| 428 | + |
| 429 | +vi.stubGlobal( |
| 430 | +"fetch", |
| 431 | +vi.fn(async () => makeOversizedJsonResponse()), |
| 432 | +); |
| 433 | +const showInfo = await queryOllamaModelShowInfo("http://127.0.0.1:11434", "evil-model:latest"); |
| 434 | +expect(showInfo).toEqual({}); |
| 435 | +expect(canceled).toBe(true); |
| 436 | +expect(bytesPulled).toBeLessThan(TOTAL_CHUNKS * ONE_MIB); |
| 437 | +}); |
383 | 438 | }); |