helloadhavan/github_issues huggingface dataset:
Repository: nodejs/node
Pull request: test: make `test-permission-sqlite-load-extension` more robust

Necessary for https://github.com/nodejs/node/pull/48409
<!--
Before submitting a pull request, please read:

- the CONTRIBUTING guide at https://github.com/nodejs/node/blob/HEAD/CONTRIBUTING.md
- the commit message formatting guidelines at
  https://github.com/nodejs/node/blob/HEAD/doc/contributing/pull-requests.md#commit-message-guidelines

For code changes:
1. Include tests for any bug fixes or new features.
2. Update documentation if relevant.
3. Ensure that `make -j4 test` (UNIX), or `vcbuild test` (Windows) passes.

If you believe this PR should be highlighted in the Node.js CHANGELOG
please add the `notable-change` label.

Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.
-->


Changed files:
filename: test/parallel/test-permission-sqlite-load-extension.js
patch: @@ -1,18 +1,16 @@
 'use strict';
 const common = require('../common');
 const assert = require('node:assert');
-const childProcess = require('child_process');
 
 const code = `const sqlite = require('node:sqlite');
 const db = new sqlite.DatabaseSync(':memory:', { allowExtension: true });
 db.loadExtension('nonexistent');`.replace(/\n/g, ' ');
 
-childProcess.exec(
-  `${process.execPath} --permission -e "${code}"`,
-  {},
-  common.mustCall((err, _, stderr) => {
-    assert.strictEqual(err.code, 1);
-    assert.match(stderr, /Error: Cannot load SQLite extensions when the permission model is enabled/);
-    assert.match(stderr, /code: 'ERR_LOAD_SQLITE_EXTENSION'/);
-  })
-);
+common.spawnPromisified(
+  process.execPath,
+  ['--permission', '--eval', code],
+).then(common.mustCall(({ code, stderr }) => {
+  assert.match(stderr, /Error: Cannot load SQLite extensions when the permission model is enabled/);
+  assert.match(stderr, /code: 'ERR_LOAD_SQLITE_EXTENSION'/);
+  assert.strictEqual(code, 1);
+}));
Repository: facebook/react
Pull request: Capitalize the default Flight environment

It's cleaner and more in line with how we style other badges like "Memo" and "ForwardRef" in DevTools.


Changed files:
filename: packages/react-client/src/__tests__/ReactFlight-test.js
patch: @@ -197,7 +197,7 @@ describe('ReactFlight', () => {
       const rootModel = await ReactNoopFlightClient.read(transport);
       const greeting = rootModel.greeting;
       expect(greeting._debugInfo).toEqual(
-        __DEV__ ? [{name: 'Greeting', env: 'server'}] : undefined,
+        __DEV__ ? [{name: 'Greeting', env: 'Server'}] : undefined,
       );
       ReactNoop.render(greeting);
     });
