Skip to content

API

RAG-Core includes an API module intended to expose the endpoints your users will need to interact with your services. This API is automatically generated from a simple yaml config file.

It enables you to offer your users service documented on OpenAPI standard to facilitate interactions with your RAG.

API Configuration

API is generated according to a configuration file.
The configuration file expects the following structure:

info:
  name: "Infopro Digital RAG API"
  description_path: "examples/api_description.md"
  contact:
    name: "Antoine Meilliez"
    email: "antoine.meilliez@datagears.io"
services:
  - name: "USN RAG"
    api_prefix: "usn_rag"
    description: "Question Answering service providing answer based on USN news articles"
    module_path: "examples/advanced/usn_rag_with_sources.py"

Let's break it down

  • info: Contains generic information about your API, displayed in the header of the OpenAPI documentation.
    • name: The title shown a the header of your OpenAPI configuration
    • description_path: The path to the markdown or text file containing a description of your API.
    • contact:
      • name: The technical referent of your application
      • email: The related email
  • services: Contains a list of services to be exposed in your API.
    • ❗️ name: The name attribute of the RAG or EmbeddingPipeline instance (accessible via rag.name)
    • api_prefix: The unique prefix used to route the generic endpoints such as /invoke, /batch, /stream.
    • description: A small text displayed next to the tag (aka name) in OpenAPI docs
    • ❗️module_path: The python file or folder path where the RAG or EmbeddingPipeline is instanciated. examples: app.my_rag.py

Registering a RAG or an EmbeddingsPipeline

In the services section, simply add an item with the following information:

`  - name: "Fancy RAG" # Your RAG or EmbeddingPipeline name
    api_prefix: "fancy" # Your unique api prefix
    description: "A fancy description"
    module_path: "path/to/your/fancy_file.py"

Environment variables

You can configure some properties of your API by setting a few environment variables.

  • PORT : int


    The port behind which the API is exposed: http://0.0.0.0:{PORT}/docs

  • AUTO_RELOAD: bool


    Automatically reload the server when you make changes to your code.

  • LOG_LEVEL: str


    Can be one of NOTSET / DEBUG / INFO / WARNING / ERROR / CRITICAL

  • API_CONFIG_FILE: str


    The path when the api.yaml can be found

  • STATIC_FILE_URL: str


    The URL path of the static demo
    Demo is deactivated if not set.


    default: /static

  • DOCS_URL: str


    The URL of the OpenAPI docs
    Docs are deactivated if not set.


    default: /docs

Run it

Several ways allows you to run the API

  • Poetry
    poetry run api
    
  • Make
    make api
    
  • Fastapi
    For development and debug only
    poetry run fastapi dev api/__init__.py
    

Customize

API is generated by default but you can fully adatp it to your needs and application.

Add document schema validation

The API endpoint related to EmbeddingsPipeline (/batch) takes a list of documents as input. One can request from its users to implement a specific schema for the documents to be embed. Such feature can be critical to ensure consistency for the embeddings data your application will rely on.

In api/embeddings_router.py, update the Document BaseModel:

Source

class Document(BaseModel):
    page_content: str = Field(
        ...,
        examples=[
            "L'annonce à été faite par le successeur de Tim Cook lors de la keynote...",
            "Le NASDAQ réagit positivement à l'annonce...",
        ],
    )
    metadata: dict = Field(
        default_factory=dict,
        examples=[
            {
                "title": "Apple annonce l'iPhone 34",
                "site_short_name": "USN",
                "published_at": 1715923800000,
                "published": True,
            },
            {
                "title": "30/02/2042 - Le NASDAQ cloture en hausse",
                "site_short_name": "USN",
                "published_at": 1715934700000,
                "published": False,
            },
        ],
    )

Example

metadata fields can be enforced by implementing a custom metadata BaseModel such as:

class MetadataModel(BaseModel):
    title: str
    site_short_name: Literal["USN"]
    published_at: int
    published: bool
Such BaseModel can then be nested into the Document model:
class Document(BaseModel):
    page_content: str
    metadata: MetadataModel
That's it. From now on your user documents will have to fit these fields and types to be accepted by the API.

Custom output schema

One can customize the API output accepted schema in api/rag_router.py either by updating the RAGOutputBaseModel:

Source

class DocumentOutput(BaseModel):
    page_content: str
    metadata: dict[str, Any]
    type: str = "Document"


class RAGOutput(BaseModel):
    context: List[DocumentOutput]
    question: str = Field(
        examples=["Quels sont les contrats signés avec AirBus au bourget?"]
    )
    answer: str = Field(
        examples=[
            "Airbus a signé un contrat de 100M€ avec Safran et un autre de 200M€ avec Thales"
        ]
    )

Or by updating the parse_output function

Source

def parse_output(output: dict[str, Any]) -> RAGOutput:
    """Parse the output of the RAG model to a RAGOutput object.
    This function ensure compatibility between Langchain (based on pydantic v1) models and RAG-Core (based on pydanitc v2) models.
    """
    answer = output["answer"]
    context = output["context"]
    question = output["question"]

    if isinstance(answer, BaseMessage):
        answer = answer.content

    if all([isinstance(doc, Document) for doc in context]):
        context = [DocumentOutput(**doc.dict()) for doc in context]

    return RAGOutput(answer=answer, context=context, question=question)

Add custom endpoints

On can add custom endpoint in api/__init__.py

Example

fastapi = FastAPI(
    title=api_config.info.name,
    description=api_config.info.description,
    version=get_project_version(),
    contact=api_config.info.contact.model_dump(),
    openapi_tags=[
        {"name": ref.name, "description": ref.description}
        for ref in api_config.services
    ],
    docs_url=os.environ.get("DOCS_URL", None),
    redoc_url=None
)
add_rag_routes(fastapi, api_config)
add_embeddings_routes(fastapi, api_config)
add_exception_handlers(fastapi)
# -- Add your endpoints after these initialisation steps --

# Example of custom endpoint
@fastapi.get("/greetings")
def custom_endpoint() -> str:
    return "Hello world"