trikedb 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- trikedb-0.8.0/LICENSE +21 -0
- trikedb-0.8.0/PKG-INFO +260 -0
- trikedb-0.8.0/README.md +234 -0
- trikedb-0.8.0/pyproject.toml +35 -0
- trikedb-0.8.0/setup.cfg +4 -0
- trikedb-0.8.0/src/trikedb/__init__.py +10 -0
- trikedb-0.8.0/src/trikedb/cli.py +195 -0
- trikedb-0.8.0/src/trikedb/db.py +439 -0
- trikedb-0.8.0/src/trikedb/html.py +355 -0
- trikedb-0.8.0/src/trikedb/importers.py +102 -0
- trikedb-0.8.0/src/trikedb/mcp_server.py +123 -0
- trikedb-0.8.0/src/trikedb.egg-info/PKG-INFO +260 -0
- trikedb-0.8.0/src/trikedb.egg-info/SOURCES.txt +16 -0
- trikedb-0.8.0/src/trikedb.egg-info/dependency_links.txt +1 -0
- trikedb-0.8.0/src/trikedb.egg-info/entry_points.txt +2 -0
- trikedb-0.8.0/src/trikedb.egg-info/requires.txt +9 -0
- trikedb-0.8.0/src/trikedb.egg-info/top_level.txt +1 -0
- trikedb-0.8.0/tests/test_triplite.py +387 -0
trikedb-0.8.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ryuto Yoda
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
trikedb-0.8.0/PKG-INFO
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: trikedb
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: The DuckDB of graph databases: a knowledge graph that lives in a single YAML file, queried with full SPARQL 1.1. Built for LLM agents.
|
|
5
|
+
Author: Ryuto Yoda
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/RyutoYoda/trikedb
|
|
8
|
+
Keywords: knowledge-graph,rdf,triple-store,yaml,llm,agents,ontology
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Database
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: PyYAML>=6.0
|
|
19
|
+
Requires-Dist: rdflib>=7
|
|
20
|
+
Provides-Extra: mcp
|
|
21
|
+
Requires-Dist: mcp<2,>=1.2; extra == "mcp"
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
24
|
+
Requires-Dist: mcp<2,>=1.2; extra == "dev"
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
<p align="center">
|
|
28
|
+
<img src="docs/logo.svg" width="220" alt="trikedb — a triceratops whose three horns are the triple: subject, predicate, object">
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
# trikedb
|
|
32
|
+
|
|
33
|
+
**The DuckDB of graph databases.** You query it like a real triple store — full SPARQL 1.1, reads *and* writes. Underneath, it's a single YAML file. Built for LLM agents.
|
|
34
|
+
|
|
35
|
+
```yaml
|
|
36
|
+
triples:
|
|
37
|
+
- {s: salesflow-crm, p: PROVIDES, o: crm-sync-job}
|
|
38
|
+
- {s: crm-sync-job, p: INGESTS_TO, o: RAW_CRM_CONTACTS, schedule: hourly}
|
|
39
|
+
- {s: LEGACY_DUMP, p: MIGRATED_TO, o: RAW_CRM_CONTACTS, deprecated: true}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
That file **is** the database. No server, no daemon, no cloud deployment. It diffs cleanly in git, survives in a repo next to your code, and — the part trikedb is actually designed around — **an LLM agent can `Read` it directly and reason over your domain without hallucinating entity names.**
|
|
43
|
+
|
|
44
|
+
## Why
|
|
45
|
+
|
|
46
|
+
RDF graph databases are like Oracle: powerful, correct, and heavy. SPARQL endpoints, OWL reasoners, enterprise semantic layers — great at scale, overkill when what you need is a curated map of a few hundred facts that your AI agents (and teammates) can trust.
|
|
47
|
+
|
|
48
|
+
DuckDB proved the pattern: keep the *interface* of the big system (full SQL, in DuckDB's case) and shrink the *machinery* down to an embedded library over a file. trikedb applies the same move to RDF graph databases — the interface is real SPARQL 1.1 (rdflib's engine, not a homegrown subset), the storage is YAML you can read, diff, and commit:
|
|
49
|
+
|
|
50
|
+
| | A full triple-store deployment | trikedb |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| Storage | server / cloud service | one YAML file |
|
|
53
|
+
| Query | SPARQL 1.1 | SPARQL 1.1 (same language, rdflib engine) |
|
|
54
|
+
| Writes | SPARQL Update | SPARQL Update — persisted back to the YAML |
|
|
55
|
+
| Schema | OWL + reasoners | a list of allowed predicates |
|
|
56
|
+
| Agent integration | a service to operate | the agent reads the file, or `trikedb mcp` (stdio, embedded) |
|
|
57
|
+
| Setup time | an afternoon (or a sprint) | `pip install trikedb` |
|
|
58
|
+
|
|
59
|
+
If you need inference engines, named graphs, and multi-tenant governance, you want a full enterprise semantic platform. If you want a knowledge graph **today, in a file, in git** — that's trikedb. And because the storage maps cleanly onto RDF, graduating to a bigger system later is an export, not a rewrite: each team keeps its own YAML graph, and stitching them together (or migrating them wholesale) is just merging triples.
|
|
60
|
+
|
|
61
|
+
### Curation-first, not extraction-first
|
|
62
|
+
|
|
63
|
+
Most "AI knowledge graph" tools use an LLM to extract triples from text. That's great for bootstrapping, but extracted graphs inherit hallucinations. trikedb takes the opposite stance: **the graph is curated data** (by humans, or by agents you supervise), the ontology constrains what can be said, and LLMs *consume* the graph rather than invent it. When an agent reads
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
- {s: crm-sync-job, p: INGESTS_TO, o: RAW_CRM_CONTACTS}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
there is no step where a table name can be made up.
|
|
70
|
+
|
|
71
|
+
## Install
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install trikedb # library + CLI
|
|
75
|
+
pip install 'trikedb[mcp]' # + MCP server for AI agents
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Quickstart (Python)
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from trikedb import TrikeDB
|
|
82
|
+
|
|
83
|
+
db = TrikeDB("pipeline.yaml", ontology={
|
|
84
|
+
"PROVIDES": "SaaS vendor -> ingestion job",
|
|
85
|
+
"INGESTS_TO": "ingestion job -> warehouse table",
|
|
86
|
+
"MIGRATED_TO": "deprecated table -> its replacement",
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
db.add("salesflow-crm", "PROVIDES", "crm-sync-job")
|
|
90
|
+
db.add("crm-sync-job", "INGESTS_TO", "RAW_CRM_CONTACTS", schedule="hourly")
|
|
91
|
+
db.add("crm-sync-job", "OWNS", "x") # OntologyError: predicate not declared
|
|
92
|
+
|
|
93
|
+
# Pattern matching — None is a wildcard, '*' globs
|
|
94
|
+
for t in db.triples(p="INGESTS_TO", o="RAW_*"):
|
|
95
|
+
print(t.s, "->", t.o, t.attrs)
|
|
96
|
+
|
|
97
|
+
# Multi-pattern queries with variable joins (zero dependencies)
|
|
98
|
+
db.query(["?vendor PROVIDES ?job", "?job INGESTS_TO ?table"])
|
|
99
|
+
# [{'vendor': 'salesflow-crm', 'job': 'crm-sync-job', 'table': 'RAW_CRM_CONTACTS'}]
|
|
100
|
+
|
|
101
|
+
# Or real SPARQL 1.1 — FILTER, OPTIONAL, aggregates, the lot.
|
|
102
|
+
# Delegated to rdflib, not hand-rolled. The prefix t: is pre-bound.
|
|
103
|
+
db.sparql("""
|
|
104
|
+
SELECT ?vendor ?table WHERE {
|
|
105
|
+
?vendor t:PROVIDES ?job .
|
|
106
|
+
?job t:INGESTS_TO ?table .
|
|
107
|
+
FILTER(STRSTARTS(STR(?table), "urn:trikedb:RAW_"))
|
|
108
|
+
}
|
|
109
|
+
""")
|
|
110
|
+
db.sparql("ASK { ?x t:MIGRATED_TO ?y }") # True
|
|
111
|
+
|
|
112
|
+
# Writes go through SPARQL too, DuckDB-style — and land back in the YAML
|
|
113
|
+
db.sparql("INSERT DATA { t:figly t:PROVIDES t:figly-export-job }")
|
|
114
|
+
db.sparql("DELETE WHERE { ?job t:INGESTS_TO t:LEGACY_CONTACTS_DUMP }")
|
|
115
|
+
db.save() # or pass autosave=True and skip this
|
|
116
|
+
|
|
117
|
+
db.to_rdflib() # plain rdflib.Graph, if you want to go further
|
|
118
|
+
db.to_html("pipeline.html") # interactive graph workbench (see demos below)
|
|
119
|
+
db.to_jsonld() # best-effort export for real RDF tooling
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Quickstart (CLI)
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
trikedb add pipeline.yaml salesflow-crm PROVIDES crm-sync-job
|
|
126
|
+
trikedb add pipeline.yaml crm-sync-job INGESTS_TO RAW_CRM_CONTACTS -a schedule=hourly
|
|
127
|
+
|
|
128
|
+
trikedb query pipeline.yaml -w "?vendor PROVIDES ?job" -w "?job INGESTS_TO ?table"
|
|
129
|
+
# vendor job table
|
|
130
|
+
# ------------- ------------ ----------------
|
|
131
|
+
# salesflow-crm crm-sync-job RAW_CRM_CONTACTS
|
|
132
|
+
|
|
133
|
+
trikedb sparql pipeline.yaml \
|
|
134
|
+
"SELECT ?v ?t WHERE { ?v t:PROVIDES ?j . ?j t:INGESTS_TO ?t }"
|
|
135
|
+
|
|
136
|
+
# updates persist straight back to the file
|
|
137
|
+
trikedb sparql pipeline.yaml \
|
|
138
|
+
"INSERT DATA { t:figly t:PROVIDES t:figly-export-job }"
|
|
139
|
+
|
|
140
|
+
trikedb stats pipeline.yaml
|
|
141
|
+
trikedb html pipeline.yaml -o pipeline.html
|
|
142
|
+
trikedb jsonld pipeline.yaml
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Importing from CSV and Markdown docs
|
|
146
|
+
|
|
147
|
+
The YAML file is the store, but triples can come from wherever your team already writes:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# CSV/TSV with an s,p,o header — extra columns become edge attributes
|
|
151
|
+
trikedb import pipeline.yaml new_vendors.csv
|
|
152
|
+
|
|
153
|
+
# Markdown: every table whose header has s/p/o columns is picked up;
|
|
154
|
+
# prose and other tables are ignored. Your design docs are data.
|
|
155
|
+
trikedb import pipeline.yaml design_doc.md
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
```markdown
|
|
159
|
+
<!-- anywhere inside an ordinary design doc: -->
|
|
160
|
+
| s | p | o | schedule |
|
|
161
|
+
|-------------------|------------|--------------------|-----------|
|
|
162
|
+
| clickpath-pa | PROVIDES | clickpath-webhook | |
|
|
163
|
+
| clickpath-webhook | INGESTS_TO | RAW_PRODUCT_EVENTS | streaming |
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Imports are deterministic — no LLM extraction, so nothing gets invented. The ontology is enforced on the way in, and `"true"`/`"false"` cells become booleans. See [`examples/acme_design_doc.md`](examples/acme_design_doc.md) and [`examples/acme_new_vendors.csv`](examples/acme_new_vendors.csv).
|
|
167
|
+
|
|
168
|
+
## The file format
|
|
169
|
+
|
|
170
|
+
A trikedb file is ordinary YAML with three top-level keys (only `triples` is required):
|
|
171
|
+
|
|
172
|
+
```yaml
|
|
173
|
+
ontology: # optional — omit it for free-form predicates
|
|
174
|
+
predicates:
|
|
175
|
+
PROVIDES: "SaaS vendor -> ingestion job"
|
|
176
|
+
AFFECTED_BY: "table -> change event"
|
|
177
|
+
|
|
178
|
+
nodes: # optional — free-form node properties
|
|
179
|
+
salesflow-crm: {type: saas, url: "https://salesflow.example", plan: enterprise}
|
|
180
|
+
RAW_CRM_CONTACTS: {type: table, schema: ACME_RAW, pii: true}
|
|
181
|
+
|
|
182
|
+
triples:
|
|
183
|
+
# compact form for plain facts
|
|
184
|
+
- {s: adastra-ads, p: PROVIDES, o: ads-spend-collector}
|
|
185
|
+
|
|
186
|
+
# any extra keys become edge attributes
|
|
187
|
+
- s: RAW_AD_SPEND_DAILY
|
|
188
|
+
p: AFFECTED_BY
|
|
189
|
+
o: "2025-04-01 adastra API v3: spend now in micros (was cents)"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Three conventions worth stealing (see [`examples/acme_pipeline.yaml`](examples/acme_pipeline.yaml)):
|
|
193
|
+
|
|
194
|
+
- **Change events as objects.** `AFFECTED_BY` edges pointing at dated event strings give your graph a memory — "why did this number change in April?" becomes a query.
|
|
195
|
+
- **`deprecated: true`** on edges renders them dashed in the HTML view and lets agents filter dead paths.
|
|
196
|
+
- **`via:` / `schedule:`** attributes carry operational detail without polluting the node set.
|
|
197
|
+
- **Node properties keep growing.** That's the RDF promise: attach `type`, `url`, `schema`, owners — whatever your team needs — without a schema migration. `type` drives color grouping in the HTML view, and node properties are queryable in SPARQL (`?x t:type "table"`). Set them from code with `db.set_node("RAW_CRM_CONTACTS", pii=True)`.
|
|
198
|
+
|
|
199
|
+
## An ontology layer for AI agents (MCP)
|
|
200
|
+
|
|
201
|
+
Like the database it takes its analogy from, trikedb is embedded, not hosted. For agents, "embedded" means MCP over stdio — the graph runs inside the agent session, no server to operate:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
claude mcp add kg -- uvx --from 'trikedb[mcp]' trikedb mcp /absolute/path/to/graph.yaml
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The agent gets `sparql`, `match`, `get_node`, `ontology`, `stats` to read, and `add_triple`, `set_node`, `remove_triples`, `import_source` to write. Every write autosaves to the YAML — so agent contributions arrive as reviewable git diffs.
|
|
208
|
+
|
|
209
|
+
This is also the answer to "just throw docs at it": **the agent is the extractor, trikedb is the validated write path.** Point your agent at a pile of documents and ask it to record the facts; it reads them (any format — it's an LLM), calls `add_triple` for each fact, and the ontology rejects any predicate it tries to invent. Extraction stays flexible, the graph stays clean, and a human reviews the diff.
|
|
210
|
+
|
|
211
|
+
## Using it with LLM agents (no MCP)
|
|
212
|
+
|
|
213
|
+
The zero-setup loop:
|
|
214
|
+
|
|
215
|
+
1. Keep `graph.yaml` in your repo, next to the code it describes.
|
|
216
|
+
2. Tell your agent about it once (e.g. in `CLAUDE.md` / your system prompt):
|
|
217
|
+
|
|
218
|
+
> Before any task touching the data pipeline, read `pipeline.yaml`.
|
|
219
|
+
> It is the source of truth for which jobs feed which tables.
|
|
220
|
+
> Predicates are limited to the ontology declared in the file.
|
|
221
|
+
|
|
222
|
+
3. Agents propose edits as diffs to the YAML — reviewable in a PR like any other change. The ontology check (`trikedb.add` raises on unknown predicates) keeps generated edits inside the vocabulary you chose.
|
|
223
|
+
4. Humans browse the same graph via `trikedb html`.
|
|
224
|
+
|
|
225
|
+
One source of truth, two projections: YAML for machines, HTML for people.
|
|
226
|
+
|
|
227
|
+
## What trikedb is not
|
|
228
|
+
|
|
229
|
+
- **Not a SPARQL implementation of its own.** The SPARQL surface is deliberately *not* hand-rolled — your YAML is loaded into [rdflib](https://github.com/RDFLib/rdflib) and queried/updated by rdflib's battle-tested engine. Mapping rule: subjects/predicates become URIs under `urn:trikedb:`; objects with whitespace (change events, notes) become literals. Triples inserted via SPARQL start without edge attributes; surviving triples keep theirs. The lighter `query()`/`triples()` API also exists for quick pattern matching.
|
|
230
|
+
- **Not an extraction pipeline.** It won't turn your PDFs into a graph. Pair it with an extractor if you want that — then curate what comes out.
|
|
231
|
+
- **Not for millions of triples.** Everything is in memory and scans are linear. The sweet spot is the hundreds-to-thousands range, where a curated graph is even possible.
|
|
232
|
+
|
|
233
|
+
## Examples
|
|
234
|
+
|
|
235
|
+
- [`examples/acme_pipeline.yaml`](examples/acme_pipeline.yaml) — a fictional company's data platform: vendors, ingestion jobs, warehouse tables, change events, migrations. The use case trikedb was born from.
|
|
236
|
+
- [`examples/python_ecosystem.yaml`](examples/python_ecosystem.yaml) — dependencies and deprecations in the Python packaging world, with free-form predicates.
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
trikedb html examples/acme_pipeline.yaml -o acme.html && open acme.html
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Live demos (GitHub Pages):**
|
|
243
|
+
|
|
244
|
+
- [acme knowledge graph](https://ryutoyoda.github.io/trikedb/) — the fictional data platform
|
|
245
|
+
- [python ecosystem](https://ryutoyoda.github.io/trikedb/python_ecosystem.html) — dependencies and deprecations
|
|
246
|
+
|
|
247
|
+
The exported HTML is a small workbench, not just a picture: click a node for a right-hand panel with all its properties (URLs become links), search nodes top-right, and open the **SPARQL console** to run real SPARQL 1.1 in the browser — powered by [Oxigraph](https://github.com/oxigraph/oxigraph) compiled to WASM, loaded from CDN on first use. Change events render as red diamonds with a timeline bar at the bottom.
|
|
248
|
+
|
|
249
|
+
## Development
|
|
250
|
+
|
|
251
|
+
Uses [uv](https://docs.astral.sh/uv/):
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
uv sync --extra dev
|
|
255
|
+
uv run pytest
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT
|
trikedb-0.8.0/README.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/logo.svg" width="220" alt="trikedb — a triceratops whose three horns are the triple: subject, predicate, object">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# trikedb
|
|
6
|
+
|
|
7
|
+
**The DuckDB of graph databases.** You query it like a real triple store — full SPARQL 1.1, reads *and* writes. Underneath, it's a single YAML file. Built for LLM agents.
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
triples:
|
|
11
|
+
- {s: salesflow-crm, p: PROVIDES, o: crm-sync-job}
|
|
12
|
+
- {s: crm-sync-job, p: INGESTS_TO, o: RAW_CRM_CONTACTS, schedule: hourly}
|
|
13
|
+
- {s: LEGACY_DUMP, p: MIGRATED_TO, o: RAW_CRM_CONTACTS, deprecated: true}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
That file **is** the database. No server, no daemon, no cloud deployment. It diffs cleanly in git, survives in a repo next to your code, and — the part trikedb is actually designed around — **an LLM agent can `Read` it directly and reason over your domain without hallucinating entity names.**
|
|
17
|
+
|
|
18
|
+
## Why
|
|
19
|
+
|
|
20
|
+
RDF graph databases are like Oracle: powerful, correct, and heavy. SPARQL endpoints, OWL reasoners, enterprise semantic layers — great at scale, overkill when what you need is a curated map of a few hundred facts that your AI agents (and teammates) can trust.
|
|
21
|
+
|
|
22
|
+
DuckDB proved the pattern: keep the *interface* of the big system (full SQL, in DuckDB's case) and shrink the *machinery* down to an embedded library over a file. trikedb applies the same move to RDF graph databases — the interface is real SPARQL 1.1 (rdflib's engine, not a homegrown subset), the storage is YAML you can read, diff, and commit:
|
|
23
|
+
|
|
24
|
+
| | A full triple-store deployment | trikedb |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| Storage | server / cloud service | one YAML file |
|
|
27
|
+
| Query | SPARQL 1.1 | SPARQL 1.1 (same language, rdflib engine) |
|
|
28
|
+
| Writes | SPARQL Update | SPARQL Update — persisted back to the YAML |
|
|
29
|
+
| Schema | OWL + reasoners | a list of allowed predicates |
|
|
30
|
+
| Agent integration | a service to operate | the agent reads the file, or `trikedb mcp` (stdio, embedded) |
|
|
31
|
+
| Setup time | an afternoon (or a sprint) | `pip install trikedb` |
|
|
32
|
+
|
|
33
|
+
If you need inference engines, named graphs, and multi-tenant governance, you want a full enterprise semantic platform. If you want a knowledge graph **today, in a file, in git** — that's trikedb. And because the storage maps cleanly onto RDF, graduating to a bigger system later is an export, not a rewrite: each team keeps its own YAML graph, and stitching them together (or migrating them wholesale) is just merging triples.
|
|
34
|
+
|
|
35
|
+
### Curation-first, not extraction-first
|
|
36
|
+
|
|
37
|
+
Most "AI knowledge graph" tools use an LLM to extract triples from text. That's great for bootstrapping, but extracted graphs inherit hallucinations. trikedb takes the opposite stance: **the graph is curated data** (by humans, or by agents you supervise), the ontology constrains what can be said, and LLMs *consume* the graph rather than invent it. When an agent reads
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
- {s: crm-sync-job, p: INGESTS_TO, o: RAW_CRM_CONTACTS}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
there is no step where a table name can be made up.
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install trikedb # library + CLI
|
|
49
|
+
pip install 'trikedb[mcp]' # + MCP server for AI agents
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quickstart (Python)
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from trikedb import TrikeDB
|
|
56
|
+
|
|
57
|
+
db = TrikeDB("pipeline.yaml", ontology={
|
|
58
|
+
"PROVIDES": "SaaS vendor -> ingestion job",
|
|
59
|
+
"INGESTS_TO": "ingestion job -> warehouse table",
|
|
60
|
+
"MIGRATED_TO": "deprecated table -> its replacement",
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
db.add("salesflow-crm", "PROVIDES", "crm-sync-job")
|
|
64
|
+
db.add("crm-sync-job", "INGESTS_TO", "RAW_CRM_CONTACTS", schedule="hourly")
|
|
65
|
+
db.add("crm-sync-job", "OWNS", "x") # OntologyError: predicate not declared
|
|
66
|
+
|
|
67
|
+
# Pattern matching — None is a wildcard, '*' globs
|
|
68
|
+
for t in db.triples(p="INGESTS_TO", o="RAW_*"):
|
|
69
|
+
print(t.s, "->", t.o, t.attrs)
|
|
70
|
+
|
|
71
|
+
# Multi-pattern queries with variable joins (zero dependencies)
|
|
72
|
+
db.query(["?vendor PROVIDES ?job", "?job INGESTS_TO ?table"])
|
|
73
|
+
# [{'vendor': 'salesflow-crm', 'job': 'crm-sync-job', 'table': 'RAW_CRM_CONTACTS'}]
|
|
74
|
+
|
|
75
|
+
# Or real SPARQL 1.1 — FILTER, OPTIONAL, aggregates, the lot.
|
|
76
|
+
# Delegated to rdflib, not hand-rolled. The prefix t: is pre-bound.
|
|
77
|
+
db.sparql("""
|
|
78
|
+
SELECT ?vendor ?table WHERE {
|
|
79
|
+
?vendor t:PROVIDES ?job .
|
|
80
|
+
?job t:INGESTS_TO ?table .
|
|
81
|
+
FILTER(STRSTARTS(STR(?table), "urn:trikedb:RAW_"))
|
|
82
|
+
}
|
|
83
|
+
""")
|
|
84
|
+
db.sparql("ASK { ?x t:MIGRATED_TO ?y }") # True
|
|
85
|
+
|
|
86
|
+
# Writes go through SPARQL too, DuckDB-style — and land back in the YAML
|
|
87
|
+
db.sparql("INSERT DATA { t:figly t:PROVIDES t:figly-export-job }")
|
|
88
|
+
db.sparql("DELETE WHERE { ?job t:INGESTS_TO t:LEGACY_CONTACTS_DUMP }")
|
|
89
|
+
db.save() # or pass autosave=True and skip this
|
|
90
|
+
|
|
91
|
+
db.to_rdflib() # plain rdflib.Graph, if you want to go further
|
|
92
|
+
db.to_html("pipeline.html") # interactive graph workbench (see demos below)
|
|
93
|
+
db.to_jsonld() # best-effort export for real RDF tooling
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Quickstart (CLI)
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
trikedb add pipeline.yaml salesflow-crm PROVIDES crm-sync-job
|
|
100
|
+
trikedb add pipeline.yaml crm-sync-job INGESTS_TO RAW_CRM_CONTACTS -a schedule=hourly
|
|
101
|
+
|
|
102
|
+
trikedb query pipeline.yaml -w "?vendor PROVIDES ?job" -w "?job INGESTS_TO ?table"
|
|
103
|
+
# vendor job table
|
|
104
|
+
# ------------- ------------ ----------------
|
|
105
|
+
# salesflow-crm crm-sync-job RAW_CRM_CONTACTS
|
|
106
|
+
|
|
107
|
+
trikedb sparql pipeline.yaml \
|
|
108
|
+
"SELECT ?v ?t WHERE { ?v t:PROVIDES ?j . ?j t:INGESTS_TO ?t }"
|
|
109
|
+
|
|
110
|
+
# updates persist straight back to the file
|
|
111
|
+
trikedb sparql pipeline.yaml \
|
|
112
|
+
"INSERT DATA { t:figly t:PROVIDES t:figly-export-job }"
|
|
113
|
+
|
|
114
|
+
trikedb stats pipeline.yaml
|
|
115
|
+
trikedb html pipeline.yaml -o pipeline.html
|
|
116
|
+
trikedb jsonld pipeline.yaml
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Importing from CSV and Markdown docs
|
|
120
|
+
|
|
121
|
+
The YAML file is the store, but triples can come from wherever your team already writes:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# CSV/TSV with an s,p,o header — extra columns become edge attributes
|
|
125
|
+
trikedb import pipeline.yaml new_vendors.csv
|
|
126
|
+
|
|
127
|
+
# Markdown: every table whose header has s/p/o columns is picked up;
|
|
128
|
+
# prose and other tables are ignored. Your design docs are data.
|
|
129
|
+
trikedb import pipeline.yaml design_doc.md
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```markdown
|
|
133
|
+
<!-- anywhere inside an ordinary design doc: -->
|
|
134
|
+
| s | p | o | schedule |
|
|
135
|
+
|-------------------|------------|--------------------|-----------|
|
|
136
|
+
| clickpath-pa | PROVIDES | clickpath-webhook | |
|
|
137
|
+
| clickpath-webhook | INGESTS_TO | RAW_PRODUCT_EVENTS | streaming |
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Imports are deterministic — no LLM extraction, so nothing gets invented. The ontology is enforced on the way in, and `"true"`/`"false"` cells become booleans. See [`examples/acme_design_doc.md`](examples/acme_design_doc.md) and [`examples/acme_new_vendors.csv`](examples/acme_new_vendors.csv).
|
|
141
|
+
|
|
142
|
+
## The file format
|
|
143
|
+
|
|
144
|
+
A trikedb file is ordinary YAML with three top-level keys (only `triples` is required):
|
|
145
|
+
|
|
146
|
+
```yaml
|
|
147
|
+
ontology: # optional — omit it for free-form predicates
|
|
148
|
+
predicates:
|
|
149
|
+
PROVIDES: "SaaS vendor -> ingestion job"
|
|
150
|
+
AFFECTED_BY: "table -> change event"
|
|
151
|
+
|
|
152
|
+
nodes: # optional — free-form node properties
|
|
153
|
+
salesflow-crm: {type: saas, url: "https://salesflow.example", plan: enterprise}
|
|
154
|
+
RAW_CRM_CONTACTS: {type: table, schema: ACME_RAW, pii: true}
|
|
155
|
+
|
|
156
|
+
triples:
|
|
157
|
+
# compact form for plain facts
|
|
158
|
+
- {s: adastra-ads, p: PROVIDES, o: ads-spend-collector}
|
|
159
|
+
|
|
160
|
+
# any extra keys become edge attributes
|
|
161
|
+
- s: RAW_AD_SPEND_DAILY
|
|
162
|
+
p: AFFECTED_BY
|
|
163
|
+
o: "2025-04-01 adastra API v3: spend now in micros (was cents)"
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Three conventions worth stealing (see [`examples/acme_pipeline.yaml`](examples/acme_pipeline.yaml)):
|
|
167
|
+
|
|
168
|
+
- **Change events as objects.** `AFFECTED_BY` edges pointing at dated event strings give your graph a memory — "why did this number change in April?" becomes a query.
|
|
169
|
+
- **`deprecated: true`** on edges renders them dashed in the HTML view and lets agents filter dead paths.
|
|
170
|
+
- **`via:` / `schedule:`** attributes carry operational detail without polluting the node set.
|
|
171
|
+
- **Node properties keep growing.** That's the RDF promise: attach `type`, `url`, `schema`, owners — whatever your team needs — without a schema migration. `type` drives color grouping in the HTML view, and node properties are queryable in SPARQL (`?x t:type "table"`). Set them from code with `db.set_node("RAW_CRM_CONTACTS", pii=True)`.
|
|
172
|
+
|
|
173
|
+
## An ontology layer for AI agents (MCP)
|
|
174
|
+
|
|
175
|
+
Like the database it takes its analogy from, trikedb is embedded, not hosted. For agents, "embedded" means MCP over stdio — the graph runs inside the agent session, no server to operate:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
claude mcp add kg -- uvx --from 'trikedb[mcp]' trikedb mcp /absolute/path/to/graph.yaml
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The agent gets `sparql`, `match`, `get_node`, `ontology`, `stats` to read, and `add_triple`, `set_node`, `remove_triples`, `import_source` to write. Every write autosaves to the YAML — so agent contributions arrive as reviewable git diffs.
|
|
182
|
+
|
|
183
|
+
This is also the answer to "just throw docs at it": **the agent is the extractor, trikedb is the validated write path.** Point your agent at a pile of documents and ask it to record the facts; it reads them (any format — it's an LLM), calls `add_triple` for each fact, and the ontology rejects any predicate it tries to invent. Extraction stays flexible, the graph stays clean, and a human reviews the diff.
|
|
184
|
+
|
|
185
|
+
## Using it with LLM agents (no MCP)
|
|
186
|
+
|
|
187
|
+
The zero-setup loop:
|
|
188
|
+
|
|
189
|
+
1. Keep `graph.yaml` in your repo, next to the code it describes.
|
|
190
|
+
2. Tell your agent about it once (e.g. in `CLAUDE.md` / your system prompt):
|
|
191
|
+
|
|
192
|
+
> Before any task touching the data pipeline, read `pipeline.yaml`.
|
|
193
|
+
> It is the source of truth for which jobs feed which tables.
|
|
194
|
+
> Predicates are limited to the ontology declared in the file.
|
|
195
|
+
|
|
196
|
+
3. Agents propose edits as diffs to the YAML — reviewable in a PR like any other change. The ontology check (`trikedb.add` raises on unknown predicates) keeps generated edits inside the vocabulary you chose.
|
|
197
|
+
4. Humans browse the same graph via `trikedb html`.
|
|
198
|
+
|
|
199
|
+
One source of truth, two projections: YAML for machines, HTML for people.
|
|
200
|
+
|
|
201
|
+
## What trikedb is not
|
|
202
|
+
|
|
203
|
+
- **Not a SPARQL implementation of its own.** The SPARQL surface is deliberately *not* hand-rolled — your YAML is loaded into [rdflib](https://github.com/RDFLib/rdflib) and queried/updated by rdflib's battle-tested engine. Mapping rule: subjects/predicates become URIs under `urn:trikedb:`; objects with whitespace (change events, notes) become literals. Triples inserted via SPARQL start without edge attributes; surviving triples keep theirs. The lighter `query()`/`triples()` API also exists for quick pattern matching.
|
|
204
|
+
- **Not an extraction pipeline.** It won't turn your PDFs into a graph. Pair it with an extractor if you want that — then curate what comes out.
|
|
205
|
+
- **Not for millions of triples.** Everything is in memory and scans are linear. The sweet spot is the hundreds-to-thousands range, where a curated graph is even possible.
|
|
206
|
+
|
|
207
|
+
## Examples
|
|
208
|
+
|
|
209
|
+
- [`examples/acme_pipeline.yaml`](examples/acme_pipeline.yaml) — a fictional company's data platform: vendors, ingestion jobs, warehouse tables, change events, migrations. The use case trikedb was born from.
|
|
210
|
+
- [`examples/python_ecosystem.yaml`](examples/python_ecosystem.yaml) — dependencies and deprecations in the Python packaging world, with free-form predicates.
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
trikedb html examples/acme_pipeline.yaml -o acme.html && open acme.html
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Live demos (GitHub Pages):**
|
|
217
|
+
|
|
218
|
+
- [acme knowledge graph](https://ryutoyoda.github.io/trikedb/) — the fictional data platform
|
|
219
|
+
- [python ecosystem](https://ryutoyoda.github.io/trikedb/python_ecosystem.html) — dependencies and deprecations
|
|
220
|
+
|
|
221
|
+
The exported HTML is a small workbench, not just a picture: click a node for a right-hand panel with all its properties (URLs become links), search nodes top-right, and open the **SPARQL console** to run real SPARQL 1.1 in the browser — powered by [Oxigraph](https://github.com/oxigraph/oxigraph) compiled to WASM, loaded from CDN on first use. Change events render as red diamonds with a timeline bar at the bottom.
|
|
222
|
+
|
|
223
|
+
## Development
|
|
224
|
+
|
|
225
|
+
Uses [uv](https://docs.astral.sh/uv/):
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
uv sync --extra dev
|
|
229
|
+
uv run pytest
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## License
|
|
233
|
+
|
|
234
|
+
MIT
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "trikedb"
|
|
7
|
+
version = "0.8.0"
|
|
8
|
+
description = "The DuckDB of graph databases: a knowledge graph that lives in a single YAML file, queried with full SPARQL 1.1. Built for LLM agents."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [{ name = "Ryuto Yoda" }]
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
dependencies = ["PyYAML>=6.0", "rdflib>=7"]
|
|
14
|
+
keywords = ["knowledge-graph", "rdf", "triple-store", "yaml", "llm", "agents", "ontology"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Topic :: Database",
|
|
21
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://github.com/RyutoYoda/trikedb"
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
mcp = ["mcp>=1.2,<2"]
|
|
29
|
+
dev = ["pytest>=7", "mcp>=1.2,<2"]
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
trikedb = "trikedb.cli:main"
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.packages.find]
|
|
35
|
+
where = ["src"]
|
trikedb-0.8.0/setup.cfg
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""trikedb — the DuckDB of graph databases.
|
|
2
|
+
|
|
3
|
+
A knowledge graph that lives in a single YAML file, queried and
|
|
4
|
+
updated with full SPARQL 1.1. Built for LLM agents.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .db import OntologyError, Triple, TrikeDB
|
|
8
|
+
|
|
9
|
+
__version__ = "0.8.0"
|
|
10
|
+
__all__ = ["TrikeDB", "Triple", "OntologyError", "__version__"]
|