agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
109 lines • 5.33 kB
JavaScript
;
/**
* ontology/toSkos — the reverse walk: our map written as a SKOS concept
* scheme in JSON-LD, with what SKOS cannot say in a `footprint:` namespace
* declared in the `@context`, so nothing of ours is lost.
*
* Pattern: one pure function over an `OntologySpec`; the twin of
* `fromSkos.ts · readSkos`, pinned as a round trip
* (`readSkos(toSkos(spec))` ≡ the spec's terms and edges).
* Role: Map. A term becomes a `skos:Concept` (prefLabel = id, definition
* = meaning, altLabel = aliases, `footprint:unit`); `is-a` becomes
* `skos:broader`, `related` becomes `skos:related`, every other
* relation — and any edge carrying a `meaning` — becomes a
* `footprint:edge`; a source becomes a `footprint:Source` node and
* a holding a `footprint:heldBy` entry (source, via, coverage).
* The scheme node carries the version as `owl:versionInfo`.
* Emits: N/A.
*
* NOT serving: the model is still served `serve.ts · ontologyPiece`; this
* is an export for a host that keeps its taxonomy in SKOS (the owner's
* ruling: serving in SKOS vocabulary waits for the bench).
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.toSkos = exports.SKOS_IRI_BASE = void 0;
const skosJsonLd_js_1 = require("./skosJsonLd.js");
/** The base every IRI `toSkos` mints hangs off: `<base><id>` for the scheme, `<base><id>#<term>` for a concept. */
exports.SKOS_IRI_BASE = 'https://footprintjs.dev/ontology/';
const CONTEXT = Object.freeze({
skos: skosJsonLd_js_1.SKOS_NS,
owl: skosJsonLd_js_1.OWL_NS,
footprint: skosJsonLd_js_1.FOOTPRINT_NS,
});
/** Write the map as a SKOS concept scheme in JSON-LD. Pure: same spec, same document. */
function toSkos(spec, options = {}) {
const language = options.language ?? 'en';
const schemeIri = `${exports.SKOS_IRI_BASE}${spec.id}`;
const conceptIri = (id) => `${schemeIri}#${id}`;
const sourceIri = (id) => `${schemeIri}/sources/${id}`;
const edges = spec.edges ?? [];
const nodeIds = Object.keys(spec.nodes).sort();
const literal = (value) => ({
'@value': value,
'@language': language,
});
const concepts = nodeIds.map((id) => {
const node = spec.nodes[id];
const outgoing = edges.filter((e) => e.from === id);
const broader = outgoing
.filter((e) => e.relation === 'is-a')
.map((e) => ({ '@id': conceptIri(e.to) }));
const related = outgoing
.filter((e) => e.relation === 'related')
.map((e) => ({ '@id': conceptIri(e.to) }));
const declared = outgoing.filter(needsFootprintEdge).map((e) => ({
'footprint:to': { '@id': conceptIri(e.to) },
'footprint:relation': e.relation,
...(e.meaning !== undefined && { 'footprint:meaning': e.meaning }),
}));
const heldBy = (node.sources ?? []).map((held) => ({
'footprint:source': { '@id': sourceIri(held.source) },
...(held.via !== undefined && { 'footprint:via': [...held.via] }),
...(held.coverage !== undefined && { 'footprint:coverage': held.coverage }),
}));
return {
'@id': conceptIri(id),
'@type': 'skos:Concept',
'skos:inScheme': { '@id': schemeIri },
'skos:prefLabel': literal(id),
'skos:definition': literal(node.meaning),
...(node.aliases !== undefined &&
node.aliases.length > 0 && { 'skos:altLabel': node.aliases.map(literal) }),
...(broader.length > 0 && { 'skos:broader': broader }),
...(related.length > 0 && { 'skos:related': related }),
...(node.unit !== undefined && { 'footprint:unit': node.unit }),
...(declared.length > 0 && { 'footprint:edge': declared }),
...(heldBy.length > 0 && { 'footprint:heldBy': heldBy }),
};
});
const sources = Object.keys(spec.sources)
.sort()
.map((id) => {
const source = spec.sources[id];
return {
'@id': sourceIri(id),
'@type': 'footprint:Source',
'footprint:meaning': source.meaning,
...(source.coverage !== undefined && { 'footprint:coverage': source.coverage }),
...(source.configured !== undefined && { 'footprint:configured': source.configured }),
...(source.aliases !== undefined &&
source.aliases.length > 0 && { 'footprint:aliases': [...source.aliases] }),
};
});
const topConcepts = nodeIds
.filter((id) => !edges.some((e) => e.from === id && e.relation === 'is-a'))
.map((id) => ({ '@id': conceptIri(id) }));
const scheme = {
'@id': schemeIri,
'@type': 'skos:ConceptScheme',
'owl:versionInfo': spec.version,
...(topConcepts.length > 0 && { 'skos:hasTopConcept': topConcepts }),
};
return { '@context': CONTEXT, '@graph': [scheme, ...concepts, ...sources] };
}
exports.toSkos = toSkos;
/** An edge SKOS cannot carry whole: another relation, or an `is-a`/`related` with a meaning. */
function needsFootprintEdge(edge) {
return (edge.relation !== 'is-a' && edge.relation !== 'related') || edge.meaning !== undefined;
}
//# sourceMappingURL=toSkos.js.map