Deploying as an API
In this section, we will see how you can deploy your pipeline as a REST API using the power of FastAPI.
The NLP pipeline
Let's create a simple NLP model, that can:
- match synonyms of COVID19
- check for negation, speculation and reported speech.
You know the drill:
import edsnlp, edsnlp.pipes as eds
nlp = edsnlp.blank('eds')
nlp.add_pipe(eds.sentences())
nlp.add_pipe(
eds.matcher(
regex=dict(
covid=[
"covid",
r"covid[-\s]?19",
r"sars[-\s]?cov[-\s]?2",
r"corona[-\s]?virus",
],
),
attr="LOWER",
),
)
nlp.add_pipe(eds.negation())
nlp.add_pipe(eds.family())
nlp.add_pipe(eds.hypothesis())
nlp.add_pipe(eds.rspeech())
Creating the FastAPI app
FastAPI is a incredibly efficient framework, based on Python type hints from the ground up, with the help of Pydantic (another great library for building modern Python). We won't go into too much detail about FastAPI in this tutorial. For further information on how the framework operates, go to its excellent documentation!
We'll need to create two things:
- A module containing the models for inputs and outputs.
- The script that defines the application itself.
from typing import List
from pydantic import BaseModel
class Entity(BaseModel): # (1)
# OMOP-style attributes
start: int
end: int
label: str
lexical_variant: str
normalized_variant: str
# Qualifiers
negated: bool
hypothesis: bool
family: bool
reported_speech: bool
class Document(BaseModel): # (2)
text: str
ents: List[Entity]
- The
Entity
model contains attributes that define a matched entity, as well as variables that contain the output of the qualifier components. - The
Document
model contains the input text, and a list of detected entities
Having defined the output models and the pipeline, we can move on to creating the application itself:
from typing import List
from fastapi import FastAPI
from pipeline import nlp
from models import Entity, Document
app = FastAPI(title="EDS-NLP", version=edsnlp.__version__)
@app.post("/covid", response_model=List[Document]) # (1)
async def process(
notes: List[str], # (2)
):
documents = []
for doc in nlp.pipe(notes):
entities = []
for ent in doc.ents:
entity = Entity(
start=ent.start_char,
end=ent.end_char,
label=ent.label_,
lexical_variant=ent.text,
normalized_variant=ent._.normalized_variant,
negated=ent._.negation,
hypothesis=ent._.hypothesis,
family=ent._.family,
reported_speech=ent._.reported_speech,
)
entities.append(entity)
documents.append(
Document(
text=doc.text,
ents=entities,
)
)
return documents
- By telling FastAPI what output format is expected, you get automatic data validation.
- In FastAPI, input and output schemas are defined through Python type hinting. Here, we tell FastAPI to expect a list of strings in the
POST
request body. As a bonus, you get data validation for free.
Running the API
Our simple API is ready to launch! We'll just need to install FastAPI along with a ASGI server to run it. This can be done in one go:
$ pip install 'fastapi[uvicorn]'
---> 100%
color:green Successfully installed fastapi
Launching the API is trivial:
$ uvicorn app:app --reload
Go to localhost:8000/docs
to admire the automatically generated documentation!
Using the API
You can try the API directly from the documentation. Otherwise, you may use the requests
package:
import requests
notes = [
"Le père du patient n'est pas atteint de la covid.",
"Probable coronavirus.",
]
r = requests.post(
"http://localhost:8000/covid",
json=notes,
)
r.json()
You should get something like:
[
{
"text": "Le père du patient n'est pas atteint de la covid.",
"ents": [
{
"start": 43,
"end": 48,
"label": "covid",
"lexical_variant": "covid",
"normalized_variant": "covid",
"negated": true,
"hypothesis": false,
"family": true,
"reported_speech": false
}
]
},
{
"text": "Probable coronavirus.",
"ents": [
{
"start": 9,
"end": 20,
"label": "covid",
"lexical_variant": "coronavirus",
"normalized_variant": "coronavirus",
"negated": false,
"hypothesis": true,
"family": false,
"reported_speech": false
}
]
}
]