Reputation: 1
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
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 float32
Here 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
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)
I also referred to this link to understand the Azure OpenAI Embedding skill in Azure AI Search.
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