Documentation
¶
Overview ¶
Package client is raglit's Go client: how another program asks raglit about raglit's data.
It exists because the alternative was tried and failed. kgraph read relations.jsonl directly, raglit later moved rulings into a database projected from an audit trail, and kgraph went on reading a file nobody wrote any more — no error, no warning, just stale answers. Parsing another tool's storage makes every storage change a silent break in a program you did not edit.
So the contract is the HTTP API and never the files. raglit may reorganise its storage whenever it likes; a consumer only has to keep speaking this.
Availability is not an error. A consumer asking about rulings on a machine where raglit is not running should be told "unknown", not fail: kgraph must still scan a corpus when no daemon is up. Every call here returns ErrUnavailable for that case so the caller can tell "nothing ruled" from "could not ask" — a distinction that matters, because the first is a fact about the corpus and the second is a fact about this machine.
Index ¶
- Constants
- Variables
- type Client
- func (c *Client) Alive(ctx context.Context) bool
- func (c *Client) Document(ctx context.Context, index, path string) (Doc, error)
- func (c *Client) Documents(ctx context.Context, index string) ([]DocSummary, error)
- func (c *Client) Relations(ctx context.Context, project, doc string) ([]Relation, error)
- func (c *Client) Search(ctx context.Context, index, q string, n int) ([]Hit, error)
- func (c *Client) Slices(ctx context.Context, project, parent string) ([]Slice, error)
- func (c *Client) Withdrawals(ctx context.Context, index string) ([]Withdrawal, error)
- type Doc
- type DocSummary
- type Hit
- type HitTrust
- type Page
- type Relation
- type Slice
- type TokenSource
- type Transport
- type Withdrawal
Constants ¶
const DefaultAddr = "http://127.0.0.1:7420"
DefaultAddr is the shared per-user daemon.
Variables ¶
ErrUnavailable means raglit could not be reached — NOT that it had nothing to say. Callers must not read it as an empty result.
Functions ¶
This section is empty.
Types ¶
type Client ¶
Client talks to a raglit daemon.
func New ¶
New returns a client for base, defaulting to $RAGLIT_DAEMON then the shared per-user daemon. Discovery also consults the daemon's own state file, so a daemon on a non-default port is found instead of a dead default being probed.
func (*Client) Document ¶
Document returns one document's text, per page where raglit knows the pages.
PAGES, not fragments. A fragment may span a page boundary and carries a single page number, so a hit inside one cannot be resolved to the page it is on — a page number right for the start of a fragment and wrong for the rest is worse than none, because a reader turns confidently to the wrong page. This returns the per-page text raglit produced at ingest.
An unreachable daemon is an error, for the reason on Search.
func (*Client) Documents ¶
Documents lists what an index holds. Path is raglit's absolute doc id.
One call instead of one per document: a consumer asking "does this have readable text" for a whole corpus was otherwise a round trip per file, which turns a backlog listing into a network storm.
IT DECODED INTO THE WRONG TYPE AND THEREFORE ALWAYS FAILED. This returned []Doc, whose `pages` is a LIST of pages with their text; the endpoint sends `pages` as a COUNT. Every real response failed to unmarshal — "cannot unmarshal number into Go struct field Doc.documents.pages" — so this function had never once returned a document.
The consequence was silent and lived one layer up: kgraph's `docsWithText` maps an error to nil, meaning "could not ask", and its caller falls back to looking for a sidecar on disk. So the backlog was permanently answering the question the index was supposed to have answered, for every corpus, and the fallback made it look like it was working.
func (*Client) Relations ¶
Relations returns every ruling for a project. doc, when non-empty, narrows to rulings involving that document.
func (*Client) Search ¶
Search runs one query against a named index and returns the best n fragments.
index is raglit's full index name, which for a project-scoped index is "<project>__<index>" — the daemon namespaces them, and passing a bare project name silently searches somebody else's corpus.
An unreachable daemon is an ERROR, never an empty result. A consumer deciding whether a corpus holds a document cannot be handed "no" when the truth is "not asked": that is the one answer that stops a person looking for evidence that is actually there.
func (*Client) Slices ¶
Slices returns declared sub-documents for a project, optionally one bundle's.
func (*Client) Withdrawals ¶
Withdrawals lists what has been ruled out of an index, path order.
Read off the health report rather than a ledger endpoint of its own, because that report is already where a withdrawal is published — `kind=withdrawn` IS the list, and it is exempted there from the filter that hides withdrawn documents from every other kind.
AN UNREACHABLE DAEMON IS AN ERROR, and a caller must not read it as "nothing has been withdrawn". Those are the two answers this exists to keep apart: one says a ruling was made, the other says nobody could ask. `get` returns ErrUnavailable for both a stopped daemon and one too old to have the route.
type Doc ¶
type Doc struct {
Index string `json:"index"`
Path string `json:"path"`
Title string `json:"title,omitempty"`
Pages []Page `json:"pages,omitempty"`
Text string `json:"text,omitempty"`
// Truncated reports that Text is not the whole document. A consumer checking
// whether a quotation appears MUST NOT read a miss as absence when this is
// set: the passage may be in the part that was cut.
Truncated bool `json:"truncated,omitempty"`
}
Doc is a document's text as raglit holds it, per page and whole.
type DocSummary ¶
type DocSummary struct {
Index string `json:"index"`
Path string `json:"path"`
Title string `json:"title,omitempty"`
// Fragments is the indexed text: zero means nothing readable came out of it.
Fragments int `json:"fragments"`
// Pages is the page COUNT, not the pages. A document may have pages and no
// fragments, which is exactly the un-OCR'd scan.
Pages int `json:"pages"`
// Vision counts what was read by a vision model rather than a text layer.
Vision int `json:"vision"`
FragMode string `json:"frag_mode,omitempty"`
}
DocSummary is one document as the INDEX holds it: what it is, and how much of it was actually read.
Fragments is the number that answers "does this have readable text". A row with zero is IN the index and was never read — a scan nobody OCR'd, an image nobody captioned — and that is a different state from a document the index has never heard of, which is a different state again from one on disk with a text layer. Collapsing any two of them produces a backlog that asks somebody to classify a document nothing can read.
type Hit ¶
type Hit struct {
Index string `json:"index"`
Doc string `json:"doc_id"`
Title string `json:"title,omitempty"`
Page int `json:"page,omitempty"`
Score float64 `json:"score"`
Snippet string `json:"snippet,omitempty"`
// Caveat is the one line to show beside the snippet when it cannot be read
// as the record — a model's description of an image, or a read nobody
// measured. EMPTY for an ordinary transcription, because a caveat on every
// row is a caveat on none.
//
// A consumer that shows the snippet and drops this is showing a model's
// account of a picture as though somebody wrote it down.
Caveat string `json:"caveat,omitempty"`
// Trust is the same fact structured: what produced the text, whether a
// person has ruled on it, and the per-facet confidences. Nil when raglit
// recorded nothing about how the document was read — which is NOT a claim
// that it is reliable, and is why Caveat is non-empty in that case.
Trust *HitTrust `json:"trust,omitempty"`
}
Hit is one search result: a fragment of a document raglit holds.
Doc is raglit's own document id, which is an ABSOLUTE path — a consumer whose own document paths are relative has to relativize it, and comparing the two unnormalized matches nothing while looking exactly like "found nothing".
type HitTrust ¶
type HitTrust struct {
// Method is what produced the text: text-layer, vision-ocr, oidio-asr, pandoc…
Method string `json:"method,omitempty"`
// Level is machine, adapted or attested — whether a person has been through it.
Level string `json:"level,omitempty"`
// DescribedPct is how much of the document a model DESCRIBED rather than
// transcribed. -1 means it was never measured, which is distinct from 0.
DescribedPct int `json:"described_pct,omitempty"`
// Facets is the per-facet confidence, 0-100: text, speaker, layout, subject.
// A facet that is ABSENT is not claimed, rather than distrusted.
Facets map[string]int `json:"facets,omitempty"`
// Summary renders Facets weakest-first for a reader ("subject 80%", "text 90%").
Summary []string `json:"summary,omitempty"`
// RuledBy is the person who ruled, when one has.
RuledBy string `json:"ruled_by,omitempty"`
}
HitTrust is how far a hit's text can be relied on, and about WHAT.
Mirrored here rather than imported from raglit's root package, for the reason this package exists: the contract is the JSON, so raglit may reshape its internals and a consumer only has to keep speaking this.
type Relation ¶
type Relation struct {
A string `json:"a"`
B string `json:"b"`
Kind string `json:"kind"` // copy | version | unrelated
Supersedes string `json:"supersedes,omitempty"`
Note string `json:"note,omitempty"`
By string `json:"by,omitempty"`
At string `json:"at,omitempty"`
Relation string `json:"relation,omitempty"`
Coverage float64 `json:"coverage,omitempty"`
}
Relation is a ruling on a pair of documents.
type Slice ¶
type Slice struct {
ID string `json:"id"`
Parent string `json:"parent"`
From int `json:"from"`
To int `json:"to"`
Title string `json:"title,omitempty"`
Note string `json:"note,omitempty"`
By string `json:"by,omitempty"`
At string `json:"at,omitempty"`
}
Slice is a declared sub-document: a page range of a bundle.
type TokenSource ¶
type TokenSource struct {
// contains filtered or unexported fields
}
TokenSource yields the bearer token. The zero value and nil have no token.
func EnvTokenSource ¶
func EnvTokenSource(configCmd string) *TokenSource
EnvTokenSource reads $RAGLIT_TOKEN and $RAGLIT_TOKEN_COMMAND, falling back to configCmd (config auth.token_command). Nil when none is set.
func LocalAdminTokenSource ¶ added in v0.4.0
func LocalAdminTokenSource() *TokenSource
LocalAdminTokenSource reads the admin token of a daemon on this machine.
type Transport ¶
type Transport struct {
Base http.RoundTripper
Src *TokenSource
Allow func(host string) bool
}
Transport adds the bearer token to requests for the hosts Allow accepts, and retries once with a renewed token after a 401. Every other request passes through untouched — a daemon token must never reach a model endpoint.
type Withdrawal ¶
Withdrawal is a document ruled OUT of the corpus, with the grounds recorded.
Withdrawing and deleting are different acts, and the difference is the whole point of reading this: a delete removes rows and the next sweep puts the document back, so it is a preference. A withdrawal is a RULING — it carries grounds, it is recorded where git can blame it, and the ingest path honours it. A consumer that walks the directory instead of asking will offer a withdrawn document as a candidate forever, identically on every run, and nothing anywhere will say it was ruled out.
Only Path and Reason: the report this reads is keyed on the ruling, and who ruled and when are not on it. Ask the judgement store if you need them.