ошибка проверки схемы FastAPI и pydantic

#python #fastapi #pydantic

#python #fastapi #pydantic

Вопрос:

Я создаю API с использованием фреймворка FastAPI, и моя полезная нагрузка post-запроса выглядит примерно так

 {
    "batch_id": "7r3683693242",
    "queries": [
        "What is the capital of Germany",
        "How is Electricity generated"
    ],
    "num_items_to_return": 2,
    "passage_id_and_score_matrix": [
        [
            ["16278720_0", 0.8], ["169339_0", 0.6]
        ],
        [
            ["19258753_14", 0.6], ["3270043_12", 0.4]
        ]
    ]
 }
 

Я определил свою модель pydantic как

 class ReRankerPayload(BaseModel):
    batch_id: str
    queries: List[str]
    num_items_to_return: int
    passage_id_and_score_matrix: List[List[Tuple[str, float]]]
 

и моя подпись API

 @app.post("/rerank-passages")
async def rerank_passages(payload: ReRankerPayload):
    response = reranker.get_results(
        payload.batch_id, payload.queries,                                    
        payload.passage_id_and_score_matrix, 
        payload.num_items_to_return,
    )
    return response

uvicorn.run(app, host="0.0.0.0", port=8080, log_config=None)
 

Когда я открываю страницу swagger, на которой размещен 127.0.0.1:8080/docs , я получаю следующую ошибку

   File "/home/dimension/.virtualenvs/buddy/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/home/dimension/.virtualenvs/buddy/lib/python3.8/site-packages/starlette/exceptions.py", line 82, in __call__
    raise exc
  File "/home/dimension/.virtualenvs/buddy/lib/python3.8/site-packages/starlette/exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "/home/dimension/.virtualenvs/buddy/lib/python3.8/site-packages/starlette/routing.py", line 656, in __call__
    await route.handle(scope, receive, send)
  File "/home/dimension/.virtualenvs/buddy/lib/python3.8/site-packages/starlette/routing.py", line 259, in handle
    await self.app(scope, receive, send)
  File "/home/dimension/.virtualenvs/buddy/lib/python3.8/site-packages/starlette/routing.py", line 61, in app
    response = await func(request)
  File "/home/dimension/.virtualenvs/buddy/lib/python3.8/site-packages/fastapi/applications.py", line 161, in openapi
    return JSONResponse(self.openapi())
  File "/home/dimension/.virtualenvs/buddy/lib/python3.8/site-packages/fastapi/applications.py", line 136, in openapi
    self.openapi_schema = get_openapi(
  File "/home/dimension/.virtualenvs/buddy/lib/python3.8/site-packages/fastapi/openapi/utils.py", line 410, in get_openapi
    return jsonable_encoder(OpenAPI(**output), by_alias=True, exclude_none=True)  # type: ignore
  File "pydantic/main.py", line 362, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 2 validation errors for OpenAPI
components -> schemas -> ReRankerPayload -> properties -> passage_id_and_score_matrix -> items -> items -> items
  value is not a valid dict (type=type_error.dict)
components -> schemas -> ReRankerPayload -> $ref
  field required (type=value_error.missing)
 

Приведенный выше код отлично работает на версии 0.65.2 fastapi, но не на 0.70.0 . Как я могу заставить приведенный выше код работать независимо от версии fastapi

Комментарии:

1. Что произойдет, если вы установите тип списка ‘passage_id_and_score_matrix’ [Список [Список [str, float]]]?

2. @noninertialframe, я получаю эту ошибку — ошибка типа: слишком много параметров для ввода. Список; фактическое значение 2, ожидаемое значение 1, если вы задаете тип ‘passage_id_and_score_matrix’ как List[Список [Список [str, float]]]

3. Не могли бы вы попробовать List[Список [Список[Объединение [str, float]]]]?

4. Это приводит к следующему проходу_id_and_score_matix — [[‘9808094’, ‘0.8’], [‘4494115’, ‘0.6’]], [[‘1792034’, ‘0.6’], [‘20974116’, ‘0.4’]], т.е. float преобразуется в string

5. В качестве последнего средства, что произойдет, если вы передадите «passage_id_and_score_matrix»: [ [(«16278720_0», 0.8), («169339_0», 0.6)], [(«19258753_14», 0.6), («3270043_12», 0.4)] ] } для соответствия типу «Кортеж»

Ответ №1:

Похоже, что кортежи в настоящее время не поддерживаются в OpenAPI.

Есть несколько способов обойти это:

  1. List Union Вместо этого используйте with:
 from pydantic import BaseModel
from typing import List, Union

class ReRankerPayload(BaseModel):
    batch_id: str
    queries: List[str]
    num_items_to_return: int
    passage_id_and_score_matrix: List[List[List[Union[str, float]]]]
 

Это самое быстрое решение, но оно не гарантирует количество элементов во внутреннем списке / массиве.
введите описание изображения здесь

  1. Как было предложено в связанном потоке, вы могли бы использовать Pydantic conlist :
 from pydantic import BaseModel, conlist
from typing import List, Union

class ReRankerPayload(BaseModel):
    batch_id: str
    queries: List[str]
    num_items_to_return: int
    passage_id_and_score_matrix: List[List[conlist(Union[str, float], min_items=2, max_items=2)]]
 

Это гарантировало бы, что в самом внутреннем списке могут быть переданы только два элемента, и что они являются либо a str , либо a float , однако это не обеспечит порядок.
введите описание изображения здесь

  1. Если вы можете адаптировать тело запроса, подумайте об изменении его на что-то вроде:
 from pydantic import BaseModel
from typing import List

class Entry(BaseModel):
    id: str
    score: float

class ReRankerPayload(BaseModel):
    batch_id: str
    queries: List[str]
    num_items_to_return: int
    passage_id_and_score_matrix: List[List[Entry]]
 

Это улучшает читаемость и гарантирует, что Entry всегда имеет точно два свойства ( id и score ).
введите описание изображения здесь

Комментарии:

1. спасибо за ответ, но варианты 1 и 2 преобразуют значение с плавающей точкой, переданное во входных данных API, в строку. Вариант 3 выглядит многообещающим, но для того, чтобы это сработало, мне нужно изменить внутренний кортеж passage_id_and_score_matrix на формат словаря, чтобы он работал.