Skip to main content
You will write a normalizer: a plugin that turns a file into the text Parts Quivr indexes. Yours reads CSV files and makes each row a Part, so a search hit says which row it came from. You start from an empty folder, certify the plugin with quivr plugin test, then run it in the local Quivr from the Quickstart and search a CSV file through it. It takes about twenty minutes.

Before you start

  • The Quickstart works on your machine, in a quivr-v2 checkout. The last part of this tutorial uses that local stack, so it needs Linux x86_64; the plugin itself runs anywhere.
  • Python 3.12 or later with venv: python3 --version.
Work in the folder that contains your quivr-v2 checkout.
1

Install the quivr command

The quivr binary has the plugin tools. Build it from your checkout, and remember where the checkout is:
Check it by inspecting the manifest of a first-party plugin. The report starts with:
2

Create the plugin

quivr plugin init writes a working plugin to start from. Create one, then install the Python SDK, which is not on PyPI yet, from your checkout:
The template reads Markdown. You replace its manifest, its code and its test file, so remove the Markdown sample and its tests:
3

Declare what the plugin does

Replace quivr-plugin.yaml with this manifest. It says the plugin is a normalizer for text/csv files, which versions of Quivr and of the plugin protocol it works with, and how to start it:
quivr-plugin.yaml
Check it:
4

Write the normalizer

Replace csv_rows/normalizer.py. The SDK calls normalize with the invocation; read_input() downloads the file and checks its size and checksum. The function returns one body Part per row, keyed row-1, row-2 and so on:
csv_rows/normalizer.py
A TerminalError tells Quivr the file can never be read, so it stops retrying and records why. Quivr indexes Parts whose role is title or body.
5

Try it on a sample file

Add a CSV file and a fixture that describes it. Fixtures are the test inputs the plugin tools replay:
fixtures/events.csv
fixtures/events.json
quivr plugin dev starts the plugin, checks that it serves this exact manifest, sends it the fixture and validates the answer the way Quivr will:
It prints the plugin’s answer, then:
6

Certify it

The Contract Runner checks everything Quivr relies on: discovery, deadlines, replays that must give the same answer, and refusal of invalid requests.
7

Run it in Quivr

In the terminal you used so far, still in the plugin folder, serve the plugin on port 9900. It keeps running and restarts when you edit a file:
Open a second terminal in the folder that contains your checkout, and restart the local stack with your plugin pinned. QUIVR_NORMALIZER points at the plugin folder; Quivr then sends every text/csv file to it:
make dev prints External normalizer: …/csv-rows, pinned at http://127.0.0.1:9900. Running make dev again without the variable goes back to the default PDF plugin.
8

Upload a CSV file

Files go through an upload session: Quivr gives you a URL to send the bytes to, then checks them. Sending the same request again returns the same session, already verified, so the PUT runs only once. In the second terminal, create a Corpus and upload the sample file:
The last line prints the verified Blob’s identifier, such as blob_…. Now add it as a Record:
9

Search it

Quivr calls your plugin in the background. After a second or two, the Receipt is resolved; if its state is still pending, run the command again:
Search for a word from the second row:
The hit names row-2: the row your plugin produced. The Version also records which plugin made it:

What you built

A certified normalizer that Quivr calls for every CSV file, and a Record whose rows are searchable one by one. If your plugin is down, Quivr keeps the file and retries until it is back; if it raises a TerminalError, Quivr quarantines that Version and shows your error code in its diagnostics.

Pin a plugin

Pin your plugin in a real deployment’s configuration.

Plugin types

Connectors, ingestion, retrieval and alert rules.

Plugin manifest

Every field of quivr-plugin.yaml.

Plugin protocol

The routes and fields, to write a plugin in another language.