#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.
Есть несколько способов обойти это:
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]]]]
Это самое быстрое решение, но оно не гарантирует количество элементов во внутреннем списке / массиве.
- Как было предложено в связанном потоке, вы могли бы использовать 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
, однако это не обеспечит порядок.
- Если вы можете адаптировать тело запроса, подумайте об изменении его на что-то вроде:
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 на формат словаря, чтобы он работал.