Fix(Go): make Baidu Encode fail loudly on malformed responses (#14721)

### 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)
This commit is contained in:
Joseff
2026-05-13 00:54:00 -04:00
committed by GitHub
parent 8b6dd6a5c2
commit 733d75d6a7

View File

@@ -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