Skip to content

effect-native/just-prolog

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

just-prolog

TypeScript Prolog runtime for agent workflows.

Designed in the spirit of just-bash: strong DX, safe defaults, predictable behavior in tool-calling loops.

Install

npm install just-prolog

Quick start

import { Prolog } from "just-prolog";

const prolog = new Prolog({
  program: `
    parent(alice, bob).
    parent(bob, carol).
    ancestor(X, Y) :- parent(X, Y).
    ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y).
  `,
});

const result = await prolog.query("ancestor(alice, Who).");
console.log(result.solutions.map((solution) => solution.text.Who));
// ["bob", "carol"]

Programmatic knowledge base API

KnowledgeBase gives typed, composable rule construction.

import { KnowledgeBase } from "just-prolog";

const kb = KnowledgeBase.define({
  parent: 2,
  ancestor: 2,
});

const X = kb.variable("X");
const Y = kb.variable("Y");
const Z = kb.variable("Z");

kb
  .addFact("parent", ["tom", "bob"])
  .addFact("parent", ["tom", "liz"])
  .addRule("ancestor", [X, Y], [["parent", [X, Y]]])
  .addRule("ancestor", [X, Y], [
    ["parent", [X, Z]],
    ["ancestor", [Z, Y]],
  ]);

const result = await kb.query("ancestor", ["tom", kb.variable("Who")]);
console.log(result.solutions.map((solution) => solution.text.Who));
// ["bob", "liz"]

What you get:

  • Predicate-name safety from schema keys.
  • Arity checks at compile-time (literal schemas) and runtime.
  • Built-in term helpers: kb.variable (kb.var shorthand), kb.atom, kb.number, kb.compound, kb.list.

Note: string inputs are atoms. Use kb.variable("Name") for variables.

Schema-first API

createKnowledgeBase provides validator-backed row clients with inferred types.

import { createKnowledgeBase } from "just-prolog";
import { z } from "zod";

const kb = createKnowledgeBase({
  repo: z.object({
    owner: z.string(),
    name: z.string(),
    visibility: z.enum(["public", "private", "internal"]),
  }),
  pullRequest: z.object({
    owner: z.string(),
    repo: z.string(),
    number: z.number().int().positive(),
    state: z.enum(["open", "closed"]),
    headSha: z.string(),
    author: z.string(),
    draft: z.boolean(),
    mergedBy: z.string().nullable(),
    labels: z.array(z.string()),
  }),
});

await kb.repo.create({
  owner: "vercel",
  name: "nextjs",
  visibility: "public",
});

const sameActor = kb.var("Actor");
const selfMerged = await kb.pullRequest.findMany({
  where: {
    author: sameActor,
    mergedBy: sameActor,
  },
});

What you get:

  • Runtime validation from any Standard Schema-compatible validator (zod, etc.).
  • Full TypeScript inference for model clients like kb.repo, kb.pullRequest.
  • Predicate field order inferred from object schema key order.
  • Shared runtime option ({ prolog }) when you need raw query interop.

Safe interpolation tags

Use tagged templates to interpolate dynamic data safely.

import { Prolog, prolog, query, raw } from "just-prolog";

const person = "tom";

const runtime = new Prolog({
  program: prolog`
    parent(${person}, bob).
    parent(${person}, liz).
  `,
});

const descendants = await query(runtime)`parent(${person}, Who)`.all();

const unsafe = "p(a)";
const noExecute = await query("p(a).")`${unsafe}`.all();
const execute = await query("p(a).")`${raw("p(a)")}`.all();

Rules:

  • Strings become atoms and are quoted/escaped when needed.
  • Numbers become number terms.
  • Arrays become Prolog lists.
  • raw(...) is explicit opt-in for unescaped insertion.

API

Core runtime:

  • new Prolog(options) create runtime.
  • consult(program) append clauses.
  • assert(clause) alias for consult.
  • assertz(clause) append one clause.
  • retract(pattern) remove first matching clause.
  • abolish(indicator) remove all clauses for indicator.
  • query(queryText, options) collect solutions.
  • queryFirst(queryText, options) first solution or null.
  • solve(queryText, options) async stream.

Programmatic API:

  • new KnowledgeBase(options) or KnowledgeBase.define(schema, options).
  • addFact(predicate, args).
  • addRule(predicate, headArgs, bodyGoals).
  • query(predicate, args, options).
  • queryFirst(predicate, args, options).
  • solve(predicate, args, options).

Schema-first API:

  • createKnowledgeBase(schema, options).
  • kb.<model>.create(row).
  • kb.<model>.createMany(rows).
  • kb.<model>.findMany({ where, queryOptions }).
  • kb.<model>.findFirst({ where, queryOptions }).
  • kb.<model>.exists({ where, queryOptions }).

Template API:

  • prolog\...`` build escaped Prolog text.
  • query(source)\...`` build + run escaped query templates.
  • raw(source) explicit unescaped interpolation.

Solution object:

  • bindings: structured terms.
  • text: rendered Prolog text.

Language support

  • Facts and rules (:-)
  • Lists ([], [A, B], [H|T])
  • Control operators: ,, ;, ->, !
  • Built-ins: true/0, fail/0, =/2, \=/2, call/1, not/1
  • Dynamic DB: assertz/1, retract/1, abolish/1

Custom predicates (tool calling)

import { Prolog, definePredicate, isAtomTerm } from "just-prolog";

const search = definePredicate("search", 2, async function* ({ args, term }) {
  const q = args[0];
  if (q === undefined || !isAtomTerm(q)) {
    return;
  }

  yield [q, term.atom(`result_for_${q.name}`)];
});

const runtime = new Prolog({ predicates: [search] });
const result = await runtime.query("search(weather, Result).");

Limits and safety

Defaults:

  • maxDepth: 256
  • maxInferences: 100000
  • defaultMaxSolutions: 1000

Tune in PrologOptions.

Performance

Unification/backtracking uses a trail-based binding store for low-allocation rollback.

Benchmarks: bench/query-engine.bench.ts

npm run bench:run

Examples

Real agent-oriented examples live in examples/.

  • examples/agent-task-planning.ts
  • examples/progressive-context-routing.ts
  • examples/task-hierarchy-model.ts

Companion tool package

packages/just-prolog-tool provides an AI SDK-compatible prolog tool builder.

import { createPrologTool } from "just-prolog-tool";

const { tools } = createPrologTool({
  prologOptions: { program: "fact(answer)." },
});

const result = await tools.prolog.execute({ query: "fact(X)." });

See packages/just-prolog-tool/README.md.

Development

npm install
npm run typecheck
npm run test:run
npm run bench:run
npm run build

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • TypeScript 100.0%