Repository: ollama/ollama
Pull request: Fix hanging issue when sending empty content

This fixes an issue where the prompt would be templated as an empty string `""`.

Fixes https://github.com/ollama/ollama/issues/2397

```shell
# loads model
% curl http://localhost:11434/api/chat -d '{
  "model": "llama2",
  "messages": [],
  "stream": false           
}'
{"model":"llama2","created_at":"2024-02-07T23:21:14.816749Z","message":{"role":"assistant","content":""},"done":true}
```

```shell
# loads model
% curl http://localhost:11434/api/chat -d '{
  "model": "llama2",
  "messages": [{ "role": "user", "content": ""}],
  "stream": false
}'
{"model":"llama2","created_at":"2024-02-07T23:20:28.175454Z","message": {"role":"assistant","content":""},"done":true}
```

```shell
# still works
% curl http://localhost:11434/api/chat -d '{
  "model": "llama2",
  "messages": [{ "role": "system", "content": "sing me a song!"}],
  "stream": false
}'
{"model":"llama2","created_at":"2024-02-07T23:19:01.582144Z","message":{"role":"assistant","content":"Of course, I'd be happy to sing you a song! *clears throat* Here we go:\n\n\"Oh, the stars are shining bright and bold,\nA celestial show, so fine and cold.\nThe moon is smiling, its light so pure,\nA gentle melody, for you I endure.\n\nIn the night's embrace, I find my peace,\nThe world outside, a distant release.\nI lose myself in the music of the night,\nAnd let my spirit take flight.\n\nSo come and join me, in this song so bright,\nTogether we'll dance, under the stars' delight.\nWith every note, our hearts will be as one,\nIn this magical moment, we'll have fun.\"\n\nHow was that? Did you enjoy it?"},"done":true,"total_duration":5454697000,"load_duration":1188958,"prompt_eval_duration":177470000,"eval_count":182,"eval_duration":5275568000}
```


Changed files:
filename: server/images.go
patch: @@ -181,16 +181,19 @@ func (m *Model) ChatPrompts(msgs []api.Message) (*ChatHistory, error) {
 			}
 
 			currentVars.Prompt = msg.Content
-			for i := range msg.Images {
-				id := len(images) + i
-				currentVars.Prompt += fmt.Sprintf(" [img-%d]", id)
-				currentVars.Images = append(currentVars.Images, llm.ImageData{
-					ID:   id,
-					Data: msg.Images[i],
-				})
-			}
 
-			images = append(images, currentVars.Images...)
+			if len(m.ProjectorPaths) > 0 {
+				for i := range msg.Images {
+					id := len(images) + i
+					currentVars.Prompt += fmt.Sprintf(" [img-%d]", id)
+					currentVars.Images = append(currentVars.Images, llm.ImageData{
+						ID:   id,
+						Data: msg.Images[i],
+					})
+				}
+
+				images = append(images, currentVars.Images...)
+			}
 		case "assistant":
 			currentVars.Response = msg.Content
 			prompts = append(prompts, currentVars)

filename: server/routes.go
patch: @@ -1136,18 +1136,6 @@ func ChatHandler(c *gin.Context) {
 		return
 	}
 
-	// an empty request loads the model
-	if len(req.Messages) == 0 {
-		resp := api.ChatResponse{
-			CreatedAt: time.Now().UTC(),
-			Model:     req.Model,
-			Done:      true,
-			Message:   api.Message{Role: "assistant"},
-		}
-		c.JSON(http.StatusOK, resp)
-		return
-	}
-
 	checkpointLoaded := time.Now()
 
 	chat, err := model.ChatPrompts(req.Messages)
@@ -1162,6 +1150,18 @@ func ChatHandler(c *gin.Context) {
 		return
 	}
 
+	// an empty request loads the model
+	if len(prompt) == 0 {
+		resp := api.ChatResponse{
+			CreatedAt: time.Now().UTC(),
+			Model:     req.Model,
+			Done:      true,
+			Message:   api.Message{Role: "assistant"},
+		}
+		c.JSON(http.StatusOK, resp)
+		return
+	}
+
 	slog.Debug("chat handler", "prompt", prompt)
 
 	ch := make(chan any)