Skip to content

Quickstart

Point CiteNexus at your evidence, bring your own models, and get answers that cite their source — or abstain. Pick your language in the first tab below; the tabs stay in sync, so you can read your own column top to bottom.

  1. Install the package.

    Terminal window
    go get github.com/muthuishere/citenexus/golang@v0.12.0
  2. Set up the evidence and your models. Nothing is bundled — every model is an injected OpenAI-compatible endpoint (Ollama, OpenAI, a local vLLM, Gemini’s compat endpoint).

    The Go port has no config file and no facade object: the corpus is the setup. You hand Ask documents in memory.

    import (
    "fmt"
    "github.com/muthuishere/citenexus/golang/answer"
    "github.com/muthuishere/citenexus/golang/models"
    "github.com/muthuishere/citenexus/golang/result"
    )
    corpus := []answer.Doc{
    {DocumentID: "employee-nda", Text: "The employee shall not disclose confidential information."},
    }
    // Optional: models. Omit this and the flow runs on deterministic in-process
    // fakes, so the quickstart needs no endpoint at all.
    transport := models.NewHTTPClient(nil, 0)
    providers := answer.Providers{
    Embedding: models.NewOpenAIEmbedding("http://localhost:11434/v1", "bge-m3", transport.Do),
    Generator: models.NewOpenAIChatGenerator("http://localhost:11434/v1", "qwen2.5", 0, nil, transport.Do),
    }

    Every client posts through an injected models.Transport, and auth lives in headers as ${ENV} templates expanded at call time — a key is never held as a value. See Bring your own model.

  3. Ask, and read the cited answer — or the refusal.

    res := answer.Ask(corpus, "Can the employee disclose confidential information?", answer.DefaultTopK)
    fmt.Println(res.Answer) // the verbatim quote — or the pinned refusal
    // A refusal carries NO sources, so branch on the decision first —
    // res.Sources[0] would panic.
    if res.Evidence.Decision == result.DecisionAnswered {
    src := res.Sources[0]
    fmt.Println(src.Document, src.Passage)
    } else {
    fmt.Println("refused:", res.MissingEvidence)
    }

    To run the same flow on the models from step 2, swap in answer.AskWith(corpus, question, answer.DefaultTopK, providers) — it returns (result.Result, error), because a model failure is an error, never a refusal.

    Reading files (PDF/DOCX/HTML/…) is a separate, opt-in path in Go: ingest.Ingest(...) sits behind the citenexus_ffi build tag and needs the Rust cdylib built first — see Install. Everything above runs on a plain go get.

  • How it works — what each of those calls actually did, stage by stage.
  • The deterministic core — the shared cite-or-abstain flow every port runs, byte-for-byte identical.
  • Store your corpus on S3 / MinIO instead of a local folder.
  • Models & endpoints — embedding, LLM, reranker, vision.
  • Bring your own model — a model that isn’t an HTTP endpoint? Keep the same client, swap the transport.
  • Ingest anything — a file, raw text, an image, or a URL, one source per call (there is no “ingest the whole bucket” call); crawl() walks a whole site.
  • Prove it with evaluate() against a golden set.