Skip to content

Note

Click here to download the full example code

REST API

Ragna was designed to help you quickly build custom RAG powered web applications. For this you can leverage the built-in REST API.

This tutorial walks you through basic steps of using Ragnas REST API.

Step 1: Start the REST API

Ragnas REST API is normally started from a terminal with

$ ragna api

For this tutorial we use our helper that does the equivalent just from Python.

Note

By default, the REST API is started from the ragna.toml configuration file in the current working directory. If you don't have a configuration file yet, you can run

$ ragna init

to start an interactive wizard that helps you create one. The config that we'll be using for this tutorial is equivalent of picking the first option the wizard offers you, i.e. using only demo components.

import ragna._docs as ragna_docs

from ragna.deploy import Config

config = Config()

rest_api = ragna_docs.RestApi()
_ = rest_api.start(config)

Out:

Starting Ragna REST API
...

Let's make sure the REST API is started correctly and can be reached.

import httpx

client = httpx.Client(base_url=config.api.url)
client.get("/").raise_for_status()

Out:

<Response [200 OK]>

Step 2: Authentication

In order to use Ragnas REST API, we need to authenticate first. To forge an API token we send a request to the /token endpoint. This is processed by the Authentication, which can be overridden through the config. For this tutorial, we use the default ragna.deploy.RagnaDemoAuthentication, which requires a matching username and password.

username = password = "Ragna"

response = client.post(
    "/token",
    data={"username": username, "password": password},
).raise_for_status()
token = response.json()

We set the API token on our HTTP client so we don't have to manually supply it for each request below.

client.headers["Authorization"] = f"Bearer {token}"

Step 3: Uploading documents

Before we start with the upload process, let's first have a look what kind of documents are supported.

import json

response = client.get("/components").raise_for_status()
print(json.dumps(response.json(), indent=2))

Out:

{
  "documents": [
    ".docx",
    ".md",
    ".pdf",
    ".pptx",
    ".txt"
  ],
  "source_storages": [
    {
      "properties": {},
      "required": [],
      "title": "Ragna/DemoSourceStorage",
      "type": "object"
    }
  ],
  "assistants": [
    {
      "properties": {},
      "title": "Ragna/DemoAssistant",
      "type": "object"
    }
  ]
}

For simplicity, let's use a demo document with some information about Ragna

from pathlib import Path

print(ragna_docs.SAMPLE_CONTENT)

document_path = Path.cwd() / "ragna.txt"

with open(document_path, "w") as file:
    file.write(ragna_docs.SAMPLE_CONTENT)

Out:

Ragna is an open source project built by Quansight. It is designed to allow
organizations to explore the power of Retrieval-augmented generation (RAG) based
AI tools. Ragna provides an intuitive API for quick experimentation and built-in
tools for creating production-ready applications allowing you to quickly leverage
Large Language Models (LLMs) for your work.

The Ragna website is https://ragna.chat/. The source code is available at
https://github.com/Quansight/ragna under the BSD 3-Clause license.

The upload process in Ragna consists of two parts:

  1. Announce the file to be uploaded. Under the hood this pre-registers the document in Ragnas database and returns information about how the upload is to be performed. This is handled by the ragna.core.Document class. By default, ragna.core.LocalDocument is used, which uploads the files to the local file system.
  2. Perform the actual upload with the information from step 1.
response = client.post(
    "/document", json={"name": document_path.name}
).raise_for_status()
document_upload = response.json()
print(json.dumps(response.json(), indent=2))

Out:

{
  "parameters": {
    "method": "PUT",
    "url": "http://127.0.0.1:31476/document",
    "data": {
      "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiUmFnbmEiLCJpZCI6IjhhODJmNzc4LTI0ZDMtNGFkNS04OTFmLTNkYWUxNDRhMjVjMiIsImV4cCI6MTcyNTg5MzA0OC40NTk5MjIzfQ.nf2XKthU3PuGU9wRx-JiLPOPjr0i2uvhtxuNehd6ISg"
    }
  },
  "document": {
    "id": "8a82f778-24d3-4ad5-891f-3dae144a25c2",
    "name": "ragna.txt"
  }
}

The returned JSON contains two parts: the document object that we are later going to use to create a chat as well as the upload parameters.

Note

The "token" in the response is not the Ragna REST API token, but rather a separate one to perform the document upload.

We perform the actual upload with the latter now.

document = document_upload["document"]

parameters = document_upload["parameters"]
client.request(
    parameters["method"],
    parameters["url"],
    data=parameters["data"],
    files={"file": open(document_path, "rb")},
).raise_for_status()

