From c55e23e7e263c60715aedd7716bcff19e3b38e53 Mon Sep 17 00:00:00 2001 From: Jin Hai Date: Mon, 11 May 2026 14:45:30 +0800 Subject: [PATCH] Go: refactor embedding interface (#14757) ### What problem does this PR solve? Provide embedding index according to the input text ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai --- internal/cli/response.go | 50 +++++++++- internal/cli/user_command.go | 2 +- internal/entity/models/aliyun.go | 55 +++++------ internal/entity/models/baidu.go | 83 +++++++--------- internal/entity/models/deepseek.go | 4 +- internal/entity/models/dummy.go | 4 +- internal/entity/models/gitee.go | 62 ++++++------ internal/entity/models/google.go | 11 ++- internal/entity/models/huggingface.go | 26 ++--- internal/entity/models/lmstudio.go | 46 ++------- internal/entity/models/minimax.go | 4 +- internal/entity/models/moonshot.go | 4 +- internal/entity/models/nvidia.go | 37 ++----- internal/entity/models/ollama.go | 46 ++------- internal/entity/models/openai.go | 58 +++++------ internal/entity/models/openrouter.go | 50 +++++----- internal/entity/models/siliconflow.go | 115 ++++++++-------------- internal/entity/models/types.go | 13 +-- internal/entity/models/vllm.go | 42 +++----- internal/entity/models/volcengine.go | 55 +++++++---- internal/entity/models/xai.go | 4 +- internal/entity/models/zhipu-ai.go | 136 ++++++++++++++------------ internal/handler/providers.go | 4 +- internal/service/model_service.go | 36 ++----- internal/service/nlp/retrieval.go | 4 +- internal/service/skill_indexer.go | 33 ++++--- internal/service/skill_search.go | 8 +- uv.lock | 18 +--- 28 files changed, 443 insertions(+), 567 deletions(-) diff --git a/internal/cli/response.go b/internal/cli/response.go index 4331a76adb..b505a7a53f 100644 --- a/internal/cli/response.go +++ b/internal/cli/response.go @@ -277,6 +277,48 @@ func (r *KeyValueResponse) PrintOut() { } } +type EmbeddingData struct { + Index int `json:"index"` + Embedding []float64 `json:"embedding"` +} + +type EmbeddingsResponse struct { + Code int `json:"code"` + Data []EmbeddingData `json:"data"` + Message string `json:"message"` + Duration float64 + OutputFormat OutputFormat +} + +func (r *EmbeddingsResponse) Type() string { + return "common" +} + +func (r *EmbeddingsResponse) TimeCost() float64 { + return r.Duration +} + +func (r *EmbeddingsResponse) SetOutputFormat(format OutputFormat) { + r.OutputFormat = format +} + +func (r *EmbeddingsResponse) PrintOut() { + var data []map[string]interface{} + for _, embedding := range r.Data { + data = append(data, map[string]interface{}{ + "index": formatValue(embedding.Index), + "dimension": len(embedding.Embedding), + }) + } + + if r.Code == 0 { + PrintTableSimpleByFormat(data, r.OutputFormat) + } else { + fmt.Println("ERROR") + fmt.Printf("%d, %s\n", r.Code, r.Message) + } +} + // ==================== ContextEngine Commands ==================== // ContextListResponse represents the response for ls command @@ -325,9 +367,9 @@ func (r *ContextSearchResponse) PrintOut() { // ContextCatResponse represents the response for cat command type ContextCatResponse struct { - Code int `json:"code"` - Content string `json:"content"` - Message string `json:"message"` + Code int `json:"code"` + Content string `json:"content"` + Message string `json:"message"` Duration float64 OutputFormat OutputFormat } @@ -343,5 +385,3 @@ func (r *ContextCatResponse) PrintOut() { fmt.Printf("%d, %s\n", r.Code, r.Message) } } - - diff --git a/internal/cli/user_command.go b/internal/cli/user_command.go index a8394e40a6..14a058aa25 100644 --- a/internal/cli/user_command.go +++ b/internal/cli/user_command.go @@ -1838,7 +1838,7 @@ func (c *RAGFlowClient) EmbedUserText(cmd *Command) (ResponseIf, error) { if resp.StatusCode != 200 { return nil, fmt.Errorf("failed to embed text: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) } - var result CommonResponse + var result EmbeddingsResponse if err = json.Unmarshal(resp.Body, &result); err != nil { return nil, fmt.Errorf("embed text failed: invalid JSON (%w)", err) } diff --git a/internal/entity/models/aliyun.go b/internal/entity/models/aliyun.go index 3ec313e1f0..325eb0ac6d 100644 --- a/internal/entity/models/aliyun.go +++ b/internal/entity/models/aliyun.go @@ -362,16 +362,28 @@ func (z *AliyunModel) ChatStreamlyWithSender(modelName string, messages []Messag } type aliyunEmbeddingResponse struct { - Data []struct { - Index int `json:"index"` - Embedding []interface{} `json:"embedding"` - } `json:"data"` + Data []EmbeddingData `json:"data"` + Model string `json:"model"` + Object string `json:"object"` + Usage aliyunUsage `json:"usage"` + ID string `json:"id"` } -// Encode encodes a list of texts into embeddings -func (z *AliyunModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +type aliyunEmbeddingData struct { + Embedding []float64 `json:"embedding"` + Index int `json:"index"` + Object string `json:"object"` +} + +type aliyunUsage struct { + PromptTokens int `json:"prompt_tokens"` + TotalTokens int `json:"total_tokens"` +} + +// Embed embeds a list of texts into embeddings +func (z *AliyunModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { if len(texts) == 0 { - return [][]float64{}, nil + return []EmbeddingData{}, nil } if apiConfig == nil || apiConfig.ApiKey == nil || *apiConfig.ApiKey == "" { @@ -440,29 +452,12 @@ func (z *AliyunModel) Encode(modelName *string, texts []string, apiConfig *APICo return nil, fmt.Errorf("failed to parse response: %w", err) } - embeddings := make([][]float64, len(texts)) - for _, item := range parsed.Data { - if item.Index < 0 || item.Index >= len(texts) { - return nil, fmt.Errorf("unexpected embedding index %d for %d inputs", item.Index, len(texts)) - } - vec := make([]float64, len(item.Embedding)) - for j, v := range item.Embedding { - switch val := v.(type) { - case float64: - vec[j] = val - case float32: - vec[j] = float64(val) - default: - return nil, fmt.Errorf("unexpected embedding value type at item %d index %d", item.Index, j) - } - } - embeddings[item.Index] = vec - } - - for i, vec := range embeddings { - if vec == nil { - return nil, fmt.Errorf("missing embedding for input at index %d", i) - } + var embeddings []EmbeddingData + for _, dataElem := range parsed.Data { + var embeddingData EmbeddingData + embeddingData.Embedding = dataElem.Embedding + embeddingData.Index = dataElem.Index + embeddings = append(embeddings, embeddingData) } return embeddings, nil diff --git a/internal/entity/models/baidu.go b/internal/entity/models/baidu.go index ad24ced9b4..15fb4f4284 100644 --- a/internal/entity/models/baidu.go +++ b/internal/entity/models/baidu.go @@ -385,14 +385,14 @@ func (b *BaiduModel) ChatStreamlyWithSender(modelName string, messages []Message reasoningContent, ok := delta["reasoning_content"].(string) if ok && reasoningContent != "" { - if err := sender(nil, &reasoningContent); err != nil { + if err = sender(nil, &reasoningContent); err != nil { return err } } content, ok := delta["content"].(string) if ok && content != "" { - if err := sender(&content, nil); err != nil { + if err = sender(&content, nil); err != nil { return err } } @@ -412,9 +412,29 @@ func (b *BaiduModel) ChatStreamlyWithSender(modelName string, messages []Message return scanner.Err() } -func (b *BaiduModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +type baiduEmbeddingResponse struct { + ID string `json:"id"` + Object string `json:"object"` + Created int64 `json:"created"` + Data []baiduEmbeddingData `json:"data"` + Model string `json:"model"` + Usage baiduUsage `json:"usage"` +} + +type baiduEmbeddingData struct { + Object string `json:"object"` + Embedding []float64 `json:"embedding"` + Index int `json:"index"` +} + +type baiduUsage struct { + PromptTokens int `json:"prompt_tokens"` + TotalTokens int `json:"total_tokens"` +} + +func (b *BaiduModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { if len(texts) == 0 { - return [][]float64{}, nil + return []EmbeddingData{}, nil } var region = "default" @@ -457,52 +477,17 @@ func (b *BaiduModel) Encode(modelName *string, texts []string, apiConfig *APICon return nil, fmt.Errorf("Baidu embedding API error: status %d, body: %s", resp.StatusCode, string(body)) } - var result map[string]interface{} - if err = json.Unmarshal(body, &result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) + var parsed baiduEmbeddingResponse + if err = json.Unmarshal(body, &parsed); err != nil { + return nil, fmt.Errorf("failed to parse response: %w", err) } - dataObj, ok := result["data"].([]interface{}) - if !ok || len(dataObj) == 0 { - return nil, fmt.Errorf("Baidu embedding response contains no data: %s", string(body)) - } - - embeddings := make([][]float64, len(texts)) - - for _, item := range dataObj { - dataMap, ok := item.(map[string]interface{}) - if !ok { - continue - } - - indexFloat, ok := dataMap["index"].(float64) - if !ok { - continue - } - index := int(indexFloat) - - if index < 0 || index >= len(texts) { - continue - } - - embeddingSlice, ok := dataMap["embedding"].([]interface{}) - if !ok { - continue - } - - embedding := make([]float64, len(embeddingSlice)) - for j, v := range embeddingSlice { - switch val := v.(type) { - case float64: - embedding[j] = val - case float32: - embedding[j] = float64(val) - default: - return nil, fmt.Errorf("unexpected embedding value type") - } - } - - embeddings[index] = embedding + var embeddings []EmbeddingData + for _, dataElem := range parsed.Data { + var embeddingData EmbeddingData + embeddingData.Embedding = dataElem.Embedding + embeddingData.Index = dataElem.Index + embeddings = append(embeddings, embeddingData) } return embeddings, nil @@ -567,7 +552,7 @@ func (b *BaiduModel) Rerank(modelName *string, query string, documents []string, } `json:"results"` } - if err := json.Unmarshal(body, &rerankResp); err != nil { + if err = json.Unmarshal(body, &rerankResp); err != nil { return nil, fmt.Errorf("failed to decode response: %w", err) } diff --git a/internal/entity/models/deepseek.go b/internal/entity/models/deepseek.go index dc06ebbfbd..1f4e107e42 100644 --- a/internal/entity/models/deepseek.go +++ b/internal/entity/models/deepseek.go @@ -415,8 +415,8 @@ func (z *DeepSeekModel) ChatStreamlyWithSender(modelName string, messages []Mess return scanner.Err() } -// Encode encodes a list of texts into embeddings -func (z *DeepSeekModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +// Embed embeds a list of texts into embeddings +func (z *DeepSeekModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { return nil, fmt.Errorf("%s, no such method", z.Name()) } diff --git a/internal/entity/models/dummy.go b/internal/entity/models/dummy.go index ffc0f9f4b7..149c69af73 100644 --- a/internal/entity/models/dummy.go +++ b/internal/entity/models/dummy.go @@ -52,8 +52,8 @@ func (z *DummyModel) ChatStreamlyWithSender(modelName string, messages []Message return fmt.Errorf("not implemented") } -// Encode encodes a list of texts into embeddings -func (z *DummyModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +// Embed embeds a list of texts into embeddings +func (z *DummyModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { return nil, fmt.Errorf("not implemented") } diff --git a/internal/entity/models/gitee.go b/internal/entity/models/gitee.go index 417b7e2ddf..335ec63484 100644 --- a/internal/entity/models/gitee.go +++ b/internal/entity/models/gitee.go @@ -29,13 +29,6 @@ import ( "time" ) -type giteeEmbeddingResponse struct { - Data []struct { - Index int `json:"index"` - Embedding []interface{} `json:"embedding"` - } `json:"data"` -} - // GiteeModel implements ModelDriver for Gitee type GiteeModel struct { BaseURL map[string]string @@ -405,10 +398,28 @@ func (z *GiteeModel) ChatStreamlyWithSender(modelName string, messages []Message return scanner.Err() } -// Encode encodes a list of texts into embeddings -func (z *GiteeModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +type giteeEmbeddingResponse struct { + Object string `json:"object"` + Data []giteeEmbeddingData `json:"data"` + Model string `json:"model"` + Usage giteeUsage `json:"usage"` +} + +type giteeEmbeddingData struct { + Object string `json:"object"` + Embedding []float64 `json:"embedding"` + Index int `json:"index"` +} + +type giteeUsage struct { + PromptTokens int `json:"prompt_tokens"` + TotalTokens int `json:"total_tokens"` +} + +// Embed embeds a list of texts into embeddings +func (z *GiteeModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { if len(texts) == 0 { - return [][]float64{}, nil + return []EmbeddingData{}, nil } if apiConfig == nil || apiConfig.ApiKey == nil || *apiConfig.ApiKey == "" { @@ -480,29 +491,12 @@ func (z *GiteeModel) Encode(modelName *string, texts []string, apiConfig *APICon return nil, fmt.Errorf("failed to parse response: %w", err) } - embeddings := make([][]float64, len(texts)) - for _, item := range parsed.Data { - if item.Index < 0 || item.Index >= len(texts) { - return nil, fmt.Errorf("unexpected embedding index %d for %d inputs", item.Index, len(texts)) - } - vec := make([]float64, len(item.Embedding)) - for j, v := range item.Embedding { - switch val := v.(type) { - case float64: - vec[j] = val - case float32: - vec[j] = float64(val) - default: - return nil, fmt.Errorf("unexpected embedding value type at item %d index %d", item.Index, j) - } - } - embeddings[item.Index] = vec - } - - for i, vec := range embeddings { - if vec == nil { - return nil, fmt.Errorf("missing embedding for input at index %d", i) - } + var embeddings []EmbeddingData + for _, dataElem := range parsed.Data { + var embeddingData EmbeddingData + embeddingData.Embedding = dataElem.Embedding + embeddingData.Index = dataElem.Index + embeddings = append(embeddings, embeddingData) } return embeddings, nil @@ -588,7 +582,7 @@ func (z *GiteeModel) Rerank(modelName *string, query string, documents []string, } if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("Gitee rerank API error: %s, body: %s", resp.Status, string(body)) + return nil, fmt.Errorf("gitee rerank API error: %s, body: %s", resp.Status, string(body)) } var rerankResponse RerankResponse diff --git a/internal/entity/models/google.go b/internal/entity/models/google.go index a1b3a96bca..fabd51e4c3 100644 --- a/internal/entity/models/google.go +++ b/internal/entity/models/google.go @@ -259,9 +259,9 @@ func (z *GoogleModel) ChatStreamlyWithSender(modelName string, messages []Messag return err } -// Encode generates embeddings for a batch of texts using the Gemini embeddings API. +// Embed generates embeddings for a batch of texts using the Gemini embeddings API. // The SDK routes to batchEmbedContents internally, so all texts are sent in one request. -func (z *GoogleModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +func (z *GoogleModel) 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") } @@ -303,13 +303,16 @@ func (z *GoogleModel) Encode(modelName *string, texts []string, apiConfig *APICo return nil, fmt.Errorf("expected %d embeddings, got %d", len(texts), len(resp.Embeddings)) } - result := make([][]float64, len(resp.Embeddings)) + result := make([]EmbeddingData, len(resp.Embeddings)) for i, emb := range resp.Embeddings { vec := make([]float64, len(emb.Values)) for j, v := range emb.Values { vec[j] = float64(v) } - result[i] = vec + result[i] = EmbeddingData{ + Embedding: vec, + Index: i, + } } return result, nil diff --git a/internal/entity/models/huggingface.go b/internal/entity/models/huggingface.go index d1160d1c46..1dad00a565 100644 --- a/internal/entity/models/huggingface.go +++ b/internal/entity/models/huggingface.go @@ -351,15 +351,9 @@ func (h *HuggingFaceModel) ChatStreamlyWithSender(modelName string, messages []M return scanner.Err() } -type hfEmbeddingRequest struct { - Inputs []string `json:"inputs"` -} - -type hfEmbeddingResponse [][]float64 - -func (h *HuggingFaceModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +func (h *HuggingFaceModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { if len(texts) == 0 { - return [][]float64{}, nil + return []EmbeddingData{}, nil } if modelName == nil || *modelName == "" { @@ -404,12 +398,20 @@ func (h *HuggingFaceModel) Encode(modelName *string, texts []string, apiConfig * return nil, fmt.Errorf("HF embeddings API error: %s", string(body)) } - var result [][]float64 - if err = json.Unmarshal(body, &result); err != nil { - return nil, err + var parsed openaiEmbeddingResponse + if err = json.Unmarshal(body, &parsed); err != nil { + return nil, fmt.Errorf("failed to parse response: %w", err) } - return result, nil + var embeddings []EmbeddingData + for _, dataElem := range parsed.Data { + var embeddingData EmbeddingData + embeddingData.Embedding = dataElem.Embedding + embeddingData.Index = dataElem.Index + embeddings = append(embeddings, embeddingData) + } + + return embeddings, nil } func (h *HuggingFaceModel) Rerank(modelName *string, query string, documents []string, apiConfig *APIConfig, rerankConfig *RerankConfig) (*RerankResponse, error) { diff --git a/internal/entity/models/lmstudio.go b/internal/entity/models/lmstudio.go index ba55cf7247..136d8bb571 100644 --- a/internal/entity/models/lmstudio.go +++ b/internal/entity/models/lmstudio.go @@ -362,16 +362,9 @@ func (l *LmStudioModel) ChatStreamlyWithSender(modelName string, messages []Mess return scanner.Err() } -type lmstudioEmbeddingResponse struct { - Data []struct { - Index int `json:"index"` - Embedding []interface{} `json:"embedding"` - } `json:"data"` -} - -func (l *LmStudioModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +func (l *LmStudioModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { if len(texts) == 0 { - return [][]float64{}, nil + return []EmbeddingData{}, nil } if modelName == nil || *modelName == "" { @@ -434,38 +427,17 @@ func (l *LmStudioModel) Encode(modelName *string, texts []string, apiConfig *API return nil, fmt.Errorf("LM Studio embeddings API error: %s, body: %s", resp.Status, string(body)) } - var parsed lmstudioEmbeddingResponse + var parsed openaiEmbeddingResponse if err = json.Unmarshal(body, &parsed); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } - if len(parsed.Data) != len(texts) { - return nil, fmt.Errorf("lmstudio embeddings: expected %d results, got %d", len(texts), len(parsed.Data)) - } - - embeddings := make([][]float64, len(texts)) - for _, item := range parsed.Data { - if item.Index < 0 || item.Index >= len(texts) { - return nil, fmt.Errorf("unexpected embedding index %d for %d inputs", item.Index, len(texts)) - } - vec := make([]float64, len(item.Embedding)) - for j, v := range item.Embedding { - switch val := v.(type) { - case float64: - vec[j] = val - case float32: - vec[j] = float64(val) - default: - return nil, fmt.Errorf("unexpected embedding value type at item %d index %d", item.Index, j) - } - } - embeddings[item.Index] = vec - } - - for i, vec := range embeddings { - if vec == nil { - return nil, fmt.Errorf("missing embedding for input at index %d", i) - } + var embeddings []EmbeddingData + for _, dataElem := range parsed.Data { + var embeddingData EmbeddingData + embeddingData.Embedding = dataElem.Embedding + embeddingData.Index = dataElem.Index + embeddings = append(embeddings, embeddingData) } return embeddings, nil diff --git a/internal/entity/models/minimax.go b/internal/entity/models/minimax.go index d40bfef4bd..67b4e83907 100644 --- a/internal/entity/models/minimax.go +++ b/internal/entity/models/minimax.go @@ -344,8 +344,8 @@ func (z *MinimaxModel) ChatStreamlyWithSender(modelName string, messages []Messa return scanner.Err() } -// Encode encodes a list of texts into embeddings -func (z *MinimaxModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +// Embed embeds a list of texts into embeddings +func (z *MinimaxModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { return nil, fmt.Errorf("not implemented") } diff --git a/internal/entity/models/moonshot.go b/internal/entity/models/moonshot.go index 68af2fada8..2c1443251b 100644 --- a/internal/entity/models/moonshot.go +++ b/internal/entity/models/moonshot.go @@ -357,8 +357,8 @@ func (k *MoonshotModel) ChatStreamlyWithSender(modelName string, messages []Mess return scanner.Err() } -// Encode encodes a list of texts into embeddings -func (z *MoonshotModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +// Embed embeds a list of texts into embeddings +func (z *MoonshotModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { return nil, fmt.Errorf("not implemented") } diff --git a/internal/entity/models/nvidia.go b/internal/entity/models/nvidia.go index c1deac13c3..fe50dcd425 100644 --- a/internal/entity/models/nvidia.go +++ b/internal/entity/models/nvidia.go @@ -332,14 +332,14 @@ func (n *NvidiaModel) ChatStreamlyWithSender(modelName string, messages []Messag type nvidiaEmbeddingResponse struct { Data []struct { - Index int `json:"index"` - Embedding []interface{} `json:"embedding"` + Index int `json:"index"` + Embedding []float64 `json:"embedding"` } `json:"data"` } -func (n NvidiaModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +func (n NvidiaModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { if len(texts) == 0 { - return [][]float64{}, nil + return []EmbeddingData{}, nil } if apiConfig == nil || apiConfig.ApiKey == nil || *apiConfig.ApiKey == "" { @@ -412,29 +412,12 @@ func (n NvidiaModel) Encode(modelName *string, texts []string, apiConfig *APICon return nil, fmt.Errorf("failed to parse response: %w", err) } - embeddings := make([][]float64, len(texts)) - for _, item := range parsed.Data { - if item.Index < 0 || item.Index >= len(texts) { - return nil, fmt.Errorf("unexpected embedding index %d for %d inputs", item.Index, len(texts)) - } - vec := make([]float64, len(item.Embedding)) - for j, v := range item.Embedding { - switch val := v.(type) { - case float64: - vec[j] = val - case float32: - vec[j] = float64(val) - default: - return nil, fmt.Errorf("unexpected embedding value type at item %d index %d", item.Index, j) - } - } - embeddings[item.Index] = vec - } - - for i, vec := range embeddings { - if vec == nil { - return nil, fmt.Errorf("missing embedding for input at index %d", i) - } + var embeddings []EmbeddingData + for _, dataElem := range parsed.Data { + var embeddingData EmbeddingData + embeddingData.Embedding = dataElem.Embedding + embeddingData.Index = dataElem.Index + embeddings = append(embeddings, embeddingData) } return embeddings, nil diff --git a/internal/entity/models/ollama.go b/internal/entity/models/ollama.go index 3b22039c3b..d1b05588d7 100644 --- a/internal/entity/models/ollama.go +++ b/internal/entity/models/ollama.go @@ -360,16 +360,9 @@ func (o *OllamaModel) ChatStreamlyWithSender(modelName string, messages []Messag return scanner.Err() } -type ollamaEmbeddingResponse struct { - Data []struct { - Index int `json:"index"` - Embedding []interface{} `json:"embedding"` - } `json:"data"` -} - -func (o *OllamaModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +func (o *OllamaModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { if len(texts) == 0 { - return [][]float64{}, nil + return []EmbeddingData{}, nil } if modelName == nil || *modelName == "" { @@ -432,38 +425,17 @@ func (o *OllamaModel) Encode(modelName *string, texts []string, apiConfig *APICo return nil, fmt.Errorf("Ollama embeddings API error: %s, body: %s", resp.Status, string(body)) } - var parsed ollamaEmbeddingResponse + var parsed openaiEmbeddingResponse if err = json.Unmarshal(body, &parsed); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } - if len(parsed.Data) != len(texts) { - return nil, fmt.Errorf("ollama embeddings: expected %d results, got %d", len(texts), len(parsed.Data)) - } - - embeddings := make([][]float64, len(texts)) - for _, item := range parsed.Data { - if item.Index < 0 || item.Index >= len(texts) { - return nil, fmt.Errorf("unexpected embedding index %d for %d inputs", item.Index, len(texts)) - } - vec := make([]float64, len(item.Embedding)) - for j, v := range item.Embedding { - switch val := v.(type) { - case float64: - vec[j] = val - case float32: - vec[j] = float64(val) - default: - return nil, fmt.Errorf("unexpected embedding value type at item %d index %d", item.Index, j) - } - } - embeddings[item.Index] = vec - } - - for i, vec := range embeddings { - if vec == nil { - return nil, fmt.Errorf("missing embedding for input at index %d", i) - } + var embeddings []EmbeddingData + for _, dataElem := range parsed.Data { + var embeddingData EmbeddingData + embeddingData.Embedding = dataElem.Embedding + embeddingData.Index = dataElem.Index + embeddings = append(embeddings, embeddingData) } return embeddings, nil diff --git a/internal/entity/models/openai.go b/internal/entity/models/openai.go index fcacb6d22b..6461444e7b 100644 --- a/internal/entity/models/openai.go +++ b/internal/entity/models/openai.go @@ -403,24 +403,31 @@ func (z *OpenAIModel) ChatStreamlyWithSender(modelName string, messages []Messag return nil } -// openaiEmbeddingResponse is the response shape returned by -// /v1/embeddings. The "index" field gives the position of the embedding -// in the input array, which we use to keep the output order stable -// even if the API returns items in a different order. type openaiEmbeddingResponse struct { - Data []struct { - Index int `json:"index"` - Embedding []interface{} `json:"embedding"` - } `json:"data"` + Data []openrouterEmbeddingData `json:"data"` + Model string `json:"model"` + Object string `json:"object"` + Usage openrouterUsage `json:"usage"` } -// Encode turns a list of texts into embedding vectors using the +type openaiEmbeddingData struct { + Embedding []float64 `json:"embedding"` + Object string `json:"object"` + Index int `json:"index"` +} + +type openaiUsage struct { + PromptTokens int `json:"prompt_tokens"` + TotalTokens int `json:"total_tokens"` +} + +// Embed turns a list of texts into embedding vectors using the // OpenAI /v1/embeddings endpoint (e.g. text-embedding-3-small, // text-embedding-3-large, text-embedding-ada-002). The output has // one vector per input, in the same order the inputs were given. -func (z *OpenAIModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +func (z *OpenAIModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { if len(texts) == 0 { - return [][]float64{}, nil + return []EmbeddingData{}, nil } if apiConfig == nil || apiConfig.ApiKey == nil || *apiConfig.ApiKey == "" { @@ -486,29 +493,12 @@ func (z *OpenAIModel) Encode(modelName *string, texts []string, apiConfig *APICo return nil, fmt.Errorf("failed to parse response: %w", err) } - embeddings := make([][]float64, len(texts)) - for _, item := range parsed.Data { - if item.Index < 0 || item.Index >= len(texts) { - continue - } - vec := make([]float64, len(item.Embedding)) - for j, v := range item.Embedding { - switch val := v.(type) { - case float64: - vec[j] = val - case float32: - vec[j] = float64(val) - default: - return nil, fmt.Errorf("unexpected embedding value type at item %d index %d", item.Index, j) - } - } - embeddings[item.Index] = vec - } - - for i, vec := range embeddings { - if vec == nil { - return nil, fmt.Errorf("missing embedding for input at index %d", i) - } + var embeddings []EmbeddingData + for _, dataElem := range parsed.Data { + var embeddingData EmbeddingData + embeddingData.Embedding = dataElem.Embedding + embeddingData.Index = dataElem.Index + embeddings = append(embeddings, embeddingData) } return embeddings, nil diff --git a/internal/entity/models/openrouter.go b/internal/entity/models/openrouter.go index 1be3f49e56..7ebf09b5fb 100644 --- a/internal/entity/models/openrouter.go +++ b/internal/entity/models/openrouter.go @@ -352,15 +352,26 @@ func (o *OpenRouterModel) ChatStreamlyWithSender(modelName string, messages []Me } type openrouterEmbeddingResponse struct { - Data []struct { - Index int `json:"index"` - Embedding []float64 `json:"embedding"` - } `json:"data"` + Data []openrouterEmbeddingData `json:"data"` + Model string `json:"model"` + Object string `json:"object"` + Usage openrouterUsage `json:"usage"` } -func (o *OpenRouterModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +type openrouterEmbeddingData struct { + Embedding []float64 `json:"embedding"` + Object string `json:"object"` + Index int `json:"index"` +} + +type openrouterUsage struct { + PromptTokens int `json:"prompt_tokens"` + TotalTokens int `json:"total_tokens"` +} + +func (o *OpenRouterModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { if len(texts) == 0 { - return [][]float64{}, nil + return []EmbeddingData{}, nil } if modelName == nil || *modelName == "" { return nil, fmt.Errorf("model name is required") @@ -412,26 +423,17 @@ func (o *OpenRouterModel) Encode(modelName *string, texts []string, apiConfig *A return nil, fmt.Errorf("OpenRouter embedding API error: status %d, body: %s", resp.StatusCode, string(body)) } - var result openrouterEmbeddingResponse - if err = json.Unmarshal(body, &result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) + var parsed openrouterEmbeddingResponse + if err = json.Unmarshal(body, &parsed); err != nil { + return nil, fmt.Errorf("failed to parse response: %w", err) } - if len(result.Data) != len(texts) { - return nil, fmt.Errorf("expected %d embeddings, got %d", len(texts), len(result.Data)) - } - - embeddings := make([][]float64, len(texts)) - seen := make([]bool, len(texts)) - for _, item := range result.Data { - if item.Index < 0 || item.Index >= len(texts) { - return nil, fmt.Errorf("embedding index %d out of range", item.Index) - } - if seen[item.Index] { - return nil, fmt.Errorf("duplicate embedding index %d", item.Index) - } - seen[item.Index] = true - embeddings[item.Index] = item.Embedding + var embeddings []EmbeddingData + for _, dataElem := range parsed.Data { + var embeddingData EmbeddingData + embeddingData.Embedding = dataElem.Embedding + embeddingData.Index = dataElem.Index + embeddings = append(embeddings, embeddingData) } return embeddings, nil diff --git a/internal/entity/models/siliconflow.go b/internal/entity/models/siliconflow.go index 118273a8a1..3659ddef02 100644 --- a/internal/entity/models/siliconflow.go +++ b/internal/entity/models/siliconflow.go @@ -19,7 +19,6 @@ package models import ( "bufio" "bytes" - "context" "encoding/json" "fmt" "io" @@ -370,20 +369,37 @@ func (z *SiliconflowModel) ChatStreamlyWithSender(modelName string, messages []M } type siliconflowEmbeddingResponse struct { - Data []struct { - Index int `json:"index"` - Embedding []float64 `json:"embedding"` - } `json:"data"` + Object []string `json:"object"` + Model string `json:"model"` + Data []siliconflowEmbeddingData `json:"data"` + Usage siliconflowUsage `json:"usage"` +} + +type siliconflowEmbeddingData struct { + Object string `json:"object"` + Embedding []float64 `json:"embedding"` + Index int `json:"index"` +} + +type siliconflowUsage struct { + PromptTokens int `json:"prompt_tokens"` + CompletionTokens int `json:"completion_tokens"` + TotalTokens int `json:"total_tokens"` } // siliconflowMaxBatchSize is the per-request input limit documented at // https://docs.siliconflow.cn/en/api-reference/embeddings/create-embeddings. const siliconflowMaxBatchSize = 32 -func (s *SiliconflowModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +// Embed embeds a list of texts into embeddings +func (s *SiliconflowModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { if len(texts) == 0 { - return [][]float64{}, nil + return []EmbeddingData{}, nil } + if len(texts) > siliconflowMaxBatchSize { + return nil, fmt.Errorf("siliconflow supports a maximum of %d inputs per request", siliconflowMaxBatchSize) + } + if modelName == nil || *modelName == "" { return nil, fmt.Errorf("model name is required") } @@ -400,48 +416,19 @@ func (s *SiliconflowModel) Encode(modelName *string, texts []string, apiConfig * apiKey = *apiConfig.ApiKey } - dimension := 0 - if embeddingConfig != nil { - dimension = embeddingConfig.Dimension - } - - embeddings := make([][]float64, len(texts)) - for start := 0; start < len(texts); start += siliconflowMaxBatchSize { - end := start + siliconflowMaxBatchSize - if end > len(texts) { - end = len(texts) - } - batch := texts[start:end] - - if err := s.encodeBatch(url, *modelName, apiKey, dimension, batch, embeddings[start:end]); err != nil { - return nil, err - } - } - - return embeddings, nil -} - -func (s *SiliconflowModel) encodeBatch(url, modelName, apiKey string, dimension int, batch []string, out [][]float64) error { reqBody := map[string]interface{}{ - "model": modelName, - "input": batch, - "encoding_format": "float", - } - if dimension > 0 { - reqBody["dimensions"] = dimension + "model": modelName, + "input": texts, } jsonData, err := json.Marshal(reqBody) if err != nil { - return fmt.Errorf("failed to marshal request: %w", err) + return nil, fmt.Errorf("failed to marshal request: %w", err) } - ctx, cancel := context.WithTimeout(context.Background(), nonStreamCallTimeout) - defer cancel() - - req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonData)) + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) if err != nil { - return fmt.Errorf("failed to create request: %w", err) + return nil, fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Content-Type", "application/json") @@ -451,50 +438,34 @@ func (s *SiliconflowModel) encodeBatch(url, modelName, apiKey string, dimension resp, err := s.httpClient.Do(req) if err != nil { - return fmt.Errorf("failed to send request: %w", err) + return nil, fmt.Errorf("failed to send request: %w", err) } - defer resp.Body.Close() body, err := io.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { - return fmt.Errorf("failed to read response: %w", err) + return nil, fmt.Errorf("failed to read response: %w", err) } if resp.StatusCode != http.StatusOK { - return fmt.Errorf("SILICONFLOW API error: %s, body: %s", resp.Status, string(body)) + return nil, fmt.Errorf("SILICONFLOW API error: %s, body: %s", resp.Status, string(body)) } - var result siliconflowEmbeddingResponse - if err = json.Unmarshal(body, &result); err != nil { - return fmt.Errorf("failed to parse response: %w", err) + var parsed siliconflowEmbeddingResponse + if err = json.Unmarshal(body, &parsed); err != nil { + return nil, fmt.Errorf("failed to parse response: %w", err) } - if len(result.Data) != len(batch) { - return fmt.Errorf("expected %d embeddings, got %d", len(batch), len(result.Data)) + var embeddings []EmbeddingData + for _, dataElem := range parsed.Data { + var embeddingData EmbeddingData + embeddingData.Embedding = dataElem.Embedding + embeddingData.Index = dataElem.Index + embeddings = append(embeddings, embeddingData) } - seen := make([]bool, len(batch)) - for _, item := range result.Data { - if item.Index < 0 || item.Index >= len(batch) { - return fmt.Errorf("embedding index %d out of range", item.Index) - } - if seen[item.Index] { - return fmt.Errorf("duplicate embedding index %d", item.Index) - } - if len(item.Embedding) == 0 { - return fmt.Errorf("empty embedding at index %d", item.Index) - } - seen[item.Index] = true - out[item.Index] = item.Embedding - } - - for i, ok := range seen { - if !ok { - return fmt.Errorf("missing embedding index %d", i) - } - } - - return nil + return embeddings, nil } func (z *SiliconflowModel) ListModels(apiConfig *APIConfig) ([]string, error) { diff --git a/internal/entity/models/types.go b/internal/entity/models/types.go index 250e41bc51..3a32cec9dd 100644 --- a/internal/entity/models/types.go +++ b/internal/entity/models/types.go @@ -23,7 +23,7 @@ type ModelDriver interface { // messages accepts []Message which supports multimodal content (e.g., [{"type": "text", "text": "..."}, {"type": "image_url", "image_url": {"url": "..."}}]) ChatStreamlyWithSender(modelName string, messages []Message, apiConfig *APIConfig, modelConfig *ChatConfig, sender func(*string, *string) error) error // Encode encodes a list of texts into embeddings - Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) + Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) // Rerank calculates similarity scores between query and texts Rerank(modelName *string, query string, documents []string, apiConfig *APIConfig, rerankConfig *RerankConfig) (*RerankResponse, error) // ListModels List supported models @@ -39,14 +39,9 @@ type ChatResponse struct { ReasonContent *string `json:"reason_content"` } -type EmbeddingResult struct { - Index int `json:"index"` - Dimension int `json:"dimension"` - //Embedding []float64 `json:"embedding"` -} - -type EmbeddingResponse struct { - Data []EmbeddingResult `json:"data"` +type EmbeddingData struct { + Embedding []float64 `json:"embedding"` + Index int `json:"index"` } type RerankResult struct { diff --git a/internal/entity/models/vllm.go b/internal/entity/models/vllm.go index aabf597f0f..a7e3e118fb 100644 --- a/internal/entity/models/vllm.go +++ b/internal/entity/models/vllm.go @@ -381,14 +381,15 @@ func (z *VllmModel) ChatStreamlyWithSender(modelName string, messages []Message, // Encode encodes a list of texts into embeddings type vllmEmbeddingResponse struct { Data []struct { - Index int `json:"index"` - Embedding []interface{} `json:"embedding"` + Index int `json:"index"` + Embedding []float64 `json:"embedding"` } `json:"data"` } -func (z *VllmModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +// Embed embeds a list of texts into embeddings +func (z *VllmModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { if len(texts) == 0 { - return [][]float64{}, nil + return []EmbeddingData{}, nil } if modelName == nil || *modelName == "" { @@ -456,33 +457,12 @@ func (z *VllmModel) Encode(modelName *string, texts []string, apiConfig *APIConf return nil, fmt.Errorf("failed to parse response: %w", err) } - if len(parsed.Data) != len(texts) { - return nil, fmt.Errorf("vllm embeddings: expected %d results, got %d", len(texts), len(parsed.Data)) - } - - embeddings := make([][]float64, len(texts)) - for _, item := range parsed.Data { - if item.Index < 0 || item.Index >= len(texts) { - return nil, fmt.Errorf("unexpected embedding index %d for %d inputs", item.Index, len(texts)) - } - vec := make([]float64, len(item.Embedding)) - for j, v := range item.Embedding { - switch val := v.(type) { - case float64: - vec[j] = val - case float32: - vec[j] = float64(val) - default: - return nil, fmt.Errorf("unexpected embedding value type at item %d index %d", item.Index, j) - } - } - embeddings[item.Index] = vec - } - - for i, vec := range embeddings { - if vec == nil { - return nil, fmt.Errorf("missing embedding for input at index %d", i) - } + var embeddings []EmbeddingData + for _, dataElem := range parsed.Data { + var embeddingData EmbeddingData + embeddingData.Embedding = dataElem.Embedding + embeddingData.Index = dataElem.Index + embeddings = append(embeddings, embeddingData) } return embeddings, nil diff --git a/internal/entity/models/volcengine.go b/internal/entity/models/volcengine.go index d03cebaa1a..22da539936 100644 --- a/internal/entity/models/volcengine.go +++ b/internal/entity/models/volcengine.go @@ -406,10 +406,35 @@ func (z *VolcEngine) ChatStreamlyWithSender(modelName string, messages []Message return scanner.Err() } -// Encode encodes a list of texts into embeddings -func (z *VolcEngine) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +type volcengineEmbeddingResponse struct { + Created int64 `json:"created"` + Data volcengineEmbeddingData `json:"data"` + ID string `json:"id"` + Model string `json:"model"` + Object string `json:"object"` + Usage volcengineUsage `json:"usage"` +} + +type volcengineEmbeddingData struct { + Embedding []float64 `json:"embedding"` + Object string `json:"object"` +} + +type volcengineUsage struct { + PromptTokens int `json:"prompt_tokens"` + TotalTokens int `json:"total_tokens"` + PromptTokensDetails *volcenginePromptTokensDetails `json:"prompt_tokens_details,omitempty"` +} + +type volcenginePromptTokensDetails struct { + ImageTokens int `json:"image_tokens"` + TextTokens int `json:"text_tokens"` +} + +// Embed embeds a list of texts into embeddings +func (z *VolcEngine) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { if len(texts) == 0 { - return [][]float64{}, nil + return []EmbeddingData{}, nil } var region = "default" @@ -419,7 +444,7 @@ func (z *VolcEngine) Encode(modelName *string, texts []string, apiConfig *APICon url := fmt.Sprintf("%s/%s", z.BaseURL[region], z.URLSuffix.Embedding) - embeddings := make([][]float64, len(texts)) + var embeddings []EmbeddingData for i, text := range texts { @@ -466,25 +491,15 @@ func (z *VolcEngine) Encode(modelName *string, texts []string, apiConfig *APICon return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body)) } - // Volcengine multimodal embedding response - type VolcengineEmbeddingResponse struct { - Data struct { - Embedding []float64 `json:"embedding"` - Object string `json:"object"` - } `json:"data"` - } - - var result VolcengineEmbeddingResponse - - if err = json.Unmarshal(body, &result); err != nil { + var parsed volcengineEmbeddingResponse + if err = json.Unmarshal(body, &parsed); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } - if len(result.Data.Embedding) == 0 { - return nil, fmt.Errorf("empty embedding in response") - } - - embeddings[i] = result.Data.Embedding + var embeddingData EmbeddingData + embeddingData.Index = i + embeddingData.Embedding = parsed.Data.Embedding + embeddings = append(embeddings, embeddingData) } return embeddings, nil diff --git a/internal/entity/models/xai.go b/internal/entity/models/xai.go index 96617320cf..1b3175d4b7 100644 --- a/internal/entity/models/xai.go +++ b/internal/entity/models/xai.go @@ -397,9 +397,9 @@ func (z *XAIModel) ChatStreamlyWithSender(modelName string, messages []Message, return nil } -// Encode encodes a list of texts into embeddings. xAI does not expose a +// Embed embeds a list of texts into embeddings. xAI does not expose a // public embedding API yet, so this is left unimplemented. -func (z *XAIModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +func (z *XAIModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { return nil, fmt.Errorf("not implemented") } diff --git a/internal/entity/models/zhipu-ai.go b/internal/entity/models/zhipu-ai.go index 98bd5a7a52..adccae7024 100644 --- a/internal/entity/models/zhipu-ai.go +++ b/internal/entity/models/zhipu-ai.go @@ -362,8 +362,39 @@ func (z *ZhipuAIModel) ChatStreamlyWithSender(modelName string, messages []Messa return scanner.Err() } +type zhipuEmbeddingResponse struct { + Data []zhipuEmbeddingData `json:"data"` + Model string `json:"model"` + Object string `json:"object"` + Usage zhipuUsage `json:"usage"` +} + +type zhipuEmbeddingData struct { + Embedding []float64 `json:"embedding"` + Index int `json:"index"` + Object string `json:"object"` +} + +type zhipuUsage struct { + CompletionTokens int `json:"completion_tokens"` + PromptTokens int `json:"prompt_tokens"` + TotalTokens int `json:"total_tokens"` +} + // Encode encodes a list of texts into embeddings -func (z *ZhipuAIModel) Encode(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +func (z *ZhipuAIModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) { + if len(texts) == 0 { + return []EmbeddingData{}, nil + } + + 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") + } + var region = "default" if apiConfig.Region != nil { region = *apiConfig.Region @@ -371,79 +402,54 @@ func (z *ZhipuAIModel) Encode(modelName *string, texts []string, apiConfig *APIC url := fmt.Sprintf("%s/%s", strings.TrimSuffix(z.BaseURL[region], "/"), z.URLSuffix.Embedding) - embeddings := make([][]float64, len(texts)) + reqBody := map[string]interface{}{} + reqBody["model"] = modelName + reqBody["input"] = texts + if embeddingConfig.Dimension > 0 { + reqBody["dimensions"] = embeddingConfig.Dimension + } - for i, text := range texts { - reqBody := map[string]interface{}{} - reqBody["model"] = modelName - reqBody["input"] = text - if embeddingConfig.Dimension > 0 { - reqBody["dimensions"] = embeddingConfig.Dimension - } + jsonData, err := json.Marshal(reqBody) + if err != nil { + return nil, fmt.Errorf("failed to marshal request: %w", err) + } - jsonData, err := json.Marshal(reqBody) - if err != nil { - return nil, fmt.Errorf("failed to marshal request: %w", err) - } + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, fmt.Errorf("failed to create request: %w", err) + } - req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *apiConfig.ApiKey)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *apiConfig.ApiKey)) + resp, err := z.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to send request: %w", err) + } - resp, err := z.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to send request: %w", err) - } + body, err := io.ReadAll(resp.Body) + resp.Body.Close() - body, err := io.ReadAll(resp.Body) - resp.Body.Close() + if err != nil { + return nil, fmt.Errorf("failed to read response: %w", err) + } - if err != nil { - return nil, fmt.Errorf("failed to read response: %w", err) - } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body)) + } - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body)) - } + // Parse response + var zhipuResp zhipuEmbeddingResponse + if err = json.Unmarshal(body, &zhipuResp); err != nil { + return nil, fmt.Errorf("failed to parse response: %w", err) + } - // Parse response - var result map[string]interface{} - if err = json.Unmarshal(body, &result); err != nil { - return nil, fmt.Errorf("failed to parse response: %w", err) - } - - data, ok := result["data"].([]interface{}) - if !ok || len(data) == 0 { - return nil, fmt.Errorf("no data in response") - } - - firstData, ok := data[0].(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("invalid data format") - } - - embeddingSlice, ok := firstData["embedding"].([]interface{}) - if !ok { - return nil, fmt.Errorf("invalid embedding format") - } - - embedding := make([]float64, len(embeddingSlice)) - for j, v := range embeddingSlice { - switch val := v.(type) { - case float64: - embedding[j] = val - case float32: - embedding[j] = float64(val) - default: - return nil, fmt.Errorf("unexpected embedding value type") - } - } - - embeddings[i] = embedding + var embeddings []EmbeddingData + for _, dataElem := range zhipuResp.Data { + var embeddingData EmbeddingData + embeddingData.Embedding = dataElem.Embedding + embeddingData.Index = dataElem.Index + embeddings = append(embeddings, embeddingData) } return embeddings, nil diff --git a/internal/handler/providers.go b/internal/handler/providers.go index 758919f406..af101c60e3 100644 --- a/internal/handler/providers.go +++ b/internal/handler/providers.go @@ -950,7 +950,7 @@ func (h *ProviderHandler) EmbedText(c *gin.Context) { } // Non-stream response - var response *models.EmbeddingResponse + var response []models.EmbeddingData var errorCode common.ErrorCode var err error @@ -966,7 +966,7 @@ func (h *ProviderHandler) EmbedText(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "code": 0, - "data": response.Data, + "data": response, "message": "success", }) } diff --git a/internal/service/model_service.go b/internal/service/model_service.go index 1a107d4231..a32daa7eeb 100644 --- a/internal/service/model_service.go +++ b/internal/service/model_service.go @@ -891,7 +891,7 @@ func (m *ModelProviderService) ChatToModelStreamWithSender(providerName, instanc } // EmbedText sends texts to the embedding model -func (m *ModelProviderService) EmbedText(providerName, instanceName, modelName, userID string, texts []string, apiConfig *modelModule.APIConfig, modelConfig *modelModule.EmbeddingConfig) (*modelModule.EmbeddingResponse, common.ErrorCode, error) { +func (m *ModelProviderService) EmbedText(providerName, instanceName, modelName, userID string, texts []string, apiConfig *modelModule.APIConfig, modelConfig *modelModule.EmbeddingConfig) ([]modelModule.EmbeddingData, common.ErrorCode, error) { if apiConfig == nil { apiConfig = &modelModule.APIConfig{} } @@ -949,26 +949,15 @@ func (m *ModelProviderService) EmbedText(providerName, instanceName, modelName, apiConfig.Region = ®ion apiConfig.ApiKey = &instance.APIKey - var embeddingList [][]float64 - embeddingList, err = providerInfo.ModelDriver.Encode(&modelName, texts, apiConfig, modelConfig) + var response []modelModule.EmbeddingData + response, err = providerInfo.ModelDriver.Embed(&modelName, texts, apiConfig, modelConfig) if err != nil { return nil, common.CodeServerError, err } - if embeddingList == nil { + if response == nil || len(response) == 0 { return nil, common.CodeServerError, errors.New("empty embed response") } - response := &modelModule.EmbeddingResponse{ - Data: make([]modelModule.EmbeddingResult, len(embeddingList)), - } - for i, embedding := range embeddingList { - response.Data[i] = modelModule.EmbeddingResult{ - Index: i, - Dimension: len(embedding), - //Embedding: embedding, - } - } - return response, common.CodeSuccess, nil } @@ -994,26 +983,15 @@ func (m *ModelProviderService) EmbedText(providerName, instanceName, modelName, } newProviderInfo := providerInfo.ModelDriver.NewInstance(newURL) - var embeddingList [][]float64 - embeddingList, err = newProviderInfo.Encode(&modelName, texts, apiConfig, modelConfig) + var response []modelModule.EmbeddingData + response, err = newProviderInfo.Embed(&modelName, texts, apiConfig, modelConfig) if err != nil { return nil, common.CodeServerError, err } - if embeddingList == nil { + if response == nil || len(response) == 0 { return nil, common.CodeServerError, errors.New("empty embed response") } - response := &modelModule.EmbeddingResponse{ - Data: make([]modelModule.EmbeddingResult, len(embeddingList)), - } - for i, embedding := range embeddingList { - response.Data[i] = modelModule.EmbeddingResult{ - Index: i, - Dimension: len(embedding), - //Embedding: embedding, - } - } - return response, common.CodeSuccess, nil } diff --git a/internal/service/nlp/retrieval.go b/internal/service/nlp/retrieval.go index 2754571120..a3a2e8debe 100644 --- a/internal/service/nlp/retrieval.go +++ b/internal/service/nlp/retrieval.go @@ -607,12 +607,12 @@ func (s *RetrievalService) Search(ctx context.Context, req *RetrievalSearchReque // GetVector computes query vector and returns MatchDenseExpr for hybrid search func (s *RetrievalService) GetVector(txt string, embModel *models.EmbeddingModel, topk int, similarity float64) (*types.MatchDenseExpr, error) { - embeddings, err := embModel.ModelDriver.Encode(embModel.ModelName, []string{txt}, embModel.APIConfig, nil) + embeddings, err := embModel.ModelDriver.Embed(embModel.ModelName, []string{txt}, embModel.APIConfig, nil) if err != nil { return nil, err } - vector := embeddings[0] + vector := embeddings[0].Embedding vectorSize := len(vector) vectorColumnName := fmt.Sprintf("q_%d_vec", vectorSize) diff --git a/internal/service/skill_indexer.go b/internal/service/skill_indexer.go index ec36a7948e..8c234e0986 100644 --- a/internal/service/skill_indexer.go +++ b/internal/service/skill_indexer.go @@ -25,6 +25,7 @@ import ( "ragflow/internal/dao" "ragflow/internal/engine" "ragflow/internal/entity" + "ragflow/internal/entity/models" "ragflow/internal/storage" "ragflow/internal/tokenizer" "strings" @@ -237,7 +238,8 @@ func (s *SkillIndexerService) BatchIndexSkills(ctx context.Context, tenantID, sp // Generate embeddings in batch common.Info(fmt.Sprintf("Generating embeddings for %d skills with embdID=%s", len(skills), embdID)) - vectors, err := s.generateEmbeddings(ctx, vectorTexts, embdID, tenantID) + var vectors []models.EmbeddingData + vectors, err = s.generateEmbeddings(ctx, vectorTexts, embdID, tenantID) if err != nil { common.Warn(fmt.Sprintf("Failed to generate embeddings: %v. Continuing with text-only index.", err)) vectors = nil // Continue without vectors @@ -311,7 +313,7 @@ func (s *SkillIndexerService) BatchIndexSkills(ctx context.Context, tenantID, sp // Add vector only if available if vectors != nil && i < len(vectors) { - doc[vectorField] = vectors[i] + doc[vectorField] = vectors[i].Embedding } else { common.Info(fmt.Sprintf("No vector for skill %s, creating text-only index", skill.ID)) // For Infinity: use zero vector as placeholder (table schema requires vector column) @@ -932,20 +934,21 @@ func (s *SkillIndexerService) generateEmbedding(ctx context.Context, text, embdI } truncatedText := truncate(text, maxLen-10) - vectors, err := embeddingModel.ModelDriver.Encode(embeddingModel.ModelName, []string{truncatedText}, embeddingModel.APIConfig, nil) + var response []models.EmbeddingData + response, err = embeddingModel.ModelDriver.Embed(embeddingModel.ModelName, []string{truncatedText}, embeddingModel.APIConfig, nil) if err != nil { return nil, fmt.Errorf("failed to encode text: %w", err) } - if len(vectors) == 0 { + if len(response) == 0 { return nil, fmt.Errorf("embedding returned empty result") } - return vectors[0], nil + return response[0].Embedding, nil } // generateEmbeddings generates embeddings for multiple texts in batch // This is more efficient than calling generateEmbedding individually -func (s *SkillIndexerService) generateEmbeddings(ctx context.Context, texts []string, embdID, tenantID string) ([][]float64, error) { +func (s *SkillIndexerService) generateEmbeddings(ctx context.Context, texts []string, embdID, tenantID string) ([]models.EmbeddingData, error) { common.Info(fmt.Sprintf("generateEmbeddings called: texts=%d, embdID=%s, tenantID=%s", len(texts), embdID, tenantID)) if s.modelProvider == nil { @@ -975,18 +978,19 @@ func (s *SkillIndexerService) generateEmbeddings(ctx context.Context, texts []st common.Info(fmt.Sprintf("Encoding %d texts", len(truncatedTexts))) // Use batch encode API (consistent with Python's encode(texts: list)) - vectors, err := embeddingModel.ModelDriver.Encode(embeddingModel.ModelName, truncatedTexts, embeddingModel.APIConfig, nil) + var response []models.EmbeddingData + response, err = embeddingModel.ModelDriver.Embed(embeddingModel.ModelName, truncatedTexts, embeddingModel.APIConfig, nil) if err != nil { common.Error(fmt.Sprintf("Failed to encode texts: %v", err), err) return nil, fmt.Errorf("failed to encode texts: %w", err) } - common.Info(fmt.Sprintf("Encoded successfully, got %d vectors", len(vectors))) - if len(vectors) > 0 { - common.Info(fmt.Sprintf("Vector dimension: %d", len(vectors[0]))) + common.Info(fmt.Sprintf("Encoded successfully, got %d vectors", len(response))) + if len(response) > 0 { + common.Info(fmt.Sprintf("Vector dimension: %d", len(response[0].Embedding))) } - return vectors, nil + return response, nil } // truncate truncates text to maxLen characters @@ -1021,16 +1025,17 @@ func (s *SkillIndexerService) getEmbeddingDimension(ctx context.Context, tenantI // Use simple test text like Python does: embedding_model.encode(["ok"]) testText := "ok" - vectors, err := embeddingModel.ModelDriver.Encode(embeddingModel.ModelName, []string{testText}, embeddingModel.APIConfig, nil) + var response []models.EmbeddingData + response, err = embeddingModel.ModelDriver.Embed(embeddingModel.ModelName, []string{testText}, embeddingModel.APIConfig, nil) if err != nil { return 0, fmt.Errorf("failed to encode test text: %w", err) } - if len(vectors) == 0 || len(vectors[0]) == 0 { + if len(response) == 0 || len(response[0].Embedding) == 0 { return 0, fmt.Errorf("embedding returned empty vector") } - dimension := len(vectors[0]) + dimension := len(response[0].Embedding) common.Info(fmt.Sprintf("Got embedding dimension from API: %d", dimension)) return dimension, nil } diff --git a/internal/service/skill_search.go b/internal/service/skill_search.go index c48d0f1314..d7a91a6011 100644 --- a/internal/service/skill_search.go +++ b/internal/service/skill_search.go @@ -27,6 +27,7 @@ import ( "ragflow/internal/engine" "ragflow/internal/engine/types" "ragflow/internal/entity" + "ragflow/internal/entity/models" "ragflow/internal/utility" "strings" @@ -679,15 +680,16 @@ func (s *SkillSearchService) getEmbedding(ctx context.Context, text, embdID, ten } truncatedText := truncate(text, maxLen-10) - vectors, err := embeddingModel.ModelDriver.Encode(embeddingModel.ModelName, []string{truncatedText}, embeddingModel.APIConfig, nil) + var response []models.EmbeddingData + response, err = embeddingModel.ModelDriver.Embed(embeddingModel.ModelName, []string{truncatedText}, embeddingModel.APIConfig, nil) if err != nil { return nil, fmt.Errorf("failed to encode query: %w", err) } - if len(vectors) == 0 { + if len(response) == 0 { return nil, fmt.Errorf("embedding returned empty result") } - return vectors[0], nil + return response[0].Embedding, nil } // Helper functions diff --git a/uv.lock b/uv.lock index 44fe6fca92..9bf11d19a0 100644 --- a/uv.lock +++ b/uv.lock @@ -1,4 +1,5 @@ version = 1 +revision = 3 requires-python = ">=3.12, <3.15" resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'darwin'", @@ -3624,10 +3625,6 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/47/66/eea81dfff765ed66c68fd2ed8c96245109e13c896c2a5015c7839c92367e/jiter-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:24dc96eca9f84da4131cdf87a95e6ce36765c3b156fc9ae33280873b1c32d5f6" }, { url = "https://mirrors.aliyun.com/pypi/packages/ff/32/4ac9c7a76402f8f00d00842a7f6b83b284d0cf7c1e9d4227bc95aa6d17fa/jiter-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0a8d76c7524087272c8ae913f5d9d608bd839154b62c4322ef65723d2e5bb0b8" }, { url = "https://mirrors.aliyun.com/pypi/packages/f9/8e/7def204fea9f9be8b3c21a6f2dd6c020cf56c7d5ff753e0e23ed7f9ea57e/jiter-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2c26cf47e2cad140fa23b6d58d435a7c0161f5c514284802f25e87fddfe11024" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/b3/3c29819a27178d0e461a8571fb63c6ae38be6dc36b78b3ec2876bbd6a910/jiter-0.13.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b1cbfa133241d0e6bdab48dcdc2604e8ba81512f6bbd68ec3e8e1357dd3c316c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/eb/ae/60993e4b07b1ac5ebe46da7aa99fdbb802eb986c38d26e3883ac0125c4e0/jiter-0.13.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:db367d8be9fad6e8ebbac4a7578b7af562e506211036cba2c06c3b998603c3d2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/77/fa/2227e590e9cf98803db2811f172b2d6460a21539ab73006f251c66f44b14/jiter-0.13.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45f6f8efb2f3b0603092401dc2df79fa89ccbc027aaba4174d2d4133ed661434" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2d/92/015173281f7eb96c0ef580c997da8ef50870d4f7f4c9e03c845a1d62ae04/jiter-0.13.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:597245258e6ad085d064780abfb23a284d418d3e61c57362d9449c6c7317ee2d" }, { url = "https://mirrors.aliyun.com/pypi/packages/80/60/e50fa45dd7e2eae049f0ce964663849e897300433921198aef94b6ffa23a/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:3d744a6061afba08dd7ae375dcde870cffb14429b7477e10f67e9e6d68772a0a" }, { url = "https://mirrors.aliyun.com/pypi/packages/d2/73/a009f41c5eed71c49bec53036c4b33555afcdee70682a18c6f66e396c039/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:ff732bd0a0e778f43d5009840f20b935e79087b4dc65bd36f1cd0f9b04b8ff7f" }, { url = "https://mirrors.aliyun.com/pypi/packages/c4/10/528b439290763bff3d939268085d03382471b442f212dca4ff5f12802d43/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab44b178f7981fcaea7e0a5df20e773c663d06ffda0198f1a524e91b2fde7e59" }, @@ -5932,8 +5929,6 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886" }, { url = "https://mirrors.aliyun.com/pypi/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2" }, { url = "https://mirrors.aliyun.com/pypi/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9f/7c/f5b0556590e7b4e710509105e668adb55aa9470a9f0e4dea9c40a4a11ce1/pycryptodome-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:350ebc1eba1da729b35ab7627a833a1a355ee4e852d8ba0447fafe7b14504d56" }, - { url = "https://mirrors.aliyun.com/pypi/packages/33/38/dcc795578d610ea1aaffef4b148b8cafcfcf4d126b1e58231ddc4e475c70/pycryptodome-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:93837e379a3e5fd2bb00302a47aee9fdf7940d83595be3915752c74033d17ca7" }, ] [[package]] @@ -5952,8 +5947,6 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/48/7d/0f2b09490b98cc6a902ac15dda8760c568b9c18cfe70e0ef7a16de64d53a/pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7a7a8f33a1f1fb762ede6cc9cbab8f2a9ba13b196bfaf7bc6f0b39d2ba315a43" }, { url = "https://mirrors.aliyun.com/pypi/packages/b0/1c/375adb14b71ee1c8d8232904e928b3e7af5bbbca7c04e4bec94fe8e90c3d/pycryptodomex-3.20.0-cp35-abi3-win32.whl", hash = "sha256:c39778fd0548d78917b61f03c1fa8bfda6cfcf98c767decf360945fe6f97461e" }, { url = "https://mirrors.aliyun.com/pypi/packages/b2/e8/1b92184ab7e5595bf38000587e6f8cf9556ebd1bf0a583619bee2057afbd/pycryptodomex-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:2a47bcc478741b71273b917232f521fd5704ab4b25d301669879e7273d3586cc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e7/c5/9140bb867141d948c8e242013ec8a8011172233c898dfdba0a2417c3169a/pycryptodomex-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:1be97461c439a6af4fe1cf8bf6ca5936d3db252737d2f379cc6b2e394e12a458" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5e/6a/04acb4978ce08ab16890c70611ebc6efd251681341617bbb9e53356dee70/pycryptodomex-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:19764605feea0df966445d46533729b645033f134baeb3ea26ad518c9fdf212c" }, ] [[package]] @@ -6036,10 +6029,6 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa" }, { url = "https://mirrors.aliyun.com/pypi/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c" }, { url = "https://mirrors.aliyun.com/pypi/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008" }, - { url = "https://mirrors.aliyun.com/pypi/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad" }, { url = "https://mirrors.aliyun.com/pypi/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd" }, { url = "https://mirrors.aliyun.com/pypi/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc" }, { url = "https://mirrors.aliyun.com/pypi/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56" }, @@ -6958,7 +6947,7 @@ requires-dist = [ { name = "google-cloud-storage", specifier = ">=2.19.0,<3.0.0" }, { name = "google-genai", specifier = ">=1.41.0,<2.0.0" }, { name = "google-search-results", specifier = "==2.4.2" }, - { name = "graspologic", git = "https://gitee.com/infiniflow/graspologic.git?rev=38e680cab72bc9fb68a7992c3bcc2d53b24e42fd#38e680cab72bc9fb68a7992c3bcc2d53b24e42fd" }, + { name = "graspologic", git = "https://gitee.com/infiniflow/graspologic.git?rev=38e680cab72bc9fb68a7992c3bcc2d53b24e42fd" }, { name = "groq", specifier = "==0.9.0" }, { name = "grpcio-status", specifier = "==1.67.1" }, { name = "html-text", specifier = "==0.6.2" }, @@ -8457,9 +8446,6 @@ dependencies = [ { name = "typing-extensions", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or sys_platform == 'darwin'" }, { name = "wrapt", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or sys_platform == 'darwin'" }, ] -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/12/cb/5d428ab3861782f2f50b59813d105cbe6da6f452f7f1a03341cb8d12a9cc/tensorflow_cpu-2.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e0f27dbd92c6d380ae0ccfe73c7343f65c127b0aa98467c30c2e71eda7c76a4" }, -] [[package]] name = "tensorflow-intel"