Repository: ollama/ollama
File: server/routes.go (Go)

Code before:
		return
	}

	var g errgroup.Group
	embeddings := make([][]float32, len(input))
	var totalTokens int
	for i, text := range input {
		g.Go(func() error {
			embedding, tokenCount, err := r.Embedding(c.Request.Context(), text, truncate)
			if err != nil {
				return err
			}
			// TODO: this first normalization should be done by the model
			embedding = normalize(embedding)
			if req.Dimensions > 0 && req.Dimensions < len(embedding) {
				embedding = normalize(embedding[:req.Dimensions])
			}
			embeddings[i] = embedding
			atomic.AddUint64((*uint64)(unsafe.Pointer(&totalTokens)), uint64(tokenCount))
			return nil
		})
	}

	if err := g.Wait(); err != nil {
		errMsg := strings.TrimSpace(err.Error())
		if strings.Contains(errMsg, "input length exceeds the context length") {
			c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": errMsg})
		} else {
			c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": errMsg})
		}
		return
	}

	resp := api.EmbedResponse{
		Model:           req.Model,
		Embeddings:      embeddings,
		TotalDuration:   time.Since(checkpointStart),
		LoadDuration:    checkpointLoaded.Sub(checkpointStart),
		PromptEvalCount: totalTokens,
	}
	c.JSON(http.StatusOK, resp)
}

func normalize(vec []float32) []float32 {
	var sum float32
	for _, v := range vec {
		sum += v * v
	}

	norm := float32(1.0 / max(math.Sqrt(float64(sum)), 1e-12))
	for i := range vec {

Review comment (suggestion):
This one is a bit tricky. Instead of checking the error text (which could change and break this check), we'll want to bubble up a StatusError similar to how we do this in https://github.com/ollama/ollama/blob/main/server/routes.go#L283

The way we did this in chat was to return an `api.StatusError` [here](https://github.com/ollama/ollama/blob/main/llm/server.go#L1471)

A similar thing could be done here https://github.com/ollama/ollama/blob/main/llm/server.go#L1598

More on error types and wrapping errors in Go: https://gobyexample.com/errors

Code after:
	}

	var g errgroup.Group
	embeddings := make([][]float32, len(input))
	var totalTokens uint64
	for i, text := range input {
		g.Go(func() error {
			embedding, tokenCount, err := r.Embedding(c.Request.Context(), text, truncate)
			if err != nil {
				return err
			}
			// TODO: this first normalization should be done by the model
			embedding = normalize(embedding)
			if req.Dimensions > 0 && req.Dimensions < len(embedding) {
				embedding = normalize(embedding[:req.Dimensions])
			}
			embeddings[i] = embedding
			atomic.AddUint64(&totalTokens, uint64(tokenCount))
			return nil
		})
	}

	if err := g.Wait(); err != nil {
		var serr api.StatusError
		if errors.As(err, &serr) {
			c.AbortWithStatusJSON(serr.StatusCode, gin.H{"error": strings.TrimSpace(serr.ErrorMessage)})
		} else {
			c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": strings.TrimSpace(err.Error())})
		}
		return
	}

	resp := api.EmbedResponse{
		Model:           req.Model,
		Embeddings:      embeddings,
		TotalDuration:   time.Since(checkpointStart),
		LoadDuration:    checkpointLoaded.Sub(checkpointStart),
		PromptEvalCount: int(totalTokens),
	}
	c.JSON(http.StatusOK, resp)
}

func normalize(vec []float32) []float32 {
	var sum float32
	for _, v := range vec {
		sum += v * v
	}

	norm := float32(1.0 / max(math.Sqrt(float64(sum)), 1e-12))
	for i := range vec {
		vec[i] *= norm