Alex123
Alex123

Reputation: 1

Azure AISearch Indexer: "'JSON arrays with element type 'Float' map to Collection(Edm.Double)"

I have the following problem. I am trying to build an indexer in Azure AI Search. I have a skillset with a “Custom.WebApiSkill” skill. This provides me with the following response body:

{
  "values": [
    {
      "recordId": "1",
      "data": {
        "embedding": [
          -0.013657977,
          0.004854262,
          -0.015335504,
          -0.010732211,
          ...
        ]
      }
    }
  ]
}

As part of the indexer, I am now trying to map the “embedding” value of the response body to a field in my index:

  "outputFieldMappings": [
    {
      "sourceFieldName": "/document/pages/*/embedding",
      "targetFieldName": "content_vector",
      "mappingFunction": null
    }
  ]

My index field "content_vector" looks like that:

    {
      "name": "content_vector",
      "type": "Collection(Edm.Single)",
      "key": false,
      "retrievable": true,
      "stored": true,
      "searchable": true,
      "filterable": false,
      "sortable": false,
      "facetable": false,
      "synonymMaps": [],
      "dimensions": 1536,
      "vectorSearchProfile": "myHnswProfile"
    }

However, I receive the following error when executing:

The data field 'content_vector/0' in the document with key 'aHR0cHM6Ly9zdHJhZ3Byb3RvdHlwZGV2My5ibG9iLmNvcmUud2luZG93cy5uZXQvdGVzdGRhdGEvS29tbXVuaWthdGlvbnN0ZWNobmlrLUZpYmVsLnBkZg2' has an invalid value of type 'Collection(Edm.Double)' ('JSON arrays with element type 'Float' map to Collection(Edm.Double)'). The expected type was 'Collection(Edm.Single)'.

How can I make sure that my custom WebApi returns the embedding array with float32 values, or how can I make sure that my indexer interprets the values as float32 (Edm.Single) and not as float64 (Edm-Double)?

Thank you very much!

I tried to use numpy in my Custon WebAPI (python) to convert the values of "embedding" to float32, but that didn't worked.

Something like that:

embedding_float32 = np.array(embedding, dtype=np.float32).tolist()

UPDATE:

I tried using “numpy” to convert the array to “float32”, just like you showed in your first code snippet. Nevertheless, the indexer interprets it as float64 (Edm.Double):

The data field 'content_vector/0' in the document with key 'xyz' has an invalid value of type 'Collection(Edm.Double)' ('JSON arrays with element type 'Float' map to Collection(Edm.Double)'). The expected type was 'Collection(Edm.Single)

Is there a possibility that the indexer interprets the values as float32 (Edm.Single) or that I force the data type in my CustomWebAPI? The problem is that Python does not natively differentiate between float32 and float64 and therefore treats and returns the value as float64 by default.

Here is the link to my WebAPI in GitHub: https://github.com/Alexkanns/CustomWebAPI/blob/main/init.py

Upvotes: 0

Views: 143

Answers (1)

Sampath
Sampath

Reputation: 3639

I have tired your code and the same error .To solve the issue check you have below options:

One way is Modify the Web API response to return float32 values.

using numpy to convert the array to float32Here is a Python snippet to ensure your response body has float32 values before returning them:

I referred to this link to learn about integrated vectorization using Python.

import numpy as np

embedding_float32 = np.array(embedding, dtype=np.float32).tolist()

response = {
    "values": [
        {
            "recordId": "1",
            "data": {
                "embedding": embedding_float32
            }
        }
    ]
}

return response

Output with web app

Alternative to use custom skill generates vector embeddings for provided content with the HuggingFace all-MiniLM-L6-v2 model. The code taken from git.

import numpy as np

class TextEmbedder():
    def __init__(self):
   
        model_path = 'sentence-transformers/all-MiniLM-L6-v2'
        self.tokenizer = AutoTokenizer.from_pretrained(model_path)
        self.model = AutoModel.from_pretrained(model_path)
    def _mean_pooling(self, model_output, attention_mask):
        token_embeddings = model_output[0]  
        input_mask_expanded = (attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float())
        return torch.sum(token_embeddings * input_mask_expanded, 1) / torch.clamp(input_mask_expanded.sum(1), min=1e-9)

    def generate_embeddings(self, sentences):
        encoded_input = self.tokenizer(sentences, padding=True, truncation=True, return_tensors='pt')
        with torch.no_grad():
            model_output = self.model(**encoded_input)
        sentence_embeddings = self._mean_pooling(model_output, encoded_input['attention_mask'])
        sentence_embeddings = F.normalize(sentence_embeddings, p=2, dim=1)

        return sentence_embeddings.cpu().numpy().astype(np.float32)

enter image description here

I also referred to this link to understand the Azure OpenAI Embedding skill in Azure AI Search.

Output

Update:

import logging
import json
import http.client
import numpy as np
from azure.functions import HttpRequest, HttpResponse

class Float32Encoder(json.JSONEncoder):
    # Custom encoder to ensure np.float32 values are serialized correctly
    def default(self, obj):
        if isinstance(obj, np.float32):
            return float(obj)
        return super(Float32Encoder, self).default(obj)

def main(req: HttpRequest) -> HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')
    
    # Retrieve API key from the headers
    api_key = req.headers.get('api-key')
    if not api_key:
        logging.error("Missing 'api-key' header.")
        return HttpResponse("Missing 'api-key' header.", status_code=400)
    
    try:
        req_body = req.get_json()
    except ValueError as e:
        logging.error(f"Invalid JSON: {e}")
        return HttpResponse("Bad Request. Invalid JSON.", status_code=400)
    
    values = req_body.get('values')
    if not values:
        logging.error("The 'values' field is required.")
        return HttpResponse("Bad Request. The 'values' field is required.", status_code=400)
    
    # Validate each value has the 'text' parameter
    for value in values:
        data = value.get('data')
        if not data or 'text' not in data:
            logging.error("The 'text' parameter is required in each record.")
            return HttpResponse("Bad Request. The 'text' parameter is required in each record.", status_code=400)
    
    response_values = []
    for value in values:
        record_id = value.get('recordId')
        input_text = value['data']['text']
        logging.info(f"Processing recordId: {record_id}, input: {input_text}")
        
        # Setup API connection for OpenAI embeddings
        host = "xxxyyyzzz"  # Replace with your Azure API endpoint
        path = "/openai/deployments/text-embedding-ada-002/embeddings?api-version=2023-05-15"
        conn = http.client.HTTPSConnection(host)
        
        payload = json.dumps({"input": input_text})
        headers = {
            'Content-Type': 'application/json',
            'api-key': api_key
        }

        conn.request("POST", path, body=payload, headers=headers)
        response = conn.getresponse()
        data = response.read()
        conn.close()

        response_json = json.loads(data)
        embedding = response_json['data'][0]['embedding']
        
        # Convert embedding to np.float32 to ensure it matches the Edm.Single requirement
        embedding_float32 = np.array(embedding, dtype=np.float32)

        # Explicitly serialize numpy array as list to ensure all elements are float32
        response_values.append({
            "recordId": record_id,
            "data": {
                "embedding": embedding_float32.astype(np.float32).tolist()  # Force conversion to float32
            },
            "errors": [],
            "warnings": []
        })

    response_body = {
        "values": response_values
    }

    # Return response with Float32Encoder to ensure proper float conversion
    return HttpResponse(
        json.dumps(response_body, cls=Float32Encoder),
        status_code=200,
        mimetype="application/json"
    )

Upvotes: 0

Related Questions