Python reference
Generated from the installed lokf package. Every entry below is the real signature and docstring of the shipped code.
lokf.model
Section titled “lokf.model”Load and represent LOKF knowledge bundles.
A bundle is a directory of markdown concept files (OKF layout). This module lifts it into Python objects and, via the published JSON-LD context, into an RDF graph:
import lokf
bundle = lokf.load_bundle("examples/acme-knowledge")g = bundle.graph() # rdflib.Graph of the whole bundleload_bundle(path: 'str | pathlib.Path') -> 'Bundle'
Section titled “load_bundle(path: 'str | pathlib.Path') -> 'Bundle'”Load a bundle directory into a Bundle.
index.md/log.md are reserved (OKF §3) and not parsed as concepts;
the root index.md frontmatter becomes Bundle.meta.
class Concept
Section titled “class Concept”One concept document: frontmatter data (with body) plus its file.
class Bundle
Section titled “class Bundle”A knowledge bundle: root index.md metadata plus its concepts.
Bundle.resolve(self, ref: 'str') -> 'str'
Section titled “Bundle.resolve(self, ref: 'str') -> 'str'”Resolve a Concept ID or IRI to an absolute Concept IRI.
Bundle.iri(self, concept: 'Concept') -> 'str'
Section titled “Bundle.iri(self, concept: 'Concept') -> 'str'”A concept’s IRI: explicit id or base_iri + Concept ID.
Bundle.by_iri(self) -> 'dict[str, Concept]'
Section titled “Bundle.by_iri(self) -> 'dict[str, Concept]'”IRI -> Concept index (built once, cached).
Bundle.duplicate_iris(self) -> 'list[tuple[str, list[str]]]'
Section titled “Bundle.duplicate_iris(self) -> 'list[tuple[str, list[str]]]'”IRIs that more than one file declares, each with those files’ Concept IDs.
A second file carrying an existing id - a sync client’s conflict
copy, a pasted duplicate, or an explicit id equal to another
file’s path-derived one - validates on its own and then merges into
the first in by_iri and in the projected graph, where nothing
can tell two subjects were meant. Only the loaded bundle, before that
merge, can see it; the schema validates concepts one at a time.
Bundle.get(self, ref: 'str') -> 'Concept | None'
Section titled “Bundle.get(self, ref: 'str') -> 'Concept | None'”Look up a concept by IRI, Concept ID, or bundle-relative path.
Bundle.in_namespace(self, ref: 'str') -> 'bool'
Section titled “Bundle.in_namespace(self, ref: 'str') -> 'bool'”Whether ref names something this bundle is responsible for.
A relative ref (metrics/wau, /glossary/active-user.md) always
is - resolve mints it under base_iri. An absolute IRI is
only if it sits under base_iri; anything else is an external
resource, and a bundle cannot vouch for what it does not contain.
Bundle.dangling_refs(self, schema_path: "'str | pathlib.Path | None'" = None) -> 'list[tuple[str, str, str]]'
Section titled “Bundle.dangling_refs(self, schema_path: "'str | pathlib.Path | None'" = None) -> 'list[tuple[str, str, str]]'”Typed-relation targets in this bundle’s namespace that resolve to no concept.
Covers every slot a Concept class holds concept ids in, single- or
multivalued (isPartOf, dependsOn, about - see
lokf.schema.Vocabulary.reference_slots), plus the target of
each reified entry under the generic relations slot, whose domain
is Relation rather than Concept and so is not in that set. JSON
Schema cannot express this: a fabricated or stale IRI is a perfectly
valid string.
Only targets satisfying in_namespace are checked. Several
relation slots are documented as taking an external resource -
definedBy is “a resource that formally defines this concept”,
source “a resource from which this concept is derived” - so an
off-site URL there is correct usage, not a broken link, and flagging it
would make the check unusable on any bundle that cites the outside
world.
A target containing whitespace is skipped: some slots (measures)
are documented as accepting a description instead of an IRI, and no
valid IRI or bundle-relative path contains a space.
schema_path should be the schema the caller validated against, so
the relation vocabulary matches an explicit --schema; the default
resolves independently and may disagree with one.
Returns (concept_id, slot, target) per unresolved target.
Bundle.docs(self) -> 'list[dict]'
Section titled “Bundle.docs(self) -> 'list[dict]'”Each concept’s frontmatter with its IRI injected as id.
Bundle.to_jsonld(self, context: 'dict | None' = None) -> 'list[dict]'
Section titled “Bundle.to_jsonld(self, context: 'dict | None' = None) -> 'list[dict]'”Each concept’s frontmatter as a JSON-LD document (context attached).
Bundle.graph(self, context: 'dict | None' = None, schema: 'str | pathlib.Path | None' = None)
Section titled “Bundle.graph(self, context: 'dict | None' = None, schema: 'str | pathlib.Path | None' = None)”The whole bundle as one rdflib.Graph, under the domain
schema when one is given.
All concepts are parsed in a single pass (one @graph document) so
the JSON-LD context is compiled once, not once per concept.
lokf.schema
Section titled “lokf.schema”Programmatic access to the LOKF LinkML schema and its vocabulary.
The schema (lokf.yaml) and the published JSON-LD context are the single
source of truth for LOKF’s types, typed-relation slots, and relation
vocabulary. This module reads them so tools never hardcode the vocabulary.
Both files are resolved in this order: an explicit path argument, a walk
up from the current directory for a repo checkout (so local edits always
win), then the copies packaged under lokf/data/ (regenerated by
lokf-build) so installed wheels work outside a checkout.
schema_path(path: 'str | pathlib.Path | None' = None) -> 'pathlib.Path'
Section titled “schema_path(path: 'str | pathlib.Path | None' = None) -> 'pathlib.Path'”Resolve the LOKF schema file (lokf.yaml) to a filesystem path.
Resolution order (same as every other reader here): an explicit path, a
lokf.yaml found in the current directory or an ancestor, the the copy
shipped inside the installed lokf package - so a bundle can be
validated without a schema file of its own.
project_schema(start: 'str | pathlib.Path | None' = None) -> 'pathlib.Path | None'
Section titled “project_schema(start: 'str | pathlib.Path | None' = None) -> 'pathlib.Path | None'”The domain schema the project declares, or None.
A project names it once, in the pyproject.toml that already pins
lokf, so every command reads the same file without a --schema on
each call:
[tool.lokf]schema = "domain.yaml" # relative to this filestart is the bundle or concept file a command was given; the nearest
pyproject.toml with a [tool.lokf] table, from there upwards,
decides. The schema is the bundle’s project’s, wherever the shell is, and
a bundle reached through a link (knowledge_bundle -> .lokf/knowledge)
is searched where it really lives. Without start, the search runs from
the working directory. A declared file that does not exist raises
FileNotFoundError: validating against core lokf.yaml instead
would pass a bundle the project meant to check more strictly.
domain_schema(explicit: 'str | pathlib.Path | None' = None, start: 'str | pathlib.Path | None' = None) -> 'pathlib.Path | None'
Section titled “domain_schema(explicit: 'str | pathlib.Path | None' = None, start: 'str | pathlib.Path | None' = None) -> 'pathlib.Path | None'”The domain schema a command validates or projects under: explicit
(--schema) when given, else the one the project around start
declares (project_schema), else None for core LOKF.
load_schema(path: 'str | pathlib.Path | None' = None) -> 'dict'
Section titled “load_schema(path: 'str | pathlib.Path | None' = None) -> 'dict'”Return the LOKF LinkML schema as a plain dict (cached per file).
load_context(path: 'str | pathlib.Path | None' = None) -> 'dict'
Section titled “load_context(path: 'str | pathlib.Path | None' = None) -> 'dict'”Return the published JSON-LD @context mapping (cached per file).
authoring_context(context: 'dict') -> 'dict'
Section titled “authoring_context(context: 'dict') -> 'dict'”Return a gen-jsonld-context @context mapping made fit for OKF
frontmatter. lokf-build publishes LOKF’s own context this way, and
schema_context builds a domain schema’s the same way.
schema_context(schema_path: 'str | pathlib.Path | None' = None) -> 'dict'
Section titled “schema_context(schema_path: 'str | pathlib.Path | None' = None) -> 'dict'”Return the @context that projects a bundle under schema_path.
With no schema_path, this is the published context
(load_context). A domain schema that imports: [lokf] gets a
context generated from it, so its classes and slots expand to their own
IRIs rather than to lokf: terms. Generating one needs LinkML (the
build extra).
vocabulary(schema_path: 'str | pathlib.Path | None' = None) -> 'Vocabulary'
Section titled “vocabulary(schema_path: 'str | pathlib.Path | None' = None) -> 'Vocabulary'”Return the Vocabulary of the schema and every schema it
imports (cached per file).
class Relation
Section titled “class Relation”One relationship predicate: a frontmatter name bound to an RDF IRI.
Relation.as_row(self) -> 'dict'
Section titled “Relation.as_row(self) -> 'dict'”A JSON-serializable row (the lokf vocab --json / MCP shape).
class Vocabulary
Section titled “class Vocabulary”The LOKF vocabulary derived from the LinkML schema.
Vocabulary.references(self, type_name: 'str') -> 'set[str]'
Section titled “Vocabulary.references(self, type_name: 'str') -> 'set[str]'”The slots a concept of type_name holds concept ids in. A type no class declares is a plain Concept (SPEC §8), so it gets Concept’s.
Vocabulary.subclasses_of(self, ancestor: 'str') -> 'set[str]'
Section titled “Vocabulary.subclasses_of(self, ancestor: 'str') -> 'set[str]'”Class names descending from ancestor (inclusive), via is_a.
Vocabulary.expand(self, curie: 'str') -> 'str'
Section titled “Vocabulary.expand(self, curie: 'str') -> 'str'”Expand a prefix:local CURIE to a full IRI.
Vocabulary.compact(self, uri: 'str') -> 'str'
Section titled “Vocabulary.compact(self, uri: 'str') -> 'str'”Compact a full IRI to a CURIE where a prefix matches.
Vocabulary.class_docs(self) -> 'list[dict]'
Section titled “Vocabulary.class_docs(self) -> 'list[dict]'”Every non-abstract class, each with its schema description and
aliases. is_type_value marks the subset that is also valid in a
Concept’s type: field (Concept/Agent descendants); the rest are
embedded object shapes (e.g. Parameter, Source, Verification) that
only ever appear nested under another concept’s slots. slot_usage
carries the class’s own narrowing of a slot, and its recommended
fields, which no generated artifact otherwise reports.
Vocabulary.slot_docs(self) -> 'list[dict]'
Section titled “Vocabulary.slot_docs(self) -> 'list[dict]'”Every schema slot that carries a description - the frontmatter field
reference, with each slot’s range, multivalued flag, required and
pattern where set, so a client that cannot run lokf validate can
still check a value. A slot a class redefines locally under its own
attributes: (e.g. Parameter.type, Source.id) is emitted as an
additional row carrying class, so that class’s own description isn’t
shadowed by the generic top-level one; a slot_usage narrowing is
reported on the class row instead.
Vocabulary.enum_values(self, enum_name: 'str') -> 'list[dict]'
Section titled “Vocabulary.enum_values(self, enum_name: 'str') -> 'list[dict]'”A schema enum’s permissible values, each with its meaning (CURIE/IRI),
description, and aliases where the schema gives them. A value with no
explicit meaning falls back to a minted lokf:<value> term, matching
relation_types’ convention - so the same RelationType value reports
the same curie/uri from vocab.relation_types and from here.
Vocabulary.manifest(self) -> 'dict'
Section titled “Vocabulary.manifest(self) -> 'dict'”The full vocabulary as a JSON-serializable manifest - classes, slots,
and the value enums, each carrying the schema’s own descriptions. The
machine-readable reference a downstream tool (an editor’s field help, a
docs site) consumes instead of parsing lokf.yaml or the generated
JSON Schema itself.
lokf.parse
Section titled “lokf.parse”Parse OKF/LOKF concept markdown into JSON-LD-ready dictionaries.
These helpers are the seed of the LOKF toolkit’s parser: parse_concept
splits a concept file into frontmatter + body, and isoify normalizes
YAML-parsed dates to the ISO-8601 Z form used by the committed RDF
projections.
isoify(o, key: 'str | None' = None)
Section titled “isoify(o, key: 'str | None' = None)”Recursively convert datetime/date values to ISO-8601 strings.
+00:00 offsets are normalized to Z so JSON/RDF output matches the
committed examples/*.nt projections byte-for-byte. key is the
frontmatter key the value sits under: a bare date (YAML-typed or quoted)
under a DATETIME_SLOTS key becomes midnight UTC of that day.
parse_concept(path: 'str') -> 'dict'
Section titled “parse_concept(path: 'str') -> 'dict'”Read one concept markdown file into a dict of frontmatter + body.
Raises ValueError with a clear message if the file has no ---
delimited YAML frontmatter (e.g. a plain markdown or reserved file).
lokf.rdf
Section titled “lokf.rdf”Convert LOKF markdown (a single concept or a whole bundle) to RDF.
This is the engine behind lokf convert. A concept file is lifted to
JSON-LD via the published context and parsed with rdflib, so it serializes to
any format rdflib supports.
from lokf import rdf
print(rdf.serialize("examples/acme-knowledge/metrics/weekly-active-users.md"))print(rdf.serialize("examples/acme-knowledge", fmt="nt"))docs_to_graph(docs: 'list[dict]', context: 'dict | None' = None, base: 'str | None' = None, schema: 'str | pathlib.Path | None' = None)
Section titled “docs_to_graph(docs: 'list[dict]', context: 'dict | None' = None, base: 'str | None' = None, schema: 'str | pathlib.Path | None' = None)”Parse a list of concept docs into one rdflib.Graph.
The docs are wrapped in a single @graph JSON-LD document so the
context is compiled once. This is the shared seam behind
lokf.model.Bundle.graph and graph_of.
base (normally the bundle’s base_iri) anchors relative @id
values — concept ids are injected absolute, but OKF v0.2 sources[].id
keys are relative and would otherwise resolve against the local file
system.
schema is a domain schema to project under. Its context replaces the
published one unless context is given, and its classes count as
declared, so a domain type keeps its own class instead of reading as
lokf:Concept.
graph_of(source: 'str | pathlib.Path', context: 'dict | None' = None, schema: 'str | pathlib.Path | None' = None)
Section titled “graph_of(source: 'str | pathlib.Path', context: 'dict | None' = None, schema: 'str | pathlib.Path | None' = None)”RDF graph for a concept file or a bundle directory.
A directory is projected as a whole bundle. A single .md file is
resolved against its enclosing bundle when one exists (so id follows
the bundle’s base_iri); a standalone file falls back to its explicit
id or a file:// IRI derived from its path. schema is as for
docs_to_graph.
serialize(source: 'str | pathlib.Path', fmt: 'str' = 'ttl', context: 'dict | None' = None, schema: 'str | pathlib.Path | None' = None) -> 'str'
Section titled “serialize(source: 'str | pathlib.Path', fmt: 'str' = 'ttl', context: 'dict | None' = None, schema: 'str | pathlib.Path | None' = None) -> 'str'”Serialize a concept file or bundle to RDF text in fmt (default Turtle), under the domain schema when one is given.
lokf.store
Section titled “lokf.store”A pyoxigraph-backed RDF store for querying a LOKF knowledge base.
Load a bundle (or any RDF graph) into an in-memory Oxigraph store and run SPARQL over it — the knowledge-retrieval layer of the toolkit.
from lokf.store import GraphStore
store = GraphStore.from_bundle("examples/acme-knowledge")for row in store.select("SELECT ?s WHERE { ?s a lokf:Metric }"): print(row["s"])query_form(sparql: 'str') -> 'str'
Section titled “query_form(sparql: 'str') -> 'str'”The SPARQL query form: ‘select’, ‘ask’, ‘construct’, or ‘describe’.
Leading comments and PREFIX/BASE lines are skipped. Defaults to ‘select’ when no form keyword is found. Shared by the CLI, server, and MCP so all three classify a query identically.
class GraphStore
Section titled “class GraphStore”An in-memory SPARQL store over a LOKF knowledge base.
prefixes (the schema’s namespace table) are prepended to every query
and update, so lokf:/schema:/prov: and friends work without a
per-query PREFIX block.
GraphStore.query(self, sparql: 'str', **kw)
Section titled “GraphStore.query(self, sparql: 'str', **kw)”Run a SPARQL query with the schema prefixes in scope.
Returns pyoxigraph’s native result: QuerySolutions (SELECT),
QueryBoolean (ASK), or QueryTriples (CONSTRUCT/DESCRIBE).
Federated SERVICE queries are rejected: the engine would issue an
outbound request to a query-controlled host, which is an SSRF vector
for the local lokf serve endpoint. (LOAD/INSERT/DELETE
are already rejected by pyoxigraph’s read-only query.)
GraphStore.select(self, sparql: 'str', **kw) -> 'list[dict]'
Section titled “GraphStore.select(self, sparql: 'str', **kw) -> 'list[dict]'”Run a SELECT and return rows as {var: python value} dicts.
Every selected variable is a key in every row (None when unbound),
so the column set is complete even when a variable is unbound in all
rows; bound bindings are reduced to their lexical value.
GraphStore.ask(self, sparql: 'str', **kw) -> 'bool'
Section titled “GraphStore.ask(self, sparql: 'str', **kw) -> 'bool'”Run an ASK query and return the boolean.
GraphStore.construct(self, sparql: 'str', fmt: 'str' = 'ttl', **kw) -> 'str'
Section titled “GraphStore.construct(self, sparql: 'str', fmt: 'str' = 'ttl', **kw) -> 'str'”Run a CONSTRUCT/DESCRIBE and serialize the resulting graph.
GraphStore.serialize_results(self, sparql: 'str', fmt: 'str' = 'json', **kw) -> 'bytes'
Section titled “GraphStore.serialize_results(self, sparql: 'str', fmt: 'str' = 'json', **kw) -> 'bytes'”Run a SELECT/ASK and serialize results in the SPARQL results format.
GraphStore.rdflib_graph(self, sparql: 'str | None' = None)
Section titled “GraphStore.rdflib_graph(self, sparql: 'str | None' = None)”The store (or a CONSTRUCT/DESCRIBE result) as an rdflib.Graph.
With no query, the whole store is returned. This backs the live graph visualization, which projects the graph to cytoscape elements.
GraphStore.dump(self, fmt=<RdfFormat Turtle>) -> 'bytes'
Section titled “GraphStore.dump(self, fmt=<RdfFormat Turtle>) -> 'bytes'”Serialize the whole store (default graph) as RDF text.
lokf.server
Section titled “lokf.server”A local SPARQL endpoint + live graph visualization for a knowledge base.
lokf serve <bundle> loads the bundle into a ~lokf.store.GraphStore
and publishes it over HTTP so agents (and people) can query and explore it
locally — no external services, entirely offline:
GET|POST /sparql— the SPARQL 1.1 protocol. SELECT/ASK returnapplication/sparql-results+json; CONSTRUCT/DESCRIBE return Turtle.GET /graph.json— cytoscape.js elements for the whole graph.GET /— an interactive cytoscape view with a live SPARQL box.
The server is stdlib-only (http.server); cytoscape.js ships in the package.
build_server(store: 'GraphStore', host: 'str' = '127.0.0.1', port: 'int' = 8000)
Section titled “build_server(store: 'GraphStore', host: 'str' = '127.0.0.1', port: 'int' = 8000)”A ThreadingHTTPServer serving store (start/stop it yourself).
serve(source, host: 'str' = '127.0.0.1', port: 'int' = 8000, schema=None) -> 'None'
Section titled “serve(source, host: 'str' = '127.0.0.1', port: 'int' = 8000, schema=None) -> 'None'”Load source, under the domain schema when one is given, and serve it until interrupted.
lokf.propose
Section titled “lokf.propose”Propose typed relations from the markdown links in concept bodies.
Concept prose often links to other concepts without asserting the
relationship in frontmatter. This module extracts those links
(extract_links), classifies each one against a cue-phrase table
(propose), and can write accepted proposals back into the source
files’ frontmatter without disturbing formatting (apply).
extract_links(concept: 'Concept', bundle: 'Bundle') -> 'list[Link]'
Section titled “extract_links(concept: 'Concept', bundle: 'Bundle') -> 'list[Link]'”Markdown prose links in concept.body (code blocks/spans excluded).
propose(bundle: 'Bundle', vocab: 'Vocabulary | None' = None, concept: 'Concept | None' = None) -> 'list[Proposal]'
Section titled “propose(bundle: 'Bundle', vocab: 'Vocabulary | None' = None, concept: 'Concept | None' = None) -> 'list[Proposal]'”Propose relations for prose links not already asserted in frontmatter.
proposal_row(proposal: 'Proposal', applied_ids: 'set[int] | None' = None) -> 'dict'
Section titled “proposal_row(proposal: 'Proposal', applied_ids: 'set[int] | None' = None) -> 'dict'”A JSON-serializable row for a proposal.
Shared wire shape for lokf propose --json and the MCP
propose_relations tool. When applied_ids is given (apply mode), the
row gains an applied flag; in dry-run mode it is omitted.
apply(proposals: 'list[Proposal]', min_confidence: 'float' = 0.0) -> 'list[Proposal]'
Section titled “apply(proposals: 'list[Proposal]', min_confidence: 'float' = 0.0) -> 'list[Proposal]'”Write accepted proposals into source frontmatter; return those written.
Slot relations append the target IRI under the slot key; non-slot
relations append {predicate, target} to relations. Files are
edited with round-trip YAML so comments, key order, and quoting survive;
duplicates are never written, and everything outside the frontmatter
block (any preamble before the first --- and the whole body after
the second) is preserved byte-for-byte.
class Link
Section titled “class Link”One markdown prose link, resolved (where possible) to a bundle concept.
class Proposal
Section titled “class Proposal”A proposed relation from source to link.target.
lokf.export
Section titled “lokf.export”Graph and search projections of a bundle, derived without rdflib.
The knowledge-graph page and JSON-LD blocks in the docs are built in a CI
environment that has neither rdflib nor the installed lokf package, so
everything here is derived from concept frontmatter and the Vocabulary
alone. to_cytoscape projects typed relations to edges (RDF predicates
from typed relation slots and reified relations entries, never plain
markdown body links); dataset_search_jsonld emits schema.org
Dataset documents for Google Dataset Search.
to_cytoscape(bundle, vocab=None) -> 'dict'
Section titled “to_cytoscape(bundle, vocab=None) -> 'dict'”Bundle as cytoscape.js elements (nodes + typed-relation edges).
Nodes are the bundle’s concepts; edges are RDF predicates drawn from each
concept’s typed relation slots and reified relations entries, keeping
only those whose target resolves to another concept in the bundle.
Named-slot edges match the concept-to-concept subset of
Bundle.graph one-to-one and carry data.reified = False. Reified
relations entries are flattened to direct labeled edges for display
(in the RDF projection they remain reified statements, not direct
concept-to-concept triples) and are marked data.reified = True.
Non-dict relations entries are skipped; validation reports them.
graph_to_cytoscape(graph, vocab=None) -> 'dict'
Section titled “graph_to_cytoscape(graph, vocab=None) -> 'dict'”Any rdflib.Graph as cytoscape.js elements.
Nodes are resources that carry an rdf:type in the graph (labelled by
schema:name/rdfs:label when present, else the local IRI part);
edges are triples whose predicate is a LOKF relation predicate and whose
object is itself a node. This drives the live server’s visualization of a
SPARQL CONSTRUCT result, mirroring to_cytoscape for a store.
dataset_search_jsonld(bundle, vocab=None) -> 'list[dict]'
Section titled “dataset_search_jsonld(bundle, vocab=None) -> 'list[dict]'”schema.org Dataset JSON-LD docs for each Dataset/Table concept.
Shaped for Google Dataset Search; only keys with values are emitted, and
bundle-relative references (url/isPartOf/hasPart) are resolved
to absolute IRIs through Bundle.resolve.
lokf.registry
Section titled “lokf.registry”Cross-bundle federation: a registry of LOKF bundles (meta-lokf).
A registry (lokf-registry.yaml) maps each member bundle’s base_iri to
where its exported artifacts and source documents live. The one load-bearing
operation is Registry.owner — longest-base_iri-prefix routing, the
exact inverse of lokf.model.Bundle.iri minting — so following an IRI
from one bundle into another is pure string math: no network, no shared
database. See SPEC §11.
This module is the offline core (Phase 1): loading/saving the manifest, building an entry from a local bundle, and resolving an IRI to its owning bundle + Concept ID + source URL. Harvesting remote artifacts and traversing a federated store land in later phases.
load_registry(path: 'str | pathlib.Path') -> 'Registry'
Section titled “load_registry(path: 'str | pathlib.Path') -> 'Registry'”Load lokf-registry.yaml with a plain YAML reader (no LinkML).
Tolerant of a hand-edited manifest: a null repos: or null field values
fall back to defaults rather than crashing a later lookup.
entry_for_bundle(bundle_dir: 'str | pathlib.Path', source_base: 'str | None' = None) -> 'RepoEntry'
Section titled “entry_for_bundle(bundle_dir: 'str | pathlib.Path', source_base: 'str | None' = None) -> 'RepoEntry'”Build a RepoEntry from a local bundle directory.
Derives base_iri/title/publisher from the bundle’s index.md,
computes a VoID planning index (triple + per-type counts), and harvests an
id_index of explicit-id IRIs that diverge from base_iri +
Concept ID (so they still route). Raises ValueError if the bundle has
no base_iri or a concept IRI falls outside it and is not indexable.
class RepoEntry
Section titled “class RepoEntry”One member bundle. base_iri is its identity and routing key.
class Resolution
Section titled “class Resolution”The result of resolving an IRI against a registry.
class Registry
Section titled “class Registry”A registry of LOKF bundles, loaded from lokf-registry.yaml.
Registry.owner(self, iri: 'str') -> 'RepoEntry | None'
Section titled “Registry.owner(self, iri: 'str') -> 'RepoEntry | None'”The member bundle that owns iri, or None if external.
Registry.resolve(self, iri: 'str') -> 'Resolution'
Section titled “Registry.resolve(self, iri: 'str') -> 'Resolution'”Resolve iri to its owning bundle, Concept ID, and source URL.
Registry.add(self, entry: 'RepoEntry') -> 'None'
Section titled “Registry.add(self, entry: 'RepoEntry') -> 'None'”Validate and append entry. Raises ValueError on conflict.
A base_iri must be non-empty, end in a path separator (/ or #)
so prefix routing respects segment boundaries (…/team/ never
captures …/team-archive/), stay clear of the vocabulary namespace,
and neither duplicate nor nest with an existing member (either would
make routing ambiguous).
Registry.to_dict(self) -> 'dict'
Section titled “Registry.to_dict(self) -> 'dict'”The manifest as a plain dict for YAML serialization.
Registry.save(self) -> 'None'
Section titled “Registry.save(self) -> 'None'”Write the manifest back to path (block-style YAML).
lokf.agentskills
Section titled “lokf.agentskills”Access and install the Claude Code agent skills bundled with LOKF.
The skills live under lokf/skills/<name>/SKILL.md, each a directory with a
SKILL.md carrying YAML frontmatter (name/description) and a markdown
body of instructions that drive the real lokf CLI. This module locates them
(zip-safe, like lokf.schema), lists their frontmatter, and copies the
tree into a project’s .claude/skills so an agent can use them.
skills_dir() -> 'pathlib.Path'
Section titled “skills_dir() -> 'pathlib.Path'”Return the packaged skills directory (lokf/skills).
list_skills() -> 'list[tuple[str, str]]'
Section titled “list_skills() -> 'list[tuple[str, str]]'”Return (name, description) from each skill’s SKILL.md frontmatter.
install(dest: 'str | pathlib.Path | None' = None) -> 'pathlib.Path'
Section titled “install(dest: 'str | pathlib.Path | None' = None) -> 'pathlib.Path'”Copy the skills tree into dest (default .claude/skills); return it.
Parents are created and same-named skills are overwritten in place.
lokf.mcp_server
Section titled “lokf.mcp_server”An MCP stdio server that exposes the LOKF toolkit to agents.
LOKF is a semantic profile of the Open Knowledge Framework: a knowledge base
is a bundle of markdown concepts (frontmatter + prose) that projects to
RDF and is queryable with SPARQL. The tools here let an agent list and inspect
concepts, run SPARQL, convert markdown to RDF, propose typed relations from
prose links, read the relation vocabulary, and summarize a bundle. Point tools
at a bundle directory (e.g. examples/acme-knowledge) unless noted.
list_concepts(bundle: 'str') -> 'list[dict]'
Section titled “list_concepts(bundle: 'str') -> 'list[dict]'”List every concept as {concept_id, type, title, iri, status, trust_tier}.
bundle is a LOKF bundle directory. A fast index of what the knowledge
base contains — including each concept’s OKF v0.2 lifecycle status and
derived trust tier — use describe_concept for the full record of any one.
describe_concept(bundle: 'str', concept_id: 'str') -> 'dict'
Section titled “describe_concept(bundle: 'str', concept_id: 'str') -> 'dict'”Describe one concept: {iri, type, title, body, frontmatter, turtle}.
concept_id is a bundle-relative id (e.g. metrics/weekly-active-users,
with or without .md). turtle is the concept’s RDF projection.
Returns {error: …} if no concept matches.
sparql_query(bundle: 'str', query: 'str') -> 'dict'
Section titled “sparql_query(bundle: 'str', query: 'str') -> 'dict'”Run a SPARQL query over a bundle’s in-memory graph.
Schema prefixes (lokf/schema/prov/dcterms/skos/foaf/rdf/rdfs/owl/xsd/dcat) are preset, so queries need no PREFIX block. SELECT returns {columns: […], rows: [{…}]}; ASK returns {boolean: bool}; CONSTRUCT/DESCRIBE returns {turtle: ‘…’}. Returns {error: …} on a bad query.
convert(source: 'str', format: 'str' = 'ttl') -> 'dict'
Section titled “convert(source: 'str', format: 'str' = 'ttl') -> 'dict'”Convert LOKF markdown to RDF: {format, rdf}.
source is a concept .md file or a bundle directory; format is one
of ttl | nt | jsonld | xml | n3 | trig. Returns {error: …} for a bad
format.
propose_relations(bundle: 'str', min_confidence: 'float' = 0.0, apply: 'bool' = False) -> 'list[dict]'
Section titled “propose_relations(bundle: 'str', min_confidence: 'float' = 0.0, apply: 'bool' = False) -> 'list[dict]'”Propose typed relations from the markdown links in concept prose.
Each proposal is {source, link_text, target, predicate, curie, confidence,
rationale}. Proposals below min_confidence are dropped. When apply
is True the accepted proposals are written into the source files’
frontmatter and each carries an applied flag.
get_vocabulary(all: 'bool' = False) -> 'dict'
Section titled “get_vocabulary(all: 'bool' = False) -> 'dict'”Return the LOKF vocabulary: {classes, relations} by default.
classes maps class name -> CURIE. relations is a list of
{name, curie, uri, frontmatter_key, description}, where frontmatter_key
is True when the relation can be asserted as a frontmatter slot (else it is
used via a relations: entry).
Pass all=True for the full schema reference instead: every class,
frontmatter slot, and value enum with its own description (see
Vocabulary.manifest()) - the same data as lokf vocab --all --json.
bundle_summary(bundle: 'str') -> 'dict'
Section titled “bundle_summary(bundle: 'str') -> 'dict'”Summarize a bundle for quick orientation.
Returns {concept_count, types, triple_count, relation_edge_count,
trust_tiers, stale_count}: the number of concepts, a per-type count, the
size of the RDF projection, the number of typed-relation edges between
concepts, a per-trust-tier count (OKF v0.2 §5.3), and how many concepts
are past their stale_after date.
main() -> 'None'
Section titled “main() -> 'None'”Run the LOKF MCP server over stdio.
lokf.build
Section titled “lokf.build”Reproduce every generated LOKF artifact from the single source of truth
(lokf.yaml), then assemble the reference bundle, validate it against the
schema, and project it to RDF.
Run from anywhere inside the repository:
uv syncuv run lokf-build # or: just buildOutputs (regenerated in place): lokf.context.jsonld JSON-LD context (+ type->@type, id->@id aliases) lokf.schema.json JSON Schema lokf.shacl.ttl SHACL shapes lokf.owl.ttl OWL ontology lokf.sql relational schema (CREATE TABLE DDL) src/lokf/datamodel.py LinkML dataclass bindings (from lokf.datamodel import …) examples/acme-knowledge.bundle.json assembled bundle (git-ignored) examples/acme-knowledge.nt RDF triples for the whole bundle examples/weekly-active-users.nt RDF triples for one concept
run(cmd, **kw)
Section titled “run(cmd, **kw)”Echo cmd, then run it with subprocess.run(..., check=True).
generate(root: 'pathlib.Path') -> 'None'
Section titled “generate(root: 'pathlib.Path') -> 'None'”Run the four LinkML generators, then publish the authoring context.
assemble(root: 'pathlib.Path') -> 'dict'
Section titled “assemble(root: 'pathlib.Path') -> 'dict'”Assemble all concept files (+ root index.md metadata) into one bundle.
validate(root: 'pathlib.Path') -> 'None'
Section titled “validate(root: 'pathlib.Path') -> 'None'”Validate the assembled bundle against the schema’s KnowledgeBundle root.
to_rdf(root: 'pathlib.Path', bundle: 'dict') -> 'None'
Section titled “to_rdf(root: 'pathlib.Path', bundle: 'dict') -> 'None'”Project the bundle (and its Metric concept) to N-Triples in examples/.
main() -> 'int'
Section titled “main() -> 'int'”Entry point for the lokf-build console script.