Repository: ollama/ollama
File: runner/ollamarunner/runner.go (Go)

Code before:
			found = true
			break
		}
	}
	s.mu.Unlock()

	if !found {
		s.seqsSem.Release(1)
		http.Error(w, "could not find an available sequence", http.StatusInternalServerError)
		return
	}

	for {
		select {
		case <-r.Context().Done():
			close(seq.quit)
			return
		case content, ok := <-seq.responses:
			if ok {
				var thinking string
				var err error
				content, thinking, err = parser.AddContent(content)
				if err != nil {
					fmt.Println("err", err)
					http.Error(w, err.Error(), http.StatusInternalServerError)
					seq.doneReason = llm.DoneReasonTokenRepeatLimit
					close(seq.quit)
					return
				}

				if err := json.NewEncoder(w).Encode(&llm.CompletionResponse{
					Content:  content,
					Thinking: thinking,
				}); err != nil {
					http.Error(w, fmt.Sprintf("failed to encode response: %v", err), http.StatusInternalServerError)
					close(seq.quit)
					return
				}

				flusher.Flush()
			} else {
				toolCalls := parser.Drain()
				fmt.Println("toolCalls", toolCalls)
				if err := json.NewEncoder(w).Encode(&llm.CompletionResponse{
					ToolCalls:          toolCalls,
					Done:               true,
					DoneReason:         seq.doneReason,
					PromptEvalCount:    seq.numPromptInputs,
					PromptEvalDuration: seq.startGenerationTime.Sub(seq.startProcessingTime),
					EvalCount:          seq.numPredicted,
					EvalDuration:       time.Since(seq.startGenerationTime),

Review comment (suggestion):
It's a little bit odd to set the `doneReason` to a specific reason for all errors. One possible solution is to check for a specific error value being returned and use that to set doneReason (or a generic one otherwise). Or if there will be a lot of reasons, just pass it back directly `AddContent`.

Code after:
			found = true
			break
		}
	}
	s.mu.Unlock()

	if !found {
		s.seqsSem.Release(1)
		http.Error(w, "could not find an available sequence", http.StatusInternalServerError)
		return
	}

	for {
		select {
		case <-r.Context().Done():
			close(seq.quit)
			return
		case content, ok := <-seq.responses:
			if ok {
				var thinking string
				var err error
				content, thinking, err = tokenParser.AddContent(content)
				if err != nil {
					http.Error(w, err.Error(), http.StatusInternalServerError)
					close(seq.quit)
					return
				}

				if err := json.NewEncoder(w).Encode(&llm.CompletionResponse{
					Content:  content,
					Thinking: thinking,
				}); err != nil {
					http.Error(w, fmt.Sprintf("failed to encode response: %v", err), http.StatusInternalServerError)
					close(seq.quit)
					return
				}

				flusher.Flush()
			} else {
				toolCalls := tokenParser.Drain()
				if err := json.NewEncoder(w).Encode(&llm.CompletionResponse{
					ToolCalls:          toolCalls,
					Done:               true,
					DoneReason:         seq.doneReason,
					PromptEvalCount:    seq.numPromptInputs,
					PromptEvalDuration: seq.startGenerationTime.Sub(seq.startProcessingTime),
					EvalCount:          seq.numPredicted,
					EvalDuration:       time.Since(seq.startGenerationTime),
				}); err != nil {
					http.Error(w, fmt.Sprintf("failed to encode final response: %v", err), http.StatusInternalServerError)
				}