From 733d75d6a740cd9b891380a32b5e48d1e6e15d37 Mon Sep 17 00:00:00 2001 From: Joseff Date: Wed, 13 May 2026 00:54:00 -0400 Subject: [PATCH] Fix(Go): make Baidu Encode fail loudly on malformed responses (#14721) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What problem does this PR solve? The Baidu (Qianfan) `Encode` method silently swallowed malformed responses. If a `data[]` item from the API was missing a field (`index`, `embedding`, or unexpected shape), the loop did `continue` instead of returning an error, leaving `nil` entries in the result slice. Callers got back partial results with no indication anything went wrong, which then crashes downstream consumers when they try to use a `nil` vector. Concrete gaps fixed: - No count-mismatch check between `data` length and input texts (only checked for empty) - No duplicate-index detection (a duplicate would silently overwrite) - No missing-index final scan - No empty-embedding rejection - No per-call context timeout - `EmbeddingConfig.Dimension` (added in #14735) was not propagated This PR replaces `map[string]interface{}` parsing with a typed `baiduEmbeddingResponse` struct, applies the standard four-layer validation (count → out-of-range → duplicate → empty → final missing-index scan), adds `context.WithTimeout(nonStreamCallTimeout)`, and forwards `embeddingConfig.Dimension` as the `dimensions` parameter (Baidu Qianfan v2 uses an OpenAI-compatible API). ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- internal/entity/models/baidu.go | 48 ++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/internal/entity/models/baidu.go b/internal/entity/models/baidu.go index 7e81995a70..470dbc3f5d 100644 --- a/internal/entity/models/baidu.go +++ b/internal/entity/models/baidu.go @@ -429,7 +429,7 @@ type baiduEmbeddingResponse struct { type baiduEmbeddingData struct { Object string `json:"object"` Embedding []float64 `json:"embedding"` - Index int `json:"index"` + Index *int `json:"index"` } type baiduUsage struct { @@ -438,6 +438,12 @@ type baiduUsage struct { } func (b *BaiduModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { + if apiConfig == nil || apiConfig.ApiKey == nil || *apiConfig.ApiKey == "" { + return nil, fmt.Errorf("api key is required") + } + if modelName == nil || *modelName == "" { + return nil, fmt.Errorf("model name is required") + } if len(texts) == 0 { return []EmbeddingData{}, nil } @@ -453,6 +459,9 @@ func (b *BaiduModel) Embed(modelName *string, texts []string, apiConfig *APIConf "model": *modelName, "input": texts, } + if embeddingConfig != nil && embeddingConfig.Dimension > 0 { + reqBody["dimensions"] = embeddingConfig.Dimension + } jsonData, err := json.Marshal(reqBody) if err != nil { @@ -487,12 +496,37 @@ func (b *BaiduModel) Embed(modelName *string, texts []string, apiConfig *APIConf return nil, fmt.Errorf("failed to parse response: %w", err) } - var embeddings []EmbeddingData - for _, dataElem := range parsed.Data { - var embeddingData EmbeddingData - embeddingData.Embedding = dataElem.Embedding - embeddingData.Index = dataElem.Index - embeddings = append(embeddings, embeddingData) + if len(parsed.Data) != len(texts) { + return nil, fmt.Errorf("expected %d embeddings, got %d", len(texts), len(parsed.Data)) + } + + embeddings := make([]EmbeddingData, len(texts)) + seen := make([]bool, len(texts)) + for _, item := range parsed.Data { + if item.Index == nil { + return nil, fmt.Errorf("missing index field in embedding item") + } + idx := *item.Index + if idx < 0 || idx >= len(texts) { + return nil, fmt.Errorf("embedding index %d out of range", idx) + } + if seen[idx] { + return nil, fmt.Errorf("duplicate embedding index %d", idx) + } + if len(item.Embedding) == 0 { + return nil, fmt.Errorf("empty embedding at index %d", idx) + } + seen[idx] = true + embeddings[idx] = EmbeddingData{ + Embedding: item.Embedding, + Index: idx, + } + } + + for i, ok := range seen { + if !ok { + return nil, fmt.Errorf("missing embedding index %d", i) + } } return embeddings, nil