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.
infona.ai (waitlist / demo) · what's free · API
Ask in English, get Cypher, get the row. ingest → ask is the payoff. The graph is the trials.csv sample.
10-minute quickstart
Need: Docker + Node 20+ (for the infona CLI). A stranger gets a real answer with no API key.
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 + loads the 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).
./scripts/oss_up.sh compose-ups, waits until /health reports Neo4j up, writes ~/.infona/config.json, and runs ./scripts/load_prebuilt_trials.sh.
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.
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. infona init --local connects without starting Docker. 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/.
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. The CLI reads env:VAR locally and sends an inline token; the API never reads the server process environment.
# 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
infona ingest --dlt spec.json --kg crm
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.
Eval
Published pin: 6 / 8 (75%) on 16 synthetic oncology rows — two misses stay visible. That is the pin, not a footnote. The homepage loop is ingest → FLAURA2. Full table, misses, and repro: docs/EVAL.md. Backing JSON: docs/eval/public_results.json. 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."
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)
Writes go through insert_facts / refresh_after_write. Instance relationships use https://graph.infona.ai/onto/<leaf>. 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
MCP (agents)
Same ask, same graph, same exact rows — as a tool result:
{
"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, export_kg, ontology, jobs — same backend the CLI hits. packages/mcp/README.md.
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.
Full table: docs/BOUNDARY.md.
Product path: FastAPI + Neo4j GraphStore (Cypher). SPARQL / Neptune are not product backends.
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.45.tar.gz.
File metadata
- Download URL: infona_client-0.1.45.tar.gz
- Upload date:
- Size: 3.3 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0e7c901ea8e2834f48330d24fa5adabef3b4c26241a79d416ad187ecd9cd73ff
|
|
| MD5 |
8aa9f7c7f848bbfb1816726b209c9580
|
|
| BLAKE2b-256 |
5308ab6c0cf6181758a9e4bbc3d62e09a2c9ef076f472e7b5d8352322c68cabf
|
File details
Details for the file infona_client-0.1.45-py3-none-any.whl.
File metadata
- Download URL: infona_client-0.1.45-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 |
d421e90fa4712c3c4445e3d376ec87a5f45bf2818b6754dfd24c888fc0e76d24
|
|
| MD5 |
1da5b468026617c6b7ef423f2b480b97
|
|
| BLAKE2b-256 |
dc4c0a0c2452115ce40c96571f2da69b6d17b6da84d8a09c69c43ed5c3fd8118
|