Out:

<Response [200 OK]>

Step 4: Select a source storage and assistant

The configuration we are using only supports demo components for the source storage and assistant and so we pick them here.

from ragna import source_storages, assistants

source_storage = source_storages.RagnaDemoSourceStorage.display_name()
assistant = assistants.RagnaDemoAssistant.display_name()

print(f"{source_storage=}, {assistant=}")

Out:

source_storage='Ragna/DemoSourceStorage', assistant='Ragna/DemoAssistant'

Step 5: Start chatting

Now that we have uploaded a document, and selected a source storage and assistant to be used, we can create a new chat.

response = client.post(
    "/chats",
    json={
        "name": "Tutorial REST API",
        "documents": [document],
        "source_storage": source_storage,
        "assistant": assistant,
        "params": {},
    },
).raise_for_status()
chat = response.json()
print(json.dumps(chat, indent=2))

Out:

{
  "id": "3044b511-dd7f-4d20-b8e5-8e1b094eb91b",
  "metadata": {
    "name": "Tutorial REST API",
    "source_storage": "Ragna/DemoSourceStorage",
    "assistant": "Ragna/DemoAssistant",
    "params": {},
    "documents": [
      {
        "id": "8a82f778-24d3-4ad5-891f-3dae144a25c2",
        "name": "ragna.txt"
      }
    ]
  },
  "messages": [],
  "prepared": false
}

As can be seen by the "prepared" field in the chat JSON object we still need to prepare it.

client.post(f"/chats/{chat['id']}/prepare").raise_for_status()

Out:

<Response [200 OK]>

Finally, we can get answers to our questions.

response = client.post(
    f"/chats/{chat['id']}/answer",
    json={"prompt": "What is Ragna?"},
).raise_for_status()
answer = response.json()
print(json.dumps(answer, indent=2))

Out:

{
  "id": "b2bd5043-4fa9-4b44-94a5-bc82cab0e12c",
  "content": "I'm a demo assistant and can be used to try Ragna's workflow.\nI will only mirror back my inputs. \n\nSo far I have received 1 messages.\n\nYour last prompt was:\n\n> What is Ragna?\n\nThese are the sources I was given:\n\n- ragna.txt: Ragna is an open source project built by Quansight. It is designed to allow organizations to [...]",
  "role": "assistant",
  "sources": [
    {
      "id": "a9c66cdc-e54d-48ad-868c-c73ed3067e20",
      "document": {
        "id": "8a82f778-24d3-4ad5-891f-3dae144a25c2",
        "name": "ragna.txt"
      },
      "location": "",
      "content": "Ragna is an open source project built by Quansight. It is designed to allow organizations to [...]",
      "num_tokens": 17
    }
  ],
  "timestamp": "2024-09-09T14:39:08.533956Z"
}
print(answer["content"])

Out:

I'm a demo assistant and can be used to try Ragna's workflow.
I will only mirror back my inputs. 

So far I have received 1 messages.

Your last prompt was:

> What is Ragna?

These are the sources I was given:

- ragna.txt: Ragna is an open source project built by Quansight. It is designed to allow organizations to [...]

Before we close the tutorial, let's stop the REST API and have a look at what would have printed in the terminal if we had started it with the ragna api command.

rest_api.stop()

Out:

INFO:   RagnaDemoAuthentication: You can log in with any username and a matching
password.
INFO:     127.0.0.1:53500 - "GET / HTTP/1.1" 200 OK
INFO:     127.0.0.1:53508 - "GET / HTTP/1.1" 200 OK
INFO:     127.0.0.1:53508 - "POST /token HTTP/1.1" 200 OK
INFO:     127.0.0.1:53508 - "GET /components HTTP/1.1" 200 OK
INFO:     127.0.0.1:53508 - "POST /document HTTP/1.1" 200 OK
INFO:     127.0.0.1:53508 - "PUT /document HTTP/1.1" 200 OK
INFO:     127.0.0.1:53508 - "POST /chats HTTP/1.1" 200 OK
INFO:     127.0.0.1:53508 - "POST /chats/3044b511-dd7f-4d20-b8e5-8e1b094eb91b/prepare HTTP/1.1" 200 OK
INFO:     127.0.0.1:53508 - "POST /chats/3044b511-dd7f-4d20-b8e5-8e1b094eb91b/answer HTTP/1.1" 200 OK

Total running time of the script: ( 0 minutes 3.299 seconds)

Download Python source code: gallery_rest_api.py

Download Jupyter notebook: gallery_rest_api.ipynb

Gallery generated by mkdocs-gallery