@@ -224,7 +224,7 @@ describe('ReactFlight', () => {
     await act(async () => {
       const promise = ReactNoopFlightClient.read(transport);
       expect(promise._debugInfo).toEqual(
-        __DEV__ ? [{name: 'Greeting', env: 'server'}] : undefined,
+        __DEV__ ? [{name: 'Greeting', env: 'Server'}] : undefined,
       );
       ReactNoop.render(await promise);
     });
@@ -1970,7 +1970,7 @@ describe('ReactFlight', () => {
     await act(async () => {
       const promise = ReactNoopFlightClient.read(transport);
       expect(promise._debugInfo).toEqual(
-        __DEV__ ? [{name: 'ServerComponent', env: 'server'}] : undefined,
+        __DEV__ ? [{name: 'ServerComponent', env: 'Server'}] : undefined,
       );
       const result = await promise;
       const thirdPartyChildren = await result.props.children[1];

filename: packages/react-server/src/ReactFlightServer.js
patch: @@ -305,7 +305,7 @@ export function createRequest(
   }: any);
   if (__DEV__) {
     request.environmentName =
-      environmentName === undefined ? 'server' : environmentName;
+      environmentName === undefined ? 'Server' : environmentName;
   }
   const rootTask = createTask(request, model, null, false, abortSet);
   pingedTasks.push(rootTask);

filename: packages/react/src/__tests__/ReactFetch-test.js
patch: @@ -86,7 +86,7 @@ describe('ReactFetch', () => {
     const promise = render(Component);
     expect(await promise).toMatchInlineSnapshot(`"GET world []"`);
     expect(promise._debugInfo).toEqual(
-      __DEV__ ? [{name: 'Component', env: 'server'}] : undefined,
+      __DEV__ ? [{name: 'Component', env: 'Server'}] : undefined,
     );
     expect(fetchCount).toBe(1);
   });
Repository: ggml-org/llama.cpp
Pull request: llama : fix shapes for bert/mpt q/k norm

Broken in #15662
Fixes #16392

Changed files:
filename: src/llama-model.cpp
patch: @@ -7843,6 +7843,8 @@ struct llm_build_bert : public llm_graph_context {
                 }
 
                 if (model.layers[il].attn_q_norm) {
+                    Qcur = ggml_reshape_2d(ctx0, Qcur, n_embd_head*n_head, n_tokens);
+
                     Qcur = build_norm(Qcur,
                             model.layers[il].attn_q_norm,
                             model.layers[il].attn_q_norm_b,
@@ -7852,6 +7854,8 @@ struct llm_build_bert : public llm_graph_context {
                 }
 
                 if (model.layers[il].attn_k_norm) {
+                    Kcur = ggml_reshape_2d(ctx0, Kcur, n_embd_head*n_head_kv, n_tokens);
+
                     Kcur = build_norm(Kcur,
                             model.layers[il].attn_k_norm,
                             model.layers[il].attn_k_norm_b,
@@ -8234,6 +8238,9 @@ struct llm_build_mpt : public llm_graph_context {
 
                 // Q/K Layernorm
                 if (model.layers[il].attn_q_norm) {
+                    Qcur = ggml_reshape_2d(ctx0, Qcur, n_embd_head*n_head,    n_tokens);
+                    Kcur = ggml_reshape_2d(ctx0, Kcur, n_embd_head*n_head_kv, n_tokens);
+
                     Qcur = build_norm(Qcur,
                             model.layers[il].attn_q_norm,
                             model.layers[il].attn_q_norm_b,
Repository: huggingface/transformers
Pull request: Higgs modules_to_not_convert standardization

# What does this PR do?

Standardizes the way higgs quantization uses the `module_to_not_convert` attribute

Changed files:
filename: src/transformers/integrations/higgs.py
patch: @@ -554,6 +554,7 @@ def replace_with_higgs_linear(
     quantization_config=None,
     current_key_name=None,
     has_been_replaced=False,
+    modules_to_not_convert=None,
 ):
     """
     Public method that recursively replaces the Linear layers of the given model with HIGGS quantized layers.
@@ -582,7 +583,7 @@ def replace_with_higgs_linear(
         if isinstance(module, nn.Linear):
             # Check if the current key is not in the `quantization_config.modules_to_not_convert`
             current_key_name_str = ".".join(current_key_name)
-            if not any(current_key_name_str.endswith(key) for key in quantization_config.modules_to_not_convert):
+            if not any(current_key_name_str.endswith(key) for key in modules_to_not_convert):
                 with init_empty_weights():
                     in_features = module.in_features
                     out_features = module.out_features
@@ -607,6 +608,7 @@ def replace_with_higgs_linear(
                 quantization_config=quantization_config,
                 current_key_name=current_key_name,
                 has_been_replaced=has_been_replaced,
+                modules_to_not_convert=modules_to_not_convert,
             )
         # Remove the last key for recursion
         current_key_name.pop(-1)

filename: src/transformers/quantizers/quantizer_higgs.py
patch: @@ -123,13 +123,19 @@ def create_quantized_param(
     def _process_model_before_weight_loading(
         self,
         model: "PreTrainedModel",
+        keep_in_fp32_modules: Optional[list[str]] = None,
         **kwargs,
     ):
         from ..integrations import replace_with_higgs_linear
 
+        self.modules_to_not_convert = self.get_modules_to_not_convert(
+            model, self.quantization_config.modules_to_not_convert, keep_in_fp32_modules
+        )
+
         replace_with_higgs_linear(
             model,
             quantization_config=self.quantization_config,
+            modules_to_not_convert=self.modules_to_not_convert,
         )
         model.config.quantization_config = self.quantization_config
Repository: ollama/ollama
Pull request: Some simple modelfile examples

Added 3 modelfiles as examples. Also update development.doc, but wondering if i should also get rid of dev altogether.

Changed files:
filename: docs/development.md
patch: @@ -3,13 +3,13 @@
 Install required tools:
 
 ```
-brew install cmake go node
+brew install go
 ```
 
-Then run `make`:
+Then build ollama:
 
 ```
-make
+go build .
 ```
 
 Now you can run `ollama`:

filename: examples/modelfiles/midjourneyprompter
patch: @@ -0,0 +1,14 @@
+# Modelfile for creating a Midjourney prompts from a topic
+# Run `ollama create mj -f pathtofile` and then `ollama run mj` and enter a topic
+
+FROM library/nous-hermes:latest
+PROMPT """
+{{- if not .Context }}
+### System:
+Embrace your role as an AI-powered creative assistant, employing Midjourney to manifest compelling AI-generated art. I will outline a specific image concept, and in response, you must produce an exhaustive, multifaceted prompt for Midjourney, ensuring every detail of the original concept is represented in your instructions. Midjourney doesn't do well with text, so after the prompt, give me instructions that I can use to create the titles in a image editor.
+{{- end }}
+### Instruction:
+{{ .Prompt }}
+
+### Response:
+"""
\ No newline at end of file

filename: examples/modelfiles/recipemaker
patch: @@ -0,0 +1,13 @@
+# Modelfile for creating a recipe from a list of ingredients
+# Run `ollama create recipemaker -f pathtofile` and then `ollama run recipemaker` and feed it lists of ingredients to create recipes around.
+FROM library/nous-hermes:latest
+PROMPT """
+{{- if not .Context }}
+### System:
+The instruction will be a list of ingredients. You should generate a recipe that can be made in less than an hour. You can also include ingredients that most people will find in their pantry every day. The recipe should be 4 people and you should include a description of what the meal will taste like
+{{- end }}
+### Instruction:
+{{ .Prompt }}
+
+### Response:
+"""

filename: examples/modelfiles/tweetwriter
patch: @@ -0,0 +1,14 @@
+# Modelfile for creating a tweet from a topic
+# Run `ollama create tweetwriter -f pathtofile` and then `ollama run tweetwriter` and enter a topic 
+
+FROM library/nous-hermes:latest
+PROMPT """
+{{- if not .Context }}
+### System:
+You are a content marketer who needs to come up with a short but succinct tweet. Make sure to include the appropriate hashtags and links. Sometimes when appropriate, describe a meme that can be includes as well. All answers should be in the form of a tweet which has a max size of 280 characters. Every instruction will be the topic to create a tweet about.
+{{- end }}
+### Instruction:
+{{ .Prompt }}
+
+### Response:
+"""
\ No newline at end of file
Repository: electron/electron
Pull request: perf: avoid redundant map lookup in `WebFrameMain` constructor

#### Description of Change

Another PR to remove another redundant map lookup. :slightly_smiling_face: 

This one was in the `WebFrameMain` constructor.

#### Checklist

- [x] PR description included and stakeholders cc'd
- [x] `npm test` passes
- [x] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples).

#### Release Notes

Notes: none

Changed files:
filename: shell/browser/api/electron_api_web_frame_main.cc
patch: @@ -155,8 +155,8 @@ WebFrameMain::WebFrameMain(content::RenderFrameHost* rfh)
   if (!render_frame_detached_)
     GetFrameTreeNodeIdMap().emplace(frame_tree_node_id_, this);
 
-  DCHECK(!GetFrameTokenMap().contains(frame_token_));
-  GetFrameTokenMap().emplace(frame_token_, this);
+  const auto [_, inserted] = GetFrameTokenMap().emplace(frame_token_, this);
+  DCHECK(inserted);
 
   // WebFrameMain should only be created for active or unloading frames.
   DCHECK(GetLifecycleState(rfh) == LifecycleState::kActive ||
Repository: facebook/react
Pull request: Remove RTR from ReactCreateRef-test

## Summary

Cleaning up internal usage of ReactTestRenderer

## How did you test this change?

`yarn test packages/react/src/__tests__/ReactCreateRef-test.js`


Changed files:
filename: packages/react/src/__tests__/ReactCreateRef-test.js
patch: @@ -10,14 +10,16 @@
 'use strict';
 
 let React;
-let ReactTestRenderer;
+let ReactDOM;
+let ReactDOMClient;
 
 describe('ReactCreateRef', () => {
   beforeEach(() => {
     jest.resetModules();
 
     React = require('react');
-    ReactTestRenderer = require('react-test-renderer');
+    ReactDOM = require('react-dom');
+    ReactDOMClient = require('react-dom/client');
   });
 
   it('should warn in dev if an invalid ref object is provided', () => {
@@ -31,12 +33,15 @@ describe('ReactCreateRef', () => {
       }
     }
 
+    const root = ReactDOMClient.createRoot(document.createElement('div'));
     expect(() =>
-      ReactTestRenderer.create(
-        <Wrapper>
-          <div ref={{}} />
-        </Wrapper>,
-      ),
+      ReactDOM.flushSync(() => {
+        root.render(
+          <Wrapper>
+            <div ref={{}} />
+          </Wrapper>,
+        );
+      }),
     ).toErrorDev(
       'Unexpected ref object provided for div. ' +
         'Use either a ref-setter function or React.createRef().\n' +
@@ -45,11 +50,13 @@ describe('ReactCreateRef', () => {
     );
 
     expect(() =>
-      ReactTestRenderer.create(
-        <Wrapper>
-          <ExampleComponent ref={{}} />
-        </Wrapper>,
-      ),
+      ReactDOM.flushSync(() => {
+        root.render(
+          <Wrapper>
+            <ExampleComponent ref={{}} />
+          </Wrapper>,
+        );
+      }),
     ).toErrorDev(
       'Unexpected ref object provided for ExampleComponent. ' +
         'Use either a ref-setter function or React.createRef().\n' +
Repository: denoland/deno
Pull request: fix(ext/node): fix error type in DiffieHellman constructor

This updates the error type and message in DiffieHellman constructor and enables `test-crypto-dh-errors.js`

towards #29637 

Changed files:
filename: ext/node/polyfills/internal/crypto/diffiehellman.ts
patch: @@ -117,7 +117,10 @@ export class DiffieHellmanImpl {
       // The supplied parameter is our primeLength, generate a suitable prime.
       this.#primeLength = sizeOrKey as number;
       if (this.#primeLength < 2) {
-        throw new NodeError("ERR_OSSL_BN_BITS_TOO_SMALL", "bits too small");
+        throw new NodeError(
+          "ERR_OSSL_DH_MODULUS_TOO_SMALL",
+          "modulus too small",
+        );
       }
 
       this.#prime = Buffer.from(

filename: tests/node_compat/config.jsonc
patch: @@ -43,7 +43,6 @@
       "test-console-formatTime.js",
       "test-console-instance.js",
       "test-console-tty-colors.js",
-      "test-crypto-dh-errors.js",
       "test-crypto-hash.js",
       "test-crypto-hkdf.js",
       "test-crypto-hmac.js",

filename: tests/node_compat/test/parallel/test-crypto-dh-errors.js
patch: @@ -2,7 +2,7 @@
 // deno-lint-ignore-file
 
 // Copyright Joyent and Node contributors. All rights reserved. MIT license.
-// Taken from Node 20.11.1
+// Taken from Node 23.9.0
 // This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
 
 'use strict';
@@ -12,6 +12,7 @@ if (!common.hasCrypto)
 
 const assert = require('assert');
 const crypto = require('crypto');
+const { hasOpenSSL3 } = require('../common/crypto');
 
 // https://github.com/nodejs/node/issues/32738
 // XXX(bnoordhuis) validateInt32() throwing ERR_OUT_OF_RANGE and RangeError
@@ -31,7 +32,7 @@ assert.throws(() => crypto.createDiffieHellman('abcdef', 13.37), {
 });
 
 for (const bits of [-1, 0, 1]) {
-  if (common.hasOpenSSL3) {
+  if (hasOpenSSL3) {
     assert.throws(() => crypto.createDiffieHellman(bits), {
       code: 'ERR_OSSL_DH_MODULUS_TOO_SMALL',
       name: 'Error',
@@ -50,7 +51,7 @@ for (const g of [-1, 1]) {
   const ex = {
     code: 'ERR_OSSL_DH_BAD_GENERATOR',
     name: 'Error',
-    message: /bad generator/,
+    message: /(?:bad[_ ]generator)/i,
   };
   assert.throws(() => crypto.createDiffieHellman('abcdef', g), ex);
   assert.throws(() => crypto.createDiffieHellman('abcdef', 'hex', g), ex);
@@ -62,7 +63,7 @@ for (const g of [Buffer.from([]),
   const ex = {
     code: 'ERR_OSSL_DH_BAD_GENERATOR',
     name: 'Error',
-    message: /bad generator/,
+    message: /(?:bad[_ ]generator)/i,
   };
   assert.throws(() => crypto.createDiffieHellman('abcdef', g), ex);
   assert.throws(() => crypto.createDiffieHellman('abcdef', 'hex', g), ex);
Repository: ggml-org/llama.cpp
Pull request: vulkan: Fix FA coopmat1 invalid array indexing

When computing sinks, the cm1 shader was looping r from 0 to Br rather than to rows_per_thread. I must have copied this from the scalar path (where it is correct), and somehow it wasn't causing failures on current drivers.


Changed files:
filename: ggml/src/ggml-vulkan/vulkan-shaders/flash_attn_cm1.comp
patch: @@ -358,8 +358,8 @@ void main() {
     }
 
     if ((p.mask_n_head_log2 & SINK_ENABLE_BIT) != 0) {
-        [[unroll]] for (uint32_t r = 0; r < Br; ++r) {
-            float sink = perElemOpGetSink(r, 0u, ACC_TYPE(0), iq2);
+        [[unroll]] for (uint32_t r = 0; r < rows_per_thread; ++r) {
+            float sink = perElemOpGetSink(tile_row(r), 0u, ACC_TYPE(0), iq2);
 
             float ms = 1.0f;
             float vs = 1.0f;
Repository: vercel/next.js
Pull request: Turbopack: fix glob with empty alternative branch

This came up during NFT where the ignore pattern `'**/node_modules/react{,-dom,-dom-server-turbopack}/**/*.development.js'` wasn't applied.

Closes PACK-5165

Changed files:
filename: turbopack/crates/turbo-tasks-fs/src/glob.rs
patch: @@ -193,6 +193,8 @@ mod tests {
     #[case::alternatives_nested2("{a,b/c,d/e/{f,g/h}}", "b/c")]
     #[case::alternatives_nested3("{a,b/c,d/e/{f,g/h}}", "d/e/f")]
     #[case::alternatives_nested4("{a,b/c,d/e/{f,g/h}}", "d/e/g/h")]
+    #[case::alternatives_empty1("react{,-dom}", "react")]
+    #[case::alternatives_empty2("react{,-dom}", "react-dom")]
     #[case::alternatives_chars("[abc]", "b")]
     fn glob_match(#[case] glob: &str, #[case] path: &str) {
         let glob = Glob::parse(glob).unwrap();

filename: turbopack/crates/turbo-tasks-fs/src/globset.rs
patch: @@ -253,10 +253,13 @@ impl Tokens {
 
 fn build_alternates(re: &mut String, patterns: &Vec<Tokens>, branch_fn: fn(&[Token], &mut String)) {
     let mut parts = Vec::with_capacity(patterns.len());
+    let mut has_empty_part = false;
     for pat in patterns {
         let mut altre = String::new();
         branch_fn(pat, &mut altre);
-        if !altre.is_empty() {
+        if altre.is_empty() {
+            has_empty_part = true;
+        } else {
             parts.push(altre);
         }
     }
@@ -265,6 +268,9 @@ fn build_alternates(re: &mut String, patterns: &Vec<Tokens>, branch_fn: fn(&[Tok
     // resulting alternation '()' would be an error.
     if !parts.is_empty() {
         re.push_str("(?:");
+        if has_empty_part {
+            re.push('|');
+        }
         re.push_str(&parts.join("|"));
         re.push(')');
     }
Repository: ollama/ollama
Pull request: fix multibyte responses


Changed files:
filename: llama/llama.go
patch: @@ -78,12 +78,14 @@ llama_token llama_sample(
 */
 import "C"
 import (
+	"bytes"
 	"errors"
 	"fmt"
 	"io"
 	"os"
 	"strings"
 	"time"
+	"unicode/utf8"
 	"unsafe"
 
 	"github.com/jmorganca/ollama/api"
@@ -204,6 +206,7 @@ func (llm *llama) generate(input []C.llama_token, fn func(api.GenerateResponse))
 		context.PushLeft(int(in))
 	}
 
+	var b bytes.Buffer
 	for C.llama_get_kv_cache_token_count(llm.ctx) < C.int(llm.NumCtx) {
 		if retval := C.llama_eval(llm.ctx, unsafe.SliceData(input), C.int(len(input)), C.llama_get_kv_cache_token_count(llm.ctx), C.int(llm.NumThread)); retval != 0 {
 			return errors.New("llama: eval")
@@ -216,13 +219,17 @@ func (llm *llama) generate(input []C.llama_token, fn func(api.GenerateResponse))
 			return err
 		}
 
-		// call the callback
-		fn(api.GenerateResponse{
-			Response: llm.detokenize(token),
-		})
+		b.WriteString(llm.detokenize(token))
+		if utf8.Valid(b.Bytes()) || b.Len() >= utf8.UTFMax {
+			// call the callback
+			fn(api.GenerateResponse{
+				Response: b.String(),
+			})
 
-		output.PushLeft(token)
-		context.PushLeft(int(token))
+			output.PushLeft(token)
+			context.PushLeft(int(token))
+			b.Reset()
+		}
 
 		input = []C.llama_token{token}
 	}
Repository: ollama/ollama
Pull request: fix race

block on write which only returns when the channel is closed. this is contrary to the previous arrangement where the handler may return but the stream hasn't finished writing. it can lead to the client receiving unexpected responses (since the request has been handled) or worst case a nil-pointer dereference as the stream tries to flush a nil writer

Changed files:
filename: server/routes.go
patch: @@ -58,9 +58,6 @@ func generate(c *gin.Context) {
 		req.Model = path.Join(cacheDir(), "models", req.Model+".bin")
 	}
 
-	ch := make(chan any)
-	go stream(c, ch)
-
 	templateNames := make([]string, 0, len(templates.Templates()))
 	for _, template := range templates.Templates() {
 		templateNames = append(templateNames, template.Name())
@@ -84,21 +81,21 @@ func generate(c *gin.Context) {
 	}
 	defer llm.Close()
 
-	fn := func(r api.GenerateResponse) {
-		r.Model = req.Model
-		r.CreatedAt = time.Now().UTC()
-		if r.Done {
-			r.TotalDuration = time.Since(start)
-		}
-
-		ch <- r
-	}
-
-	if err := llm.Predict(req.Context, req.Prompt, fn); err != nil {
-		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
-		return
-	}
+	ch := make(chan any)
+	go func() {
+		defer close(ch)
+		llm.Predict(req.Context, req.Prompt, func(r api.GenerateResponse) {
+			r.Model = req.Model
+			r.CreatedAt = time.Now().UTC()
+			if r.Done {
+				r.TotalDuration = time.Since(start)
+			}
+
+			ch <- r
+		})
+	}()
 
+	streamResponse(c, ch)
 }
 
 func pull(c *gin.Context) {
@@ -133,20 +130,18 @@ func pull(c *gin.Context) {
 	}
 
 	ch := make(chan any)
-	go stream(c, ch)
-
-	fn := func(total, completed int64) {
-		ch <- api.PullProgress{
-			Total:     total,
-			Completed: completed,
-			Percent:   float64(completed) / float64(total) * 100,
-		}
-	}
+	go func() {
+		defer close(ch)
+		saveModel(remote, func(total, completed int64) {
+			ch <- api.PullProgress{
+				Total:     total,
+				Completed: completed,
+				Percent:   float64(completed) / float64(total) * 100,
+			}
+		})
+	}()
 
-	if err := saveModel(remote, fn); err != nil {
-		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
-		return
-	}
+	streamResponse(c, ch)
 }
 
 func Serve(ln net.Listener) error {
@@ -179,7 +174,7 @@ func matchRankOne(source string, targets []string) (bestMatch string, bestRank i
 	return
 }
 
-func stream(c *gin.Context, ch chan any) {
+func streamResponse(c *gin.Context, ch chan any) {
 	c.Stream(func(w io.Writer) bool {
 		val, ok := <-ch
 		if !ok {