quivrplugin implements the connector Contribution of the Plugin
Protocol v0 (contract,
Plugin API 0.3), so a source collector is one Go type. The core keeps the
schedules, checkpoints, credentials and health; your plugin only fetches pages
from the source. It depends only on gopkg.in/yaml.v3 and
santhosh-tekuri/jsonschema/v6, never on Quivr’s engine packages. Go 1.24 or
later. It also serves the ingestion Contribution (Plugin API 0.6,
below) and the retrieval Contribution (Plugin API 0.7,
below); write normalizers and alert rules with the
Python SDK.
examples/static-source is a complete
plugin: a manifest, a connector, fixtures and a test.
A connector
Declare each kind inquivr-plugin.yaml with the JSON Schemas of its
configuration and credential:
Fetch and CheckCredential and serve:
An ingestion plugin
Declarecontributions.ingestion with the vector spaces the plugin owns and
register a type with two methods, SegmentAndEmbed (segments of the Version’s
text Parts, each with a vector per requested space) and EmbedQuery, through
plugin.Ingestion(impl). The SDK refuses undeclared spaces and checks offsets,
dimensions and max_segments before answering; a RetryableIngestError delays
the Version, a TerminalIngestError blocks it.
examples/hash-embedder is certified in CI, and
Write an ingestion plugin
covers pinning it.
A retrieval plugin
Declarecontributions.retrieval with its profiles (default required) and
register a Retriever through plugin.Retrieval(impl). Each round, Search
answers quivrplugin.Ask(requests...) for candidates or
quivrplugin.Rank(hits...) for the final ranking; req.Served holds every
candidate served so far. Before answering, the SDK refuses undeclared profiles
and checks what the core checks: no requests in the last round, request and k
limits, and a ranking of served candidates only. A TerminalSearchError
refuses the query. examples/fusion-retriever is
certified in CI, and
Write a retrieval plugin
covers pinning it.
What the SDK does
Checkpoints, items and errors
- The checkpoint is yours. Any JSON value that resumes after the returned
items: a cursor, a timestamp, a set of seen ids. The core stores it and
sends it back unchanged, and moves it forward only after your items are
accepted, so a crash never loses items. Keep it under 64 KiB, or declare
limits.max_checkpoint_bytes(at most 1 MiB). - Pages. Return at most
max_itemsitems andMore: trueto be called again at once in the same run, with the checkpoint moved. Usereq.Now, nottime.Now(), andreq.PageInRunandreq.ReadsTodayto respect source quotas.req.Connector.CorpusIDandreq.Connector.SourceNamespace(Plugin API 0.3.1) name where the instance writes, to bind Relation targets. - Items carry a stable
RecordKey, aRevisionwhen the source has one (the core skips an unchanged revision), text or a Manifest of text Parts (quivrplugin.NewManifest), extensions in namespaces your manifest declares, orWithdraw: truefor an item deleted at the source. - Attachments (Plugin API 0.4): declare
contributions.connector.attachmentsand implementOpenAttachment, which streamsreq.Attachment.Ref. The SDK reads the bytes once into a temporary file, answers their size and SHA-256 or a skip (optionalSkippedAttachmentupdates the item’s extensions), then uploads them to the core’s grant, which never prints. SetSizeBytesandSHA256when you already hold the exact bytes. - Errors tell the core what to show operators:
AccessErrorfor a refused credential (healthaccess_error),TransientErrorfor an outage or a rate limit (retried),SourceErrorfor data you cannot use. - Push (Plugin API 0.5): declare
modes: [pull, push]and implementReceiver. The core relays each request the source sends to the instance’s publicWebhookURL(in fetch requests; register it with the source).Receiveverifies it with the credential overreq.Request.Body(), then answersAccept(items...),Respond(200, type, body)for a challenge, orRefuse(401, reason), which changes nothing. Map a delivered item exactly asFetchmaps it, so both paths converge on one Receipt.Page.Push(PushIsActive,PushIsPending,PushHasFailed) reports your setup at the source; an active one may relax polling.
Test it
plugintest replays a fixture in process, through the same handler the core
talks to:
contracts/plugins/v0/connector-fixture.schema.json))
names the kind, config, credential and starting checkpoint, and what to
expect: the Record Keys of each page, an error class, the credential check,
and for a push kind the receive cases with their expected verdicts.
Use test credentials of 8 characters or more, so the leak check can see them.
Then certify the plugin with the Contract Runner:
go-connector-contract-report artifact. To run it in Quivr, pin it as in
Run a connector plugin.
Rules
- Import only this SDK and public packages:
make plugin-boundaryfails when code underplugins/orsdks/go/imports Quivr’sinternal/packages or requires the engine module. - The schema copies under
quivrplugin/schemascome fromcontracts/; runpython3 sdks/go/scripts/sync_schemas.pyafter a contract change (make testfails when they are stale).