Messy CSV, JSON, or text → one LLM schema pass → deterministic rows into Neo4j → ask in English (Cypher).
Luna (or your configured model) sees the file once and names types, attributes, relationships.
Every cell maps through that schema via insert_facts. Then
ask compiles English to Cypher on the populated graph.
Ingest does not merge intra-file fragments — er rebuild is the second pass.
Ontology extend and field-conflict handling are part of the same loop.
infona.ai (waitlist / demo) · what's free · API · agents
Ask in English → Cypher on Neo4j. The graph is the trials.csv sample. Zero-key FLAURA2 is a cached-plan replay, not live inference.
This repo is the OSS runtime (infona_client, @infona-ai/cli, @infona-ai/mcp). Paid adapters, production Clerk, Explorer, billing/entitlement, and cloud infra live in the private parent, not here. Full table: docs/BOUNDARY.md.
10-minute quickstart
Need: Docker + Node 20+ (for the infona CLI). A stranger gets a real answer with no API key.
./scripts/oss_up.sh compose-ups Neo4j + API, waits until /health reports Neo4j up, writes ~/.infona/config.json, and loads the prebuilt trials graph. After that, bare infona works. --local / --no-login are first-run / one-off flags; they do not rewrite config. Re-connect without compose: infona init --local.
Zero-key (cached-plan replay)
The prebuilt path replays a cached Cypher plan. It is not live inference.
/ask stays always-LLM Cypher whenever a real model key (or INFONA_LLM_BASE_URL) is configured.
git clone https://github.com/infona-ai/infona-oss.git && cd infona-oss
cp .env.example .env # leave OPENROUTER_API_KEY empty / as the placeholder
npm i -g @infona-ai/cli # or use npx @infona-ai/cli in place of infona
./scripts/oss_up.sh # Neo4j + API + ~/.infona/config.json + prebuilt trials graph
infona ask "Which Phase 3 NSCLC trials is AstraZeneca running?" --kg trials
That question should return FLAURA2, labelled as a cached-plan replay (not live inference).
Reload the snapshot later (still no key):
./scripts/load_prebuilt_trials.sh
infona ask "Which Phase 3 NSCLC trials is AstraZeneca running?" --kg trials
Advertised bound stays 10 minutes. Measured 1 min 42 s cold on macOS 26.5.1 + Colima (Ubuntu 24.04 VM, 4 CPU, 6 GB; warm daemon, empty project, docker compose build --no-cache) from git clone to that zero-key ask. Native Linux was not measured. First-time neo4j:5-community pull is extra; 10 minutes still covers it. Timing notes: docs/quickstart-timing.md.
Placeholder keys from .env.example (sk-or-...) count as no key.
INFONA_ASK_CACHED_PLAN=1 forces replay even with a key (tests). =0 disables it.
1. Messy suppliers — URI merge
examples/suppliers-messy.csv is synthetic (Acme / Globex / Initech, fake tax IDs). No real customer data.
Schema inference needs a key (paste OPENROUTER_API_KEY=sk-or-... into .env):
infona ingest examples/suppliers-messy.csv --kg suppliers
infona er rebuild --kg suppliers
Ingest writes every row as its own Supplier fragment. er rebuild re-blocks the already-ingested graph and collapses fragment URIs (6→3). Headquarters and credit_rating then land as graph state: Austin is the current HQ (San Francisco stays stored, closed); equal-trust credit_rating stays dual-current and flagged.
Rebuilding entity resolution for suppliers…
Supplier 6 → 3 (−3 fragments across 2 clusters)
merge https://graph.infona.ai/entities/Supplier/ERP-1001
losers: https://graph.infona.ai/entities/Supplier/CRM-4402, https://graph.infona.ai/entities/Supplier/DIR-8891
reason: signal-richest
score: 1.00
provenance: erp @ 2026-03-01T12:00:00+00:00 (source_of_truth)
merge https://graph.infona.ai/entities/Supplier/ERP-2001
losers: https://graph.infona.ai/entities/Supplier/CRM-5503
reason: signal-richest
score: 1.00
provenance: erp @ 2026-03-01T12:00:00+00:00 (source_of_truth)
conflict headquarters
entity: https://graph.infona.ai/entities/Supplier/ERP-1001
winner: Austin (erp, source_of_truth, 2026-03-01T12:00:00+00:00)
loser: San Francisco (directory, supplementary, 2024-06-01T00:00:00+00:00)
reason: authority
unresolved credit_rating
entity: https://graph.infona.ai/entities/Supplier/ERP-1001
crm: BBB @ 2026-03-01T12:00:00+00:00 (source_of_truth)
erp: A @ 2026-03-01T12:00:00+00:00 (source_of_truth)
flagged: equal-trust sources — not silently guessed
Done. 3 fragments absorbed.
- merge — three Acme name variants (and two Globex) became one entity each. The surviving URI is the signal-richest fragment; its
provenanceis the source row that won (erp, timestamp, authority). That URI collapse is applied to the graph. - conflict / headquarters — the report names Austin as the authority-axis winner (ERP
source_of_truthover a stale directory scrape). Austin is current; San Francisco stays stored and closed. - unresolved / credit_rating — ERP says
A, CRM saysBBB. Same authority, same timestamp. The report flags the pair instead of silently picking; both stay dual-current.
Fixture notes: examples/suppliers-messy.md.
Hermetic proof: tests/test_suppliers_messy_fixture.py.
2. ingest → ask — the payoff
With a key, live /ask is always-LLM Cypher. The cached plan is not consulted when a real key is present.
infona ingest examples/trials.csv --kg my-data
infona ask "Which Phase 3 NSCLC trials is AstraZeneca running?" --kg my-data
That question should return FLAURA2. examples/trials.csv is a 16-row oncology sample (8 sponsors, 11 drugs, 7 indications) — public program names, synthetic TRIAL-* IDs, no patient data.
The looping SVGs are generated from scripts/render_readme_demos.py. Local Neo4j notes: docs/neo4j-local.md. If something fails, the CLI should name the next command.
Python package (library, not the infona CLI — that is @infona-ai/cli). Same version as the npm packages:
pip install infona-client
Import path is infona_client. Graph IRIs live under https://graph.infona.ai/. Env prefix is INFONA_* only.
How to use
CLI, MCP, and HTTP share one canonical route per operation. Do not invent a bespoke path.
| Command | What it does |
|---|---|
infona use <kg> |
Save the working graph. Later ingest / ask can drop --kg. |
infona ingest <file> --kg <kg> |
Schema once, then deterministic rows. Does not merge intra-file fragments. |
infona er rebuild --kg <kg> |
Second-pass URI collapse: apply winners, report leftovers. --kg is required (does not read infona use). |
infona ask "…" --kg <kg> |
Always-LLM Cypher with a key; cached-plan replay with none. |
infona ontology types |
List types / attributes. |
infona ontology resolve "…" |
Evolve the ontology from English (--kg is optional context; not the use default). |
infona export --kg <kg> |
JSON or CSV out. --kg is required. |
infona use trials
infona ingest examples/trials.csv
infona ask "Which Phase 3 NSCLC trials is AstraZeneca running?"
infona ontology types
infona ontology resolve "add a runs relationship from Sponsor to Trial"
infona er rebuild --kg trials
infona export --kg trials -f json -o trials.json
Writes go through insert_facts + refresh_after_write. Entity URIs via graph.ontology_queries.entity_uri. Instance relationships use https://graph.infona.ai/onto/<leaf>.
More CLI: packages/cli/README.md. HTTP: docs/API.md.
Optional: 3rd-party REST / SQL extract (dlt)
pip install infona-client does not pull dlt. Install the extra on the backend only (pip install 'infona-client[dlt]'). Infona is the destination — there is no dlt warehouse sink.
There is no infona ingest --dlt. The same spec is POST /graphs/{tenant}/ingest/dlt (SDK Client.ingestDlt, MCP ingest_dlt). The CLI is not on that route.
# frozen body for POST /graphs/{tenant}/ingest/dlt
cat > spec.json <<'EOF'
{
"source": {
"kind": "rest_api",
"base_url": "https://api.example.com",
"auth": {"type": "bearer", "secret_ref": "env:EXAMPLE_TOKEN"},
"resources": ["v1/contacts"]
},
"map": {"v1/contacts": {"type": "Contact", "id_field": "id"}},
"kg": "crm"
}
EOF
curl -sS -X POST http://localhost:8000/graphs/default/ingest/dlt \
-H 'Content-Type: application/json' \
--data-binary @spec.json
SQL is the same shape with "kind": "sql" and "dsn": "env:EXAMPLE_DSN". Hosted Explorer Connect / Run is premium (ONTA-554) and hits this same route.
MCP (agents)
Same ask, same graph, same exact rows — as a tool result. Same HTTP routes the CLI hits.
{
"mcpServers": {
"infona": {
"command": "npx",
"args": ["-y", "-p", "@infona-ai/mcp", "infona-mcp"],
"env": {
"INFONA_API_URL": "http://localhost:8000",
"INFONA_TENANT": "default"
}
}
}
}
ask, search, agent, ingest_csv, ingest_dlt, er_rebuild, export_kg, ontology, jobs. packages/mcp/README.md.
Eval
Query accuracy is a live always-LLM Cypher pin; the small-n 8-question run is historical. Protocol, dated table, and repro: docs/EVAL.md. Eval is Python-only; there is no infona eval CLI.
What leaves your machine
Infona does not phone home unless you turn it on. Default off.
export INFONA_TELEMETRY=1 # opt in
export INFONA_TELEMETRY=0 # force off (wins over a previous yes)
The first-run CLI prompt (infona / infona init on a TTY) asks the same question and writes ~/.infona/telemetry.json. There is no opt-out default.
Only when enabled, one anonymous JSON object per job:
- job type (
ingest/ask/er rebuild/export) - a row-count bucket (not the exact count)
- source type (
csv/json/jsonl/text/http— never a filename) - error class (exception type or HTTP family — never the message)
A random install_id (UUID) identifies the install, not you.
Never leaves: your data, column names, file names, graph content, workspace / tenant ids, prompts, answers, Cypher, emails, API keys.
When enabled, the default collector is the public Infona-oss PostHog project (write-only project token). Override with INFONA_TELEMETRY_URL, set it to off, or use INFONA_TELEMETRY_SINK=stderr / file locally.
Full contract: docs/TELEMETRY.md.
What this is not
Infona is not a memory or context layer that stuffs retrieved chunks into a prompt window. This repo registers no default open-web page fetcher; you bring retrieval or you skip web fetch (docs/BOUNDARY.md). It is not RAG over a vector index, and it is not "chat with your CSV."
Product path is Neo4j GraphStore / Cypher. SPARQL is not the product query language. Neptune is not the product store.
What you get
| Entity resolution | infona er rebuild collapses fragment URIs (6→3 on the suppliers fixture). Winner URI, reason, score, provenance timestamp. Authority-axis winners become the current graph value (Austin HQ; SF stored/closed). Equal-trust credit_rating stays dual-current and flagged. |
| Provenance | Source + timestamp + authority on the winning fact in the report. Answers carry per-fact citations (tests/test_answer_citations.py). |
| Schema from one pass | Luna (or your configured model) sees the file once. Types, attributes, relationships. No per-row LLM. |
| Deterministic rows | Every cell maps through that schema via insert_facts. |
| A real graph | Neo4j. Sponsors, trials, drugs, indications are nodes. |
| Ask | Always-LLM Cypher when a key is present. Cached-plan replay when it is not. Fail-closed when the plan is a silent wrong total. |
| CLI + MCP + HTTP | Same canonical routes. infona, @infona-ai/mcp, POST /graphs/{tenant}/ask. |
| Export | JSON or CSV back out. The graph is yours. |
CSV / JSON / text
→ schema inference (1 LLM call; skipped for the prebuilt snapshot)
→ deterministic row mapping
→ Neo4j knowledge graph (GraphStore / Cypher)
→ er rebuild (URI collapse; field winners applied; equal-trust flagged)
→ ask (cached-plan replay with no key; always-LLM Cypher with a key)
Ask is always-LLM Cypher when a model is configured. Grounding, probes, and few-shots inform the model; they do not replace it.
export OPENROUTER_API_KEY=sk-or-...
export INFONA_QUERY_PROVIDER=openrouter
export INFONA_QUERY_MODEL=openai/gpt-oss-120b
What's free
- OSS (this repo): ingest, ontology, ask, MCP / CLI / HTTP, export, free sources, BYOK registry, plugin seams.
- Bring your own retrieval: OSS registers no open-web page fetcher. Enrichment that needs a URL fetch declines unless you register one — or you use hosted Infona.
- Hosted-only: managed keys Infona bills, paid search/scrape ladders, curated Enhanced ontology, Explorer, billing, Clerk, cloud infra.
Full table: docs/BOUNDARY.md.
Product path: FastAPI + Neo4j GraphStore (Cypher). SPARQL / Neptune are not product backends.
For coding agents
Humans and coding agents follow the same contract. Read AGENTS.md before writing code. How to set up a clone and open a PR: CONTRIBUTING.md. First-time authors sign CLA.md on the PR (I have read the CLA Document and I hereby sign the CLA). Apache-2.0; public publication is a one-way door. Never commit secrets.
Do
- Import
infona_client. npm:@infona-ai/cli,@infona-ai/mcp. Env:INFONA_*. IRIs:https://graph.infona.ai/…. - Writes through
insert_facts+refresh_after_write. Mint entities withentity_urionly. - File budget ~500 lines; hard 550 for new
infona_client/packages/testsfiles. Oversized files are pinned intests/test_file_size_budget.pyand must not grow. - Hermetic tests (
MemoryGraphStore, mocks). No live Neo4j required. Optional: docs/neo4j-local.md. - Run
scripts/check_boundary.shand the tests that import what you touched.
pytest tests/test_file_size_budget.py -q
pytest tests/<touched>.py -q
npm test --workspace packages/cli # if you touched TS
Do not
from infona.*/import infona.*(this package imports on its own).- Short-circuit production
/askwith golden-string Cypher, or hardcode persona-CSV answers. - Treat SPARQL as the product query language, or Neptune as the product store. Residual
NeptuneClientimports stay — do not delete them in drive-by cleanup. - Re-register a default
StaticHttpFetcher(BYOR). Do not ship or imply a shared platform key (BYOK = the caller's env).
License and contributing
Shipped packages share one lockstep version: infona-client on PyPI and @infona-ai/cli / @infona-ai/mcp on npm. Release notes: CHANGELOG.md.
docs/API.md · docs/BOUNDARY.md · ROADMAP.md · SECURITY.md · CODE_OF_CONDUCT.md · CHANGELOG.md · CONTRIBUTING.md · CLA.md · AGENTS.md
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file infona_client-0.1.53.tar.gz.
File metadata
- Download URL: infona_client-0.1.53.tar.gz
- Upload date:
- Size: 3.4 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
077739155c3a1f84a1319f4b635ccbc8cc79b1d32f2ee2e2229bec4d1623f19a
|
|
| MD5 |
242c8facb250e62d1dcbd0d43d82f3b0
|
|
| BLAKE2b-256 |
81f4e802bc7d7269ae1eca7de231791ded033b8f6ff4d94055236e5de8f366ba
|
File details
Details for the file infona_client-0.1.53-py3-none-any.whl.
File metadata
- Download URL: infona_client-0.1.53-py3-none-any.whl
- Upload date:
- Size: 3.8 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c9f8d1e2584d05e1c72464a5146b4125f37e9d860266114c4cf7de19d964ddb
|
|
| MD5 |
b7b854719980195b9469c41b53d382e7
|
|
| BLAKE2b-256 |
4195d478088b58dc955ad8f5fd784d499d3873039d58ef6357c540a172997d22
|