Skip to content

Repository files navigation

Composable Model Graph

A TypeScript library ecosystem for building inspectable transformation graphs.

Core shape

input → transforms → output → evaluation → feedback

A model graph makes transformation chains explicit, records intermediate state, evaluates outputs, and can return feedback actions.

What it is

  • typed transforms
  • inspectable graph runs
  • traceable intermediate states
  • evaluation-first outputs
  • optional feedback actions

What it is not

  • not an ML framework
  • not an agent framework
  • not a workflow engine
  • not a harness
  • not LangChain
  • not a graph database

The six concepts

Concept Definition
Transform the thing that maps input to output
Data the thing that flows through transforms
Graph the composition of transforms
Trace recorded intermediate state
Evaluation judgment of output quality
Feedback next action suggested by evaluation

Quick start

pnpm install
pnpm build
pnpm test
import {
  createModelGraph,
  createTransform,
} from "@composable-model-graph/core";

const normalize = createTransform<string, string>({
  id: "normalize",
  name: "Normalize",
  run: (input) => input.trim().toLowerCase(),
});

const tokenize = createTransform<string, string[]>({
  id: "tokenize",
  name: "Tokenize",
  run: (input) => (input === "" ? [] : input.split(/\s+/)),
});

const graph = createModelGraph<string, string[]>({
  id: "tokens",
  name: "Tokenizer",
  transforms: [normalize, tokenize],
});

const run = await graph.run("  Hello   World  ");
console.log(run.output); // ["hello", "world"]
console.log(run.trace); // every intermediate state

The library

The library is a single package: the model graph.

  • @composable-model-graph/coreTransform, ModelGraph, Evaluator, FeedbackResolver, TraceStep, RunContext, GraphRun, and the createTransform / createEvaluator / createFeedbackResolver / createModelGraph / createRunContext factories.

It has no dependencies and depends on no harness package. Domain-specific building blocks (numeric layers, data transforms, evaluator/feedback libraries) are intentionally out of scope — they belong in consumers built on top of these primitives.

Model shapes

1. Linear Transform Model

Input ───▶ Transform ───▶ Output

2. Pipeline Model

Input
  ↓
Transform A
  ↓
State A
  ↓
Transform B
  ↓
State B
  ↓
Transform C
  ↓
Output

3. Evaluated Model

Input
  ↓
Transform Chain
  ↓
Output
  ↓
Evaluator
  ↓
EvaluationResult

4. Feedback Model

Input
  ↓
Transform Chain
  ↓
Output
  ↓
Evaluation
  ↓
Feedback
  ├── accept
  ├── retry
  ├── adjust
  ├── reject
  └── custom

5. Input Data Building Model

Raw Input Data
  ↓
Normalize
  ↓
Data State 1
  ↓
Extract / Pick
  ↓
Data State 2
  ↓
Structure
  ↓
Built Data
  ↓
Evaluate
  ↓
Feedback
  • Data is not the transform.
  • Data flows through transforms.
  • The graph turns raw state into useful state.
  • The trace exposes each intermediate state.
D0 = raw input data
D1 = normalize(D0)
D2 = extract(D1)
D3 = structure(D2)

BuiltData  = D3
Evaluation = E(D3)

Input Data Building is the process of turning raw state into useful state through explicit, inspectable transforms.

See docs/03-input-data-building-model.md.

6. Mathematical Model

Input Vector
  ↓
Dense Layer
  ↓
Activation
  ↓
Hidden Representation
  ↓
Dense Layer
  ↓
Prediction
  ↓
Loss / Error

This is only one illustrative instance of the generic graph. The library does not ship numeric layers; you implement transforms like these in a consumer.

7. Branching Model

Input
  ├──▶ Transform B ──┐
  │                  ├──▶ Merge ──▶ Output
  └──▶ Transform C ──┘

Pass connections to createModelGraph to run transforms as a branch/merge graph instead of a straight line. A transform runs once every transform feeding it has produced output; transforms whose inputs are all ready run together. A transform fed by several others (a merge) receives the list of their outputs, in connection order.

const graph = createModelGraph({
  id: "branch-merge",
  name: "Branch then merge",
  transforms: [start, draftB, draftC, merge],
  connections: [
    { from: "start", to: "draftB" },
    { from: "start", to: "draftC" },
    { from: "draftB", to: "merge" },
    { from: "draftC", to: "merge" },
  ],
});

The graph input goes to the single transform with no incoming connection, and the graph output is the single transform with no outgoing connection (set start / end explicitly when there is more than one). Cycles are not allowed: repeating a run belongs to the layer above the graph.

8. Comparison Model

Input
  ├──▶ Graph A ──▶ Output A ──┐
  │                           ├──▶ Compare / Evaluate
  └──▶ Graph B ──▶ Output B ──┘

9. Lifecycle Model

Raw Run Data
  ↓
Extract Signals
  ↓
Measured State
  ↓
Evaluate
  ↓
Evaluation Result
  ↓
Resolve Feedback
  ↓
Next Action

Composable Model Graph does not define what a "run" is. It only provides the primitives to model a lifecycle. Some systems improve not by generating better outputs directly, but by making the lifecycle visible: what happened, what signals were measured, what violated expectations, and what should update next.

Examples

Runnable examples live in examples/. Build first, then run any example:

pnpm build
pnpm --filter @composable-model-graph/example-05-input-data-building start

See docs/06-examples.md for the full list.

Documentation

Repository layout

composable-model-graph/
  package.json
  pnpm-workspace.yaml
  tsconfig.base.json
  vitest.config.ts
  README.md
  packages/
    core/
  examples/
    01-linear-transform/ ... 08-comparison-model/
  docs/
    00-overview.md ... 06-examples.md

Development

pnpm install      # install dependencies
pnpm build        # build all packages (tsc -b project references)
pnpm test         # run the full vitest suite
pnpm lint         # eslint
pnpm format       # prettier --write

License

MIT

About

A small model primitive for explicit transformations, error computation, and feedback.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages